diff --git a/config/defaults.js b/config/defaults.js index 91bddf24..329f3851 100644 --- a/config/defaults.js +++ b/config/defaults.js @@ -46,6 +46,7 @@ const defaultConfig = { }, discord: { enabled: false, + autoReconnect: true, // if enabled, will try to reconnect to discord every 5 seconds after disconnecting or failing to connect activityTimoutEnabled: true, // if enabled, the discord rich presence gets cleared when music paused after the time specified below activityTimoutTime: 10 * 60 * 1000, // 10 minutes listenAlong: true, // add a "listen along" button to rich presence @@ -54,7 +55,7 @@ const defaultConfig = { notifications: { enabled: false, unpauseNotification: false, - urgency: "normal", //has effect only on Linux + urgency: "normal", //has effect only on Linux interactive: false //has effect only on Windows }, "precise-volume": { diff --git a/index.js b/index.js index cbf3650e..f00660a1 100644 --- a/index.js +++ b/index.js @@ -486,13 +486,12 @@ function removeContentSecurityPolicy( // Custom listener to tweak the content security policy session.webRequest.onHeadersReceived(function (details, callback) { - if ( - !details.responseHeaders["content-security-policy-report-only"] && - !details.responseHeaders["content-security-policy"] - ) - return callback({ cancel: false }); + details.responseHeaders ??= {} + + // Remove the content security policy delete details.responseHeaders["content-security-policy-report-only"]; delete details.responseHeaders["content-security-policy"]; + callback({ cancel: false, responseHeaders: details.responseHeaders }); }); diff --git a/package.json b/package.json index a061891b..84f166e6 100644 --- a/package.json +++ b/package.json @@ -91,13 +91,14 @@ }, "engines": { "node": ">=16.0.0", - "npm": "Please use yarn and not npm" + "npm": "Please use yarn instead" }, "dependencies": { "@cliqz/adblocker-electron": "^1.25.2", "@ffmpeg/core": "^0.11.0", "@ffmpeg/ffmpeg": "^0.11.6", "@foobar404/wave": "^2.0.4", + "@xhayper/discord-rpc": "^1.0.15", "async-mutex": "^0.4.0", "browser-id3-writer": "^4.4.0", "butterchurn": "^2.6.7", @@ -105,7 +106,6 @@ "chokidar": "^3.5.3", "custom-electron-prompt": "^1.5.1", "custom-electron-titlebar": "^4.1.5", - "discord-rpc": "^4.0.1", "electron-better-web-request": "^1.0.1", "electron-debug": "^3.2.0", "electron-is": "^3.0.0", @@ -140,11 +140,6 @@ "playwright": "^1.29.2", "xo": "^0.53.1" }, - "resolutions": { - "glob-parent": "5.1.2", - "minimist": "1.2.7", - "yargs-parser": "21.1.1" - }, "auto-changelog": { "hideCredit": true, "package": true, diff --git a/plugins/adblocker/preload.js b/plugins/adblocker/preload.js index 621c9fae..2b5a12df 100644 --- a/plugins/adblocker/preload.js +++ b/plugins/adblocker/preload.js @@ -1,4 +1,4 @@ module.exports = () => { // Preload adblocker to inject scripts/styles - require("@cliqz/adblocker-electron-preload/dist/preload.cjs"); + require("@cliqz/adblocker-electron-preload"); }; diff --git a/plugins/discord/back.js b/plugins/discord/back.js index d1ff3a01..a8a75986 100644 --- a/plugins/discord/back.js +++ b/plugins/discord/back.js @@ -1,4 +1,5 @@ -const Discord = require("discord-rpc"); +"use strict"; +const Discord = require("@xhayper/discord-rpc"); const { dev } = require("electron-is"); const { dialog, app } = require("electron"); @@ -9,58 +10,81 @@ const clientId = "1043858434585526382"; /** * @typedef {Object} Info - * @property {import('discord-rpc').Client} rpc + * @property {import('@xhayper/discord-rpc').Client} rpc * @property {boolean} ready + * @property {boolean} autoReconnect * @property {import('../../providers/song-info').SongInfo} lastSongInfo */ /** * @type {Info} */ const info = { - rpc: null, + rpc: new Discord.Client({ + clientId + }), ready: false, + autoReconnect: true, lastSongInfo: null, }; + /** * @type {(() => void)[]} */ const refreshCallbacks = []; + const resetInfo = () => { - info.rpc = null; info.ready = false; clearTimeout(clearActivity); if (dev()) console.log("discord disconnected"); refreshCallbacks.forEach(cb => cb()); }; +info.rpc.on("connected", () => { + if (dev()) console.log("discord connected"); + refreshCallbacks.forEach(cb => cb()); +}); + +info.rpc.on("ready", () => { + info.ready = true; + if (info.lastSongInfo) updateActivity(info.lastSongInfo) +}); + +info.rpc.on("disconnected", () => { + resetInfo(); + + if (info.autoReconnect) { + connectTimeout(); + } +}); + +const connectTimeout = () => new Promise((resolve, reject) => setTimeout(() => { + if (!info.autoReconnect || info.rpc.isConnected) return; + info.rpc.login().then(resolve).catch(reject); +}, 5000)); + +const connectRecursive = () => { + if (!info.autoReconnect || info.rpc.isConnected) return; + connectTimeout().catch(connectRecursive); +} + let window; const connect = (showErr = false) => { - if (info.rpc) { + if (info.rpc.isConnected) { if (dev()) - console.log('Attempted to connect with active RPC object'); + console.log('Attempted to connect with active connection'); return; } - info.rpc = new Discord.Client({ - transport: "ipc", - }); info.ready = false; - info.rpc.once("connected", () => { - if (dev()) console.log("discord connected"); - refreshCallbacks.forEach(cb => cb()); - }); - info.rpc.once("ready", () => { - info.ready = true; - if (info.lastSongInfo) updateActivity(info.lastSongInfo) - }); - info.rpc.once("disconnected", resetInfo); - // Startup the rpc client info.rpc.login({ clientId }).catch(err => { resetInfo(); if (dev()) console.error(err); - if (showErr) dialog.showMessageBox(window, { title: 'Connection failed', message: err.message || String(err), type: 'error' }); + if (info.autoReconnect) { + connectRecursive(); + } + else if (showErr) dialog.showMessageBox(window, { title: 'Connection failed', message: err.message || String(err), type: 'error' }); }); }; @@ -70,7 +94,9 @@ let clearActivity; */ let updateActivity; -module.exports = (win, { activityTimoutEnabled, activityTimoutTime, listenAlong, hideDurationLeft }) => { +module.exports = (win, { autoReconnect, activityTimoutEnabled, activityTimoutTime, listenAlong, hideDurationLeft }) => { + info.autoReconnect = autoReconnect; + window = win; // We get multiple events // Next song: PAUSE(n), PAUSE(n+1), PLAY(n+1) @@ -92,7 +118,7 @@ module.exports = (win, { activityTimoutEnabled, activityTimoutTime, listenAlong, // clear directly if timeout is 0 if (songInfo.isPaused && activityTimoutEnabled && activityTimoutTime === 0) { - info.rpc.clearActivity().catch(console.error); + info.rpc.user?.clearActivity().catch(console.error); return; } @@ -100,7 +126,6 @@ module.exports = (win, { activityTimoutEnabled, activityTimoutTime, listenAlong, // @see https://discord.com/developers/docs/topics/gateway#activity-object // not all options are transfered through https://github.com/discordjs/RPC/blob/6f83d8d812c87cb7ae22064acd132600407d7d05/src/client.js#L518-530 const activityInfo = { - type: 2, // Listening, addressed in https://github.com/discordjs/RPC/pull/149 details: songInfo.title, state: songInfo.artist, largeImageKey: songInfo.imageSrc, @@ -116,7 +141,7 @@ module.exports = (win, { activityTimoutEnabled, activityTimoutTime, listenAlong, activityInfo.smallImageText = "Paused"; // Set start the timer so the activity gets cleared after a while if enabled if (activityTimoutEnabled) - clearActivity = setTimeout(() => info.rpc.clearActivity().catch(console.error), activityTimoutTime ?? 10000); + clearActivity = setTimeout(() => info.rpc.user?.clearActivity().catch(console.error), activityTimoutTime ?? 10000); } else if (!hideDurationLeft) { // Add the start and end time of the song const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000; @@ -125,7 +150,7 @@ module.exports = (win, { activityTimoutEnabled, activityTimoutTime, listenAlong, songStartTime + songInfo.songDuration * 1000; } - info.rpc.setActivity(activityInfo).catch(console.error); + info.rpc.user?.setActivity(activityInfo).catch(console.error); }; // If the page is ready, register the callback @@ -137,9 +162,10 @@ module.exports = (win, { activityTimoutEnabled, activityTimoutTime, listenAlong, }; module.exports.clear = () => { - if (info.rpc) info.rpc.clearActivity(); + if (info.rpc) info.rpc.user?.clearActivity(); clearTimeout(clearActivity); }; + module.exports.connect = connect; module.exports.registerRefresh = (cb) => refreshCallbacks.push(cb); module.exports.isConnected = () => info.rpc !== null; diff --git a/plugins/discord/menu.js b/plugins/discord/menu.js index 5ce72704..2853a502 100644 --- a/plugins/discord/menu.js +++ b/plugins/discord/menu.js @@ -18,6 +18,15 @@ module.exports = (win, options, refreshMenu) => { enabled: !isConnected(), click: connect, }, + { + label: "Auto reconnect", + type: "checkbox", + checked: options.autoReconnect, + click: (item) => { + options.autoReconnect = item.checked; + setMenuOptions('discord', options); + }, + }, { label: "Clear activity", click: clear,