Skip to content
This repository has been archived by the owner on Aug 23, 2024. It is now read-only.

Basic WinRT API (System Media Transport Controls) usage implementation #171

Merged
merged 2 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .erb/scripts/ElectronRebuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if (
fs.existsSync(nodeModulesPath)
) {
const electronRebuildCmd =
'../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .';
'../node_modules/.bin/electron-rebuild --no-parallel --force --types prod,dev,optional --module-dir .';
const cmd =
process.platform === 'win32'
? electronRebuildCmd.replace(/\//g, '\\')
Expand Down
41 changes: 41 additions & 0 deletions src/components/settings/ConfigPanels/PlayerConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { appendPlaybackFilter, setAudioDeviceId } from '../../../redux/configSli
import { notifyToast } from '../../shared/toast';
import ConfigOption from '../ConfigOption';
import { Server } from '../../../types';
import { isWindows, isWindows10 } from '../../../shared/utils';

const getAudioDevice = async () => {
const devices = await navigator.mediaDevices.enumerateDevices();
Expand Down Expand Up @@ -74,6 +75,9 @@ const PlayerConfig = () => {
const [globalMediaHotkeys, setGlobalMediaHotkeys] = useState(
Boolean(settings.getSync('globalMediaHotkeys'))
);
const [systemMediaTransportControls, setSystemMediaTransportControls] = useState(
Boolean(settings.getSync('systemMediaTransportControls'))
);
const [scrobble, setScrobble] = useState(Boolean(settings.getSync('scrobble')));
const [audioDevices, setAudioDevices] = useState<MediaDeviceInfo[]>();
const audioDevicePickerContainerRef = useRef(null);
Expand Down Expand Up @@ -220,13 +224,50 @@ const PlayerConfig = () => {
setGlobalMediaHotkeys(e);
if (e) {
ipcRenderer.send('enableGlobalHotkeys');

settings.setSync('systemMediaTransportControls', !e);
setSystemMediaTransportControls(!e);
ipcRenderer.send('disableSystemMediaTransportControls');
} else {
ipcRenderer.send('disableGlobalHotkeys');
}
}}
/>
}
/>

{isWindows() && isWindows10() && (
<ConfigOption
name="Windows System Media Transport Controls"
description={
<>
Enable or disable the Windows System Media Transport Controls (play/pause, next,
previous, stop). This will show the Windows Media Popup (Windows 10 only) when
pressing a media key. This feauture will override the Global Media Hotkeys option.
</>
}
option={
<StyledToggle
defaultChecked={systemMediaTransportControls}
checked={systemMediaTransportControls}
onChange={(e: boolean) => {
settings.setSync('systemMediaTransportControls', e);
setSystemMediaTransportControls(e);
if (e) {
ipcRenderer.send('enableSystemMediaTransportControls');

settings.setSync('globalMediaHotkeys', !e);
setGlobalMediaHotkeys(!e);
ipcRenderer.send('disableGlobalHotkeys');
} else {
ipcRenderer.send('disableSystemMediaTransportControls');
}
}}
/>
}
/>
)}

<ConfigOption
name="Scrobble"
description="Send player updates to your server. This is required by servers such as Jellyfin and Navidrome to track play counts and use external services such as Last.fm."
Expand Down
4 changes: 4 additions & 0 deletions src/components/shared/setDefaultSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ const setDefaultSettings = (force: boolean) => {
settings.setSync('globalMediaHotkeys', false);
}

if (force || !settings.hasSync('systemMediaTransportControls')) {
settings.setSync('systemMediaTransportControls', false);
}

if (force || !settings.hasSync('cachePath')) {
settings.setSync('cachePath', path.join(path.dirname(settings.file())));
}
Expand Down
154 changes: 126 additions & 28 deletions src/main.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,14 @@ import playQueueReducer, {
} from './redux/playQueueSlice';
import multiSelectReducer from './redux/multiSelectSlice';
import MenuBuilder from './menu';
import { getCurrentEntryList } from './shared/utils';
import { getCurrentEntryList, isWindows, isWindows10, isMacOS, isLinux } from './shared/utils';
import setDefaultSettings from './components/shared/setDefaultSettings';

settings.configure({
prettify: true,
numSpaces: 2,
});

const isWindows = process.platform === 'win32';
const isWindows10 = os.release().match(/^10\.*/g);
const isMacOS = process.platform === 'darwin';
const isLinux = process.platform === 'linux';

setDefaultSettings(false);

export const store = configureStore({
Expand Down Expand Up @@ -101,7 +96,7 @@ const getAssetPath = (...paths) => {
};

const createWinThumbnailClip = () => {
if (isWindows) {
if (isWindows()) {
// Set the current song image as thumbnail
mainWindow.setThumbnailClip({
x: 15,
Expand Down Expand Up @@ -174,7 +169,7 @@ const previousTrack = () => {
}
};

if (isLinux) {
if (isLinux()) {
const mprisPlayer = Player({
name: 'Sonixd',
identity: 'Sonixd',
Expand Down Expand Up @@ -336,8 +331,106 @@ if (isLinux) {
});
}

if (isWindows() && isWindows10()) {
const windowsMedia = require('@nodert-win10-au/windows.media');
const windowsMediaPlayback = require('@nodert-win10-au/windows.media.playback');
const windowsStorageStreams = require('@nodert-win10-au/windows.storage.streams');
const windowsFoundation = require('@nodert-win10-au/windows.foundation');

const Controls = windowsMediaPlayback.BackgroundMediaPlayer.current.systemMediaTransportControls;

if (settings.getSync('systemMediaTransportControls')) {
Controls.isEnabled = true;
} else {
Controls.isEnabled = false;
}

ipcMain.on('enableSystemMediaTransportControls', () => {
Controls.isEnabled = true;
});

ipcMain.on('disableSystemMediaTransportControls', () => {
Controls.isEnabled = false;
});

Controls.isChannelUpEnabled = false;
Controls.isChannelDownEnabled = false;
Controls.isFastForwardEnabled = false;
Controls.isRewindEnabled = false;
Controls.isRecordEnabled = false;
Controls.isPlayEnabled = true;
Controls.isPauseEnabled = true;
Controls.isStopEnabled = true;
Controls.isNextEnabled = true;
Controls.isPreviousEnabled = true;

Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.closed;
Controls.displayUpdater.type = windowsMedia.MediaPlaybackType.music;

Controls.displayUpdater.musicProperties.title = 'Sonixd';
Controls.displayUpdater.musicProperties.artist = 'No Track Playing';
Controls.displayUpdater.musicProperties.albumTitle = 'No Album Playing';
Controls.displayUpdater.update();

Controls.on('buttonpressed', (sender, eventArgs) => {
switch (eventArgs.button) {
case windowsMedia.SystemMediaTransportControlsButton.play:
play();
Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.playing;
break;
case windowsMedia.SystemMediaTransportControlsButton.pause:
pause();
Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.paused;
break;
case windowsMedia.SystemMediaTransportControlsButton.stop:
stop();
Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.stopped;
break;
case windowsMedia.SystemMediaTransportControlsButton.next:
nextTrack();
break;
case windowsMedia.SystemMediaTransportControlsButton.previous:
previousTrack();
break;
default:
break;
}
});

ipcMain.on('playpause', (_event, arg) => {
if (arg.status === 'PLAYING') {
Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.playing;
} else {
Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.paused;
}
});

ipcMain.on('current-song', (_event, arg) => {
if (Controls.playbackStatus !== windowsMedia.MediaPlaybackStatus.playing) {
Controls.playbackStatus = windowsMedia.MediaPlaybackStatus.playing;
}

Controls.displayUpdater.musicProperties.title = arg.title || 'Unknown Title';
Controls.displayUpdater.musicProperties.artist =
arg.artist?.length !== 0
? arg.artist?.map((artist) => artist.title).join(', ')
: 'Unknown Artist';
Controls.displayUpdater.musicProperties.albumTitle = arg.album || 'Unknown Album';

Controls.displayUpdater.thumbnail = windowsStorageStreams.RandomAccessStreamReference.createFromUri(
new windowsFoundation.Uri(
arg.image.includes('placeholder')
? 'https://raw.githubusercontent.com/jeffvli/sonixd/main/src/img/placeholder.png'
: arg.image
)
);

Controls.displayUpdater.update();
});
}

const createWinThumbarButtons = () => {
if (isWindows) {
if (isWindows()) {
mainWindow.setThumbarButtons([
{
tooltip: 'Previous Track',
Expand Down Expand Up @@ -405,7 +498,7 @@ const createWindow = async () => {
globalShortcut.register('MediaPreviousTrack', () => {
previousTrack();
});
} else {
} else if (!settings.getSync('systemMediaTransportControls')) {
electronLocalshortcut.register(mainWindow, 'MediaStop', () => {
stop();
});
Expand All @@ -424,6 +517,8 @@ const createWindow = async () => {
}

ipcMain.on('enableGlobalHotkeys', () => {
electronLocalshortcut.unregisterAll(mainWindow);

globalShortcut.register('MediaStop', () => {
stop();
});
Expand All @@ -443,21 +538,24 @@ const createWindow = async () => {

ipcMain.on('disableGlobalHotkeys', () => {
globalShortcut.unregisterAll();
electronLocalshortcut.register(mainWindow, 'MediaStop', () => {
stop();
});

electronLocalshortcut.register(mainWindow, 'MediaPlayPause', () => {
playPause();
});
if (!settings.getSync('systemMediaTransportControls')) {
electronLocalshortcut.register(mainWindow, 'MediaStop', () => {
stop();
});

electronLocalshortcut.register(mainWindow, 'MediaNextTrack', () => {
nextTrack();
});
electronLocalshortcut.register(mainWindow, 'MediaPlayPause', () => {
playPause();
});

electronLocalshortcut.register(mainWindow, 'MediaPreviousTrack', () => {
previousTrack();
});
electronLocalshortcut.register(mainWindow, 'MediaNextTrack', () => {
nextTrack();
});

electronLocalshortcut.register(mainWindow, 'MediaPreviousTrack', () => {
previousTrack();
});
}
});

mainWindow.loadURL(`file://${__dirname}/index.html#${settings.getSync('startPage')}`);
Expand Down Expand Up @@ -493,7 +591,7 @@ const createWindow = async () => {
mainWindow.hide();
}

if (isWindows && isWindows10) {
if (isWindows() && isWindows10()) {
mainWindow.setThumbnailClip({
x: 0,
y: 0,
Expand All @@ -504,7 +602,7 @@ const createWindow = async () => {
});

mainWindow.on('restore', () => {
if (isWindows && isWindows10) {
if (isWindows() && isWindows10()) {
createWinThumbnailClip();
}
});
Expand All @@ -516,7 +614,7 @@ const createWindow = async () => {
}
});

if (isWindows) {
if (isWindows()) {
mainWindow.on('resize', () => {
const window = mainWindow.getContentBounds();

Expand All @@ -540,7 +638,7 @@ const createWindow = async () => {
});
}

if (isMacOS) {
if (isMacOS()) {
mainWindow.on('resize', () => {
const window = mainWindow.getContentBounds();

Expand Down Expand Up @@ -592,11 +690,11 @@ const createWindow = async () => {
};

const createTray = () => {
if (isMacOS) {
if (isMacOS()) {
return;
}

tray = isLinux ? new Tray(getAssetPath('icon.png')) : new Tray(getAssetPath('icon.ico'));
tray = isLinux() ? new Tray(getAssetPath('icon.png')) : new Tray(getAssetPath('icon.ico'));
const contextMenu = Menu.buildFromTemplate([
{
label: 'Open main window',
Expand Down
4 changes: 4 additions & 0 deletions src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
},
"license": "GPL-3.0",
"dependencies": {
"@nodert-win10-au/windows.foundation": "^0.4.4",
"@nodert-win10-au/windows.media": "^0.4.4",
"@nodert-win10-au/windows.media.playback": "^0.4.4",
"@nodert-win10-au/windows.storage.streams": "^0.4.4",
"mpris-service": "^2.1.2"
}
}
1 change: 1 addition & 0 deletions src/shared/mockSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const mockSettings = {
theme: 'defaultDark',
showDebugWindow: false,
globalMediaHotkeys: true,
systemMediaTransportControls: false,
cachePath: 'C:\\Users\\jli\\AppData\\Roaming\\Electron',
legacyAuth: false,
volume: 0.93,
Expand Down
17 changes: 17 additions & 0 deletions src/shared/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'fs';
import _ from 'lodash';
import os from 'os';
import path from 'path';
import moment from 'moment';
import arrayMove from 'array-move';
Expand Down Expand Up @@ -611,3 +612,19 @@ export const smoothScroll = (
};
animateScroll();
};

export const isWindows = () => {
return process.platform === 'win32';
};

export const isWindows10 = () => {
return os.release().match(/^10\.*/g);
};

export const isMacOS = () => {
return process.platform === 'darwin';
};

export const isLinux = () => {
return process.platform === 'linux';
};
Loading