Merge pull request #749 from foonathan/master

Support MPRIS loop and volume change
This commit is contained in:
th-ch
2022-08-21 22:53:29 +02:00
committed by GitHub
4 changed files with 102 additions and 21 deletions

View File

@ -11,6 +11,7 @@ module.exports = (_options) => {
document.addEventListener('apiLoaded', e => { document.addEventListener('apiLoaded', e => {
api = e.detail; api = e.detail;
ipcRenderer.on('changeVolume', (_, toIncrease) => changeVolume(toIncrease)); ipcRenderer.on('changeVolume', (_, toIncrease) => changeVolume(toIncrease));
ipcRenderer.on('setVolume', (_, value) => setVolume(value));
firstRun(); firstRun();
}, { once: true, passive: true }) }, { once: true, passive: true })
}; };
@ -163,26 +164,29 @@ function setupSliderObserver() {
}); });
} }
/** if (toIncrease = false) then volume decrease */ function setVolume(value) {
function changeVolume(toIncrease) { api.setVolume(value);
// Apply volume change if valid
const steps = Number(options.steps || 1);
api.setVolume(toIncrease ?
Math.min(api.getVolume() + steps, 100) :
Math.max(api.getVolume() - steps, 0));
// Save the new volume // Save the new volume
saveVolume(api.getVolume()); saveVolume(value);
// change slider position (important) // change slider position (important)
updateVolumeSlider(); updateVolumeSlider();
// Change tooltips to new value // Change tooltips to new value
setTooltip(options.savedVolume); setTooltip(value);
// Show volume slider // Show volume slider
showVolumeSlider(); showVolumeSlider();
// Show volume HUD // Show volume HUD
showVolumeHud(options.savedVolume); showVolumeHud(value);
}
/** if (toIncrease = false) then volume decrease */
function changeVolume(toIncrease) {
// Apply volume change if valid
const steps = Number(options.steps || 1);
setVolume(toIncrease ?
Math.min(api.getVolume() + steps, 100) :
Math.max(api.getVolume() - steps, 0));
} }
function updateVolumeSlider() { function updateVolumeSlider() {

View File

@ -2,6 +2,7 @@ const mpris = require("mpris-service");
const { ipcMain } = require("electron"); const { ipcMain } = require("electron");
const registerCallback = require("../../providers/song-info"); const registerCallback = require("../../providers/song-info");
const getSongControls = require("../../providers/song-controls"); const getSongControls = require("../../providers/song-controls");
const config = require("../../config");
function setupMPRIS() { function setupMPRIS() {
const player = mpris({ const player = mpris({
@ -19,7 +20,7 @@ function setupMPRIS() {
function registerMPRIS(win) { function registerMPRIS(win) {
const songControls = getSongControls(win); const songControls = getSongControls(win);
const { playPause, next, previous } = songControls; const { playPause, next, previous, volumeMinus10, volumePlus10 } = songControls;
try { try {
const secToMicro = n => Math.round(Number(n) * 1e6); const secToMicro = n => Math.round(Number(n) * 1e6);
const microToSec = n => Math.round(Number(n) / 1e6); const microToSec = n => Math.round(Number(n) / 1e6);
@ -34,6 +35,35 @@ function registerMPRIS(win) {
let currentSeconds = 0; let currentSeconds = 0;
ipcMain.on('timeChanged', (_, t) => currentSeconds = t); ipcMain.on('timeChanged', (_, t) => currentSeconds = t);
let currentLoopStatus = undefined;
let manuallySwitchingStatus = false;
ipcMain.on("repeatChanged", (_, mode) => {
if (manuallySwitchingStatus)
return;
if (mode === "Repeat off")
currentLoopStatus = "None";
else if (mode === "Repeat one")
currentLoopStatus = "Track";
else if (mode === "Repeat all")
currentLoopStatus = "Playlist";
player.loopStatus = currentLoopStatus;
});
player.on("loopStatus", (status) => {
// switchRepeat cycles between states in that order
const switches = ["None", "Playlist", "Track"];
const currentIndex = switches.indexOf(currentLoopStatus);
const targetIndex = switches.indexOf(status);
// Get a delta in the range [0,2]
const delta = (targetIndex - currentIndex + 3) % 3;
manuallySwitchingStatus = true;
songControls.switchRepeat(delta);
manuallySwitchingStatus = false;
})
player.getPosition = () => secToMicro(currentSeconds) player.getPosition = () => secToMicro(currentSeconds)
player.on("raise", () => { player.on("raise", () => {
@ -53,21 +83,45 @@ function registerMPRIS(win) {
playPause() playPause()
} }
}); });
player.on("playpause", () => {
player.playbackStatus = player.playbackStatus === 'Playing' ? "Paused" : "Playing";
playPause();
});
player.on("playpause", playPause);
player.on("next", next); player.on("next", next);
player.on("previous", previous); player.on("previous", previous);
player.on('seek', seekBy); player.on('seek', seekBy);
player.on('position', seekTo); player.on('position', seekTo);
registerCallback(songInfo => { ipcMain.on('volumeChanged', (_, value) => {
if (player) { player.volume = value;
const data = { });
'mpris:length': secToMicro(songInfo.songDuration), player.on('volume', (newVolume) => {
'mpris:artUrl': songInfo.imageSrc, if (config.plugins.isEnabled('precise-volume')) {
'xesam:title': songInfo.title, // With precise volume we can set the volume to the exact value.
'xesam:artist': [songInfo.artist], win.webContents.send('setVolume', newVolume)
} else {
// With keyboard shortcuts we can only change the volume in increments of 10, so round it.
const deltaVolume = Math.round((newVolume - player.volume) / 10);
if (deltaVolume > 0) {
for (let i = 0; i < deltaVolume; i++)
volumePlus10();
} else {
for (let i = 0; i < -deltaVolume; i++)
volumeMinus10();
}
}
});
registerCallback(songInfo => {
if (player) {
const data = {
'mpris:length': secToMicro(songInfo.songDuration),
'mpris:artUrl': songInfo.imageSrc,
'xesam:title': songInfo.title,
'xesam:artist': [songInfo.artist],
'mpris:trackid': '/' 'mpris:trackid': '/'
}; };
if (songInfo.album) data['xesam:album'] = songInfo.album; if (songInfo.album) data['xesam:album'] = songInfo.album;

View File

@ -20,7 +20,10 @@ module.exports = (win) => {
go1sBack: () => pressKey(win, "h", ["shift"]), go1sBack: () => pressKey(win, "h", ["shift"]),
go1sForward: () => pressKey(win, "l", ["shift"]), go1sForward: () => pressKey(win, "l", ["shift"]),
shuffle: () => pressKey(win, "s"), shuffle: () => pressKey(win, "s"),
switchRepeat: () => pressKey(win, "r"), switchRepeat: (n = 1) => {
for (let i = 0; i < n; i++)
pressKey(win, "r");
},
// General // General
volumeMinus10: () => pressKey(win, "-"), volumeMinus10: () => pressKey(win, "-"),
volumePlus10: () => pressKey(win, "="), volumePlus10: () => pressKey(win, "="),

View File

@ -22,6 +22,8 @@ module.exports = () => {
if (config.plugins.isEnabled('tuna-obs') || if (config.plugins.isEnabled('tuna-obs') ||
(is.linux() && config.plugins.isEnabled('shortcuts'))) { (is.linux() && config.plugins.isEnabled('shortcuts'))) {
setupTimeChangeListener(); setupTimeChangeListener();
setupRepeatChangeListener();
setupVolumeChangeListener(apiEvent.detail);
} }
const video = $('video'); const video = $('video');
// name = "dataloaded" and abit later "dataupdated" // name = "dataloaded" and abit later "dataupdated"
@ -63,3 +65,21 @@ function setupTimeChangeListener() {
}); });
progressObserver.observe($('#progress-bar'), { attributeFilter: ["value"] }) progressObserver.observe($('#progress-bar'), { attributeFilter: ["value"] })
} }
function setupRepeatChangeListener() {
const repeatObserver = new MutationObserver(mutations => {
ipcRenderer.send('repeatChanged', mutations[0].target.title);
});
repeatObserver.observe($('#right-controls .repeat'), { attributeFilter: ["title"] });
// Emit the initial value as well; as it's persistent between launches.
ipcRenderer.send('repeatChanged', $('#right-controls .repeat').title);
}
function setupVolumeChangeListener(api) {
$('video').addEventListener('volumechange', (_) => {
ipcRenderer.send('volumeChanged', api.getVolume());
});
// Emit the initial value as well; as it's persistent between launches.
ipcRenderer.send('volumeChanged', api.getVolume());
}