├── .gitignore ├── archive ├── node ├── audiodevice ├── icon.icns ├── castingTemplate.png ├── castingTemplate@2x.png ├── not-castingTemplate.png ├── not-castingTemplate@2x.png ├── package.json ├── LICENSE └── main.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /archive/node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/node -------------------------------------------------------------------------------- /archive/audiodevice: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/audiodevice -------------------------------------------------------------------------------- /archive/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/icon.icns -------------------------------------------------------------------------------- /archive/castingTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/castingTemplate.png -------------------------------------------------------------------------------- /archive/castingTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/castingTemplate@2x.png -------------------------------------------------------------------------------- /archive/not-castingTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/not-castingTemplate.png -------------------------------------------------------------------------------- /archive/not-castingTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andresgottlieb/soundcast/HEAD/archive/not-castingTemplate@2x.png -------------------------------------------------------------------------------- /archive/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soundcast", 3 | "version": "1.7.0", 4 | "description": "Cast OSX audio to Chromecast menubar app", 5 | "author": "Andres Gottlieb", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/andresgottlieb/soundcast" 9 | }, 10 | "dependencies": { 11 | "chromecast-osx-audio": "^0.3.0", 12 | "menubar": "2.3.0", 13 | "shelljs": "0.5.3" 14 | }, 15 | "devDependencies": { 16 | "electron-packager": "^5.1.0", 17 | "electron-prebuilt": "0.34.0" 18 | }, 19 | "main": "main.js", 20 | "scripts": { 21 | "start": "electron ." 22 | }, 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Announcement 2 | 3 | I'm no longer developing Soundcast. I strongly recommend a newly available commercial solution that works flawlessly and includes all of the features people were asking for Soundcast: 4 | - Automatic synchronized playback using as many devices as you wish (Chromecast, Apple TV, Airport Express, Bluetooth, iOS, Linux, etc) 5 | - Sends cover artwork for the currently playing song to the TV 6 | - Allows to optionally stream only a specific app's audio (eg. Spotify, Apple Music), so system sounds won't interfere with music 7 | - Allows changing the streaming volume (and even linking it to the system volume) 8 | 9 | You can get it [here](https://goo.gl/VVc725) 10 | -------------------------------------------------------------------------------- /archive/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andrés Gottlieb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /archive/main.js: -------------------------------------------------------------------------------- 1 | //Audiodevice by http://whoshacks.blogspot.com/2009/01/change-audio-devices-via-shell-script.html 2 | 3 | //Settings 4 | const port = '7531'; 5 | const caption = 'Soundcast'; 6 | 7 | //Shell and filesystem dependencies 8 | require('shelljs/global'); 9 | var path = require('path'); 10 | 11 | //Electron dependencies 12 | var menubar = require('menubar'); 13 | var Menu = require('menu'); 14 | var MenuItem = require('menu-item'); 15 | var dialog = require('dialog'); 16 | var mb = menubar({dir: __dirname, icon: 'not-castingTemplate.png'}); 17 | //Pointer to the chromecast-osx-audio process 18 | var chromecast; 19 | //Indicates if the user reset the OSX selected sound adapters 20 | var adapter_reset = false; 21 | 22 | //Stores sound input device in use before starting soundcast 23 | var original_input; 24 | getDevice('input', function(data){ 25 | original_input = data; 26 | }); 27 | //Stores sound output device in use before starting soundcast 28 | var original_output; 29 | getDevice('output', function(data){ 30 | original_output = data; 31 | }); 32 | 33 | //Gets OSX currently selected sound device 34 | function getDevice(which, callback){ 35 | exec(path.join(__dirname,'/audiodevice '+which), {async:true}).stdout.on('data', function(data) { 36 | callback(data.replace(/(\r\n|\n|\r)/gm,"")); 37 | }); 38 | } 39 | //Sets OSX selected sound device 40 | function setDevice(which, what){ 41 | exec(path.join(__dirname,'/audiodevice '+which+' "'+what+'"'), {async:true}); 42 | } 43 | 44 | //Gets available chromecast-osx-audio 45 | function getChromecasts(callback){ 46 | //TODO: This dirty workaround should be fixed after updating chromecast-osx-audio module to break if no chromecast is found 47 | exec(path.join(__dirname,'/node ',__dirname,'/node_modules/chromecast-osx-audio/bin/chromecast.js -j'), function (error, stdout, stderr) { 48 | if(stdout){ 49 | var chromecasts = JSON.parse(stdout); 50 | callback(chromecasts); 51 | } 52 | 53 | }); 54 | } 55 | 56 | //Menubar construction 57 | mb.on('ready', function ready () { 58 | //Menu startup message 59 | menu = new Menu(); 60 | menu.append(new MenuItem({ 61 | label: 'Scanning for Chromecasts...' 62 | })); 63 | mb.tray.setContextMenu(menu); 64 | 65 | //Scan for Chromecasts and populate menus 66 | getChromecasts(function(chromecasts) { 67 | //Reset menu to delete startup message 68 | menu = new Menu(); 69 | castmenu = new Menu(); 70 | console.log("Start"); 71 | for(var i in chromecasts) { 72 | var chromecastProcess; 73 | var chromecast = chromecasts[i]; 74 | console.log('chromecast', chromecast.name); 75 | 76 | var label = chromecast.name; 77 | 78 | if(chromecast.txtRecord.md === 'Chromecast Audio'){ 79 | label += ' ' + String.fromCharCode('0xD83D','0xDD0A'); 80 | } 81 | 82 | castmenu.append(new MenuItem({ 83 | label: label, 84 | //sublabel used to pass selected device's unmodified name (no emoticon) to chromecast.js 85 | sublabel: chromecast.name, 86 | click: function(current){ 87 | console.log(current); 88 | //Disables "Start casting" options 89 | for(var j=0;j