feat: enable context-isolation (#1361)

This commit is contained in:
JellyBrick
2023-11-06 17:21:29 +09:00
committed by GitHub
parent 6e52178074
commit 6366dc026e
35 changed files with 655 additions and 474 deletions

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/await-thenable */
/* renderer */
import { blockers } from './blocker-types';

View File

@ -1,6 +1,6 @@
import { FastAverageColor } from 'fast-average-color';
import { ConfigType } from '../../config/dynamic';
import type { ConfigType } from '../../config/dynamic';
function hexToHSL(H: string) {
// Convert hex to RGB first

View File

@ -1,6 +1,4 @@
import { ipcRenderer } from 'electron';
import { ConfigType } from '../../config/dynamic';
import type { ConfigType } from '../../config/dynamic';
export default (config: ConfigType<'ambient-mode'>) => {
let interpolationTime = config.interpolationTime; // interpolation time (ms)
@ -30,7 +28,7 @@ export default (config: ConfigType<'ambient-mode'>) => {
/* effect */
let lastEffectWorkId: number | null = null;
let lastImageData: ImageData | null = null;
const onSync = () => {
if (typeof lastEffectWorkId === 'number') cancelAnimationFrame(lastEffectWorkId);
@ -40,6 +38,7 @@ export default (config: ConfigType<'ambient-mode'>) => {
const width = qualityRatio;
let height = Math.max(Math.floor(blurCanvas.height / blurCanvas.width * width), 1);
if (!Number.isFinite(height)) height = width;
if (!height) return;
context.globalAlpha = 1;
if (lastImageData) {
@ -50,8 +49,7 @@ export default (config: ConfigType<'ambient-mode'>) => {
}
context.drawImage(video, 0, 0, width, height);
const nowImageData = context.getImageData(0, 0, width, height);
lastImageData = nowImageData;
lastImageData = context.getImageData(0, 0, width, height); // current image data
lastEffectWorkId = null;
});
@ -102,8 +100,8 @@ export default (config: ConfigType<'ambient-mode'>) => {
applyVideoAttributes();
};
ipcRenderer.on('ambient-mode:config-change', onConfigSync);
window.ipcRenderer.on('ambient-mode:config-change', onConfigSync);
/* hooking */
let canvasInterval: NodeJS.Timeout | null = null;
canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / buffer)));
@ -135,17 +133,17 @@ export default (config: ConfigType<'ambient-mode'>) => {
observer.disconnect();
resizeObserver.disconnect();
ipcRenderer.off('ambient-mode:config-change', onConfigSync);
window.ipcRenderer.removeListener('ambient-mode:config-change', onConfigSync);
window.removeEventListener('resize', applyVideoAttributes);
wrapper.removeChild(blurCanvas);
};
};
const playerPage = document.querySelector<HTMLElement>('#player-page');
const ytmusicAppLayout = document.querySelector<HTMLElement>('#layout');
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes') {
@ -164,4 +162,4 @@ export default (config: ConfigType<'ambient-mode'>) => {
if (playerPage) {
observer.observe(playerPage, { attributes: true });
}
};
};

View File

@ -0,0 +1,4 @@
import { PluginConfig } from '../../config/dynamic-renderer';
const configRenderer = new PluginConfig('captions-selector', { enableFront: true });
export default configRenderer;

View File

@ -1,13 +1,8 @@
/* eslint-disable @typescript-eslint/await-thenable */
/* renderer */
import { ipcRenderer } from 'electron';
import configProvider from './config';
import configProvider from './config-renderer';
import CaptionsSettingsButtonHTML from './templates/captions-settings-template.html';
import { ElementFromHtml } from '../utils';
import { ElementFromHtml } from '../utils-renderer';
import { YoutubePlayer } from '../../types/youtube-player';
import type { ConfigType } from '../../config/dynamic';
@ -25,18 +20,17 @@ interface LanguageOptions {
vss_id: string;
}
let config: ConfigType<'captions-selector'>;
let captionsSelectorConfig: ConfigType<'captions-selector'>;
const $ = <Element extends HTMLElement>(selector: string): Element => document.querySelector(selector)!;
const captionsSettingsButton = ElementFromHtml(CaptionsSettingsButtonHTML);
export default async () => {
// RENDERER
config = await configProvider.getAll();
export default () => {
captionsSelectorConfig = configProvider.getAll();
configProvider.subscribeAll((newConfig) => {
config = newConfig;
captionsSelectorConfig = newConfig;
});
document.addEventListener('apiLoaded', (event) => setup(event.detail), { once: true, passive: true });
};
@ -47,7 +41,7 @@ function setup(api: YoutubePlayer) {
let captionTrackList = api.getOption<LanguageOptions[]>('captions', 'tracklist') ?? [];
$('video').addEventListener('srcChanged', () => {
if (config.disableCaptions) {
if (captionsSelectorConfig.disableCaptions) {
setTimeout(() => api.unloadModule('captions'), 100);
captionsSettingsButton.style.display = 'none';
return;
@ -58,9 +52,9 @@ function setup(api: YoutubePlayer) {
setTimeout(() => {
captionTrackList = api.getOption('captions', 'tracklist') ?? [];
if (config.autoload && config.lastCaptionsCode) {
if (captionsSelectorConfig.autoload && captionsSelectorConfig.lastCaptionsCode) {
api.setOption('captions', 'track', {
languageCode: config.lastCaptionsCode,
languageCode: captionsSelectorConfig.lastCaptionsCode,
});
}
@ -82,7 +76,7 @@ function setup(api: YoutubePlayer) {
'None',
];
currentIndex = await ipcRenderer.invoke('captionsSelector', captionLabels, currentIndex) as number;
currentIndex = await window.ipcRenderer.invoke('captionsSelector', captionLabels, currentIndex) as number;
if (currentIndex === null) {
return;
}

View File

@ -0,0 +1,4 @@
import { PluginConfig } from '../../config/dynamic-renderer';
const config = new PluginConfig('crossfade', { enableFront: true });
export default config;

View File

@ -1,13 +1,9 @@
/* eslint-disable @typescript-eslint/await-thenable */
/* renderer */
import { ipcRenderer } from 'electron';
import { Howl } from 'howler';
// Extracted from https://github.com/bitfasching/VolumeFader
import { VolumeFader } from './fader';
import configProvider from './config';
import configProvider from './config-renderer';
import defaultConfigs from '../../config/defaults';
@ -19,11 +15,11 @@ let waitForTransition: Promise<unknown>;
const defaultConfig = defaultConfigs.plugins.crossfade;
let config: ConfigType<'crossfade'>;
let crossfadeConfig: ConfigType<'crossfade'>;
const configGetNumber = (key: keyof ConfigType<'crossfade'>): number => Number(config[key]) || (defaultConfig[key] as number);
const configGetNumber = (key: keyof ConfigType<'crossfade'>): number => Number(crossfadeConfig[key]) || (defaultConfig[key] as number);
const getStreamURL = async (videoID: string) => ipcRenderer.invoke('audio-url', videoID) as Promise<string>;
const getStreamURL = async (videoID: string) => window.ipcRenderer.invoke('audio-url', videoID) as Promise<string>;
const getVideoIDFromURL = (url: string) => new URLSearchParams(url.split('?')?.at(-1)).get('v');
@ -119,7 +115,7 @@ const onApiLoaded = () => {
return;
}
await createAudioForCrossfade(url);
createAudioForCrossfade(url);
});
};
@ -150,11 +146,11 @@ const crossfade = (cb: () => void) => {
});
};
export default async () => {
config = await configProvider.getAll();
export default () => {
crossfadeConfig = configProvider.getAll();
configProvider.subscribeAll((newConfig) => {
config = newConfig;
crossfadeConfig = newConfig;
});
document.addEventListener('apiLoaded', onApiLoaded, {

View File

@ -1,10 +1,8 @@
import { ipcRenderer } from 'electron';
import downloadHTML from './templates/download.html';
import defaultConfig from '../../config/defaults';
import { getSongMenu } from '../../providers/dom-elements';
import { ElementFromHtml } from '../utils';
import { ElementFromHtml } from '../utils-renderer';
import { getSongInfo } from '../../providers/song-info-front';
let menu: Element | null = null;
@ -13,55 +11,55 @@ const downloadButton = ElementFromHtml(downloadHTML);
let doneFirstLoad = false;
const menuObserver = new MutationObserver(() => {
if (!menu) {
menu = getSongMenu();
if (!menu) {
return;
}
}
if (menu.contains(downloadButton)) {
return;
}
const menuUrl = document.querySelector<HTMLAnchorElement>('tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint')?.href;
if (!menuUrl?.includes('watch?') && doneFirstLoad) {
return;
}
menu.prepend(downloadButton);
progress = document.querySelector('#ytmcustom-download');
if (doneFirstLoad) {
return;
}
setTimeout(() => doneFirstLoad ||= true, 500);
});
window.download = () => {
let videoUrl = getSongMenu()
// Selector of first button which is always "Start Radio"
?.querySelector('ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint')
?.getAttribute('href');
if (videoUrl) {
if (videoUrl.startsWith('watch?')) {
videoUrl = defaultConfig.url + '/' + videoUrl;
}
if (videoUrl.includes('?playlist=')) {
ipcRenderer.send('download-playlist-request', videoUrl);
return;
}
} else {
videoUrl = getSongInfo().url || window.location.href;
}
ipcRenderer.send('download-song', videoUrl);
};
export default () => {
const menuObserver = new MutationObserver(() => {
if (!menu) {
menu = getSongMenu();
if (!menu) {
return;
}
}
if (menu.contains(downloadButton)) {
return;
}
const menuUrl = document.querySelector<HTMLAnchorElement>('tp-yt-paper-listbox [tabindex="-1"] #navigation-endpoint')?.href;
if (!menuUrl?.includes('watch?') && doneFirstLoad) {
return;
}
menu.prepend(downloadButton);
progress = document.querySelector('#ytmcustom-download');
if (doneFirstLoad) {
return;
}
setTimeout(() => doneFirstLoad ||= true, 500);
});
window.download = () => {
let videoUrl = getSongMenu()
// Selector of first button which is always "Start Radio"
?.querySelector('ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint')
?.getAttribute('href');
if (videoUrl) {
if (videoUrl.startsWith('watch?')) {
videoUrl = defaultConfig.url + '/' + videoUrl;
}
if (videoUrl.includes('?playlist=')) {
window.ipcRenderer.send('download-playlist-request', videoUrl);
return;
}
} else {
videoUrl = getSongInfo().url || window.location.href;
}
window.ipcRenderer.send('download-song', videoUrl);
};
document.addEventListener('apiLoaded', () => {
menuObserver.observe(document.querySelector('ytmusic-popup-container')!, {
childList: true,
@ -69,7 +67,7 @@ export default () => {
});
}, { once: true, passive: true });
ipcRenderer.on('downloader-feedback', (_, feedback: string) => {
window.ipcRenderer.on('downloader-feedback', (_, feedback: string) => {
if (progress) {
progress.innerHTML = feedback || 'Download';
} else {

View File

@ -1,6 +1,6 @@
import { register } from 'electron-localshortcut';
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';
import { BrowserWindow, Menu, MenuItem, ipcMain, nativeImage } from 'electron';
import titlebarStyle from './titlebar.css';
@ -64,4 +64,9 @@ export default (win: BrowserWindow) => {
win.on('maximize', () => win.webContents.send('window-maximize'));
ipcMain.handle('window-unmaximize', () => win.unmaximize());
win.on('unmaximize', () => win.webContents.send('window-unmaximize'));
ipcMain.handle('image-path-to-data-url', (_, imagePath: string) => {
const nativeImageIcon = nativeImage.createFromPath(imagePath);
return nativeImageIcon?.toDataURL();
});
};

View File

@ -1,5 +1,3 @@
import { ipcRenderer, Menu } from 'electron';
import { createPanel } from './menu/panel';
import logo from './assets/menu.svg';
@ -8,8 +6,7 @@ import minimize from './assets/minimize.svg';
import maximize from './assets/maximize.svg';
import unmaximize from './assets/unmaximize.svg';
import { isEnabled } from '../../config/plugins';
import config from '../../config';
import type { Menu } from 'electron';
function $<E extends Element = Element>(selector: string) {
return document.querySelector<E>(selector);
@ -19,8 +16,8 @@ const isMacOS = navigator.userAgent.includes('Macintosh');
const isNotWindowsOrMacOS = !navigator.userAgent.includes('Windows') && !isMacOS;
export default async () => {
const hideDOMWindowControls = config.get('plugins.in-app-menu.hideDOMWindowControls');
let hideMenu = config.get('options.hideMenu');
const hideDOMWindowControls = window.mainConfig.get('plugins.in-app-menu.hideDOMWindowControls');
let hideMenu = window.mainConfig.get('options.hideMenu');
const titleBar = document.createElement('title-bar');
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
let maximizeButton: HTMLButtonElement;
@ -42,7 +39,7 @@ export default async () => {
};
logo.onclick = logoClick;
ipcRenderer.on('toggleMenu', logoClick);
window.ipcRenderer.on('toggleMenu', logoClick);
if (!isMacOS) titleBar.appendChild(logo);
document.body.appendChild(titleBar);
@ -55,10 +52,10 @@ export default async () => {
const minimizeButton = document.createElement('button');
minimizeButton.classList.add('window-control');
minimizeButton.appendChild(minimize);
minimizeButton.onclick = () => ipcRenderer.invoke('window-minimize');
minimizeButton.onclick = () => window.ipcRenderer.invoke('window-minimize');
maximizeButton = document.createElement('button');
if (await ipcRenderer.invoke('window-is-maximized')) {
if (await window.ipcRenderer.invoke('window-is-maximized')) {
maximizeButton.classList.add('window-control');
maximizeButton.appendChild(unmaximize);
} else {
@ -66,27 +63,27 @@ export default async () => {
maximizeButton.appendChild(maximize);
}
maximizeButton.onclick = async () => {
if (await ipcRenderer.invoke('window-is-maximized')) {
if (await window.ipcRenderer.invoke('window-is-maximized')) {
// change icon to maximize
maximizeButton.removeChild(maximizeButton.firstChild!);
maximizeButton.appendChild(maximize);
// call unmaximize
await ipcRenderer.invoke('window-unmaximize');
await window.ipcRenderer.invoke('window-unmaximize');
} else {
// change icon to unmaximize
maximizeButton.removeChild(maximizeButton.firstChild!);
maximizeButton.appendChild(unmaximize);
// call maximize
await ipcRenderer.invoke('window-maximize');
await window.ipcRenderer.invoke('window-maximize');
}
};
const closeButton = document.createElement('button');
closeButton.classList.add('window-control');
closeButton.appendChild(close);
closeButton.onclick = () => ipcRenderer.invoke('window-close');
closeButton.onclick = () => window.ipcRenderer.invoke('window-close');
// Create a container div for the window control buttons
const windowControlsContainer = document.createElement('div');
@ -118,7 +115,7 @@ export default async () => {
if (child !== logo) child.remove();
});
const menu = await ipcRenderer.invoke('get-menu') as Menu | null;
const menu = await window.ipcRenderer.invoke('get-menu') as Menu | null;
if (!menu) return;
menu.items.forEach((menuItem) => {
@ -137,22 +134,22 @@ export default async () => {
document.title = 'Youtube Music';
ipcRenderer.on('refreshMenu', () => updateMenu());
ipcRenderer.on('window-maximize', () => {
window.ipcRenderer.on('refreshMenu', () => updateMenu());
window.ipcRenderer.on('window-maximize', () => {
if (isNotWindowsOrMacOS && !hideDOMWindowControls && maximizeButton.firstChild) {
maximizeButton.removeChild(maximizeButton.firstChild);
maximizeButton.appendChild(unmaximize);
}
});
ipcRenderer.on('window-unmaximize', () => {
window.ipcRenderer.on('window-unmaximize', () => {
if (isNotWindowsOrMacOS && !hideDOMWindowControls && maximizeButton.firstChild) {
maximizeButton.removeChild(maximizeButton.firstChild);
maximizeButton.appendChild(unmaximize);
}
});
if (isEnabled('picture-in-picture')) {
ipcRenderer.on('pip-toggle', () => {
if (window.mainConfig.plugins.isEnabled('picture-in-picture')) {
window.ipcRenderer.on('pip-toggle', () => {
updateMenu();
});
}

View File

@ -1,8 +1,8 @@
import { nativeImage, type MenuItem, ipcRenderer, Menu } from 'electron';
import Icons from './icons';
import { ElementFromHtml } from '../../utils';
import { ElementFromHtml } from '../../utils-renderer';
import type { MenuItem } from 'electron';
interface PanelOptions {
placement?: 'bottom' | 'right';
@ -19,7 +19,7 @@ export const createPanel = (
const panel = document.createElement('menu-panel');
panel.style.zIndex = `${options.order}`;
const updateIconState = (iconWrapper: HTMLElement, item: MenuItem) => {
const updateIconState = async (iconWrapper: HTMLElement, item: MenuItem) => {
if (item.type === 'checkbox') {
if (item.checked) iconWrapper.innerHTML = Icons.checkbox;
else iconWrapper.innerHTML = '';
@ -27,8 +27,8 @@ export const createPanel = (
if (item.checked) iconWrapper.innerHTML = Icons.radio.checked;
else iconWrapper.innerHTML = Icons.radio.unchecked;
} else {
const nativeImageIcon = typeof item.icon === 'string' ? nativeImage.createFromPath(item.icon) : item.icon;
const iconURL = nativeImageIcon?.toDataURL();
const iconURL = typeof item.icon === 'string' ?
await window.ipcRenderer.invoke('image-path-to-data-url') as string : item.icon?.toDataURL();
if (iconURL) iconWrapper.style.background = `url(${iconURL})`;
}
@ -46,8 +46,8 @@ export const createPanel = (
menu.append(item.label);
menu.addEventListener('click', async () => {
await ipcRenderer.invoke('menu-event', item.commandId);
const menuItem = await ipcRenderer.invoke('get-menu-by-id', item.commandId) as MenuItem | null;
await window.ipcRenderer.invoke('menu-event', item.commandId);
const menuItem = await window.ipcRenderer.invoke('get-menu-by-id', item.commandId) as MenuItem | null;
if (menuItem) {
updateIconState(iconWrapper, menuItem);
@ -56,7 +56,7 @@ export const createPanel = (
await Promise.all(
radioGroups.map(async ([item, iconWrapper]) => {
if (item.commandId === menuItem.commandId) return;
const newItem = await ipcRenderer.invoke('get-menu-by-id', item.commandId) as MenuItem | null;
const newItem = await window.ipcRenderer.invoke('get-menu-by-id', item.commandId) as MenuItem | null;
if (newItem) updateIconState(iconWrapper, newItem);
})

View File

@ -1,6 +1,3 @@
import { ipcRenderer } from 'electron';
import is from 'electron-is';
import type { SongInfo } from '../../providers/song-info';
export default () => {
@ -22,9 +19,9 @@ export default () => {
}
};
let unregister: (() => void) | null = null;
let unregister: (() => void) | null = null;
ipcRenderer.on('update-song-info', (_, extractedSongInfo: SongInfo) => {
window.ipcRenderer.on('update-song-info', (_, extractedSongInfo: SongInfo) => {
unregister?.();
setTimeout(async () => {
@ -38,7 +35,7 @@ export default () => {
// Check if disabled
if (!tabs.lyrics?.hasAttribute('disabled')) return;
const lyrics = await ipcRenderer.invoke(
const lyrics = await window.ipcRenderer.invoke(
'search-genius-lyrics',
extractedSongInfo,
) as string | null;
@ -50,7 +47,7 @@ export default () => {
return;
}
if (is.dev()) {
if (window.electronIs.dev()) {
console.log('Fetched lyrics from Genius');
}
@ -58,7 +55,7 @@ export default () => {
const lyricsContainer = document.querySelector(
'[page-type="MUSIC_PAGE_TYPE_TRACK_LYRICS"] > ytmusic-message-renderer',
);
if (lyricsContainer) {
callback?.();

View File

@ -1,12 +1,10 @@
import { ipcRenderer } from 'electron';
import forwardHTML from './templates/forward.html';
import backHTML from './templates/back.html';
import { ElementFromHtml } from '../utils';
import { ElementFromHtml } from '../utils-renderer';
export function run() {
ipcRenderer.on('navigation-css-ready', () => {
window.ipcRenderer.on('navigation-css-ready', () => {
const forwardButton = ElementFromHtml(forwardHTML);
const backButton = ElementFromHtml(backHTML);
const menu = document.querySelector('#right-content');

View File

@ -1,4 +1,3 @@
import { ipcRenderer } from 'electron';
import { toKeyEvent } from 'keyboardevent-from-electron-accelerator';
import keyEventAreEqual from 'keyboardevents-areequal';
@ -6,7 +5,7 @@ import pipHTML from './templates/picture-in-picture.html';
import { getSongMenu } from '../../providers/dom-elements';
import { ElementFromHtml } from '../utils';
import { ElementFromHtml } from '../utils-renderer';
import type { ConfigType } from '../../config/dynamic';
@ -85,7 +84,7 @@ const togglePictureInPicture = async () => {
}
}
ipcRenderer.send('picture-in-picture');
window.ipcRenderer.send('picture-in-picture');
return false;
};
// For UI (HTML)
@ -105,7 +104,7 @@ const listenForToggle = () => {
const titlebar = $<HTMLElement>('.cet-titlebar');
ipcRenderer.on('pip-toggle', (_, isPip: boolean) => {
window.ipcRenderer.on('pip-toggle', (_, isPip: boolean) => {
if (originalExitButton && player) {
if (isPip) {
replaceButton('.exit-fullscreen-button', originalExitButton)?.addEventListener('click', () => togglePictureInPicture());

View File

@ -1,7 +1,7 @@
import sliderHTML from './templates/slider.html';
import { getSongMenu } from '../../providers/dom-elements';
import { ElementFromHtml } from '../utils';
import { ElementFromHtml } from '../utils-renderer';
import { singleton } from '../../providers/decorators';

View File

@ -1,6 +1,3 @@
import { ipcRenderer } from 'electron';
import { setOptions, setMenuOptions, isEnabled } from '../../config/plugins';
import { debounce } from '../../providers/decorators';
import { YoutubePlayer } from '../../types/youtube-player';
@ -18,15 +15,15 @@ export default (_options: ConfigType<'precise-volume'>) => {
options = _options;
document.addEventListener('apiLoaded', (e) => {
api = e.detail;
ipcRenderer.on('changeVolume', (_, toIncrease: boolean) => changeVolume(toIncrease));
ipcRenderer.on('setVolume', (_, value: number) => setVolume(value));
window.ipcRenderer.on('changeVolume', (_, toIncrease: boolean) => changeVolume(toIncrease));
window.ipcRenderer.on('setVolume', (_, value: number) => setVolume(value));
firstRun();
}, { once: true, passive: true });
};
// Without this function it would rewrite config 20 time when volume change by 20
const writeOptions = debounce(() => {
setOptions('precise-volume', options);
window.mainConfig.plugins.setOptions('precise-volume', options);
}, 1000);
export const moveVolumeHud = debounce((showVideo: boolean) => {
@ -68,7 +65,7 @@ function firstRun() {
injectVolumeHud(noVid);
if (!noVid) {
setupVideoPlayerOnwheel();
if (!isEnabled('video-toggle')) {
if (!window.mainConfig.plugins.isEnabled('video-toggle')) {
// Video-toggle handles hud positioning on its own
const videoMode = () => api.getPlayerResponse().videoDetails?.musicVideoType !== 'MUSIC_VIDEO_TYPE_ATV';
$('video')?.addEventListener('srcChanged', () => moveVolumeHud(videoMode()));
@ -76,9 +73,9 @@ function firstRun() {
}
// Change options from renderer to keep sync
ipcRenderer.on('setOptions', (_event, newOptions = {}) => {
window.ipcRenderer.on('setOptions', (_event, newOptions = {}) => {
Object.assign(options, newOptions);
setMenuOptions('precise-volume', options);
window.mainConfig.plugins.setMenuOptions('precise-volume', options);
});
}

View File

@ -1,8 +1,6 @@
import { ipcRenderer } from 'electron';
import qualitySettingsTemplate from './templates/qualitySettingsTemplate.html';
import { ElementFromHtml } from '../utils';
import { ElementFromHtml } from '../utils-renderer';
import { YoutubePlayer } from '../../types/youtube-player';
function $(selector: string): HTMLElement | null {
@ -23,7 +21,7 @@ function setup(event: CustomEvent<YoutubePlayer>) {
const currentIndex = qualityLevels.indexOf(api.getPlaybackQuality());
ipcRenderer.invoke('qualityChanger', api.getAvailableQualityLabels(), currentIndex).then((promise: { response: number }) => {
window.ipcRenderer.invoke('qualityChanger', api.getAvailableQualityLabels(), currentIndex).then((promise: { response: number }) => {
if (promise.response === -1) {
return;
}

View File

@ -1,12 +1,9 @@
import { ipcRenderer } from 'electron';
import is from 'electron-is';
import { Segment } from './types';
let currentSegments: Segment[] = [];
export default () => {
ipcRenderer.on('sponsorblock-skip', (_, segments: Segment[]) => {
window.ipcRenderer.on('sponsorblock-skip', (_, segments: Segment[]) => {
currentSegments = segments;
});
@ -24,7 +21,7 @@ export default () => {
&& target.currentTime < segment[1]
) {
target.currentTime = segment[1];
if (is.dev()) {
if (window.electronIs.dev()) {
console.log('SponsorBlock: skipping segment', segment);
}
}

View File

@ -0,0 +1,8 @@
// Creates a DOM element from an HTML string
export const ElementFromHtml = (html: string): HTMLElement => {
const template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstElementChild as HTMLElement;
};

View File

@ -1,7 +1,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { app, ipcMain, ipcRenderer } from 'electron';
import { app } from 'electron';
import is from 'electron-is';
import { ValueOf } from '../utils/type-utils';
@ -34,31 +34,6 @@ export const saveMediaIcon = () => {
}
};
// Creates a DOM element from an HTML string
export const ElementFromHtml = (html: string): HTMLElement => {
const template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstElementChild as HTMLElement;
};
// Creates a DOM element from a HTML file
export const ElementFromFile = (filepath: fs.PathOrFileDescriptor) => ElementFromHtml(fs.readFileSync(filepath, 'utf8'));
export const templatePath = (pluginPath: string, name: string) => path.join(pluginPath, 'templates', name);
export const Actions = {
NEXT: 'next',
BACK: 'back',
};
export const triggerAction = <Parameters extends unknown[]>(channel: string, action: ValueOf<typeof Actions>, ...args: Parameters) => ipcRenderer.send(channel, action, ...args);
export const triggerActionSync = <Parameters extends unknown[]>(channel: string, action: ValueOf<typeof Actions>, ...args: Parameters): unknown => ipcRenderer.sendSync(channel, action, ...args);
export const listenAction = (channel: string, callback: (event: Electron.IpcMainEvent, action: string) => void) => ipcMain.on(channel, callback);
export const fileExists = (
path: fs.PathLike,
callbackIfExists: { (): void; (): void; (): void; },

View File

@ -1,7 +1,6 @@
import buttonTemplate from './templates/button_template.html';
import { ElementFromHtml } from '../utils';
import { setOptions, isEnabled } from '../../config/plugins';
import { ElementFromHtml } from '../utils-renderer';
import { moveVolumeHud as preciseVolumeMoveVolumeHud } from '../precise-volume/front';
@ -10,7 +9,7 @@ import { ThumbnailElement } from '../../types/get-player-response';
import type { ConfigType } from '../../config/dynamic';
const moveVolumeHud = isEnabled('precise-volume') ? preciseVolumeMoveVolumeHud : () => {};
const moveVolumeHud = window.mainConfig.plugins.isEnabled('precise-volume') ? preciseVolumeMoveVolumeHud : () => {};
function $<E extends Element = Element>(selector: string): E | null {
return document.querySelector<E>(selector);
@ -99,7 +98,7 @@ function setup(e: CustomEvent<YoutubePlayer>) {
function setVideoState(showVideo: boolean) {
options.hideVideo = !showVideo;
setOptions('video-toggle', options);
window.mainConfig.plugins.setOptions('video-toggle', options);
const checkbox = $<HTMLInputElement>('.video-switch-button-checkbox'); // custom mode
if (checkbox) checkbox.checked = !options.hideVideo;

View File

@ -3,7 +3,7 @@ import ButterchurnPresets from 'butterchurn-presets';
import { Visualizer } from './visualizer';
import { ConfigType } from '../../../config/dynamic';
import type { ConfigType } from '../../../config/dynamic';
class ButterchurnVisualizer extends Visualizer<Butterchurn> {
name = 'butterchurn';