apply fix from eslint

This commit is contained in:
JellyBrick
2023-08-29 19:14:51 +09:00
parent c722896a73
commit 897cfd3c7d
33 changed files with 471 additions and 524 deletions

View File

@ -46,6 +46,7 @@ module.exports = {
allowTemplateLiterals: false, allowTemplateLiterals: false,
}], }],
'quote-props': ['error', 'consistent'], 'quote-props': ['error', 'consistent'],
'semi': ['error', 'always'],
}, },
env: { env: {
browser: true, browser: true,

View File

@ -4,10 +4,9 @@ const { restart } = require('../providers/app-controls');
function getEnabled() { function getEnabled() {
const plugins = store.get('plugins'); const plugins = store.get('plugins');
const enabledPlugins = Object.entries(plugins).filter(([plugin, options]) => return Object.entries(plugins).filter(([plugin]) =>
isEnabled(plugin), isEnabled(plugin),
); );
return enabledPlugins;
} }
function isEnabled(plugin) { function isEnabled(plugin) {

View File

@ -1,3 +1,5 @@
/* eslint-disable */
// Constants // Constants
const element = document.documentElement; const element = document.documentElement;
const { body } = document; const { body } = document;
@ -40,7 +42,8 @@ const bubbleCanvas = function (t) {
e.mouseX = 0; e.mouseX = 0;
e.mouseY = 0; e.mouseY = 0;
window.addEventListener('mousemove', (t) => { window.addEventListener('mousemove', (t) => {
(e.mouseX = t.clientX), (e.mouseY = t.clientY); e.mouseX = t.clientX;
e.mouseY = t.clientY;
}); });
e.randomise(); e.randomise();
}; };

View File

@ -36,7 +36,6 @@ if (!gotTheLock) {
} }
app.commandLine.appendSwitch('enable-features', 'SharedArrayBuffer'); // Required for downloader app.commandLine.appendSwitch('enable-features', 'SharedArrayBuffer'); // Required for downloader
app.allowRendererProcessReuse = true; // https://github.com/electron/electron/issues/18397
if (config.get('options.disableHardwareAcceleration')) { if (config.get('options.disableHardwareAcceleration')) {
if (is.dev()) { if (is.dev()) {
console.log('Disabling hardware acceleration'); console.log('Disabling hardware acceleration');
@ -60,9 +59,9 @@ require('electron-debug')({
}); });
let icon = 'assets/youtube-music.png'; let icon = 'assets/youtube-music.png';
if (process.platform == 'win32') { if (process.platform === 'win32') {
icon = 'assets/generated/icon.ico'; icon = 'assets/generated/icon.ico';
} else if (process.platform == 'darwin') { } else if (process.platform === 'darwin') {
icon = 'assets/generated/icon.icns'; icon = 'assets/generated/icon.icns';
} }
@ -393,7 +392,7 @@ app.on('ready', () => {
setupProtocolHandler(mainWindow); setupProtocolHandler(mainWindow);
app.on('second-instance', (_event, commandLine, _workingDirectory) => { app.on('second-instance', (_, commandLine) => {
const uri = `${APP_PROTOCOL}://`; const uri = `${APP_PROTOCOL}://`;
const protocolArgv = commandLine.find((arg) => arg.startsWith(uri)); const protocolArgv = commandLine.find((arg) => arg.startsWith(uri));
if (protocolArgv) { if (protocolArgv) {
@ -541,7 +540,7 @@ function removeContentSecurityPolicy(
// When multiple listeners are defined, apply them all // When multiple listeners are defined, apply them all
session.webRequest.setResolver('onHeadersReceived', (listeners) => { session.webRequest.setResolver('onHeadersReceived', (listeners) => {
const response = listeners.reduce( return listeners.reduce(
async (accumulator, listener) => { async (accumulator, listener) => {
if (accumulator.cancel) { if (accumulator.cancel) {
return accumulator; return accumulator;
@ -552,7 +551,5 @@ function removeContentSecurityPolicy(
}, },
{ cancel: false }, { cancel: false },
); );
return response;
}); });
} }

80
package-lock.json generated
View File

@ -39,7 +39,7 @@
"node-fetch": "2.7.0", "node-fetch": "2.7.0",
"simple-youtube-age-restriction-bypass": "git+https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.9", "simple-youtube-age-restriction-bypass": "git+https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.9",
"vudio": "2.1.1", "vudio": "2.1.1",
"youtubei.js": "4.3.0", "youtubei.js": "6.1.0",
"ytpl": "2.3.0" "ytpl": "2.3.0"
}, },
"devDependencies": { "devDependencies": {
@ -1919,11 +1919,6 @@
"bluebird": "^3.5.5" "bluebird": "^3.5.5"
} }
}, },
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/boolean": { "node_modules/boolean": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
@ -2607,37 +2602,6 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cssom": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
"integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="
},
"node_modules/custom-electron-prompt": { "node_modules/custom-electron-prompt": {
"version": "1.5.7", "version": "1.5.7",
"resolved": "https://registry.npmjs.org/custom-electron-prompt/-/custom-electron-prompt-1.5.7.tgz", "resolved": "https://registry.npmjs.org/custom-electron-prompt/-/custom-electron-prompt-1.5.7.tgz",
@ -5018,11 +4982,6 @@
"resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz", "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz",
"integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==" "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg=="
}, },
"node_modules/html-escaper": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
"integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
},
"node_modules/html-to-text": { "node_modules/html-to-text": {
"version": "9.0.5", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
@ -5937,18 +5896,6 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true "dev": true
}, },
"node_modules/linkedom": {
"version": "0.14.26",
"resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.26.tgz",
"integrity": "sha512-mK6TrydfFA7phrnp+1j57ycBwFI5bGSW6YXlw9acHoqF+mP/y+FooEYYyniOt5Ot57FSKB3iwmnuQ1UUyNLm5A==",
"dependencies": {
"css-select": "^5.1.0",
"cssom": "^0.5.0",
"html-escaper": "^3.0.3",
"htmlparser2": "^8.0.1",
"uhyphen": "^0.2.0"
}
},
"node_modules/locate-path": { "node_modules/locate-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
@ -6579,17 +6526,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.12.3", "version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
@ -8407,11 +8343,6 @@
"node": ">=0.8.0" "node": ">=0.8.0"
} }
}, },
"node_modules/uhyphen": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz",
"integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="
},
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
@ -8815,15 +8746,14 @@
} }
}, },
"node_modules/youtubei.js": { "node_modules/youtubei.js": {
"version": "4.3.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-4.3.0.tgz", "resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-6.1.0.tgz",
"integrity": "sha512-HdU6Awdr1nUWy0Ph7WdmoYPWL0ovx+S4w40eeTzAENr5xiUENsLuXcvULRc2fRCIxi+n7Q6142VVhmM4yK/g5g==", "integrity": "sha512-EJmPuQ1pLimrcp5nPDSeZHVthT7KJLxp9Z5sYmihXKn1ct8e7cntKaFnPvy5QsbjpipICpDpQEs+d+owDhjgIg==",
"funding": [ "funding": [
"https://github.com/sponsors/LuanRT" "https://github.com/sponsors/LuanRT"
], ],
"dependencies": { "dependencies": {
"jintr": "^1.0.0", "jintr": "^1.1.0",
"linkedom": "^0.14.12",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"undici": "^5.19.1" "undici": "^5.19.1"
} }

View File

@ -133,7 +133,7 @@
"node-fetch": "2.7.0", "node-fetch": "2.7.0",
"simple-youtube-age-restriction-bypass": "git+https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.9", "simple-youtube-age-restriction-bypass": "git+https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.9",
"vudio": "2.1.1", "vudio": "2.1.1",
"youtubei.js": "4.3.0", "youtubei.js": "6.1.0",
"ytpl": "2.3.0" "ytpl": "2.3.0"
}, },
"overrides": { "overrides": {

View File

@ -15,11 +15,10 @@
* v0.2.0, 07/2016 * v0.2.0, 07/2016
*/ */
(function (root) { 'use strict';
'use strict';
// Internal utility: check if value is a valid volume level and throw if not // Internal utility: check if value is a valid volume level and throw if not
const validateVolumeLevel = (value) => { const validateVolumeLevel = (value) => {
// Number between 0 and 1? // Number between 0 and 1?
if (!Number.isNaN(value) && value >= 0 && value <= 1) { if (!Number.isNaN(value) && value >= 0 && value <= 1) {
// Yup, that's fine // Yup, that's fine
@ -28,10 +27,10 @@
// Abort and throw an exception // Abort and throw an exception
throw new TypeError('Number between 0 and 1 expected as volume!'); throw new TypeError('Number between 0 and 1 expected as volume!');
} }
}; };
// Main class // Main class
class VolumeFader { class VolumeFader {
/** /**
* VolumeFader Constructor * VolumeFader Constructor
* *
@ -68,7 +67,7 @@
} }
// Linear volume fading? // Linear volume fading?
if (options.fadeScaling == 'linear') { if (options.fadeScaling === 'linear') {
// Pass levels unchanged // Pass levels unchanged
this.scale = { this.scale = {
internalToVolume: (level) => level, internalToVolume: (level) => level,
@ -85,7 +84,7 @@
// Default dynamic range? // Default dynamic range?
if ( if (
options.fadeScaling === undefined options.fadeScaling === undefined
|| options.fadeScaling == 'logarithmic' || options.fadeScaling === 'logarithmic'
) { ) {
// Set default of 60 dB // Set default of 60 dB
dynamicRange = 3; dynamicRange = 3;
@ -262,9 +261,6 @@
/** /**
* Internal: Update media volume. * Internal: Update media volume.
* (calls itself through requestAnimationFrame) * (calls itself through requestAnimationFrame)
*
* @param {Number} targetVolume - linear level to fade to (0…1)
* @param {Function} callback - (optional) function to be called when fade is complete
*/ */
updateVolume() { updateVolume() {
// Fader active and fade available to process? // Fader active and fade available to process?
@ -281,14 +277,13 @@
// Compute current level on internal scale // Compute current level on internal scale
const level const level
= progress * (this.fade.volume.end - this.fade.volume.start) = (progress * (this.fade.volume.end - this.fade.volume.start)) + this.fade.volume.start;
+ this.fade.volume.start;
// Map fade level to volume level and apply it to media element // Map fade level to volume level and apply it to media element
this.media.volume = this.scale.internalToVolume(level); this.media.volume = this.scale.internalToVolume(level);
// Schedule next update // Schedule next update
root.requestAnimationFrame(this.updateVolume.bind(this)); window.requestAnimationFrame(this.updateVolume.bind(this));
} else { } else {
// Log end of fade // Log end of fade
this.logger this.logger
@ -320,7 +315,7 @@
*/ */
exponentialScaler(input, dynamicRange) { exponentialScaler(input, dynamicRange) {
// Special case: make zero (or any falsy input) return zero // Special case: make zero (or any falsy input) return zero
if (input == 0) { if (input === 0) {
// Since the dynamic range is limited, // Since the dynamic range is limited,
// allow a zero to produce a plain zero instead of a small faction // allow a zero to produce a plain zero instead of a small faction
// (audio would not be recognized as silent otherwise) // (audio would not be recognized as silent otherwise)
@ -343,7 +338,7 @@
*/ */
logarithmicScaler(input, dynamicRange) { logarithmicScaler(input, dynamicRange) {
// Special case: make zero (or any falsy input) return zero // Special case: make zero (or any falsy input) return zero
if (input == 0) { if (input === 0) {
// Logarithm of zero would be -∞, which would map to zero anyway // Logarithm of zero would be -∞, which would map to zero anyway
return 0; return 0;
} }
@ -352,10 +347,10 @@
input = Math.log10(input); input = Math.log10(input);
// Scale minus something × 10 dB to 0…1 (clipping at 0) // Scale minus something × 10 dB to 0…1 (clipping at 0)
return Math.max(1 + input / dynamicRange, 0); return Math.max(1 + (input / dynamicRange), 0);
}
} }
}
// Export class to root scope module.exports = {
root.VolumeFader = VolumeFader; VolumeFader
})(window); };

View File

@ -2,23 +2,25 @@ const { ipcRenderer } = require('electron');
const { Howl } = require('howler'); const { Howl } = require('howler');
// Extracted from https://github.com/bitfasching/VolumeFader // Extracted from https://github.com/bitfasching/VolumeFader
require('./fader'); const { VolumeFader } = require('./fader');
let transitionAudio; // Howler audio used to fade out the current music let transitionAudio; // Howler audio used to fade out the current music
let firstVideo = true; let firstVideo = true;
let waitForTransition; let waitForTransition;
const defaultConfig = require('../../config/defaults').plugins.crossfade; /**
* @type {PluginConfig}
*/
const configProvider = require('./config'); const configProvider = require('./config');
const defaultConfig = require('../../config/defaults').plugins.crossfade;
let config; let config;
const configGetNumber = (key) => Number(config[key]) || defaultConfig[key]; const configGetNumber = (key) => Number(config[key]) || defaultConfig[key];
const getStreamURL = async (videoID) => { const getStreamURL = async (videoID) => {
const url = await ipcRenderer.invoke('audio-url', videoID); return await ipcRenderer.invoke('audio-url', videoID);
return url;
}; };
const getVideoIDFromURL = (url) => new URLSearchParams(url.split('?')?.at(-1)).get('v'); const getVideoIDFromURL = (url) => new URLSearchParams(url.split('?')?.at(-1)).get('v');
@ -26,7 +28,7 @@ const getVideoIDFromURL = (url) => new URLSearchParams(url.split('?')?.at(-1)).g
const isReadyToCrossfade = () => transitionAudio && transitionAudio.state() === 'loaded'; const isReadyToCrossfade = () => transitionAudio && transitionAudio.state() === 'loaded';
const watchVideoIDChanges = (cb) => { const watchVideoIDChanges = (cb) => {
navigation.addEventListener('navigate', (event) => { window.navigation.addEventListener('navigate', (event) => {
const currentVideoID = getVideoIDFromURL( const currentVideoID = getVideoIDFromURL(
event.currentTarget.currentEntry.url, event.currentTarget.currentEntry.url,
); );
@ -126,7 +128,7 @@ const crossfade = async (cb) => {
} }
let resolveTransition; let resolveTransition;
waitForTransition = new Promise((resolve, reject) => { waitForTransition = new Promise((resolve) => {
resolveTransition = resolve; resolveTransition = resolve;
}); });

View File

@ -1,10 +1,9 @@
const config = require('./config');
const defaultOptions = require('../../config/defaults').plugins.crossfade;
const prompt = require('custom-electron-prompt'); const prompt = require('custom-electron-prompt');
const config = require('./config');
const promptOptions = require('../../providers/prompt-options'); const promptOptions = require('../../providers/prompt-options');
const defaultOptions = require('../../config/defaults').plugins.crossfade;
module.exports = (win) => [ module.exports = (win) => [
{ {

View File

@ -174,10 +174,10 @@ module.exports = (win, { autoReconnect, activityTimoutEnabled, activityTimoutTim
} }
} else if (!hideDurationLeft) { } else if (!hideDurationLeft) {
// Add the start and end time of the song // Add the start and end time of the song
const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000; const songStartTime = Date.now() - (songInfo.elapsedSeconds * 1000);
activityInfo.startTimestamp = songStartTime; activityInfo.startTimestamp = songStartTime;
activityInfo.endTimestamp activityInfo.endTimestamp
= songStartTime + songInfo.songDuration * 1000; = songStartTime + (songInfo.songDuration * 1000);
} }
info.rpc.user?.setActivity(activityInfo).catch(console.error); info.rpc.user?.setActivity(activityInfo).catch(console.error);

View File

@ -31,6 +31,8 @@ const {
sendFeedback: sendFeedback_, sendFeedback: sendFeedback_,
} = require('./utils'); } = require('./utils');
const config = require('./config');
const { fetchFromGenius } = require('../lyrics-genius/back'); const { fetchFromGenius } = require('../lyrics-genius/back');
const { isEnabled } = require('../../config/plugins'); const { isEnabled } = require('../../config/plugins');
const { getImage, cleanupName } = require('../../providers/song-info'); const { getImage, cleanupName } = require('../../providers/song-info');
@ -39,8 +41,6 @@ const { cache } = require('../../providers/decorators');
const ffmpegMutex = new Mutex(); const ffmpegMutex = new Mutex();
const config = require('./config');
/** @type {Innertube} */ /** @type {Innertube} */
let yt; let yt;
let win; let win;
@ -187,14 +187,14 @@ async function downloadSongUnsafe(
return; return;
} }
const download_options = { const downloadOptions = {
type: 'audio', // Audio, video or video+audio type: 'audio', // Audio, video or video+audio
quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on. quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
format: 'any', // Media container format format: 'any', // Media container format
}; };
const format = info.chooseFormat(download_options); const format = info.chooseFormat(downloadOptions);
const stream = await info.download(download_options); const stream = await info.download(downloadOptions);
console.info( console.info(
`Downloading ${metadata.artist} - ${metadata.title} [${metadata.id}]`, `Downloading ${metadata.artist} - ${metadata.title} [${metadata.id}]`,
@ -244,14 +244,14 @@ async function downloadSongUnsafe(
async function iterableStreamToMP3( async function iterableStreamToMP3(
stream, stream,
metadata, metadata,
content_length, contentLength,
sendFeedback, sendFeedback,
increasePlaylistProgress = () => { increasePlaylistProgress = () => {
}, },
) { ) {
const chunks = []; const chunks = [];
let downloaded = 0; let downloaded = 0;
const total = content_length; const total = contentLength;
for await (const chunk of stream) { for await (const chunk of stream) {
downloaded += chunk.length; downloaded += chunk.length;
chunks.push(chunk); chunks.push(chunk);
@ -281,7 +281,7 @@ async function iterableStreamToMP3(
ffmpeg.setProgress(({ ratio }) => { ffmpeg.setProgress(({ ratio }) => {
sendFeedback(`Converting: ${Math.floor(ratio * 100)}%`, ratio); sendFeedback(`Converting: ${Math.floor(ratio * 100)}%`, ratio);
increasePlaylistProgress(0.15 + ratio * 0.85); increasePlaylistProgress(0.15 + (ratio * 0.85));
}); });
await ffmpeg.run( await ffmpeg.run(
@ -377,7 +377,7 @@ async function downloadPlaylist(givenUrl) {
}); });
} catch (error) { } catch (error) {
sendError( sendError(
`Error getting playlist info: make sure it isn\'t a private or "Mixed for you" playlist\n\n${error}`, `Error getting playlist info: make sure it isn't a private or "Mixed for you" playlist\n\n${error}`,
); );
return; return;
} }
@ -434,7 +434,7 @@ async function downloadPlaylist(givenUrl) {
const increaseProgress = (itemPercentage) => { const increaseProgress = (itemPercentage) => {
const currentProgress = (counter - 1) / playlist.items.length; const currentProgress = (counter - 1) / playlist.items.length;
const newProgress = currentProgress + progressStep * itemPercentage; const newProgress = currentProgress + (progressStep * itemPercentage);
win.setProgressBar(newProgress); win.setProgressBar(newProgress);
}; };

View File

@ -8,7 +8,7 @@ function $(selector) {
return document.querySelector(selector); return document.querySelector(selector);
} }
module.exports = (options) => { module.exports = () => {
const visible = () => Boolean($('.cet-menubar').firstChild); const visible = () => Boolean($('.cet-menubar').firstChild);
const bar = new Titlebar({ const bar = new Titlebar({
icon: 'https://cdn-icons-png.flaticon.com/512/5358/5358672.png', icon: 'https://cdn-icons-png.flaticon.com/512/5358/5358672.png',
@ -38,7 +38,7 @@ module.exports = (options) => {
}); });
if (isEnabled('picture-in-picture')) { if (isEnabled('picture-in-picture')) {
ipcRenderer.on('pip-toggle', (_, pipEnabled) => { ipcRenderer.on('pip-toggle', () => {
bar.refreshMenu(); bar.refreshMenu();
}); });
} }
@ -62,7 +62,7 @@ function setupSearchOpenObserver() {
} }
function setupMenuOpenObserver() { function setupMenuOpenObserver() {
const menuOpenObserver = new MutationObserver((mutations) => { const menuOpenObserver = new MutationObserver(() => {
$('#nav-bar-background').style.webkitAppRegion $('#nav-bar-background').style.webkitAppRegion
= [...$('.cet-menubar').childNodes].some((c) => c.classList.contains('open')) = [...$('.cet-menubar').childNodes].some((c) => c.classList.contains('open'))
? 'no-drag' : 'drag'; ? 'no-drag' : 'drag';

View File

@ -16,10 +16,10 @@ const createFormData = (parameters) => {
return formData; return formData;
}; };
const createQueryString = (parameters, api_sig) => { const createQueryString = (parameters, apiSignature) => {
// Creates a querystring // Creates a querystring
const queryData = []; const queryData = [];
parameters.api_sig = api_sig; parameters.api_sig = apiSignature;
for (const key in parameters) { for (const key in parameters) {
queryData.push(`${encodeURIComponent(key)}=${encodeURIComponent(parameters[key])}`); queryData.push(`${encodeURIComponent(key)}=${encodeURIComponent(parameters[key])}`);
} }
@ -49,15 +49,15 @@ const createApiSig = (parameters, secret) => {
return sig; return sig;
}; };
const createToken = async ({ api_key, api_root, secret }) => { const createToken = async ({ apiKey, apiRoot, secret }) => {
// Creates and stores the auth token // Creates and stores the auth token
const data = { const data = {
method: 'auth.gettoken', method: 'auth.gettoken',
api_key, apiKey,
format: 'json', format: 'json',
}; };
const api_sig = createApiSig(data, secret); const apiSigature = createApiSig(data, secret);
let response = await fetch(`${api_root}${createQueryString(data, api_sig)}`); let response = await fetch(`${apiRoot}${createQueryString(data, apiSigature)}`);
response = await response.json(); response = await response.json();
return response?.token; return response?.token;
}; };
@ -78,8 +78,8 @@ const getAndSetSessionKey = async (config) => {
method: 'auth.getsession', method: 'auth.getsession',
token: config.token, token: config.token,
}; };
const api_sig = createApiSig(data, config.secret); const apiSignature = createApiSig(data, config.secret);
let res = await fetch(`${config.api_root}${createQueryString(data, api_sig)}`); let res = await fetch(`${config.api_root}${createQueryString(data, apiSignature)}`);
res = await res.json(); res = await res.json();
if (res.error) { if (res.error) {
await authenticate(config); await authenticate(config);
@ -110,7 +110,7 @@ const postSongDataToAPI = async (songInfo, config, data) => {
postData.api_sig = createApiSig(postData, config.secret); postData.api_sig = createApiSig(postData, config.secret);
fetch('https://ws.audioscrobbler.com/2.0/', { method: 'POST', body: createFormData(postData) }) fetch('https://ws.audioscrobbler.com/2.0/', { method: 'POST', body: createFormData(postData) })
.catch((error) => { .catch((error) => {
if (error.response.data.error == 9) { if (error.response.data.error === 9) {
// Session key is invalid, so remove it from the config and reauthenticate // Session key is invalid, so remove it from the config and reauthenticate
config.session_key = undefined; config.session_key = undefined;
setOptions('last-fm', config); setOptions('last-fm', config);

View File

@ -8,7 +8,7 @@ const fetch = require('node-fetch');
const { cleanupName } = require('../../providers/song-info'); const { cleanupName } = require('../../providers/song-info');
const { injectCSS } = require('../utils'); const { injectCSS } = require('../utils');
const eastAsianChars = /\p{Script=Han}|\p{Script=Katakana}|\p{Script=Hiragana}|\p{Script=Hangul}|\p{Script=Han}/u; const eastAsianChars = /\p{Script=Katakana}|\p{Script=Hiragana}|\p{Script=Hangul}|\p{Script=Han}/u;
let revRomanized = false; let revRomanized = false;
module.exports = async (win, options) => { module.exports = async (win, options) => {
@ -89,7 +89,7 @@ const getLyricsList = async (queryString) => {
* @returns The lyrics of the song URL provided, null if none * @returns The lyrics of the song URL provided, null if none
*/ */
const getLyrics = async (url) => { const getLyrics = async (url) => {
response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
return null; return null;
} }

View File

@ -2,7 +2,7 @@ const { toggleRomanized } = require('./back');
const { setOptions } = require('../../config/plugins'); const { setOptions } = require('../../config/plugins');
module.exports = (win, options, refreshMenu) => [ module.exports = (win, options) => [
{ {
label: 'Romanized Lyrics', label: 'Romanized Lyrics',
type: 'checkbox', type: 'checkbox',

View File

@ -15,7 +15,7 @@ function removeLoginElements() {
const libraryIconPath const libraryIconPath
= 'M16,6v2h-2v5c0,1.1-0.9,2-2,2s-2-0.9-2-2s0.9-2,2-2c0.37,0,0.7,0.11,1,0.28V6H16z M18,20H4V6H3v15h15V20z M21,3H6v15h15V3z M7,4h13v13H7V4z'; = 'M16,6v2h-2v5c0,1.1-0.9,2-2,2s-2-0.9-2-2s0.9-2,2-2c0.37,0,0.7,0.11,1,0.28V6H16z M18,20H4V6H3v15h15V20z M21,3H6v15h15V3z M7,4h13v13H7V4z';
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
menuEntries = document.querySelectorAll( const menuEntries = document.querySelectorAll(
'#items ytmusic-guide-entry-renderer', '#items ytmusic-guide-entry-renderer',
); );
for (const item of menuEntries) { for (const item of menuEntries) {

View File

@ -2,8 +2,11 @@ const path = require('node:path');
const { Notification, app, ipcMain } = require('electron'); const { Notification, app, ipcMain } = require('electron');
const { notificationImage, icons, save_temp_icons, secondsToMinutes, ToastStyles } = require('./utils'); const { notificationImage, icons, saveTempIcon, secondsToMinutes, ToastStyles } = require('./utils');
/**
* @type {PluginConfig}
*/
const config = require('./config'); const config = require('./config');
const getSongControls = require('../../providers/song-controls'); const getSongControls = require('../../providers/song-controls');
@ -25,7 +28,7 @@ module.exports = (win) => {
ipcMain.on('timeChanged', (_, t) => currentSeconds = t); ipcMain.on('timeChanged', (_, t) => currentSeconds = t);
if (app.isPackaged) { if (app.isPackaged) {
save_temp_icons(); saveTempIcon();
} }
let savedSongInfo; let savedSongInfo;
@ -108,42 +111,42 @@ function sendNotification(songInfo) {
// https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-schema // https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-schema
// https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=xml // https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=xml
// https://learn.microsoft.com/en-us/uwp/api/windows.ui.notifications.toasttemplatetype // https://learn.microsoft.com/en-us/uwp/api/windows.ui.notifications.toasttemplatetype
toastXml: get_xml(songInfo, iconSrc), toastXml: getXml(songInfo, iconSrc),
}); });
savedNotification.on('close', (_) => { savedNotification.on('close', () => {
savedNotification = undefined; savedNotification = undefined;
}); });
savedNotification.show(); savedNotification.show();
} }
const get_xml = (songInfo, iconSrc) => { const getXml = (songInfo, iconSrc) => {
switch (config.get('toastStyle')) { switch (config.get('toastStyle')) {
default: default:
case ToastStyles.logo: case ToastStyles.logo:
case ToastStyles.legacy: { case ToastStyles.legacy: {
return xml_logo(songInfo, iconSrc); return xmlLogo(songInfo, iconSrc);
} }
case ToastStyles.banner_top_custom: { case ToastStyles.banner_top_custom: {
return xml_banner_top_custom(songInfo, iconSrc); return xmlBannerTopCustom(songInfo, iconSrc);
} }
case ToastStyles.hero: { case ToastStyles.hero: {
return xml_hero(songInfo, iconSrc); return xmlHero(songInfo, iconSrc);
} }
case ToastStyles.banner_bottom: { case ToastStyles.banner_bottom: {
return xml_banner_bottom(songInfo, iconSrc); return xmlBannerBottom(songInfo, iconSrc);
} }
case ToastStyles.banner_centered_bottom: { case ToastStyles.banner_centered_bottom: {
return xml_banner_centered_bottom(songInfo, iconSrc); return xmlBannerCenteredBottom(songInfo, iconSrc);
} }
case ToastStyles.banner_centered_top: { case ToastStyles.banner_centered_top: {
return xml_banner_centered_top(songInfo, iconSrc); return xmlBannerCenteredTop(songInfo, iconSrc);
} }
} }
}; };
@ -186,19 +189,19 @@ const toast = (content, isPaused) => `\
${getButtons(isPaused)} ${getButtons(isPaused)}
</toast>`; </toast>`;
const xml_image = ({ title, artist, isPaused }, imgSrc, placement) => toast(`\ const xmlImage = ({ title, artist, isPaused }, imgSrc, placement) => toast(`\
<image id="1" src="${imgSrc}" name="Image" ${placement}/> <image id="1" src="${imgSrc}" name="Image" ${placement}/>
<text id="1">${title}</text> <text id="1">${title}</text>
<text id="2">${artist}</text>\ <text id="2">${artist}</text>\
`, isPaused); `, isPaused);
const xml_logo = (songInfo, imgSrc) => xml_image(songInfo, imgSrc, 'placement="appLogoOverride"'); const xmlLogo = (songInfo, imgSrc) => xmlImage(songInfo, imgSrc, 'placement="appLogoOverride"');
const xml_hero = (songInfo, imgSrc) => xml_image(songInfo, imgSrc, 'placement="hero"'); const xmlHero = (songInfo, imgSrc) => xmlImage(songInfo, imgSrc, 'placement="hero"');
const xml_banner_bottom = (songInfo, imgSrc) => xml_image(songInfo, imgSrc, ''); const xmlBannerBottom = (songInfo, imgSrc) => xmlImage(songInfo, imgSrc, '');
const xml_banner_top_custom = (songInfo, imgSrc) => toast(`\ const xmlBannerTopCustom = (songInfo, imgSrc) => toast(`\
<image id="1" src="${imgSrc}" name="Image" /> <image id="1" src="${imgSrc}" name="Image" />
<text></text> <text></text>
<group> <group>
@ -206,11 +209,11 @@ const xml_banner_top_custom = (songInfo, imgSrc) => toast(`\
<text hint-style="body">${songInfo.title}</text> <text hint-style="body">${songInfo.title}</text>
<text hint-style="captionSubtle">${songInfo.artist}</text> <text hint-style="captionSubtle">${songInfo.artist}</text>
</subgroup> </subgroup>
${xml_more_data(songInfo)} ${xmlMoreData(songInfo)}
</group>\ </group>\
`, songInfo.isPaused); `, songInfo.isPaused);
const xml_more_data = ({ album, elapsedSeconds, songDuration }) => `\ const xmlMoreData = ({ album, elapsedSeconds, songDuration }) => `\
<subgroup hint-textStacking="bottom"> <subgroup hint-textStacking="bottom">
${album ${album
? `<text hint-style="captionSubtle" hint-wrap="true" hint-align="right">${album}</text>` : ''} ? `<text hint-style="captionSubtle" hint-wrap="true" hint-align="right">${album}</text>` : ''}
@ -218,7 +221,7 @@ const xml_more_data = ({ album, elapsedSeconds, songDuration }) => `\
</subgroup>\ </subgroup>\
`; `;
const xml_banner_centered_bottom = ({ title, artist, isPaused }, imgSrc) => toast(`\ const xmlBannerCenteredBottom = ({ title, artist, isPaused }, imgSrc) => toast(`\
<text></text> <text></text>
<group> <group>
<subgroup hint-weight="1" hint-textStacking="center"> <subgroup hint-weight="1" hint-textStacking="center">
@ -229,7 +232,7 @@ const xml_banner_centered_bottom = ({ title, artist, isPaused }, imgSrc) => toas
<image id="1" src="${imgSrc}" name="Image" hint-removeMargin="true" />\ <image id="1" src="${imgSrc}" name="Image" hint-removeMargin="true" />\
`, isPaused); `, isPaused);
const xml_banner_centered_top = ({ title, artist, isPaused }, imgSrc) => toast(`\ const xmlBannerCenteredTop = ({ title, artist, isPaused }, imgSrc) => toast(`\
<image id="1" src="${imgSrc}" name="Image" /> <image id="1" src="${imgSrc}" name="Image" />
<text></text> <text></text>
<group> <group>

View File

@ -69,18 +69,18 @@ module.exports.notificationImage = (songInfo) => {
} }
}; };
module.exports.saveImage = cache((img, save_path) => { module.exports.saveImage = cache((img, savePath) => {
try { try {
fs.writeFileSync(save_path, img.toPNG()); fs.writeFileSync(savePath, img.toPNG());
} catch (error) { } catch (error) {
console.log(`Error writing song icon to disk:\n${error.toString()}`); console.log(`Error writing song icon to disk:\n${error.toString()}`);
return icon; return icon;
} }
return save_path; return savePath;
}); });
module.exports.save_temp_icons = () => { module.exports.saveTempIcon = () => {
for (const kind of Object.keys(module.exports.icons)) { for (const kind of Object.keys(module.exports.icons)) {
const destinationPath = path.join(userData, 'icons', `${kind}.png`); const destinationPath = path.join(userData, 'icons', `${kind}.png`);
if (fs.existsSync(destinationPath)) { if (fs.existsSync(destinationPath)) {

View File

@ -1,7 +1,6 @@
const path = require('node:path'); const path = require('node:path');
const { app, ipcMain } = require('electron'); const { app, ipcMain } = require('electron');
const electronLocalshortcut = require('electron-localshortcut');
const { setOptions } = require('../../config/plugins'); const { setOptions } = require('../../config/plugins');
const { injectCSS } = require('../utils'); const { injectCSS } = require('../utils');

View File

@ -52,7 +52,7 @@ const observer = new MutationObserver(() => {
menu.prepend(pipButton); menu.prepend(pipButton);
}); });
global.togglePictureInPicture = async () => { const togglePictureInPicture = async () => {
if (useNativePiP) { if (useNativePiP) {
const isInPiP = document.pictureInPictureElement !== null; const isInPiP = document.pictureInPictureElement !== null;
const video = $('video'); const video = $('video');
@ -72,6 +72,7 @@ global.togglePictureInPicture = async () => {
ipcRenderer.send('picture-in-picture'); ipcRenderer.send('picture-in-picture');
return false; return false;
}; };
global.togglePictureInPicture = togglePictureInPicture;
const listenForToggle = () => { const listenForToggle = () => {
const originalExitButton = $('.exit-fullscreen-button'); const originalExitButton = $('.exit-fullscreen-button');
@ -123,7 +124,7 @@ function observeMenu(options) {
listenForToggle(); listenForToggle();
cloneButton('.player-minimize-button').addEventListener('click', async () => { cloneButton('.player-minimize-button').addEventListener('click', async () => {
await global.togglePictureInPicture(); await togglePictureInPicture();
setTimeout(() => $('#player').click()); setTimeout(() => $('#player').click());
}); });

View File

@ -56,8 +56,9 @@ const observePopupContainer = () => {
}; };
const observeVideo = () => { const observeVideo = () => {
$('video').addEventListener('ratechange', forcePlaybackRate); const video = $('video');+
$('video').addEventListener('srcChanged', forcePlaybackRate); video.addEventListener('ratechange', forcePlaybackRate);
video.addEventListener('srcChanged', forcePlaybackRate);
}; };
const setupWheelListener = () => { const setupWheelListener = () => {

View File

@ -1,15 +1,15 @@
const { injectCSS } = require('../utils');
const path = require('node:path'); const path = require('node:path');
const { globalShortcut } = require('electron');
const { injectCSS } = require('../utils');
/* /*
This is used to determine if plugin is actually active This is used to determine if plugin is actually active
(not if its only enabled in options) (not if its only enabled in options)
*/ */
let enabled = false; let enabled = false;
const { globalShortcut } = require('electron');
module.exports = (win, options) => { module.exports = (win, options) => {
enabled = true; enabled = true;
injectCSS(win.webContents, path.join(__dirname, 'volume-hud.css')); injectCSS(win.webContents, path.join(__dirname, 'volume-hud.css'));

View File

@ -26,7 +26,7 @@ const writeOptions = debounce(() => {
setOptions('precise-volume', options); setOptions('precise-volume', options);
}, 1000); }, 1000);
module.exports.moveVolumeHud = debounce((showVideo) => { const moveVolumeHud = debounce((showVideo) => {
const volumeHud = $('#volumeHud'); const volumeHud = $('#volumeHud');
if (!volumeHud) { if (!volumeHud) {
return; return;
@ -36,6 +36,7 @@ module.exports.moveVolumeHud = debounce((showVideo) => {
? `${($('ytmusic-player').clientHeight - $('video').clientHeight) / 2}px` ? `${($('ytmusic-player').clientHeight - $('video').clientHeight) / 2}px`
: 0; : 0;
}, 250); }, 250);
module.exports.moveVolumeHud = moveVolumeHud;
const hideVolumeHud = debounce((volumeHud) => { const hideVolumeHud = debounce((volumeHud) => {
volumeHud.style.opacity = 0; volumeHud.style.opacity = 0;
@ -215,7 +216,7 @@ const tooltipTargets = [
]; ];
function setTooltip(volume) { function setTooltip(volume) {
for (target of tooltipTargets) { for (const target of tooltipTargets) {
$(target).title = `${volume}%`; $(target).title = `${volume}%`;
} }
} }

View File

@ -6,7 +6,7 @@ const { setMenuOptions } = require('../../config/plugins');
const promptOptions = require('../../providers/prompt-options'); const promptOptions = require('../../providers/prompt-options');
function changeOptions(changedOptions, options, win) { function changeOptions(changedOptions, options, win) {
for (option in changedOptions) { for (const option in changedOptions) {
options[option] = changedOptions[option]; options[option] = changedOptions[option];
} }

View File

@ -15,6 +15,15 @@ module.exports = () => {
}; };
function setup(event) { function setup(event) {
/**
* @type {{
* getAvailableQualityLevels: () => string[],
* getPlaybackQuality: () => string,
* getAvailableQualityLabels: () => string[],
* setPlaybackQualityRange: (quality: string) => void,
* setPlaybackQuality: (quality: string) => void,
* }}
*/
const api = event.detail; const api = event.detail;
$('.top-row-buttons.ytmusic-player').prepend(qualitySettingsButton); $('.top-row-buttons.ytmusic-player').prepend(qualitySettingsButton);

View File

@ -6,7 +6,7 @@ const getSongControls = require('../../providers/song-controls');
const config = require('../../config'); const config = require('../../config');
function setupMPRIS() { function setupMPRIS() {
const player = mpris({ return mpris({
name: 'youtube-music', name: 'youtube-music',
identity: 'YouTube Music', identity: 'YouTube Music',
canRaise: true, canRaise: true,
@ -15,8 +15,6 @@ function setupMPRIS() {
supportedInterfaces: ['player'], supportedInterfaces: ['player'],
desktopEntry: 'youtube-music', desktopEntry: 'youtube-music',
}); });
return player;
} }
/** @param {Electron.BrowserWindow} win */ /** @param {Electron.BrowserWindow} win */
@ -108,7 +106,9 @@ function registerMPRIS(win) {
player.on('position', seekTo); player.on('position', seekTo);
player.on('shuffle', (enableShuffle) => { player.on('shuffle', (enableShuffle) => {
if (enableShuffle) {
shuffle(); shuffle();
}
}); });
let mprisVolNewer = false; let mprisVolNewer = false;

View File

@ -18,7 +18,7 @@ const data = {
const post = async (data) => { const post = async (data) => {
const port = 1608; const port = 1608;
headers = { const headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Headers': '*', 'Access-Control-Allow-Headers': '*',
@ -43,15 +43,6 @@ module.exports = async (win) => {
data.progress = secToMilisec(t); data.progress = secToMilisec(t);
post(data); post(data);
}); });
ipcMain.on('playPaused', (_, { isPaused, elapsedSeconds }) => {
if (!data.title) {
return;
}
data.status = isPaused ? 'stopped' : 'playing';
data.progress = secToMilisec(elapsedSeconds);
post(data);
});
registerCallback((songInfo) => { registerCallback((songInfo) => {
if (!songInfo.title && !songInfo.artist) { if (!songInfo.title && !songInfo.artist) {

View File

@ -2,6 +2,6 @@ const path = require('node:path');
const { injectCSS } = require('../utils'); const { injectCSS } = require('../utils');
module.exports = (win, options) => { module.exports = (win) => {
injectCSS(win.webContents, path.join(__dirname, 'empty-player.css')); injectCSS(win.webContents, path.join(__dirname, 'empty-player.css'));
}; };

View File

@ -23,6 +23,7 @@ class WaveVisualizer {
} }
} }
// eslint-disable-next-line no-unused-vars
resize(width, height) { resize(width, height) {
} }

View File

@ -17,19 +17,21 @@ ipcRenderer.on('update-song-info', async (_, extractedSongInfo) => {
// Used because 'loadeddata' or 'loadedmetadata' weren't firing on song start for some users (https://github.com/th-ch/youtube-music/issues/473) // Used because 'loadeddata' or 'loadedmetadata' weren't firing on song start for some users (https://github.com/th-ch/youtube-music/issues/473)
const srcChangedEvent = new CustomEvent('srcChanged'); const srcChangedEvent = new CustomEvent('srcChanged');
module.exports.setupSeekedListener = singleton(() => { const setupSeekedListener = singleton(() => {
$('video')?.addEventListener('seeked', (v) => ipcRenderer.send('seeked', v.target.currentTime)); $('video')?.addEventListener('seeked', (v) => ipcRenderer.send('seeked', v.target.currentTime));
}); });
module.exports.setupSeekedListener = setupSeekedListener;
module.exports.setupTimeChangedListener = singleton(() => { const setupTimeChangedListener = singleton(() => {
const progressObserver = new MutationObserver((mutations) => { const progressObserver = new MutationObserver((mutations) => {
ipcRenderer.send('timeChanged', mutations[0].target.value); ipcRenderer.send('timeChanged', mutations[0].target.value);
global.songInfo.elapsedSeconds = mutations[0].target.value; global.songInfo.elapsedSeconds = mutations[0].target.value;
}); });
progressObserver.observe($('#progress-bar'), { attributeFilter: ['value'] }); progressObserver.observe($('#progress-bar'), { attributeFilter: ['value'] });
}); });
module.exports.setupTimeChangedListener = setupTimeChangedListener;
module.exports.setupRepeatChangedListener = singleton(() => { const setupRepeatChangedListener = singleton(() => {
const repeatObserver = new MutationObserver((mutations) => { const repeatObserver = new MutationObserver((mutations) => {
ipcRenderer.send('repeatChanged', mutations[0].target.__dataHost.getState().queue.repeatMode); ipcRenderer.send('repeatChanged', mutations[0].target.__dataHost.getState().queue.repeatMode);
}); });
@ -38,53 +40,66 @@ module.exports.setupRepeatChangedListener = singleton(() => {
// Emit the initial value as well; as it's persistent between launches. // Emit the initial value as well; as it's persistent between launches.
ipcRenderer.send('repeatChanged', $('ytmusic-player-bar').getState().queue.repeatMode); ipcRenderer.send('repeatChanged', $('ytmusic-player-bar').getState().queue.repeatMode);
}); });
module.exports.setupRepeatChangedListener = setupRepeatChangedListener;
module.exports.setupVolumeChangedListener = singleton((api) => { const setupVolumeChangedListener = singleton((api) => {
$('video').addEventListener('volumechange', (_) => { $('video').addEventListener('volumechange', () => {
ipcRenderer.send('volumeChanged', api.getVolume()); ipcRenderer.send('volumeChanged', api.getVolume());
}); });
// Emit the initial value as well; as it's persistent between launches. // Emit the initial value as well; as it's persistent between launches.
ipcRenderer.send('volumeChanged', api.getVolume()); ipcRenderer.send('volumeChanged', api.getVolume());
}); });
module.exports.setupVolumeChangedListener = setupVolumeChangedListener;
module.exports = () => { module.exports = () => {
document.addEventListener('apiLoaded', (apiEvent) => { document.addEventListener('apiLoaded', (apiEvent) => {
ipcRenderer.on('setupTimeChangedListener', async () => { ipcRenderer.on('setupTimeChangedListener', async () => {
this.setupTimeChangedListener(); setupTimeChangedListener();
}); });
ipcRenderer.on('setupRepeatChangedListener', async () => { ipcRenderer.on('setupRepeatChangedListener', async () => {
this.setupRepeatChangedListener(); setupRepeatChangedListener();
}); });
ipcRenderer.on('setupVolumeChangedListener', async () => { ipcRenderer.on('setupVolumeChangedListener', async () => {
this.setupVolumeChangedListener(apiEvent.detail); setupVolumeChangedListener(apiEvent.detail);
}); });
ipcRenderer.on('setupSeekedListener', async () => { ipcRenderer.on('setupSeekedListener', async () => {
this.setupSeekedListener(); setupSeekedListener();
}); });
const video = $('video'); const playPausedHandler = (e, status) => {
// Name = "dataloaded" and abit later "dataupdated"
apiEvent.detail.addEventListener('videodatachange', (name, _dataEvent) => {
if (name !== 'dataloaded') {
return;
}
video.dispatchEvent(srcChangedEvent);
setTimeout(sendSongInfo, 200);
});
for (const status of ['playing', 'pause']) {
video.addEventListener(status, (e) => {
if (Math.round(e.target.currentTime) > 0) { if (Math.round(e.target.currentTime) > 0) {
ipcRenderer.send('playPaused', { ipcRenderer.send('playPaused', {
isPaused: status === 'pause', isPaused: status === 'pause',
elapsedSeconds: Math.floor(e.target.currentTime), elapsedSeconds: Math.floor(e.target.currentTime),
}); });
} }
};
const playPausedHandlers = {
playing: (e) => playPausedHandler(e, 'playing'),
pause: (e) => playPausedHandler(e, 'pause'),
};
const video = $('video');
// Name = "dataloaded" and abit later "dataupdated"
apiEvent.detail.addEventListener('videodatachange', (name) => {
if (name !== 'dataloaded') {
return;
}
video.dispatchEvent(srcChangedEvent);
for (const status of ['playing', 'pause']) { // for fix issue that pause event not fired
video.addEventListener(status, playPausedHandlers[status]);
}
setTimeout(sendSongInfo, 200);
}); });
for (const status of ['playing', 'pause']) {
video.addEventListener(status, playPausedHandlers[status]);
} }
function sendSongInfo() { function sendSongInfo() {