From ccfe7434bf708ee58156c2952234a049706edfc2 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Wed, 10 Nov 2021 00:53:44 +0200 Subject: [PATCH] fix mpris --- plugins/shortcuts/back.js | 117 ++++++++++++++++++------------- preload.js | 4 ++ providers/song-controls-front.js | 30 ++++++++ providers/song-info-front.js | 10 +++ providers/song-info.js | 2 + 5 files changed, 113 insertions(+), 50 deletions(-) create mode 100644 providers/song-controls-front.js diff --git a/plugins/shortcuts/back.js b/plugins/shortcuts/back.js index c32d0f8d..ddb70c75 100644 --- a/plugins/shortcuts/back.js +++ b/plugins/shortcuts/back.js @@ -1,12 +1,10 @@ -const { globalShortcut } = require("electron"); +const { globalShortcut, ipcMain } = require("electron"); const is = require("electron-is"); const electronLocalshortcut = require("electron-localshortcut"); const getSongControls = require("../../providers/song-controls"); const { setupMPRIS } = require("./mpris"); const registerCallback = require("../../providers/song-info"); -let player; - function _registerGlobalShortcut(webContents, shortcut, action) { globalShortcut.register(shortcut, () => { action(webContents); @@ -31,54 +29,8 @@ function registerShortcuts(win, options) { _registerLocalShortcut(win, "CommandOrControl+F", search); _registerLocalShortcut(win, "CommandOrControl+L", search); - registerCallback(songInfo => { - if (player) { - player.metadata = { - 'mpris:length': songInfo.songDuration * 60 * 1000 * 1000, // In microseconds - 'mpris:artUrl': songInfo.imageSrc, - 'xesam:title': songInfo.title, - 'xesam:artist': songInfo.artist - }; - if (!songInfo.isPaused) { - player.playbackStatus = "Playing" - } - } - } - ) - if (is.linux()) { - try { - const MPRISPlayer = setupMPRIS(); - - MPRISPlayer.on("raise", () => { - win.setSkipTaskbar(false); - win.show(); - }); - MPRISPlayer.on("play", () => { - if (MPRISPlayer.playbackStatus !== 'Playing') { - MPRISPlayer.playbackStatus = 'Playing'; - playPause() - } - }); - MPRISPlayer.on("pause", () => { - if (MPRISPlayer.playbackStatus !== 'Paused') { - MPRISPlayer.playbackStatus = 'Paused'; - playPause() - } - }); - MPRISPlayer.on("next", () => { - next() - }); - MPRISPlayer.on("previous", () => { - previous() - }); - - player = MPRISPlayer - - } catch (e) { - console.warn("Error in MPRIS", e); - } - } + if (is.linux()) registerMPRIS(); const { global, local } = options; const shortcutOptions = { global, local }; @@ -106,6 +58,71 @@ function registerShortcuts(win, options) { } } } + function registerMPRIS() { + try { + const secToMicro = n => Math.round(Number(n) * (1000 * 1000)); + const microToSec = n => Math.round(Number(n) / 1000 / 1000); + + const seekTo = e => win.webContents.send("seekTo", microToSec(e.position)); + const seek = o => win.webContents.send("seek", microToSec(o)); + + const player = setupMPRIS(); + + const mprisSeek = p => { + player.seeked(p); + } + win.webContents.send("registerOnSeek"); + + ipcMain.on('seeked', (_, t) => mprisSeek(secToMicro(t))); + + let currentSeconds = 0; + ipcMain.on('timeChanged', (_, t) => currentSeconds = t); + + player.getPosition = () => secToMicro(currentSeconds) + + player.on("raise", () => { + win.setSkipTaskbar(false); + win.show(); + }); + + player.on("play", () => { + if (player.playbackStatus !== 'Playing') { + player.playbackStatus = 'Playing'; + playPause() + } + }); + player.on("pause", () => { + if (player.playbackStatus !== 'Paused') { + player.playbackStatus = 'Paused'; + playPause() + } + }); + + player.on("playpause", playPause); + player.on("next", next); + player.on("previous", previous); + + player.on('seek', seek); + player.on('position', seekTo); + + registerCallback(songInfo => { + if (player) { + player.metadata = { + 'mpris:length': secToMicro(songInfo.songDuration), + 'mpris:artUrl': songInfo.imageSrc, + 'xesam:title': songInfo.title, + 'xesam:artist': songInfo.artist, + 'mpris:trackid': '/' + };; + mprisSeek(secToMicro(songInfo.elapsedSeconds)) + player.playbackStatus = songInfo.isPaused ? "Paused" : "Playing" + } + }) + + } catch (e) { + console.warn("Error in MPRIS", e); + } + } } module.exports = registerShortcuts; diff --git a/preload.js b/preload.js index 6c7c1b16..daffe887 100644 --- a/preload.js +++ b/preload.js @@ -6,6 +6,7 @@ const config = require("./config"); const { fileExists } = require("./plugins/utils"); const setupFrontLogger = require("./providers/front-logger"); const setupSongInfo = require("./providers/song-info-front"); +const { setupSongControls } = require("./providers/song-controls-front"); const plugins = config.plugins.getEnabled(); @@ -45,6 +46,9 @@ document.addEventListener("DOMContentLoaded", () => { // inject song-info provider setupSongInfo(); + // inject song-controls + setupSongControls(); + // inject front logger setupFrontLogger(); diff --git a/providers/song-controls-front.js b/providers/song-controls-front.js new file mode 100644 index 00000000..bb98ae30 --- /dev/null +++ b/providers/song-controls-front.js @@ -0,0 +1,30 @@ +const { ipcRenderer } = require("electron"); + +module.exports.seekTo = seekTo; +function seekTo(t) { + document.querySelector('video').currentTime = t; +} + +module.exports.seek = seek; +function seek(o) { + document.querySelector('video').currentTime += o; +} + +module.exports.setupSongControls = () => { + ipcRenderer.on("seekTo", async (_, t) => seekTo(t)); + ipcRenderer.on("seek", async (_, t) => seek(t)); + ipcRenderer.once("registerOnSeek", registerOnSeek) +}; + +async function registerOnSeek() { + const register = v => v.addEventListener('seeked', () => ipcRenderer.send('seeked', v.currentTime)); + let video = document.querySelector('video'); + if (video) { + register(video); + } + else { + document.addEventListener('apiLoaded', () => { + register(document.querySelector('video')) + }, { once: true, passive: true }) + } +} diff --git a/providers/song-info-front.js b/providers/song-info-front.js index 4ff6041d..6e14dd7a 100644 --- a/providers/song-info-front.js +++ b/providers/song-info-front.js @@ -11,9 +11,19 @@ ipcRenderer.on("update-song-info", async (_, extractedSongInfo) => { module.exports = () => { document.addEventListener('apiLoaded', e => { + setupTimeChangeListener(); + document.querySelector('video').addEventListener('loadedmetadata', () => { const data = e.detail.getPlayerResponse(); ipcRenderer.send("song-info-request", JSON.stringify(data)); }); }, { once: true, passive: true }) }; + +function setupTimeChangeListener() { + const progressObserver = new MutationObserver(mutations => { + ipcRenderer.send('timeChanged', mutations[0].target.value); + global.songInfo.elapsedSeconds = mutations[0].target.value; + }); + progressObserver.observe(document.querySelector('#progress-bar'), { attributeFilter: ["value"] }) +} diff --git a/providers/song-info.js b/providers/song-info.js index 37cd55fa..ebd1d6c4 100644 --- a/providers/song-info.js +++ b/providers/song-info.js @@ -45,6 +45,7 @@ const songInfo = { songDuration: 0, elapsedSeconds: 0, url: "", + videoId: "", }; const handleData = async (responseText, win) => { @@ -57,6 +58,7 @@ const handleData = async (responseText, win) => { songInfo.image = await getImage(songInfo.imageSrc); songInfo.uploadDate = data?.microformat?.microformatDataRenderer?.uploadDate; songInfo.url = data?.microformat?.microformatDataRenderer?.urlCanonical?.split("&")[0]; + songInfo.videoId = data?.videoDetails?.videoId; // used for options.resumeOnStart config.set("url", data?.microformat?.microformatDataRenderer?.urlCanonical);