diff --git a/icons/spotify-ico-small-black.png b/icons/spotify-ico-small-black.png new file mode 100755 index 0000000..f521237 Binary files /dev/null and b/icons/spotify-ico-small-black.png differ diff --git a/icons/spotify-ico-small-green.png b/icons/spotify-ico-small-green.png new file mode 100755 index 0000000..7896ca8 Binary files /dev/null and b/icons/spotify-ico-small-green.png differ diff --git a/icons/spotify-ico-small-lime.png b/icons/spotify-ico-small-lime.png new file mode 100755 index 0000000..dc9efa9 Binary files /dev/null and b/icons/spotify-ico-small-lime.png differ diff --git a/icons/spotify-ico-small-old.png b/icons/spotify-ico-small-old.png new file mode 100755 index 0000000..621a7eb Binary files /dev/null and b/icons/spotify-ico-small-old.png differ diff --git a/icons/spotify-ico-small-white.png b/icons/spotify-ico-small-white.png new file mode 100755 index 0000000..74a5141 Binary files /dev/null and b/icons/spotify-ico-small-white.png differ diff --git a/icons/spotify-web-player.png b/icons/spotify.png similarity index 100% rename from icons/spotify-web-player.png rename to icons/spotify.png diff --git a/js/backend/MPRISAndNotifications_service.js b/js/backend/MPRISAndNotifications_service.js index c68aa90..a546b70 100755 --- a/js/backend/MPRISAndNotifications_service.js +++ b/js/backend/MPRISAndNotifications_service.js @@ -2,70 +2,19 @@ * @author Matthew James * MPRIS D-Bus Service */ + +//Always make sure we're running as a proper name! +process.title = 'spotifywebplayer'; + var request = require('request'); var fs = require('fs'); -const interpreter = require('./dbus_interpreter'); + +const DBusInterpeter = require('./dbus_interpreter'); +var interpreter = new DBusInterpeter(process.stdin, process.stdout); + const notifications = require('freedesktop-notifications'); notifications.setUnflood(true); -let notification = notifications.createNotification({timeout: 15e4}); -function setupNotification(info){ - notification.summary = (info.status == 'Playing' ? 'Now Playing' : info.status); - notification.body = info.activeSong.name.replace(/( - .*| \(.*)/i, '') + '\n' + - info.activeSong.album.replace(/( - .*| \(.*)/i, '') + '\n' + - info.activeSong.artists; - notification.icon = info.activeSong.art; -} - -//Always make sure we're running as a proper name! -process.title = 'spotifywebplayer'; -interpreter.handle(process.stdin, { - updateMpris: (info) => { - if (info.status == 'Stopped'){ - player.playbackStatus = info.status; - } else { - player.metadata = { - 'mpris:trackid': player.objectPath('track/' + info.activeSong.id), - 'mpris:length': info.activeSong.length, // In microseconds - 'mpris:artUrl': info.activeSong.art, - 'xesam:title': info.activeSong.name.replace(/(\'| - .*| \(.*)/i, ''), //Remove long track titles - 'xesam:album': info.activeSong.album.replace(/(\'| - .*| \(.*)/i, ''), //Remove long album names - 'xesam:artist': info.activeSong.artists, - 'xesam:url': 'https://play.spotify.com/track/' + info.activeSong.uri - }; - player.playbackStatus = info.status; - player.shuffle = info.shuffle; - player.repeat = info.repeat; - } - }, - notify: (info) => { - var filepath = info.albumCache; - fs.access(filepath, fs.F_OK, (err) => { - if (err){ - fs.mkdir(filepath, (err) => { - if (err) console.log(err); - }); - } - }); - var file = (info.activeSong.art ? filepath + '/' + info.activeSong.album + '.jpeg' : process.cwd() + '/icons/spotify-web-player.png'); - fs.access(file, fs.F_OK, function(err){ - if (err){ - request(info.activeSong.art, {encoding: 'binary'}, function(error, response, body) { - if(error) console.log(error); - fs.writeFile(file, body, 'binary', function (err) { - if (err) return console.log(err); - info.activeSong.art = file; - setupNotification(info); - notification.push(); - }); - }); - } else { - info.activeSong.art = file; - setupNotification(info); - notification.push(); - } - }); - } -}); +let notification = notifications.createNotification({timeout: 2e3}); const Player = require('mpris-service'); const player = Player({ @@ -75,22 +24,83 @@ const player = Player({ supportedMimeTypes: ['application/www-url'], desktopEntry: 'spotifywebplayer' }); -function send(command, args){ - console.log('Sending ' + command + ' event'); - interpreter.send(process.stdout, command, args); + + +function setupNotification(info){ + notification.summary = (info.status == 'Playing' ? 'Now Playing' : info.status); + notification.body = info.track.name.replace(/( - .*| \(.*)/i, '') + '\n' + + info.track.album.replace(/( - .*| \(.*)/i, '') + '\n' + + info.track.artists; + notification.icon = info.track.art; } +let lastURI = ''; +interpreter.on('updateMpris', function(info){ + if (info.status == 'Stopped'){ + player.playbackStatus = info.status; + } else { + if (info.track.uri && lastURI != info.track.uri){ + player.metadata = { + 'mpris:trackid': player.objectPath('track/' + info.track.id), + 'mpris:length': info.track.length, + 'mpris:artUrl': info.track.art, + 'xesam:title': info.track.name.replace(/(\'| - .*| \(.*)/i, ''), //Remove long track titles + 'xesam:album': info.track.album.replace(/(\'| - .*| \(.*)/i, ''), //Remove long album names + 'xesam:artist': info.track.artists, + 'xesam:url': 'https://play.spotify.com/track/' + info.track.uri + }; + lastURI = info.track.uri; + } + if (player.metadata['mpris:length'] != info.track.length) { + player.metadata['mpris:length'] = info.track.length; + } + if (info.track.uri) player.position = info.track.position; + if(player.playbackStatus != info.status) player.playbackStatus = info.status; + if(player.shuffle != info.shuffle) player.shuffle = info.shuffle; + if(player.repeat != info.repeat) player.repeat = info.repeat; + } +}); + +interpreter.on('notify', function(info){ + if (!info.track.uri) return; + var filepath = (info.albumCacheDisabled ? '/tmp' : info.albumCache); + if (!info.albumCacheDisabled) fs.access(filepath, fs.F_OK, (err) => { + if (err){ + fs.mkdir(filepath, (err) => { + if (err) console.log(err); + }); + } + }); + var file = (info.track.art ? filepath + '/' + info.track.album + '.jpeg' : process.cwd() + '/icons/spotify-web-player.png'); + fs.access(file, fs.F_OK, function(err){ + if (err){ + request(info.track.art, {encoding: 'binary'}, function(error, response, body) { + if(error) console.log(error); + fs.writeFile(file, body, 'binary', function (err) { + if (err) return console.log(err); + info.track.art = file; + setupNotification(info); + notification.push(); + }); + }); + } else { + info.track.art = file; + setupNotification(info); + notification.push(); + } + }); +}); -player.on('quit', () => {send('Quit')}); -player.on('raise', () => {send('Raise')}); +player.on('quit', () => {interpreter.send('Quit')}); +player.on('raise', () => {interpreter.send('Raise')}); -player.on('playpause', () => {send('PlayPause')}); -player.on('play', () => {send('Play')}); -player.on('next', () => {send('Next')}); -player.on('previous', () => {send('Previous')}); -player.on('stop', () => {send('Stop')}); -player.on('seek', (Offset) => {send('Seek', {Offset:Offset})}); -player.on('position', (TrackID, Position) => {send('SetPosition', {TrackID:TrackID, Position:Position})}); -player.on('open', (Uri) => {send('OpenUri', {Uri:Uri})}); +player.on('playpause', () => {interpreter.send('PlayPause')}); +player.on('play', () => {interpreter.send('Play')}); +player.on('next', () => {interpreter.send('Next')}); +player.on('previous', () => {interpreter.send('Previous')}); +player.on('stop', () => {interpreter.send('Stop')}); +player.on('seek', (Offset) => {interpreter.send('Seek', {Offset:Offset})}); +player.on('position', (TrackID, Position) => {interpreter.send('SetPosition', {TrackID:TrackID, Position:Position})}); +player.on('open', (Uri) => {interpreter.send('OpenUri', {Uri:Uri})}); //Make sure we stop when we get disconnected from the main process process.on('disconnect', function(){ diff --git a/js/backend/MediaKeys_service.js b/js/backend/MediaKeys_service.js index 47dc91f..6872bc0 100644 --- a/js/backend/MediaKeys_service.js +++ b/js/backend/MediaKeys_service.js @@ -1,8 +1,13 @@ -const interpreter = require('./dbus_interpreter'); -function send(command, args){ - console.log('Sending ' + command + ' event'); - interpreter.send(process.stdout, command, args); -} +/* + * @author Matthew James + * Media Keys D-Bus Service + */ +//Always make sure we're running as a proper name! +process.title = 'spotifywebplayer'; + +const DBusInterpeter = require('./dbus_interpreter'); +var interpreter = new DBusInterpeter(null, process.stdout); + var DBus = require('dbus'); var dbus = new DBus(); var bus = dbus.getBus('session'); @@ -11,16 +16,16 @@ bus.getInterface('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKey iface.on('MediaPlayerKeyPressed', function (n, value) { switch (value) { case 'Play': - send('PlayPause'); + interpreter.send('PlayPause'); break; case 'Next': - send('Next'); + interpreter.send('Next'); break; case 'Previous': - send('Previous'); + interpreter.send('Previous'); break; case 'Stop': - send('Stop'); + interpreter.send('Stop'); break; } }); diff --git a/js/backend/Preferences/preferences.html b/js/backend/Preferences/preferences.html index e69b4d0..f10e845 100755 --- a/js/backend/Preferences/preferences.html +++ b/js/backend/Preferences/preferences.html @@ -146,7 +146,15 @@

Theme

-

Show Tray Icon

+

Tray

+ + + + + + + +

Enabled

Should Spotify Web Player show a tray icon? @@ -155,6 +163,21 @@

Show Tray Icon

+ + +

Icon

+ Mix-&-Match the tray icon to your DE/Icon style + + + + +

Close To Tray

@@ -169,7 +192,7 @@

Close To Tray

Close To Controller

- Allows you to keep the window hidden without an indicator or application icon taking up space
Hint: MPRIS controllers have to raise Spotify Web Player for it to become unhidden
+ Allows you to keep the window hidden without an indicator or application icon taking up space
Warning: MPRIS controllers have to raise Spotify Web Player for it to become unhidden
+ + +

Store Album Art?

+ Sometimes you want album art, other times it's just art...
Note: Album Art will still be downloaded temporarily to /tmp for notifications (but deleted on reboot)
+ + + + + + + +

Clean AlbumCache

+ This will remove all album art covers + + + Clean Me + + + + +

Clean LyricCache

+ This will remove all the lyrics you have saved + + + Clean Me + +

Show DevTools

diff --git a/js/backend/Preferences/preload.js b/js/backend/Preferences/preload.js index e42e0fe..0fdfd30 100755 --- a/js/backend/Preferences/preload.js +++ b/js/backend/Preferences/preload.js @@ -1,4 +1,5 @@ global.remote = require('electron').remote; +let fs = require('fs'); let props = remote.getGlobal('props'); var AutoLaunch = require('auto-launch'); let autolaunch = new AutoLaunch({ @@ -13,6 +14,19 @@ global.refresh = () => { windowHook = false; window.location.reload(); } +var deleteFolderRecursive = function(path) { + if( fs.existsSync(path) ) { + fs.readdirSync(path).forEach(function(file,index){ + var curPath = path + "/" + file; + if(fs.lstatSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } +}; document.onreadystatechange = function(){ window.$ = window.jQuery = require('../../preloaded/jquery'); var interface = require('../../preloaded/interface'); @@ -61,17 +75,41 @@ document.onreadystatechange = function(){ props.appSettings.save(); props.mainWindow.webContents.executeJavaScript('appMenu.toggleMenu(' + props.appSettings.ShowApplicationMenu + ')'); }); - + $('input[name*="AlbumCacheDisabled"]').change(function(){ + props.appSettings.AlbumCacheDisabled = $(this).prop('checked'); + props.appSettings.save(); + props.mainWindow.webContents.executeJavaScript('controller.albumCacheDisabled = ' + props.appSettings.AlbumCacheDisabled + ';'); + }); $('input[name*=\'NavBar\'], select').change(() => { - props.mainWindow.webContents.executeJavaScript('interface.load();interface.clean()'); - interface.load(); - interface.clean(); + var i = 0; + var setTheme = setInterval(() => { + if(i > 5) clearInterval(setTheme); + i += 1; + props.mainWindow.webContents.executeJavaScript('interface.load();interface.clean()'); + interface.load(); + interface.clean(); + }, 1500); }); - $('select').change(function(){ + $('select[name=\'Theme\']').change(function(){ props.appSettings.Theme = $(this).val(); props.appSettings.save(); }); - $('select').val(props.appSettings.Theme); + $('select[name=\'TrayIcon\']').change(function(){ + props.appSettings.TrayIcon = $(this).val(); + props.mainWindow.webContents.executeJavaScript('tray.toggleTray(false);tray.toggleTray(true);'); + props.appSettings.save(); + }); + + $('select[name=\'Theme\']').val(props.appSettings.Theme); + $('select[name=\'TrayIcon\']').val(props.appSettings.TrayIcon); + + $('a.clean-album-cache').click(() => { + deleteFolderRecursive(props.albumCache); + }); + + $('a.clean-lyric-cache').click(() => { + deleteFolderRecursive(props.lyricCache); + }); }; diff --git a/js/backend/dbus_implementation.js b/js/backend/dbus_implementation.js index 749c485..02186fe 100755 --- a/js/backend/dbus_implementation.js +++ b/js/backend/dbus_implementation.js @@ -1,57 +1,42 @@ -/* - * @author Matthew James - * D-Bus MPRIS, Notifications and Media Keys Messaging Implementer - */ -var child_process = require('child_process'); -var spawn = child_process.spawn; -var lib_node = process.cwd() + '/libs/node/bin/node'; -const interpreter = require('./dbus_interpreter'); -let MPRISAndNotifications = spawnMPRISAndNotificationService(); -let MediaKeys = spawnMediaKeyService(); +const childProcess = require('child_process'); +const spawn = childProcess.spawn; +const LIBNODE = process.cwd() + '/libs/node/bin/node'; +const DBusInterpreter = require('./dbus_interpreter'); -function spawnMPRISAndNotificationService(){ - var spawned = spawn(lib_node, [__dirname + '/MPRISAndNotifications_service.js']); - spawned.stderr.on('data', (data) => { - console.log('MPRIS & Notification Error: ' + data.toString()) - }); - spawned.stdout.on('data', (data) => { - console.log('MPRIS & Notification service: \n' + data.toString()); - }); - spawned.on('exit', () => { - console.log('MPRIS & Notification service quit!'); - }); - return spawned; -} -function spawnMediaKeyService(){ - var spawned = spawn(lib_node, [__dirname + '/MediaKeys_service.js']); - spawned.stderr.on('data', (data)=>{ - console.log('Media Key Error: ' + data.toString()); - }); - spawned.stdout.on('data', (data) => { - console.log('Media Keys service: ' + data.toString()); - }); - spawned.on('exit', () => { - console.log('Media Keys quit!'); - }); - return spawned; -} +let serviceMediaKeys = spawn(LIBNODE, [__dirname + '/MediaKeys_service.js']); +let serviceMPRISAndNotifications = spawn(LIBNODE, [__dirname + '/MPRISAndNotifications_service.js']); + +serviceMediaKeys.on('exit', () => { + console.log('MediaKeys service quit!'); +}); + +serviceMPRISAndNotifications.stdout.on('data', (data)=>{ + console.log(data.toString()) +}); + +serviceMPRISAndNotifications.stderr.on('data', (data) => { + console.log(data.toString()); +}) + +serviceMPRISAndNotifications.on("error", function(e) { + console.log(e); +}); + +serviceMPRISAndNotifications.on('exit', () => { + console.log('MPRIS & Notification service quit!'); +}); +let MediaKeys = new DBusInterpreter(serviceMediaKeys.stdout, null); +let MPRISAndNotifications = new DBusInterpreter(serviceMPRISAndNotifications.stdout, serviceMPRISAndNotifications.stdin); module.exports = { - instances: { - MPRISAndNotifications: MPRISAndNotifications, - MediaKeys: MediaKeys + killall: () => { + process.kill(serviceMediaKeys.pid); + process.kill(serviceMPRISAndNotifications.pid); }, - interpreter: interpreter, - reload: () => { - MPRISAndNotifications = spawnMPRISAndNotificationService(); - MediaKeys = spawnMediaKeyService(); + services: { + MediaKeys: serviceMediaKeys, + MPRISAndNotifications: serviceMPRISAndNotifications }, - quit: () => { - try { - process.kill(MPRISAndNotifications.pid); - process.kill(MediaKeys.pid); - MPRISAndNotifications = null; - MediaKeys = null; - } catch (e){} - } -}; + MediaKeys: MediaKeys, + MPRISAndNotifications: MPRISAndNotifications, +}; \ No newline at end of file diff --git a/js/backend/dbus_interpreter.js b/js/backend/dbus_interpreter.js index 6fb3f97..96d66d9 100755 --- a/js/backend/dbus_interpreter.js +++ b/js/backend/dbus_interpreter.js @@ -1,70 +1,38 @@ -/* - * @author Matthew James - * DBus Message Interpreter Language - * These are the messages that are 'encoded'/'decoded' by - * the DBus Service & Spotify Web Player so that they can - * communicate over two different processes. - * - * Communication - * The communication is simple, we just need to parse strings. - * These strings are formatted like so: - * message-data {hello:world} - * the key 'message-data' tells us we would like to intepret a - * message sent in/out. The string JSON afterwards tells us the - * actual data being sent. - * - * Data - * The JSON string has a standard format of - * {"command": "doSomething", args: {"key":"test"}} - */ - let events = {}; -const interpreter = { - /* - * Encapsulate an event object into a sendable string for decapsulation - */ - encapsulate: (data) => { - return 'message-data ' + JSON.stringify(data) + '\n'; - }, - /* - * Try to get a message from the data string given - * @params data {String} - */ - decapsulate: (data) => { - var match = data.toString().match(/(?:message\-data) (\{.*\})\n/); - return (match ? JSON.parse(match[1]) : null) - }, - /* - * Setup handlers on a process I/O. - * MUST PASS A STDIN/STDOUT object! - * @params std {object} The STDIN/STDOUT object - * @params handlers {object} The dictionary of event handlers to be handled - */ - handle: (std, handlers) => { - std.on('data', function(data){ - var message = interpreter.decapsulate(data.toString()); - events = handlers; - //Try processing the command/event - if(message && message.command && events.hasOwnProperty(message.command)) { - try{ - events[message.command](message.args); - } catch (e) { - console.log('Handles have not been released! - ' + e); - } - } - }); - }, - clearHandles: function(){ - events = {}; - }, - /* - * Send a message to a process - * MUST PASS A STDIN/STDOUT object! - * @params std {object} The STDIN/STDOUT object - * @params command {string} The event to be sent over and emit - * @params args {object} The dictionary of event handlers arguments to be sent - */ - send: (std, command, args) => { - std.write(interpreter.encapsulate({command: command, args: args})); - } - }; -module.exports = interpreter; +const EventEmitter = require('events'); + +class DBusInterpreter extends EventEmitter { + constructor(stdin, stdout){ + super(); + this.stdin = stdin; + this.stdout = stdout; + if(this.stdin) this.stdin.on('data', (data) => { + var message = this.decapsulate(data.toString()); + if(message) { + //console.log('Emit command: ' + message.command); + this.emit(message.command, message.args); + } + }); + } + /* + * Encapsulate an event object into a sendable string for decapsulation + */ + encapsulate(data) { + return 'message-data ' + JSON.stringify(data) + '\n'; + } + /* + * Try to get a message from the data string given + * @params data {String} + */ + decapsulate(data) { + var match = data.toString().match(/(?:message\-data) (\{.*\})\n/); + return (match ? JSON.parse(match[1]) : null) + } + /* + * Try to send a command message out to STDOUT + */ + send(command, args){ + //console.log('Sending command \'' + command + '\''); + if(this.stdout) this.stdout.write(this.encapsulate({command: command, args: args})); + } +} +module.exports = DBusInterpreter; \ No newline at end of file diff --git a/js/backend/properties.js b/js/backend/properties.js index 82806e7..251da1d 100755 --- a/js/backend/properties.js +++ b/js/backend/properties.js @@ -11,12 +11,13 @@ module.exports = function(electron){ CloseToController: false, ShowApplicationMenu: true, ShowTray: true, + TrayIcon: 'lime', Notifications: { ShowTrackChange: true, ShowPlaybackPlaying: true, ShowPlaybackPaused: true, ShowPlaybackStopped: true, - OnlyWhenFocused: true, + OnlyWhenFocused: true }, NavBar: { Follow: true, @@ -28,6 +29,7 @@ module.exports = function(electron){ Search: true, Sing: true }, + AlbumCacheDisabled: false, Theme: 'dark', StartOnLogin: false, StartHidden: false, @@ -48,7 +50,7 @@ module.exports = function(electron){ process: process, console: console, APP_ICON: __dirname + '/../../icons/spotify-web-player.png', - APP_ICON_SMALL: __dirname + '/../../icons/spotify-ico-small.png', + APP_ICON_DIR: __dirname + '/../../icons', userhome: home, appSettings: appSettings, globalShortcut: electron.globalShortcut, diff --git a/js/preloaded/Sing!/sing.js b/js/preloaded/Sing!/sing.js index 8715bda..cd52ff5 100755 --- a/js/preloaded/Sing!/sing.js +++ b/js/preloaded/Sing!/sing.js @@ -19,7 +19,7 @@ props.fs.readFile(__dirname + '/ui.html', function(err, data){ button = `
  • - + Sing!
  • @@ -38,10 +38,10 @@ props.fs.readFile(__dirname + '/ui.html', function(err, data){ if($('#sing-ui').length == 0){ $('#wrapper').prepend(ui); $('#nav-sing').addClass('active'); - singFuncs.load(controller.getTrackUri(), controller.getTrackName(), controller.getArtist()); + singFuncs.load(controller.track.uri, controller.track.name, controller.track.artists); } else { singFuncs.toggleUI(!singFuncs.isOpen()); - singFuncs.load(controller.getTrackUri(), controller.getTrackName(), controller.getArtist()); + singFuncs.load(controller.track.uri, controller.track.name, controller.track.artists); } }); $('a[id*=\'nav-\']').not('#nav-sing').click(function(){ @@ -118,8 +118,12 @@ const singFuncs = { $('#nav-sing').toggleClass('active'); }); }, - enableButton: () => { - $('#nav-sing').removeClass('disabled'); + toggleButton: (toggle) => { + if (toggle && $('#nav-sing').has('.disabled')){ + $('#nav-sing').removeClass('disabled'); + } else if (!toggle && !$('#nav-sing').has('.disabled')){ + $('#nav-sing').addClass('disabled'); + } }, toggleUI: (toggle) => { if(toggle) { diff --git a/js/preloaded/controller.js b/js/preloaded/controller.js index baeb062..c0dad04 100755 --- a/js/preloaded/controller.js +++ b/js/preloaded/controller.js @@ -2,176 +2,123 @@ * @author Matthew James * Controller for the player */ -function buildArtistsString(artists){ - if(!artists || artists.length == 0) return 'Unknown'; - var str = artists[0]['name']; - //Add any additional artists - for(var i = 1; i < artists.length; i++) str += ", " + artists[i]['name']; - return str; -} -//Global shortcuts assigned -//Must use a function on key value so that controller will be defined later on -//Must be placed up here so that controller can toggle the global shortcuts. -shortcuts = { - 'MediaNextTrack': () => {controller.next()}, - 'MediaPlayPause': () => {controller.playPause()}, - 'MediaPreviousTrack': () => {controller.previous()}, -} +const EventEmitter = require('events'); -const controller = { - information: { - albumCache: props.albumCache, - playlists: [], - repeat: 'None', - shuffle: false, - status: 'Stopped', - activeSong: {id: '', uri: '', name: '', album: '', artists: '', art: '', length: 0}, - update: (notify) => { - var isPlaying = controller.isPlaying() == 'Playing'; - var uri = controller.getTrackUri(); - var activeSong = controller.information.activeSong; - controller.information.status = controller.isPlaying(); - controller.information.shuffle = controller.isShuffled(); - controller.information.repeat = controller.isRepeat(); - controller.toggleGlobalShortcuts(isPlaying); - - if(uri && uri != activeSong.uri){ - controller.getTrackInfo(uri, (data) => { - activeSong.uri = uri; - activeSong.id = data['track_number']; - activeSong.name = data['name']; - activeSong.album = data['album']['name']; - activeSong.artists = buildArtistsString(data['artists']); - activeSong.art = data['album']['images'][1]['url'], - activeSong.length = data['duration_ms'] * 1000; //Length in Microseconds - controller.information._updateMpris(); - activeSong.artists = controller.getArtist(); - - sing.load(uri, activeSong.name, activeSong.artists); - if (notify) controller.information.sendNotification(); - controller.information._updateMpris(); - }); - } else if(uri && uri == activeSong.uri) { - if (notify) controller.information.sendNotification(); - controller.information._updateMpris(); - sing.load(uri, controller.getTrackName(), controller.getArtist()); - // activeSong.name = controller.getTrackName(); - // //Adverts don't have albums! - // activeSong.album = (!uri ? '' : controller.getAlbum()); - // activeSong.artists = controller.getArtist(); - // activeSong.id = 0; - // activeSong.uri = uri; - // activeSong.length = 3e7;//30 seconds for average advert? - // activeSong.art = controller.getAlbumArt(); - // if (notify) controller.information.sendNotification(); - // controller.information._updateMpris(); - } - }, - sendNotification: () => { +class Controller extends EventEmitter { + constructor (player) { + super(); + + var dbus = (process.platform == 'linux' ? require('./../backend/dbus_implementation') : null); + + if(dbus){ + //Attach DBus interaction + dbus.MediaKeys.on('PlayPause', this.playPause); + dbus.MediaKeys.on('Next', this.next); + dbus.MediaKeys.on('Previous', this.previous); + + dbus.MPRISAndNotifications.on('Quit', () => { + this.emit('Quit'); + }); + dbus.MPRISAndNotifications.on('Raise', () => { + this.emit('Raise'); + }); + dbus.MPRISAndNotifications.on('Play', this.play); + dbus.MPRISAndNotifications.on('PlayPause', this.playPause); + dbus.MPRISAndNotifications.on('Previous', this.previous); + dbus.MPRISAndNotifications.on('Next', this.next); + dbus.MPRISAndNotifications.on('Stop', this.stop); + dbus.MPRISAndNotifications.on('openUri', (uri) => {console.log('openUri not implemented (uri = \'' + uri + '\')')}); + } + + var updateMpris = () => { + if(dbus) dbus.MPRISAndNotifications.send('updateMpris', this) + }; + + this.sendNotification = () => { if(dbus) { - dbus.interpreter.send(dbus.instances.MPRISAndNotifications.stdin, "notify", controller.information); - } else { + dbus.MPRISAndNotifications.send("notify", this); //Suport OS X & Windows with other notification systems + } else { new Notification( - (controller.isPlaying() == 'Playing' ? 'Now Playing' : controller.isPlaying()), + (this.status == 'Playing' ? 'Now Playing' : this.status), { - icon: controller.information.activeSong.art, - body: controller.information.activeSong.name + "\n" + - controller.information.activeSong.album + "\n" + - controller.information.activeSong.artists + icon: this.track.art, + body: this.track.name + "\n" + this.track.album + "\n" + this.track.artists } ); } - }, - _updateMpris: () => { - if(dbus) dbus.interpreter.send(dbus.instances.MPRISAndNotifications.stdin, 'updateMpris', controller.information); - }, - }, - getTrackUri: () => { - var trackElement = $('#track-name a[href*="play.spotify.com/track/"]', $('iframe#app-player').contents()).attr('href'); - if (!trackElement) return null; - var uri = trackElement.split('/'); - return uri[uri.length - 1]; - }, - getAlbumArt: () => { - var artElement = $('.sp-image-img', $('#app-player').contents()).css('background-image'); - return (artElement ? artElement.match(/url\((?:\'|\")?(.*)(?:\'|\")?\)/)[1] : null); - }, - getArtist: () => { - return $('#track-artist', $('#app-player').contents()).text(); - }, - getTrackName: () => { - return $('#track-name', $('#app-player').contents()).text(); - }, - getAlbum: () => { - return $('#cover-art a', $('#app-player').contents()).attr('data-tooltip').replace(/( by (.*))/, ''); - }, - getTrackInfo: (uri, callback) => { - $.getJSON("https://api.spotify.com/v1/tracks/" + uri, callback); - }, - play: () => { - if(controller.isPlaying() !== 'Playing') controller.playPause(); - }, - playPause: () => { + }; + this.stopService = () => { + dbus.killall(); + }; + var update = (details) => { + //If we have a current track loaded and the track is different, or if we have never stored a track before, it's a trackChange + var trackChange = (!this.track && details.track) || (details.track && this.track.uri != details.track.uri.replace('spotify:track:', '')); + //If we are not playing and there's no track, we have reached the end of the queue, otherwise we have either paused or we're playing + var currentPlayback = (details.playing ? 'Playing' : (details.track == null ? 'Stopped' : 'Paused')); + var playbackChange = this.status != currentPlayback; + this.status = currentPlayback; + this.shuffled = details.shuffle; + this.repeat = (details.repeat == 0 ? 'None' : (details.repeat == 1 ? 'Playlist' : 'Track')); + if(details.track){ + if(!this.track) this.track = {}; + //If we have a track, update the information for it + this.track.id = details.track.number; + this.track.disc = details.track.disc; + this.track.uri = details.track.uri.replace('spotify:track:', ''); + this.track.name = details.track.name; + this.track.album = details.track.album.name; + this.track.artists = details.track.artistName; + this.track.popularity = details.track.popularity / 100; + this.track.length = details.duration * 1000; //Must be in Microseconds (from milliseconds) + this.track.position = details.position * 1000; + this.track.art = details.track.images[0][1]; //Retrieve the smallest image as our album art + } + updateMpris(); + if(trackChange || playbackChange) this.emit((trackChange ? 'track' : 'playback') + 'Change', this); + }; + + player.contentWindow.addEventListener('message', (e) => { + if(e.data.indexOf('payload') > 0){ + var obj = JSON.parse(e.data); + if (obj.payload.event){ + update(obj.payload.data); + } else if (obj.payload.track){ + update(obj.payload); + } + } + }); + + this.repeat = 'None'; + this.shuffle = false; + this.status = 'Stopped'; + this.track = null; + + this.albumCache = ''; + this.albumCacheDisabled = false; + //How many seconds till the notification expires + this.notificationTimeUntilExpire = 10; + } + play(){ + if(this.status !== 'Playing') this.playPause(); + } + playPause() { $('button#play-pause', $('iframe#app-player').contents()).click(); - controller.information.status = controller.isPlaying(); - }, - pause: () => { - if(controller.isPlaying() == 'Playing') controller.playPause(); - }, - stop: () => { + } + pause(){ + if(this.status == 'Playing') this.playPause(); + } + stop(){ //We will have to pause because we cannot stop in Spotify. - controller.pause() - //Update status information to stopped (make sure we actually stopped though) - controller.information.status = controller.information.isPlaying(); - }, - previous: () => { + this.pause(); + } + previous(){ $('button#previous', $('iframe#app-player').contents()).click(); - }, - next: () => { + } + next(){ $('button#next', $('iframe#app-player').contents()).click(); - }, - /* - * Returns whether controller is playing, paused or stopped - * @returns {String} - */ - isPlaying: () => { - var isPlaying = $('title').text().indexOf('▶') > -1; - var isStopped = $('#controls .playback button#play-pause', $('#app-player').contents()).is('.disabled'); - return (isPlaying ? 'Playing' : (isStopped ? 'Stopped' : 'Paused')) - }, - isShuffled: () => { - return $('#controls .extra button#shuffle', $('#app-player').contents()).is('.active'); - }, - toggleShuffle: (toggle) => { - var toggleButton = $('#controls .extra button#shuffle', $('#app-player').contents()); - $(toggleButton).attr('class', (toggle ? 'active' : '')); - controller.information.shuffle = toggle; - }, - isRepeat: () => { - var isRepeatActive = $('#controls .extra button#repeat', $('#app-player').contents()).is('.active'); - var isRepeatTrackActive = false; //Spotify Web Player don't allow repeating tracks? :( - return (isRepeatActive ? (isRepeatTrackActive ? 'Track' : 'Playlist') : 'None'); - }, - toggleRepeat: (enumStr) => { - var toggleButton = $('#controls .extra button#repeat', $('#app-player').contents()); - switch(enumStr){ - case 'None' && controller.isRepeat(): - //Turn off repeat - $(toggleButton.removeClass('active')); - break; - //Track repeat is not an option on Spotify Web Player :( - case 'Track' && !controller.isRepeat(): - case 'Playlist' && !controller.isRepeat(): - //Turn on repeat - $(toggleButton).addClass('active'); - break; - default: - throw new Error('Unknown repeat enum in controller.toggleRepeat() !'); - }; - controller.information.repeat = controller.isRepeat(); - }, - getPlaylists: () => { + } + getPlaylists(){ //Gets the playlists with the play button element attached (Playlist name : Playlist play button) if ($('#section-collection > div.root').length == 0) return; //Hasn't been loaded yet var playlists = {}; @@ -181,53 +128,6 @@ const controller = { playlists[playlistTitle] = button; }); return playlists; - }, - setupDBusHandlers: () => { - dbus.interpreter.clearHandles(); - if(props.process.platform == 'linux' && dbus){ - if(!dbus.instances.MPRISAndNotifications) dbus.reload(); - dbus.interpreter.handle(dbus.instances.MPRISAndNotifications.stdout, { - Quit: () => {tray.contextMenu.quit.click}, - Raise: () => {props.mainWindow.show();props.mainWindow.focus();}, - Play: controller.play, - PlayPause: controller.playPause, - Previous: controller.previous, - Next: controller.next, - Stop: controller.stop, - openUri: (uri) => {console.log('openUri (MPRIS specification) not implemented.');} - }); - dbus.interpreter.handle(dbus.instances.MediaKeys.stdout, { - PlayPause: controller.playPause, - Next: controller.next, - Previous: controller.previous - }); - } else if (props.process.platform == 'linux'){ - console.err('dbus is undefined') - } - }, - /** - * Allow people to toggle playback and swap music by the media keys - * Will not work with dbus and already-assigned shortcuts. - */ - toggleGlobalShortcuts: (activate) => { - for (var shortcut in shortcuts){ - if (shortcuts.hasOwnProperty(shortcut)){ - if (activate && !props.globalShortcut.isRegistered(shortcut)){ - props.globalShortcut.register(shortcut, shortcuts[shortcut]); - } else if (!activate && props.globalShortcut.isRegistered(shortcut)){ - props.globalShortcut.unregister(shortcut); - } - } - } } -}; -$('button#next, button#previous, button#play-pause', $('iframe#app-player').contents()).click(() => { - var times = 0; - var timer = setInterval(() => { - if (times > 5) return clearInterval(timer); - controller.update(false); - }, 1000); -}) - -controller.setupDBusHandlers(); -module.exports = controller; +} +module.exports = Controller; diff --git a/js/preloaded/facebook-popup-window.js b/js/preloaded/facebook-popup-window.js index 0575097..f8c49db 100755 --- a/js/preloaded/facebook-popup-window.js +++ b/js/preloaded/facebook-popup-window.js @@ -35,6 +35,4 @@ if(window.location.href.indexOf("oauth?") >= 0){ popup.close(); }); } -} - - +} \ No newline at end of file diff --git a/js/preloaded/interface.js b/js/preloaded/interface.js index ece89a4..5b9cc1e 100755 --- a/js/preloaded/interface.js +++ b/js/preloaded/interface.js @@ -3,7 +3,6 @@ * Interface controller that modifies the webpage to create * non-intrusive advertising and optional themes. */ - let advertisement_CSS; let fs = props.fs; let timeout; @@ -79,7 +78,6 @@ let interface = { $('body').prepend(CSS); injectIframe($(ALL_IFRAMES), CSS); } - }, showAdvert: () => { if($('#window_advert').length > 0) $('#window_advert').remove(); @@ -110,15 +108,6 @@ let interface = { $('style.controlbot:not(:first-child)').remove(); } }; -setInterval(() => { - //Will load the most recent prended as :first-child - interface.load(); - //Clean will remove everything that's not :first-child that was inserted before. - interface.clean(); - if($('#modal-notification-area').is(':visible') && $('#modal-notification-area #dialog #indicator').text() == "Can't connect to Spotify. Trying again now..."){ - props.mainWindow.loadURL('about:blank'); - } -}, 5e3); document.addEventListener("visibilitychange", function(){ window_focus = document.visibilityState == "visible"; if(!window_focus){ diff --git a/js/preloaded/main.js b/js/preloaded/main.js index 240bf97..a7dca2d 100755 --- a/js/preloaded/main.js +++ b/js/preloaded/main.js @@ -3,10 +3,6 @@ global.remote = require('electron').remote; let props = remote.getGlobal('props'); global.props = props; -let dbus = remote.getGlobal('dbus'); -global.dbus = dbus; -console.log = props.console.log; - //If the window is a pop-up window if (window.opener){ var popupWindow = remote.getCurrentWindow(); diff --git a/js/preloaded/spotify-player.js b/js/preloaded/spotify-player.js index ab0f003..a976646 100755 --- a/js/preloaded/spotify-player.js +++ b/js/preloaded/spotify-player.js @@ -5,7 +5,7 @@ */ global.user = require('./user'); global.appSettings = props.appSettings; -global.controller = require('./controller'); +global.controller = null; global.tray = require('./tray'); global.interface = require('./interface'); global.sing = require('./Sing!/sing'); @@ -14,83 +14,113 @@ global.appMenu = require('./window-menu'); * Update controls according to login/control status */ function checkControlStatus(){ - var loggedIn = user.isLoggedIn(); - windowHook = loggedIn; - tray.toggleTray(loggedIn); - appMenu.toggleMenu(loggedIn && props.appSettings.ShowApplicationMenu); + var loggedIn = user.isLoggedIn(); + windowHook = loggedIn; + tray.toggleTray(loggedIn && props.appSettings.ShowTray); + appMenu.toggleMenu(loggedIn && props.appSettings.ShowApplicationMenu); } /** * When the window closes, hide only if logged in */ window.onbeforeunload = function(e) { - if (!user.isLoggedIn()){ - props.mainWindow.setApplicationMenu(null); - return true; - } - if(windowHook && ((props.appSettings.CloseToTray && props.appSettings.ShowTray) || props.appSettings.CloseToController)){ - props.mainWindow.hide(); - return false; - } else if (windowHook && props.appSettings.CloseToTray && !props.appSettings.ShowTray){ - props.mainWindow.minimize(); - return false; - } + if(windowHook && ((props.appSettings.CloseToTray && props.appSettings.ShowTray) || props.appSettings.CloseToController)){ + props.mainWindow.hide(); + return false; + } else if (windowHook && props.appSettings.CloseToTray && !props.appSettings.ShowTray){ + props.mainWindow.minimize(); + return false; + } + controller.stopService(); + appMenu.toggleMenu(false); + tray.toggleTray(false); }; setInterval(() => { - if($('#modal-notification-area').is(':visible')) { - tray.toggleTray(false); - if(dbus) dbus.clearHandles(); - appMenu.toggleMenu(false); - windowHook = false; - window.location.reload(); - } -}, 1000); + if($('#modal-notification-area').is(':visible')) { + tray.toggleTray(false); + appMenu.toggleMenu(false); + windowHook = false; + window.location.reload(); + } +}, 10000); interface.load(); /** * Check for message events from Spotify */ window.addEventListener('message', function(event){ - var isFocusWorthy = (props.appSettings.Notifications.OnlyWhenFocused ? !props.mainWindow.isFocused() : true); - //Update our information early when the buttons play_pause, next and previous are pressed - if (event.data.indexOf('track_id') > -1) return controller.information.update(false); - if (event.data.indexOf("application_set_title") > 0) { - var args = JSON.parse(event.data)['args'][0]; - //If there's no song, don't do anything - if(args.indexOf("Spotify Web Player") >= 0) return; - //If there's nothing, we have stopped playback - if(args == "") return controller.information.update(props.appSettings.Notifications.ShowPlaybackStopped && isFocusWorthy); - //An advert from Spotify - if(args.indexOf("http") > 0) { - //An advert from Spotify, let's update manually... - console.log('advert'); - controller.information.status = 'Playing'; - controller.information.activeSong.name = controller.getTrackName(); - controller.information.activeSong.album = ''; - controller.information.activeSong.artists = controller.getArtist(); - controller.information.activeSong.id = 0; - controller.information.activeSong.uri = ''; - controller.informationactiveSong.length = 3e7;//30 seconds for average advert? - controller.informationactiveSong.art = controller.getAlbumArt(); - return controller.information._updateMpris(); - } - //Get our metadata from Spotify as we need a new image and the album name. - tray.toggleMediaButtons(true); - sing.enableButton(); - setTimeout(() => { - //1 second gives us and spotify enough time to 'change' tracks successful so we can show the right information - controller.information.update(props.appSettings.Notifications.ShowTrackChange && isFocusWorthy); - }, 500); - } else if (event.data.indexOf("player_play") > 0){ - controller.information.update(props.appSettings.Notifications.ShowPlaybackPlaying && isFocusWorthy); - } else if (event.data.indexOf("player_pause") > 0){ - //We pressed pause - update the information - controller.information.update(props.appSettings.Notifications.ShowPlaybackPaused && isFocusWorthy); - } else if (event.data.indexOf('USER_ACTIVE') > 0 || event.data.indexOf("spb-connected") > 0){ - checkControlStatus(); - if (props.appSettings.lastURL !== window.location.href){ - props.appSettings.lastURL = window.location.href; - props.appSettings.save(); - } - } else if (event.data.indexOf("user:impression") > 0){ - interface.updateAdvertisements(); - } + if (event.data.indexOf('USER_ACTIVE') > 0 || event.data.indexOf("spb-connected") > 0){ + checkControlStatus(); + if (props.appSettings.lastURL !== window.location.href){ + props.appSettings.lastURL = window.location.href; + props.appSettings.save(); + } + } else if (event.data.indexOf("user:impression") > 0){ + if(event.data.indexOf('player_loaded') > -1) { + var Controller = require('./controller'); + controller = new Controller(document.getElementById('app-player')); + var isFocusWorthy = () => { + return (props.appSettings.Notifications.OnlyWhenFocused ? !props.mainWindow.isFocused() : true); + }; + controller.albumCache = props.albumCache; + controller.albumCacheDisabled = props.appSettings.AlbumCacheDisabled; + controller.on('Quit', () => { + tray.contextMenu.quit.click(); + }); + controller.on('Raise', () => { + props.mainWindow.show(); + props.mainWindow.focus(); + }); + controller.on('trackChange', (controller) => { + sing.load(controller.track.uri, controller.track.name, controller.track.artists); + if(isFocusWorthy() && props.appSettings.Notifications.ShowTrackChange) controller.sendNotification(); + }); + controller.on('playbackChange', (controller) => { + var notificationSwitchTable = { + Playing: props.appSettings.Notifications.ShowPlaybackPlaying, + Paused: props.appSettings.Notifications.ShowPlaybackPaused, + Stopped: props.appSettings.Notifications.ShowPlaybackStopped + }; + if(isFocusWorthy() && notificationSwitchTable[controller.status]) controller.sendNotification(); + sing.toggleButton(controller.status != 'Stopped'); + sing.load(controller.track.uri, controller.track.name, controller.track.artists); + tray.toggleMediaButtons(controller.status != 'Stopped'); + }); + } + interface.updateAdvertisements(); + } +}); +//Check for updates +$.getJSON("https://api.github.com/repos/Quacky2200/Spotify-Web-Player-for-Linux/releases", (data) => { + var updateAvailable = (() => { + var version_update_tag = data[0].tag_name.match(/([0-9\.]+)/)[1].split('.'); + var version_now_tag = props.electron.app.getVersion().match(/([0-9\.]+)/)[1].split('.'); + for(var num in version_update_tag){ + if(parseInt(version_update_tag[num]) > parseInt(version_now_tag[num])) { + return true; + } else if (parseInt(version_update_tag[num]) < parseInt(version_now_tag[num])){ + return false + } + } + })(); + if(updateAvailable){ + props.fs.readFile(__dirname + '/update.png', {encoding: 'base64'}, (err, imgdata)=>{ + if (err) console.err(err); + button = ` +
  • + + + Update + +
  • + + `; + $('#main-nav #nav-items').append(button); + $('#nav-update').click(function(){ + props.electron.shell.openExternal($(this).attr('data-href')); + }); + }); + } }); diff --git a/js/preloaded/tray.js b/js/preloaded/tray.js index 4e7c340..8009ace 100755 --- a/js/preloaded/tray.js +++ b/js/preloaded/tray.js @@ -7,9 +7,15 @@ const Menu = props.electron.Menu; const tray = { appIcon: null, contextMenu: { - togglePlayback: {label: "Play/Pause", enabled: false, click: controller.playPause}, - previous: {label: "Previous", enabled: false, click: controller.previous}, - next: {label: "Next", enabled: false, click: controller.next}, + togglePlayback: {label: "Play/Pause", enabled: false, click: () => { + controller.playPause() + }}, + previous: {label: "Previous", enabled: false, click: () => { + controller.previous() + }}, + next: {label: "Next", enabled: false, click: () => { + controller.next() + }}, toggleSpotifyAppearance: {label: "Hide Spotify", click: function(){ if (props.mainWindow.isVisible()){ props.mainWindow.hide(); @@ -40,7 +46,7 @@ const tray = { }, toggleTray: function(toggle){ if (toggle && props.appSettings.ShowTray){ - if (!tray.appIcon) tray.appIcon = new Tray(props.APP_ICON_SMALL); + if (!tray.appIcon) tray.appIcon = new Tray(props.APP_ICON_DIR + '/spotify-ico-small-' + props.appSettings.TrayIcon + '.png'); tray.appIcon.setContextMenu(Menu.buildFromTemplate([ tray.contextMenu.togglePlayback, tray.contextMenu.previous, diff --git a/js/preloaded/update.png b/js/preloaded/update.png new file mode 100644 index 0000000..49703ec Binary files /dev/null and b/js/preloaded/update.png differ diff --git a/js/preloaded/window-menu.js b/js/preloaded/window-menu.js index 3c3c74d..398070d 100755 --- a/js/preloaded/window-menu.js +++ b/js/preloaded/window-menu.js @@ -1,73 +1,81 @@ -const Menu = props.electron.Menu; -var template = [ - { - label: 'File', - submenu: [ - { - label: 'Search', - accelerator: 'CmdOrCtrl+S', - visible: false, - click: () => { - $('#suggest-area').toggleClass('show'); - $($('.form-control'), $('iframe#suggest').contents()).click(); - } - }, - { - label: 'Logout', - click: () => { - user.logout(); - } - }, - { - label: 'Quit', - accelerator: 'CmdOrCtrl+Q', - click: () => { - tray.toggleTray(false); - windowHook = false; - props.electron.app.quit(); - props.process.exit(0); - } - } - ] - }, - { - label: 'View', - submenu: [ - { - label: 'Toggle Full Screen', - accelerator: (process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11'), - click: () => {props.mainWindow.setFullScreen(!props.mainWindow.isFullScreen())} - }, - { - label: 'Preferences', - click: () => {props.preferencesWindow.show();props.preferencesWindow.focus()} - } - ] - }, - { - label: 'Controls', - submenu: [ - {label: 'Play/Pause', accelerator: 'MediaPlayPause', click: controller.playPause}, - {label: 'Next', accelerator: 'MediaNextTrack', click: controller.next}, - {label: 'Previous', accelerator: 'MediaPreviousTrack', click: controller.previous} - ] - }, - { - label: 'Help', - role: 'help', - submenu: [{label: 'About', click: () => {props.aboutWindow.show();props.aboutWindow.focus();}}] - } -]; -let _menu; -module.exports = { - toggleMenu: (toggle) => { - if(toggle && !_menu){ - _menu = Menu.buildFromTemplate(template); - props.electron.app.setApplicationMenu(_menu); - } else if(!toggle && _menu){ - props.electron.app.setApplicationMenu(null); - _menu = null; - } - } +function toggleMenu(toggle){ + let Menu = props.electron.Menu; + if(toggle && !toggleMenu.menu){ + toggleMenu.menu = Menu.buildFromTemplate([ + { + label: 'File', + submenu: [ + { + label: 'Search', + accelerator: 'CmdOrCtrl+S', + visible: false, + click: () => { + $('#suggest-area').toggleClass('show'); + $($('.form-control'), $('iframe#suggest').contents()).click(); + } + }, + { + label: 'Logout', + click: () => { + user.logout(); + } + }, + { + label: 'Quit', + accelerator: 'CmdOrCtrl+Q', + click: () => { + tray.toggleTray(false); + windowHook = false; + props.electron.app.quit(); + props.process.exit(0); + } + } + ] + }, + { + label: 'View', + submenu: [ + { + label: 'Toggle Full Screen', + accelerator: (process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11'), + click: () => {props.mainWindow.setFullScreen(!props.mainWindow.isFullScreen())} + }, + { + label: 'Preferences', + click: () => {props.preferencesWindow.show();props.preferencesWindow.focus()} + } + ] + }, + { + label: 'Controls', + submenu: [ + {label: 'Play/Pause', accelerator: 'MediaPlayPause', click: () => { + controller.playPause() + }}, + {label: 'Next', accelerator: 'MediaNextTrack', click: () => { + controller.next() + }}, + {label: 'Previous', accelerator: 'MediaPreviousTrack', click: () => { + controller.previous() + }} + ] + }, + { + label: 'Help', + role: 'help', + submenu: [{ + label: 'About', + click: () => { + props.aboutWindow.show(); + props.aboutWindow.focus(); + } + }] + } + ]); + props.electron.app.setApplicationMenu(toggleMenu.menu); + } else if(!toggle && toggleMenu.menu){ + props.electron.app.setApplicationMenu(null); + toggleMenu.menu = null; + } } - \ No newline at end of file +module.exports = {toggleMenu:toggleMenu}; \ No newline at end of file diff --git a/main.js b/main.js index 1ffbe85..0e9df2e 100755 --- a/main.js +++ b/main.js @@ -4,8 +4,7 @@ */ const electron = require('electron'); const app = electron.app; -let dbus = (process.platform == 'linux' ? require('./js/backend/dbus_implementation') : null); -global.dbus = dbus; + let props = require('./js/backend/properties')(electron); global.props = props; var plugins = require('./js/backend/plugins')(app); @@ -26,13 +25,6 @@ app.on('ready', function(){ showDevToolsOnFocus(props.preferencesWindow); showDevToolsOnFocus(props.aboutWindow); props.mainWindow.on('closed', () => { - if(dbus) { - try{ - dbus.quit(); - } catch (e){ - console.log(e); - } - } app.quit(); process.exit(0); }); diff --git a/make_deb.sh b/make_deb.sh index db20cdc..f25ffe9 100755 --- a/make_deb.sh +++ b/make_deb.sh @@ -2,12 +2,10 @@ bits=$1 DIR="$(dirname $(readlink -f $0))" echo "Trying to build .deb for $bits" -node_download_link="" -electron_download_link="" debarch="" flasharch="" app="spotifywebplayer" -version="0.9.5-1" +version="1.0.0" author="Matthew James" email="Quacky2200@hotmail.com" app_path="/usr/bin/$app" @@ -20,16 +18,15 @@ app_share_execs="$app_share/applications" description="Music for every moment. Spotify is a digital music service that gives you access to millions of songs. (unofficial client - see https://github.com/Quacky2200/Spotify-Web-Player-for-Linux for details)" launcher="[Desktop Entry] Name=Spotify Web Player -Comment= +Comment=$description Version=$version Exec=bash /usr/bin/$app/$app Path=/usr/bin/$app -Icon=spotify-web-player +Icon=spotify Categories=GNOME;GTK;AudioVideo;Audio;Player; Actions=PlayPause;Next;Previous; Type=Application Terminal=false -Description=$description [Desktop Action PlayPause] Name=Play/Pause @@ -47,15 +44,11 @@ Exec=dbus-send --print-reply --session --dest=org.mpris.MediaPlayer2.$app /org/m case $bits in "86"|"x86"|"32"|"ia32"|"i386"|"i686") echo "32Bit/x86 architecture recognised" - node_download_link="https://nodejs.org/dist/v6.5.0/node-v6.5.0-linux-x86.tar.xz" - electron_download_link="https://github.com/electron/electron/releases/download/v1.3.4/electron-v1.3.4-linux-ia32.zip" debarch="i386" flasharch="ia32" ;; "64"|"x64"|"amd_64"|"x86_64") echo "64Bit/x64 architecture recognised" - node_download_link="https://nodejs.org/dist/v6.5.0/node-v6.5.0-linux-x86.tar.xz" - electron_download_link="https://github.com/electron/electron/releases/download/v1.3.4/electron-v1.3.4-linux-x64.zip" debarch="amd64" flasharch="x64" ;; @@ -72,7 +65,7 @@ echo "\nMake all temporary directories for release" mkdir -p $release_dir/DEBIAN $app_pixmaps $app_bin $app_share_execs echo "\nCopy Spotify icon to pixbufs" -cp $DIR/icons/spotify-web-player.png $app_pixmaps/ +cp $DIR/icons/spotify.png $app_pixmaps/ echo "\nCreate launcher (.desktop) into applications" echo "$launcher" > $app_share_execs/$app.desktop diff --git a/package.json b/package.json index 42106cf..0e740d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "spotifywebplayer", "productName": "Spotify Web Player", - "version": "0.9.5-1", + "version": "1.0.0", "description": "A minimal Electron application", "main": "main.js", "scripts": { @@ -26,6 +26,7 @@ }, "dependencies": { "auto-launch": "^4.0.0", + "dbus": "^0.2.19", "electron-cookies": "^1.1.0", "freedesktop-notifications": "^1.2.2", "mpris-service": "^1.1.1", diff --git a/spotifywebplayer b/spotifywebplayer index bccc184..3030554 100755 --- a/spotifywebplayer +++ b/spotifywebplayer @@ -1,4 +1,6 @@ #!/bin/sh DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -#DIR=$(dirname $0) -$DIR/libs/electron/spotifywebplayer ./ +OLDDIR=$(pwd) +cd $DIR +$DIR/libs/electron/spotifywebplayer $DIR/. +cd $OLDDIR \ No newline at end of file