const { ipcRenderer } = require('electron'); const is = require('electron-is'); const config = require('./config'); const { fileExists } = require('./plugins/utils'); const setupSongInfo = require('./providers/song-info-front'); const { setupSongControls } = require('./providers/song-controls-front'); const { startingPages } = require('./providers/extracted-data'); const plugins = config.plugins.getEnabled(); const $ = document.querySelector.bind(document); let api; plugins.forEach(async ([plugin, options]) => { const preloadPath = await ipcRenderer.invoke( 'getPath', __dirname, 'plugins', plugin, 'preload.js', ); fileExists(preloadPath, () => { const run = require(preloadPath); run(options); }); const actionPath = await ipcRenderer.invoke( 'getPath', __dirname, 'plugins', plugin, 'actions.js', ); fileExists(actionPath, () => { const actions = require(actionPath).actions || {}; // TODO: re-enable once contextIsolation is set to true // contextBridge.exposeInMainWorld(plugin + "Actions", actions); for (const actionName of Object.keys(actions)) { global[actionName] = actions[actionName]; } }); }); document.addEventListener('DOMContentLoaded', () => { plugins.forEach(async ([plugin, options]) => { const pluginPath = await ipcRenderer.invoke( 'getPath', __dirname, 'plugins', plugin, 'front.js', ); fileExists(pluginPath, () => { const run = require(pluginPath); run(options); }); }); // Wait for complete load of youtube api listenForApiLoad(); // Inject song-info provider setupSongInfo(); // Inject song-controls setupSongControls(); // Add action for reloading global.reload = () => ipcRenderer.send('reload'); // Blocks the "Are You Still There?" popup by setting the last active time to Date.now every 15min setInterval(() => window._lact = Date.now(), 900_000); // Setup back to front logger if (is.dev()) { ipcRenderer.on('log', (_event, log) => { console.log(JSON.parse(log)); }); } }); function listenForApiLoad() { api = $('#movie_player'); if (api) { onApiLoaded(); return; } const observer = new MutationObserver(() => { api = $('#movie_player'); if (api) { observer.disconnect(); onApiLoaded(); } }); observer.observe(document.documentElement, { childList: true, subtree: true }); } function onApiLoaded() { const video = $('video'); const audioContext = new AudioContext(); const audioSource = audioContext.createMediaElementSource(video); audioSource.connect(audioContext.destination); video.addEventListener( 'loadstart', () => { // Emit "audioCanPlay" for each video video.addEventListener( 'canplaythrough', () => { document.dispatchEvent( new CustomEvent('audioCanPlay', { detail: { audioContext, audioSource, }, }), ); }, { once: true }, ); }, { passive: true }, ); document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api })); ipcRenderer.send('apiLoaded'); // Navigate to "Starting page" const startingPage = config.get('options.startingPage'); if (startingPage && startingPages[startingPage]) { $('ytmusic-app')?.navigate_(startingPages[startingPage]); } // Remove upgrade button if (config.get('options.removeUpgradeButton')) { const upgradeButton = $('ytmusic-pivot-bar-item-renderer[tab-id="SPunlimited"]'); if (upgradeButton) { upgradeButton.style.display = 'none'; } } // Hide / Force show like buttons const likeButtonsOptions = config.get('options.likeButtons'); if (likeButtonsOptions) { const likeButtons = $('ytmusic-like-button-renderer'); if (likeButtons) { likeButtons.style.display = { hide: 'none', force: 'inherit', }[likeButtonsOptions] || ''; } } }