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

@ -2,9 +2,9 @@
// https://robwu.nl/crxviewer/?crx=https%3A%2F%2Faddons.mozilla.org%2Fen-US%2Ffirefox%2Faddon%2Fadblock-for-youtube%2F
/*
Parts of this code is derived from set-constant.js:
https://github.com/gorhill/uBlock/blob/5de0ce975753b7565759ac40983d31978d1f84ca/assets/resources/scriptlets.js#L704
*/
Parts of this code is derived from set-constant.js:
https://github.com/gorhill/uBlock/blob/5de0ce975753b7565759ac40983d31978d1f84ca/assets/resources/scriptlets.js#L704
*/
{
const pruner = function (o) {

View File

@ -15,347 +15,342 @@
* 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
const validateVolumeLevel = (value) => {
// Number between 0 and 1?
if (!Number.isNaN(value) && value >= 0 && value <= 1) {
// Yup, that's fine
// Internal utility: check if value is a valid volume level and throw if not
const validateVolumeLevel = (value) => {
// Number between 0 and 1?
if (!Number.isNaN(value) && value >= 0 && value <= 1) {
// Yup, that's fine
} else {
// Abort and throw an exception
throw new TypeError('Number between 0 and 1 expected as volume!');
}
};
// Main class
class VolumeFader {
/**
* VolumeFader Constructor
*
* @param media {HTMLMediaElement} - audio or video element to be controlled
* @param options {Object} - an object with optional settings
* @throws {TypeError} if options.initialVolume or options.fadeDuration are invalid
*
* options:
* .logger: {Function} logging `function(stuff, …)` for execution information (default: no logging)
* .fadeScaling: {Mixed} either 'linear', 'logarithmic' or a positive number in dB (default: logarithmic)
* .initialVolume: {Number} media volume 0…1 to apply during setup (volume not touched by default)
* .fadeDuration: {Number} time in milliseconds to complete a fade (default: 1000 ms)
*/
constructor(media, options) {
// Passed media element of correct type?
if (media instanceof HTMLMediaElement) {
// Save reference to media element
this.media = media;
} else {
// Abort and throw an exception
throw new TypeError('Number between 0 and 1 expected as volume!');
}
};
// Main class
class VolumeFader {
/**
* VolumeFader Constructor
*
* @param media {HTMLMediaElement} - audio or video element to be controlled
* @param options {Object} - an object with optional settings
* @throws {TypeError} if options.initialVolume or options.fadeDuration are invalid
*
* options:
* .logger: {Function} logging `function(stuff, …)` for execution information (default: no logging)
* .fadeScaling: {Mixed} either 'linear', 'logarithmic' or a positive number in dB (default: logarithmic)
* .initialVolume: {Number} media volume 0…1 to apply during setup (volume not touched by default)
* .fadeDuration: {Number} time in milliseconds to complete a fade (default: 1000 ms)
*/
constructor(media, options) {
// Passed media element of correct type?
if (media instanceof HTMLMediaElement) {
// Save reference to media element
this.media = media;
} else {
// Abort and throw an exception
throw new TypeError('Media element expected!');
}
// Make sure options is an object
options = options || {};
// Log function passed?
if (typeof options.logger === 'function') {
// Set log function to the one specified
this.logger = options.logger;
} else {
// Set log function explicitly to false
this.logger = false;
}
// Linear volume fading?
if (options.fadeScaling == 'linear') {
// Pass levels unchanged
this.scale = {
internalToVolume: (level) => level,
volumeToInternal: (level) => level,
};
// Log setting
this.logger && this.logger('Using linear fading.');
}
// No linear, but logarithmic fading…
else {
let dynamicRange;
// Default dynamic range?
if (
options.fadeScaling === undefined
|| options.fadeScaling == 'logarithmic'
) {
// Set default of 60 dB
dynamicRange = 3;
}
// Custom dynamic range?
else if (
!Number.isNaN(options.fadeScaling)
&& options.fadeScaling > 0
) {
// Turn amplitude dB into a multiple of 10 power dB
dynamicRange = options.fadeScaling / 2 / 10;
}
// Unsupported value
else {
// Abort and throw exception
throw new TypeError(
"Expected 'linear', 'logarithmic' or a positive number as fade scaling preference!",
);
}
// Use exponential/logarithmic scaler for expansion/compression
this.scale = {
internalToVolume: (level) =>
this.exponentialScaler(level, dynamicRange),
volumeToInternal: (level) =>
this.logarithmicScaler(level, dynamicRange),
};
// Log setting if not default
options.fadeScaling
&& this.logger
&& this.logger(
'Using logarithmic fading with '
+ String(10 * dynamicRange)
+ ' dB dynamic range.',
);
}
// Set initial volume?
if (options.initialVolume !== undefined) {
// Validate volume level and throw if invalid
validateVolumeLevel(options.initialVolume);
// Set initial volume
this.media.volume = options.initialVolume;
// Log setting
this.logger
&& this.logger(
'Set initial volume to ' + String(this.media.volume) + '.',
);
}
// Fade duration given?
if (options.fadeDuration === undefined) {
// Set default fade duration (1000 ms)
this.fadeDuration = 1000;
} else {
// Try to set given fade duration (will log if successful and throw if not)
this.setFadeDuration(options.fadeDuration);
}
// Indicate that fader is not active yet
this.active = false;
// Initialization done
this.logger && this.logger('Initialized for', this.media);
throw new TypeError('Media element expected!');
}
/**
* Re(start) the update cycle.
* (this.active must be truthy for volume updates to take effect)
*
* @return {Object} VolumeFader instance for chaining
*/
start() {
// Set fader to be active
this.active = true;
// Make sure options is an object
options = options || {};
// Start by running the update method
this.updateVolume();
// Return instance for chaining
return this;
// Log function passed?
if (typeof options.logger === 'function') {
// Set log function to the one specified
this.logger = options.logger;
} else {
// Set log function explicitly to false
this.logger = false;
}
/**
* Stop the update cycle.
* (interrupting any fade)
*
* @return {Object} VolumeFader instance for chaining
*/
stop() {
// Set fader to be inactive
this.active = false;
// Return instance for chaining
return this;
}
/**
* Set fade duration.
* (used for future calls to fadeTo)
*
* @param {Number} fadeDuration - fading length in milliseconds
* @throws {TypeError} if fadeDuration is not a number greater than zero
* @return {Object} VolumeFader instance for chaining
*/
setFadeDuration(fadeDuration) {
// If duration is a valid number > 0…
if (!Number.isNaN(fadeDuration) && fadeDuration > 0) {
// Set fade duration
this.fadeDuration = fadeDuration;
// Log setting
this.logger
&& this.logger('Set fade duration to ' + String(fadeDuration) + ' ms.');
} else {
// Abort and throw an exception
throw new TypeError('Positive number expected as fade duration!');
}
// Return instance for chaining
return this;
}
/**
* Define a new fade and start fading.
*
* @param {Number} targetVolume - level to fade to in the range 0…1
* @param {Function} callback - (optional) function to be called when fade is complete
* @throws {TypeError} if targetVolume is not in the range 0…1
* @return {Object} VolumeFader instance for chaining
*/
fadeTo(targetVolume, callback) {
// Validate volume and throw if invalid
validateVolumeLevel(targetVolume);
// Define new fade
this.fade = {
// Volume start and end point on internal fading scale
volume: {
start: this.scale.volumeToInternal(this.media.volume),
end: this.scale.volumeToInternal(targetVolume),
},
// Time start and end point
time: {
start: Date.now(),
end: Date.now() + this.fadeDuration,
},
// Optional callback function
callback,
// Linear volume fading?
if (options.fadeScaling === 'linear') {
// Pass levels unchanged
this.scale = {
internalToVolume: (level) => level,
volumeToInternal: (level) => level,
};
// Start fading
this.start();
// Log new fade
this.logger && this.logger('New fade started:', this.fade);
// Return instance for chaining
return this;
// Log setting
this.logger && this.logger('Using linear fading.');
}
// No linear, but logarithmic fading…
else {
let dynamicRange;
// Convenience shorthand methods for common fades
fadeIn(callback) {
this.fadeTo(1, callback);
}
fadeOut(callback) {
this.fadeTo(0, callback);
}
/**
* Internal: Update media volume.
* (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() {
// Fader active and fade available to process?
if (this.active && this.fade) {
// Get current time
const now = Date.now();
// Time left for fading?
if (now < this.fade.time.end) {
// Compute current fade progress
const progress
= (now - this.fade.time.start)
/ (this.fade.time.end - this.fade.time.start);
// Compute current level on internal scale
const level
= progress * (this.fade.volume.end - this.fade.volume.start)
+ this.fade.volume.start;
// Map fade level to volume level and apply it to media element
this.media.volume = this.scale.internalToVolume(level);
// Schedule next update
root.requestAnimationFrame(this.updateVolume.bind(this));
} else {
// Log end of fade
this.logger
&& this.logger(
'Fade to ' + String(this.fade.volume.end) + ' complete.',
);
// Time is up, jump to target volume
this.media.volume = this.scale.internalToVolume(this.fade.volume.end);
// Set fader to be inactive
this.active = false;
// Done, call back (if callable)
typeof this.fade.callback === 'function' && this.fade.callback();
// Clear fade
this.fade = undefined;
}
// Default dynamic range?
if (
options.fadeScaling === undefined
|| options.fadeScaling === 'logarithmic'
) {
// Set default of 60 dB
dynamicRange = 3;
}
}
/**
* Internal: Exponential scaler with dynamic range limit.
*
* @param {Number} input - logarithmic input level to be expanded (float, 0…1)
* @param {Number} dynamicRange - expanded output range, in multiples of 10 dB (float, 0…∞)
* @return {Number} - expanded level (float, 0…1)
*/
exponentialScaler(input, dynamicRange) {
// Special case: make zero (or any falsy input) return zero
if (input == 0) {
// Since the dynamic range is limited,
// allow a zero to produce a plain zero instead of a small faction
// (audio would not be recognized as silent otherwise)
return 0;
// Custom dynamic range?
else if (
!Number.isNaN(options.fadeScaling)
&& options.fadeScaling > 0
) {
// Turn amplitude dB into a multiple of 10 power dB
dynamicRange = options.fadeScaling / 2 / 10;
}
// Unsupported value
else {
// Abort and throw exception
throw new TypeError(
"Expected 'linear', 'logarithmic' or a positive number as fade scaling preference!",
);
}
// Scale 0…1 to minus something × 10 dB
input = (input - 1) * dynamicRange;
// Use exponential/logarithmic scaler for expansion/compression
this.scale = {
internalToVolume: (level) =>
this.exponentialScaler(level, dynamicRange),
volumeToInternal: (level) =>
this.logarithmicScaler(level, dynamicRange),
};
// Compute power of 10
return 10 ** input;
// Log setting if not default
options.fadeScaling
&& this.logger
&& this.logger(
'Using logarithmic fading with '
+ String(10 * dynamicRange)
+ ' dB dynamic range.',
);
}
/**
* Internal: Logarithmic scaler with dynamic range limit.
*
* @param {Number} input - exponential input level to be compressed (float, 0…1)
* @param {Number} dynamicRange - coerced input range, in multiples of 10 dB (float, 0…∞)
* @return {Number} - compressed level (float, 0…1)
*/
logarithmicScaler(input, dynamicRange) {
// Special case: make zero (or any falsy input) return zero
if (input == 0) {
// Logarithm of zero would be -∞, which would map to zero anyway
return 0;
// Set initial volume?
if (options.initialVolume !== undefined) {
// Validate volume level and throw if invalid
validateVolumeLevel(options.initialVolume);
// Set initial volume
this.media.volume = options.initialVolume;
// Log setting
this.logger
&& this.logger(
'Set initial volume to ' + String(this.media.volume) + '.',
);
}
// Fade duration given?
if (options.fadeDuration === undefined) {
// Set default fade duration (1000 ms)
this.fadeDuration = 1000;
} else {
// Try to set given fade duration (will log if successful and throw if not)
this.setFadeDuration(options.fadeDuration);
}
// Indicate that fader is not active yet
this.active = false;
// Initialization done
this.logger && this.logger('Initialized for', this.media);
}
/**
* Re(start) the update cycle.
* (this.active must be truthy for volume updates to take effect)
*
* @return {Object} VolumeFader instance for chaining
*/
start() {
// Set fader to be active
this.active = true;
// Start by running the update method
this.updateVolume();
// Return instance for chaining
return this;
}
/**
* Stop the update cycle.
* (interrupting any fade)
*
* @return {Object} VolumeFader instance for chaining
*/
stop() {
// Set fader to be inactive
this.active = false;
// Return instance for chaining
return this;
}
/**
* Set fade duration.
* (used for future calls to fadeTo)
*
* @param {Number} fadeDuration - fading length in milliseconds
* @throws {TypeError} if fadeDuration is not a number greater than zero
* @return {Object} VolumeFader instance for chaining
*/
setFadeDuration(fadeDuration) {
// If duration is a valid number > 0…
if (!Number.isNaN(fadeDuration) && fadeDuration > 0) {
// Set fade duration
this.fadeDuration = fadeDuration;
// Log setting
this.logger
&& this.logger('Set fade duration to ' + String(fadeDuration) + ' ms.');
} else {
// Abort and throw an exception
throw new TypeError('Positive number expected as fade duration!');
}
// Return instance for chaining
return this;
}
/**
* Define a new fade and start fading.
*
* @param {Number} targetVolume - level to fade to in the range 0…1
* @param {Function} callback - (optional) function to be called when fade is complete
* @throws {TypeError} if targetVolume is not in the range 0…1
* @return {Object} VolumeFader instance for chaining
*/
fadeTo(targetVolume, callback) {
// Validate volume and throw if invalid
validateVolumeLevel(targetVolume);
// Define new fade
this.fade = {
// Volume start and end point on internal fading scale
volume: {
start: this.scale.volumeToInternal(this.media.volume),
end: this.scale.volumeToInternal(targetVolume),
},
// Time start and end point
time: {
start: Date.now(),
end: Date.now() + this.fadeDuration,
},
// Optional callback function
callback,
};
// Start fading
this.start();
// Log new fade
this.logger && this.logger('New fade started:', this.fade);
// Return instance for chaining
return this;
}
// Convenience shorthand methods for common fades
fadeIn(callback) {
this.fadeTo(1, callback);
}
fadeOut(callback) {
this.fadeTo(0, callback);
}
/**
* Internal: Update media volume.
* (calls itself through requestAnimationFrame)
*/
updateVolume() {
// Fader active and fade available to process?
if (this.active && this.fade) {
// Get current time
const now = Date.now();
// Time left for fading?
if (now < this.fade.time.end) {
// Compute current fade progress
const progress
= (now - this.fade.time.start)
/ (this.fade.time.end - this.fade.time.start);
// Compute current level on internal scale
const level
= (progress * (this.fade.volume.end - this.fade.volume.start)) + this.fade.volume.start;
// Map fade level to volume level and apply it to media element
this.media.volume = this.scale.internalToVolume(level);
// Schedule next update
window.requestAnimationFrame(this.updateVolume.bind(this));
} else {
// Log end of fade
this.logger
&& this.logger(
'Fade to ' + String(this.fade.volume.end) + ' complete.',
);
// Time is up, jump to target volume
this.media.volume = this.scale.internalToVolume(this.fade.volume.end);
// Set fader to be inactive
this.active = false;
// Done, call back (if callable)
typeof this.fade.callback === 'function' && this.fade.callback();
// Clear fade
this.fade = undefined;
}
// Compute base-10 logarithm
input = Math.log10(input);
// Scale minus something × 10 dB to 0…1 (clipping at 0)
return Math.max(1 + input / dynamicRange, 0);
}
}
// Export class to root scope
root.VolumeFader = VolumeFader;
})(window);
/**
* Internal: Exponential scaler with dynamic range limit.
*
* @param {Number} input - logarithmic input level to be expanded (float, 0…1)
* @param {Number} dynamicRange - expanded output range, in multiples of 10 dB (float, 0…∞)
* @return {Number} - expanded level (float, 0…1)
*/
exponentialScaler(input, dynamicRange) {
// Special case: make zero (or any falsy input) return zero
if (input === 0) {
// Since the dynamic range is limited,
// allow a zero to produce a plain zero instead of a small faction
// (audio would not be recognized as silent otherwise)
return 0;
}
// Scale 0…1 to minus something × 10 dB
input = (input - 1) * dynamicRange;
// Compute power of 10
return 10 ** input;
}
/**
* Internal: Logarithmic scaler with dynamic range limit.
*
* @param {Number} input - exponential input level to be compressed (float, 0…1)
* @param {Number} dynamicRange - coerced input range, in multiples of 10 dB (float, 0…∞)
* @return {Number} - compressed level (float, 0…1)
*/
logarithmicScaler(input, dynamicRange) {
// Special case: make zero (or any falsy input) return zero
if (input === 0) {
// Logarithm of zero would be -∞, which would map to zero anyway
return 0;
}
// Compute base-10 logarithm
input = Math.log10(input);
// Scale minus something × 10 dB to 0…1 (clipping at 0)
return Math.max(1 + (input / dynamicRange), 0);
}
}
module.exports = {
VolumeFader
};

View File

@ -2,23 +2,25 @@ const { ipcRenderer } = require('electron');
const { Howl } = require('howler');
// 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 firstVideo = true;
let waitForTransition;
const defaultConfig = require('../../config/defaults').plugins.crossfade;
/**
* @type {PluginConfig}
*/
const configProvider = require('./config');
const defaultConfig = require('../../config/defaults').plugins.crossfade;
let config;
const configGetNumber = (key) => Number(config[key]) || defaultConfig[key];
const getStreamURL = async (videoID) => {
const url = await ipcRenderer.invoke('audio-url', videoID);
return url;
return await ipcRenderer.invoke('audio-url', videoID);
};
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 watchVideoIDChanges = (cb) => {
navigation.addEventListener('navigate', (event) => {
window.navigation.addEventListener('navigate', (event) => {
const currentVideoID = getVideoIDFromURL(
event.currentTarget.currentEntry.url,
);
@ -126,7 +128,7 @@ const crossfade = async (cb) => {
}
let resolveTransition;
waitForTransition = new Promise((resolve, reject) => {
waitForTransition = new Promise((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 config = require('./config');
const promptOptions = require('../../providers/prompt-options');
const defaultOptions = require('../../config/defaults').plugins.crossfade;
module.exports = (win) => [
{

View File

@ -174,10 +174,10 @@ module.exports = (win, { autoReconnect, activityTimoutEnabled, activityTimoutTim
}
} else if (!hideDurationLeft) {
// 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.endTimestamp
= songStartTime + songInfo.songDuration * 1000;
= songStartTime + (songInfo.songDuration * 1000);
}
info.rpc.user?.setActivity(activityInfo).catch(console.error);

View File

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

View File

@ -41,7 +41,7 @@ const menuObserver = new MutationObserver(() => {
// TODO: re-enable once contextIsolation is set to true
// contextBridge.exposeInMainWorld("downloader", {
// download: () => {
// download: () => {
global.download = () => {
let videoUrl = getSongMenu()
// Selector of first button which is always "Start Radio"

View File

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

View File

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

View File

@ -8,7 +8,7 @@ const fetch = require('node-fetch');
const { cleanupName } = require('../../providers/song-info');
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;
module.exports = async (win, options) => {
@ -89,7 +89,7 @@ const getLyricsList = async (queryString) => {
* @returns The lyrics of the song URL provided, null if none
*/
const getLyrics = async (url) => {
response = await fetch(url);
const response = await fetch(url);
if (!response.ok) {
return null;
}

View File

@ -65,14 +65,14 @@ module.exports = () => {
function setLyrics(lyricsContainer) {
lyricsContainer.innerHTML = `<div id="contents" class="style-scope ytmusic-section-list-renderer description ytmusic-description-shelf-renderer genius-lyrics">
${
${
hasLyrics
? lyrics.replaceAll(/\r\n|\r|\n/g, '<br/>')
: 'Could not retrieve lyrics from genius'
}
</div>
<yt-formatted-string class="footer style-scope ytmusic-description-shelf-renderer" style="align-self: baseline"></yt-formatted-string>`;
</div>
<yt-formatted-string class="footer style-scope ytmusic-description-shelf-renderer" style="align-self: baseline"></yt-formatted-string>`;
if (hasLyrics) {
lyricsContainer.querySelector('.footer').textContent = 'Source: Genius';
enableLyricsTab();

View File

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

View File

@ -15,7 +15,7 @@ function removeLoginElements() {
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';
const observer = new MutationObserver(() => {
menuEntries = document.querySelectorAll(
const menuEntries = document.querySelectorAll(
'#items ytmusic-guide-entry-renderer',
);
for (const item of menuEntries) {

View File

@ -2,8 +2,11 @@ const path = require('node:path');
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 getSongControls = require('../../providers/song-controls');
@ -25,7 +28,7 @@ module.exports = (win) => {
ipcMain.on('timeChanged', (_, t) => currentSeconds = t);
if (app.isPackaged) {
save_temp_icons();
saveTempIcon();
}
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/adaptive-interactive-toasts?tabs=xml
// 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.show();
}
const get_xml = (songInfo, iconSrc) => {
const getXml = (songInfo, iconSrc) => {
switch (config.get('toastStyle')) {
default:
case ToastStyles.logo:
case ToastStyles.legacy: {
return xml_logo(songInfo, iconSrc);
return xmlLogo(songInfo, iconSrc);
}
case ToastStyles.banner_top_custom: {
return xml_banner_top_custom(songInfo, iconSrc);
return xmlBannerTopCustom(songInfo, iconSrc);
}
case ToastStyles.hero: {
return xml_hero(songInfo, iconSrc);
return xmlHero(songInfo, iconSrc);
}
case ToastStyles.banner_bottom: {
return xml_banner_bottom(songInfo, iconSrc);
return xmlBannerBottom(songInfo, iconSrc);
}
case ToastStyles.banner_centered_bottom: {
return xml_banner_centered_bottom(songInfo, iconSrc);
return xmlBannerCenteredBottom(songInfo, iconSrc);
}
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)}
</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}/>
<text id="1">${title}</text>
<text id="2">${artist}</text>\
`, 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" />
<text></text>
<group>
@ -206,11 +209,11 @@ const xml_banner_top_custom = (songInfo, imgSrc) => toast(`\
<text hint-style="body">${songInfo.title}</text>
<text hint-style="captionSubtle">${songInfo.artist}</text>
</subgroup>
${xml_more_data(songInfo)}
${xmlMoreData(songInfo)}
</group>\
`, songInfo.isPaused);
const xml_more_data = ({ album, elapsedSeconds, songDuration }) => `\
const xmlMoreData = ({ album, elapsedSeconds, songDuration }) => `\
<subgroup hint-textStacking="bottom">
${album
? `<text hint-style="captionSubtle" hint-wrap="true" hint-align="right">${album}</text>` : ''}
@ -218,7 +221,7 @@ const xml_more_data = ({ album, elapsedSeconds, songDuration }) => `\
</subgroup>\
`;
const xml_banner_centered_bottom = ({ title, artist, isPaused }, imgSrc) => toast(`\
const xmlBannerCenteredBottom = ({ title, artist, isPaused }, imgSrc) => toast(`\
<text></text>
<group>
<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" />\
`, 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" />
<text></text>
<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 {
fs.writeFileSync(save_path, img.toPNG());
fs.writeFileSync(savePath, img.toPNG());
} catch (error) {
console.log(`Error writing song icon to disk:\n${error.toString()}`);
return icon;
}
return save_path;
return savePath;
});
module.exports.save_temp_icons = () => {
module.exports.saveTempIcon = () => {
for (const kind of Object.keys(module.exports.icons)) {
const destinationPath = path.join(userData, 'icons', `${kind}.png`);
if (fs.existsSync(destinationPath)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,12 +17,13 @@ class WaveVisualizer {
for (const animation of options.animations) {
this.visualizer.addAnimation(
eval(`new this.visualizer.animations.${animation.type}(
${JSON.stringify(animation.config)}
)`),
${JSON.stringify(animation.config)}
)`),
);
}
}
// eslint-disable-next-line no-unused-vars
resize(width, height) {
}