diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index ca59bd0..57e51d0 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -6,6 +6,7 @@ import React, { useState, useCallback, } from 'react'; +import { ipcRenderer } from 'electron'; import settings from 'electron-settings'; import ReactAudioPlayer from 'react-audio-player'; import { Helmet } from 'react-helmet-async'; @@ -469,6 +470,8 @@ const Player = ({ currentEntryList, children }: any, ref: any) => { const handleOnPlay = useCallback( (playerNumber: 1 | 2) => { + ipcRenderer.send('current-song', playQueue.current); + // Don't run this if resuming a song, so we'll use a 30 second check if (playQueue.scrobble) { let fadeAtTime; diff --git a/src/main.dev.js b/src/main.dev.js index c35b5df..8d9dbcf 100644 --- a/src/main.dev.js +++ b/src/main.dev.js @@ -10,9 +10,10 @@ */ import 'core-js/stable'; import 'regenerator-runtime/runtime'; +import Player from 'mpris-service'; import path from 'path'; import settings from 'electron-settings'; -import { app, BrowserWindow, shell, globalShortcut, Menu, Tray } from 'electron'; +import { ipcMain, app, BrowserWindow, shell, globalShortcut, Menu, Tray } from 'electron'; import electronLocalshortcut from 'electron-localshortcut'; import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; @@ -27,9 +28,11 @@ import playQueueReducer, { } from './redux/playQueueSlice'; import multiSelectReducer from './redux/multiSelectSlice'; import MenuBuilder from './menu'; +import { getCurrentEntryList } from './shared/utils'; const isWindows = process.platform === 'win32'; const isMacOS = process.platform === 'darwin'; +const isLinux = process.platform === 'linux'; export const store = configureStore({ reducer: { @@ -98,7 +101,7 @@ const createWinThumbnailClip = () => { const stop = () => { const storeValues = store.getState(); - const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry'; + const currentEntryList = getCurrentEntryList(storeValues.playQueue); if (storeValues.playQueue[currentEntryList].length > 0) { store.dispatch(clearPlayQueue()); @@ -109,7 +112,7 @@ const stop = () => { const playPause = () => { const storeValues = store.getState(); - const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry'; + const currentEntryList = getCurrentEntryList(storeValues.playQueue); if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.player.status === 'PAUSED') { @@ -122,7 +125,8 @@ const playPause = () => { const nextTrack = () => { const storeValues = store.getState(); - const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry'; + const currentEntryList = getCurrentEntryList(storeValues.playQueue); + if (storeValues.playQueue[currentEntryList].length > 0) { store.dispatch(resetPlayer()); store.dispatch(incrementCurrentIndex('usingHotkey')); @@ -132,7 +136,8 @@ const nextTrack = () => { const previousTrack = () => { const storeValues = store.getState(); - const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry'; + const currentEntryList = getCurrentEntryList(storeValues.playQueue); + if (storeValues.playQueue[currentEntryList].length > 0) { store.dispatch(resetPlayer()); store.dispatch(decrementCurrentIndex('usingHotkey')); @@ -141,6 +146,63 @@ const previousTrack = () => { } }; +if (isLinux) { + const mprisPlayer = Player({ + name: 'Sonixd', + identity: 'A full-featured Subsonic API compatible cross-platform desktop client', + supportedUriSchemes: ['file'], + supportedMimeTypes: ['audio/mpeg', 'application/ogg'], + supportedInterfaces: ['player'], + }); + + mprisPlayer.on('quit', () => { + process.exit(); + }); + + mprisPlayer.on('stop', () => { + stop(); + + mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_STOPPED; + }); + + mprisPlayer.on('playpause', () => { + playPause(); + + if (mprisPlayer.playbackStatus !== 'Playing') { + mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PAUSED; + } else { + mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING; + } + }); + + mprisPlayer.on('next', () => { + nextTrack(); + + if (mprisPlayer.playbackStatus !== 'Playing') { + mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING; + } + }); + + mprisPlayer.on('previous', () => { + previousTrack(); + + if (mprisPlayer.playbackStatus !== 'Playing') { + mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING; + } + }); + + ipcMain.on('current-song', (event, arg) => { + mprisPlayer.metadata = { + 'mpris:length': (arg.duration || 0) * 1000 * 1000, + 'mpris:artUrl': arg.image.match('placeholder') ? null : arg.image, + 'xesam:title': arg.title || null, + 'xesam:album': arg.album || null, + 'xesam:artist': [arg.artist || null], + 'xesam:genre': arg.genre || null, + }; + }); +} + const createWinThumbarButtons = () => { if (isWindows) { mainWindow.setThumbarButtons([ @@ -157,7 +219,9 @@ const createWinThumbarButtons = () => { { tooltip: 'Next Track', icon: getAssetPath('skip-next.png'), - click: () => nextTrack(), + click: () => { + nextTrack(); + }, }, ]); diff --git a/yarn.lock b/yarn.lock index 29fd088..acc1f61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11396,7 +11396,7 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.19: +source-map-support@^0.5.11, source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.19: version "0.5.20" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== @@ -11404,14 +11404,6 @@ source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5. buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"