From 72660f5aa17454033f35a988e3cd42fc6c9937db Mon Sep 17 00:00:00 2001 From: JellyBrick Date: Sat, 30 Sep 2023 08:35:16 +0900 Subject: [PATCH] fix: reduce unchecked type-cast --- plugins/compact-sidebar/front.ts | 2 +- plugins/crossfade/front.ts | 2 +- plugins/disable-autoplay/front.ts | 12 ++-- plugins/downloader/front.ts | 2 +- plugins/in-app-menu/front.ts | 46 ++++++++------- plugins/picture-in-picture/front.ts | 81 ++++++++++++++------------- plugins/playback-speed/front.ts | 30 ++++++---- plugins/precise-volume/front.ts | 47 +++++++++------- plugins/sponsorblock/front.ts | 22 ++++---- plugins/video-toggle/front.ts | 87 +++++++++++++++++------------ plugins/visualizer/front.ts | 21 +++++-- preload.ts | 2 +- providers/prompt-custom-titlebar.ts | 11 ++-- providers/song-info-front.ts | 67 ++++++++++++++-------- tsconfig.json | 2 +- 15 files changed, 256 insertions(+), 178 deletions(-) diff --git a/plugins/compact-sidebar/front.ts b/plugins/compact-sidebar/front.ts index b2bd8f03..ad2aab97 100644 --- a/plugins/compact-sidebar/front.ts +++ b/plugins/compact-sidebar/front.ts @@ -5,6 +5,6 @@ export default () => { || window.getComputedStyle(compactSidebar).display === 'none'; if (isCompactSidebarDisabled) { - (document.querySelector('#button') as HTMLButtonElement)?.click(); + document.querySelector('#button')?.click(); } }; diff --git a/plugins/crossfade/front.ts b/plugins/crossfade/front.ts index 746fe0cd..a38cd713 100644 --- a/plugins/crossfade/front.ts +++ b/plugins/crossfade/front.ts @@ -104,7 +104,7 @@ const syncVideoWithTransitionAudio = () => { video.removeEventListener('timeupdate', transitionBeforeEnd); // Go to next video - XXX: does not support "repeat 1" mode - (document.querySelector('.next-button') as HTMLButtonElement).click(); + document.querySelector('.next-button')?.click(); } }; diff --git a/plugins/disable-autoplay/front.ts b/plugins/disable-autoplay/front.ts index 5e4cd47d..176c6bcf 100644 --- a/plugins/disable-autoplay/front.ts +++ b/plugins/disable-autoplay/front.ts @@ -1,13 +1,17 @@ export default () => { + const timeUpdateListener = (e: Event) => { + if (e.target instanceof HTMLVideoElement) { + e.target.pause(); + } + }; + document.addEventListener('apiLoaded', (apiEvent) => { apiEvent.detail.addEventListener('videodatachange', (name: string) => { if (name === 'dataloaded') { apiEvent.detail.pauseVideo(); - (document.querySelector('video') as HTMLVideoElement)?.addEventListener('timeupdate', (e) => { - (e.target as HTMLVideoElement)?.pause(); - }); + document.querySelector('video')?.addEventListener('timeupdate', timeUpdateListener); } else { - (document.querySelector('video') as HTMLVideoElement).ontimeupdate = null; + document.querySelector('video')?.removeEventListener('timeupdate', timeUpdateListener); } }); }, { once: true, passive: true }); diff --git a/plugins/downloader/front.ts b/plugins/downloader/front.ts index 8393376d..29d9d1cc 100644 --- a/plugins/downloader/front.ts +++ b/plugins/downloader/front.ts @@ -25,7 +25,7 @@ const menuObserver = new MutationObserver(() => { return; } - const menuUrl = (document.querySelector('tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint') as HTMLAnchorElement | undefined)?.href; + const menuUrl = document.querySelector('tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint')?.href; if (!menuUrl?.includes('watch?') && doneFirstLoad) { return; } diff --git a/plugins/in-app-menu/front.ts b/plugins/in-app-menu/front.ts index ae5179c2..fc7640ce 100644 --- a/plugins/in-app-menu/front.ts +++ b/plugins/in-app-menu/front.ts @@ -5,8 +5,11 @@ import { Color, Titlebar } from 'custom-electron-titlebar'; import config from '../../config'; import { isEnabled } from '../../config/plugins'; -function $(selector: string) { - return document.querySelector(selector); +type ElectronCSSStyleDeclaration = CSSStyleDeclaration & { webkitAppRegion: 'drag' | 'no-drag' }; +type ElectronHTMLElement = HTMLElement & { style: ElectronCSSStyleDeclaration }; + +function $(selector: string) { + return document.querySelector(selector); } export default () => { @@ -59,12 +62,10 @@ export default () => { function setupSearchOpenObserver() { const searchOpenObserver = new MutationObserver((mutations) => { - ($('#nav-bar-background') as HTMLElement) - .style - .setProperty( - '-webkit-app-region', - (mutations[0].target as HTMLElement & { opened: boolean }).opened ? 'no-drag' : 'drag', - ); + const navBarBackground = $('#nav-bar-background'); + if (navBarBackground) { + navBarBackground.style.webkitAppRegion = (mutations[0].target as HTMLElement & { opened: boolean }).opened ? 'no-drag' : 'drag'; + } }); const searchBox = $('ytmusic-search-box'); if (searchBox) { @@ -76,21 +77,28 @@ function setupMenuOpenObserver() { const cetMenubar = $('.cet-menubar'); if (cetMenubar) { const menuOpenObserver = new MutationObserver(() => { - ($('#nav-bar-background') as HTMLElement) - .style - .setProperty( - '-webkit-app-region', - Array.from(cetMenubar.childNodes).some((c) => (c as HTMLElement).classList.contains('open')) ? 'no-drag' : 'drag', - ); + let isOpen = false; + for (const child of cetMenubar.children) { + if (child.classList.contains('open')) { + isOpen = true; + break; + } + } + const navBarBackground = $('#nav-bar-background'); + if (navBarBackground) { + navBarBackground.style.webkitAppRegion = isOpen ? 'no-drag' : 'drag'; + } }); menuOpenObserver.observe(cetMenubar, { subtree: true, attributeFilter: ['class'] }); } } function setNavbarMargin() { - const navBarBackground = $('#nav-bar-background') as HTMLElement; - navBarBackground.style.right - = ($('ytmusic-app-layout') as HTMLElement & { playerPageOpen_: boolean }).playerPageOpen_ - ? '0px' - : '12px'; + const navBarBackground = $('#nav-bar-background'); + if (navBarBackground) { + navBarBackground.style.right + = $('ytmusic-app-layout')?.playerPageOpen_ + ? '0px' + : '12px'; + } } diff --git a/plugins/picture-in-picture/front.ts b/plugins/picture-in-picture/front.ts index 88cdebed..7bc9d2c5 100644 --- a/plugins/picture-in-picture/front.ts +++ b/plugins/picture-in-picture/front.ts @@ -10,8 +10,8 @@ import type { ConfigType } from '../../config/dynamic'; type PiPOptions = ConfigType<'picture-in-picture'>; -function $(selector: string) { - return document.querySelector(selector); +function $(selector: string) { + return document.querySelector(selector); } let useNativePiP = false; @@ -60,10 +60,8 @@ const observer = new MutationObserver(() => { return; } - const menuUrl = ($( - 'tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint', - ) as HTMLAnchorElement)?.href; - if (menuUrl && !menuUrl.includes('watch?')) { + const menuUrl = $('tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint')?.href; + if (!menuUrl?.includes('watch?')) { return; } @@ -73,7 +71,7 @@ const observer = new MutationObserver(() => { const togglePictureInPicture = async () => { if (useNativePiP) { const isInPiP = document.pictureInPictureElement !== null; - const video = $('video') as HTMLVideoElement | null; + const video = $('video'); const togglePiP = () => isInPiP ? document.exitPictureInPicture.call(document) @@ -81,7 +79,7 @@ const togglePictureInPicture = async () => { try { await togglePiP(); - ($('#icon') as HTMLButtonElement | null)?.click(); // Close the menu + $('#icon')?.click(); // Close the menu return true; } catch { } @@ -95,42 +93,45 @@ const togglePictureInPicture = async () => { (global as any).togglePictureInPicture = togglePictureInPicture; const listenForToggle = () => { - const originalExitButton = $('.exit-fullscreen-button') as HTMLButtonElement; - const appLayout = $('ytmusic-app-layout') as HTMLElement; - const expandMenu = $('#expanding-menu') as HTMLElement; - const middleControls = $('.middle-controls') as HTMLButtonElement; - const playerPage = $('ytmusic-player-page') as HTMLElement & { playerPageOpen_: boolean }; - const togglePlayerPageButton = $('.toggle-player-page-button') as HTMLButtonElement; - const fullScreenButton = $('.fullscreen-button') as HTMLButtonElement; - const player = ($('#player') as (HTMLVideoElement & { onDoubleClick_: () => void | undefined })); + const originalExitButton = $('.exit-fullscreen-button'); + const appLayout = $('ytmusic-app-layout'); + const expandMenu = $('#expanding-menu'); + const middleControls = $('.middle-controls'); + const playerPage = $('ytmusic-player-page'); + const togglePlayerPageButton = $('.toggle-player-page-button'); + const fullScreenButton = $('.fullscreen-button'); + const player = $ void) | undefined }>('#player'); const onPlayerDblClick = player?.onDoubleClick_; + const mouseLeaveEventListener = () => middleControls?.click(); - const titlebar = $('.cet-titlebar') as HTMLElement; + const titlebar = $('.cet-titlebar'); ipcRenderer.on('pip-toggle', (_, isPip: boolean) => { - if (isPip) { - replaceButton('.exit-fullscreen-button', originalExitButton)?.addEventListener('click', () => togglePictureInPicture()); - player.onDoubleClick_ = () => { - }; + if (originalExitButton && player) { + if (isPip) { + replaceButton('.exit-fullscreen-button', originalExitButton)?.addEventListener('click', () => togglePictureInPicture()); + player.onDoubleClick_ = () => { + }; - expandMenu.addEventListener('mouseleave', () => middleControls.click()); - if (!playerPage.playerPageOpen_) { - togglePlayerPageButton.click(); - } + expandMenu?.addEventListener('mouseleave', mouseLeaveEventListener); + if (!playerPage?.playerPageOpen_) { + togglePlayerPageButton?.click(); + } - fullScreenButton.click(); - appLayout.classList.add('pip'); - if (titlebar) { - titlebar.style.display = 'none'; - } - } else { - $('.exit-fullscreen-button')?.replaceWith(originalExitButton); - player.onDoubleClick_ = onPlayerDblClick; - expandMenu.onmouseleave = null; - originalExitButton.click(); - appLayout.classList.remove('pip'); - if (titlebar) { - titlebar.style.display = 'flex'; + fullScreenButton?.click(); + appLayout?.classList.add('pip'); + if (titlebar) { + titlebar.style.display = 'none'; + } + } else { + $('.exit-fullscreen-button')?.replaceWith(originalExitButton); + player.onDoubleClick_ = onPlayerDblClick; + expandMenu?.removeEventListener('mouseleave', mouseLeaveEventListener); + originalExitButton.click(); + appLayout?.classList.remove('pip'); + if (titlebar) { + titlebar.style.display = 'flex'; + } } } }); @@ -145,7 +146,7 @@ function observeMenu(options: PiPOptions) { cloneButton('.player-minimize-button')?.addEventListener('click', async () => { await togglePictureInPicture(); - setTimeout(() => ($('#player') as HTMLButtonElement | undefined)?.click()); + setTimeout(() => $('#player')?.click()); }); // Allows easily closing the menu by programmatically clicking outside of it @@ -169,7 +170,7 @@ export default (options: PiPOptions) => { window.addEventListener('keydown', (event) => { if ( keyEventAreEqual(event, hotkeyEvent) - && !($('ytmusic-search-box') as (HTMLElement & { opened: boolean }) | undefined)?.opened + && !$('ytmusic-search-box')?.opened ) { togglePictureInPicture(); } diff --git a/plugins/playback-speed/front.ts b/plugins/playback-speed/front.ts index 77bf85cb..17c873c2 100644 --- a/plugins/playback-speed/front.ts +++ b/plugins/playback-speed/front.ts @@ -3,8 +3,8 @@ import { ElementFromFile, templatePath } from '../utils'; import { singleton } from '../../providers/decorators'; -function $(selector: string) { - return document.querySelector(selector); +function $(selector: string) { + return document.querySelector(selector); } const slider = ElementFromFile(templatePath(__dirname, 'slider.html')); @@ -17,7 +17,10 @@ const MAX_PLAYBACK_SPEED = 16; let playbackSpeed = 1; const updatePlayBackSpeed = () => { - ($('video') as HTMLVideoElement).playbackRate = playbackSpeed; + const videoElement = $('video'); + if (videoElement) { + videoElement.playbackRate = playbackSpeed; + } const playbackSpeedElement = $('#playback-speed-value'); if (playbackSpeedElement) { @@ -65,9 +68,11 @@ const observePopupContainer = () => { }; const observeVideo = () => { - const video = $('video') as HTMLVideoElement; - video.addEventListener('ratechange', forcePlaybackRate); - video.addEventListener('srcChanged', forcePlaybackRate); + const video = $('video'); + if (video) { + video.addEventListener('ratechange', forcePlaybackRate); + video.addEventListener('srcChanged', forcePlaybackRate); + } }; const setupWheelListener = () => { @@ -85,14 +90,19 @@ const setupWheelListener = () => { updatePlayBackSpeed(); // Update slider position - ($('#playback-speed-slider') as HTMLElement & { value: number }).value = playbackSpeed; + const playbackSpeedSilder = $('#playback-speed-slider'); + if (playbackSpeedSilder) { + playbackSpeedSilder.value = playbackSpeed; + } }); }; function forcePlaybackRate(e: Event) { - const videoElement = (e.target as HTMLVideoElement); - if (videoElement.playbackRate !== playbackSpeed) { - videoElement.playbackRate = playbackSpeed; + if (e.target instanceof HTMLVideoElement) { + const videoElement = e.target; + if (videoElement.playbackRate !== playbackSpeed) { + videoElement.playbackRate = playbackSpeed; + } } } diff --git a/plugins/precise-volume/front.ts b/plugins/precise-volume/front.ts index 1ec1ac21..0acc7583 100644 --- a/plugins/precise-volume/front.ts +++ b/plugins/precise-volume/front.ts @@ -7,8 +7,8 @@ import { YoutubePlayer } from '../../types/youtube-player'; import type { ConfigType } from '../../config/dynamic'; -function $(selector: string) { - return document.querySelector(selector); +function $(selector: string) { + return document.querySelector(selector); } let api: YoutubePlayer; @@ -30,7 +30,7 @@ const writeOptions = debounce(() => { }, 1000); export const moveVolumeHud = debounce((showVideo: boolean) => { - const volumeHud = $('#volumeHud') as HTMLElement | undefined; + const volumeHud = $('#volumeHud'); if (!volumeHud) { return; } @@ -103,7 +103,7 @@ function injectVolumeHud(noVid: boolean) { } function showVolumeHud(volume: number) { - const volumeHud = $('#volumeHud') as HTMLElement | undefined; + const volumeHud = $('#volumeHud'); if (!volumeHud) { return; } @@ -116,7 +116,7 @@ function showVolumeHud(volume: number) { /** Add onwheel event to video player */ function setupVideoPlayerOnwheel() { - const panel = $('#main-panel') as HTMLElement | undefined; + const panel = $('#main-panel'); if (!panel) return; panel.addEventListener('wheel', (event) => { @@ -133,7 +133,7 @@ function saveVolume(volume: number) { /** Add onwheel event to play bar and also track if play bar is hovered */ function setupPlaybar() { - const playerbar = $('ytmusic-player-bar') as HTMLElement | undefined; + const playerbar = $('ytmusic-player-bar'); if (!playerbar) return; playerbar.addEventListener('wheel', (event) => { @@ -158,14 +158,16 @@ function setupPlaybar() { function setupSliderObserver() { const sliderObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { - // This checks that volume-slider was manually set - const target = mutation.target as HTMLInputElement; - const targetValueNumeric = Number(target.value); - if (mutation.oldValue !== target.value - && (typeof options.savedVolume !== 'number' || Math.abs(options.savedVolume - targetValueNumeric) > 4)) { - // Diff>4 means it was manually set - setTooltip(targetValueNumeric); - saveVolume(targetValueNumeric); + if (mutation.target instanceof HTMLInputElement) { + // This checks that volume-slider was manually set + const target = mutation.target; + const targetValueNumeric = Number(target.value); + if (mutation.oldValue !== target.value + && (typeof options.savedVolume !== 'number' || Math.abs(options.savedVolume - targetValueNumeric) > 4)) { + // Diff>4 means it was manually set + setTooltip(targetValueNumeric); + saveVolume(targetValueNumeric); + } } } }); @@ -209,15 +211,15 @@ function updateVolumeSlider() { const savedVolume = options.savedVolume ?? 0; // Slider value automatically rounds to multiples of 5 for (const slider of ['#volume-slider', '#expand-volume-slider']) { - ($(slider) as HTMLInputElement).value - = String(savedVolume > 0 && savedVolume < 5 - ? 5 - : savedVolume); + const silderElement = $(slider); + if (silderElement) { + silderElement.value = String(savedVolume > 0 && savedVolume < 5 ? 5 : savedVolume); + } } } function showVolumeSlider() { - const slider = $('#volume-slider') as HTMLElement | null; + const slider = $('#volume-slider'); if (!slider) return; // This class display the volume slider if not in minimized mode @@ -236,14 +238,17 @@ const tooltipTargets = [ function setTooltip(volume: number) { for (const target of tooltipTargets) { - ($(target) as HTMLElement).title = `${volume}%`; + const tooltipTargetElement = $(target); + if (tooltipTargetElement) { + tooltipTargetElement.title = `${volume}%`; + } } } function setupLocalArrowShortcuts() { if (options.arrowsShortcut) { window.addEventListener('keydown', (event) => { - if (($('ytmusic-search-box') as (HTMLElement & { opened: boolean }) | null)?.opened) { + if ($('ytmusic-search-box')?.opened) { return; } diff --git a/plugins/sponsorblock/front.ts b/plugins/sponsorblock/front.ts index 960c97ba..ccb2f808 100644 --- a/plugins/sponsorblock/front.ts +++ b/plugins/sponsorblock/front.ts @@ -11,20 +11,22 @@ export default () => { }); document.addEventListener('apiLoaded', () => { - const video = document.querySelector('video') as HTMLVideoElement | undefined; + const video = document.querySelector('video'); if (!video) return; video.addEventListener('timeupdate', (e) => { - const target = e.target as HTMLVideoElement; + if (e.target instanceof HTMLVideoElement) { + const target = e.target; - for (const segment of currentSegments) { - if ( - target.currentTime >= segment[0] - && target.currentTime < segment[1] - ) { - target.currentTime = segment[1]; - if (is.dev()) { - console.log('SponsorBlock: skipping segment', segment); + for (const segment of currentSegments) { + if ( + target.currentTime >= segment[0] + && target.currentTime < segment[1] + ) { + target.currentTime = segment[1]; + if (is.dev()) { + console.log('SponsorBlock: skipping segment', segment); + } } } } diff --git a/plugins/video-toggle/front.ts b/plugins/video-toggle/front.ts index d6751dc2..49a3ecd1 100644 --- a/plugins/video-toggle/front.ts +++ b/plugins/video-toggle/front.ts @@ -10,13 +10,13 @@ import type { ConfigType } from '../../config/dynamic'; const moveVolumeHud = isEnabled('precise-volume') ? preciseVolumeMoveVolumeHud : () => {}; -function $(selector: string): HTMLElement | null { - return document.querySelector(selector); +function $(selector: string): E | null { + return document.querySelector(selector); } let options: ConfigType<'video-toggle'>; -let player: HTMLElement & { videoMode_: boolean }; -let video: HTMLVideoElement; +let player: HTMLElement & { videoMode_: boolean } | null; +let video: HTMLVideoElement | null; let api: YoutubePlayer; const switchButtonDiv = ElementFromFile( @@ -51,17 +51,22 @@ export default (_options: ConfigType<'video-toggle'>) => { function setup(e: CustomEvent) { api = e.detail; - player = $('ytmusic-player') as typeof player; - video = $('video') as HTMLVideoElement; + player = $<(HTMLElement & { videoMode_: boolean; })>('ytmusic-player'); + video = $('video'); - ($('#player') as HTMLVideoElement).prepend(switchButtonDiv); + $('#player')?.prepend(switchButtonDiv); if (options.hideVideo) { - ($('.video-switch-button-checkbox') as HTMLInputElement).checked = false; + const checkbox = $('.video-switch-button-checkbox'); + if (checkbox) { + checkbox.checked = false; + } changeDisplay(false); forcePlaybackMode(); // Fix black video - video.style.height = 'auto'; + if (video) { + video.style.height = 'auto'; + } } //Prevents bubbling to the player which causes it to stop or resume @@ -77,7 +82,7 @@ function setup(e: CustomEvent) { setOptions('video-toggle', options); }); - video.addEventListener('srcChanged', videoStarted); + video?.addEventListener('srcChanged', videoStarted); observeThumbnail(); @@ -100,17 +105,19 @@ function setup(e: CustomEvent) { } function changeDisplay(showVideo: boolean) { - player.style.margin = showVideo ? '' : 'auto 0px'; - player.setAttribute('playback-mode', showVideo ? 'OMV_PREFERRED' : 'ATV_PREFERRED'); + if (player) { + player.style.margin = showVideo ? '' : 'auto 0px'; + player.setAttribute('playback-mode', showVideo ? 'OMV_PREFERRED' : 'ATV_PREFERRED'); - $('#song-video.ytmusic-player')!.style.display = showVideo ? 'block' : 'none'; - $('#song-image')!.style.display = showVideo ? 'none' : 'block'; + $('#song-video.ytmusic-player')!.style.display = showVideo ? 'block' : 'none'; + $('#song-image')!.style.display = showVideo ? 'none' : 'block'; - if (showVideo && !video.style.top) { - video.style.top = `${(player.clientHeight - video.clientHeight) / 2}px`; + if (showVideo && video && !video.style.top) { + video.style.top = `${(player.clientHeight - video.clientHeight) / 2}px`; + } + + moveVolumeHud(showVideo); } - - moveVolumeHud(showVideo); } function videoStarted() { @@ -120,12 +127,16 @@ function videoStarted() { // Hide toggle button switchButtonDiv.style.display = 'none'; } else { + const songImage = $('#song-image img'); + if (!songImage) { + return; + } // Switch to high-res thumbnail - forceThumbnail($('#song-image img') as HTMLImageElement); + forceThumbnail(songImage); // Show toggle button switchButtonDiv.style.display = 'initial'; // Change display to video mode if video exist & video is hidden & option.hideVideo = false - if (!options.hideVideo && $('#song-video.ytmusic-player')?.style.display === 'none') { + if (!options.hideVideo && $('#song-video.ytmusic-player')?.style.display === 'none') { changeDisplay(true); } else { moveVolumeHud(!options.hideVideo); @@ -136,31 +147,37 @@ function videoStarted() { // On load, after a delay, the page overrides the playback-mode to 'OMV_PREFERRED' which causes weird aspect ratio in the image container // this function fix the problem by overriding that override :) function forcePlaybackMode() { - const playbackModeObserver = new MutationObserver((mutations) => { - for (const mutation of mutations) { - const target = mutation.target as HTMLElement; - if (target.getAttribute('playback-mode') !== 'ATV_PREFERRED') { - playbackModeObserver.disconnect(); - target.setAttribute('playback-mode', 'ATV_PREFERRED'); + if (player) { + const playbackModeObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.target instanceof HTMLElement) { + const target = mutation.target; + if (target.getAttribute('playback-mode') !== 'ATV_PREFERRED') { + playbackModeObserver.disconnect(); + target.setAttribute('playback-mode', 'ATV_PREFERRED'); + } + } } - } - }); - playbackModeObserver.observe(player, { attributeFilter: ['playback-mode'] }); + }); + playbackModeObserver.observe(player, { attributeFilter: ['playback-mode'] }); + } } function observeThumbnail() { const playbackModeObserver = new MutationObserver((mutations) => { - if (!player.videoMode_) { + if (!player?.videoMode_) { return; } for (const mutation of mutations) { - const target = mutation.target as HTMLImageElement; - if (!target.src.startsWith('data:')) { - continue; - } + if (mutation.target instanceof HTMLImageElement) { + const target = mutation.target; + if (!target.src.startsWith('data:')) { + continue; + } - forceThumbnail(target); + forceThumbnail(target); + } } }); playbackModeObserver.observe($('#song-image img')!, { attributeFilter: ['src'] }); diff --git a/plugins/visualizer/front.ts b/plugins/visualizer/front.ts index 9274a3cd..7720d9ed 100644 --- a/plugins/visualizer/front.ts +++ b/plugins/visualizer/front.ts @@ -26,21 +26,30 @@ export default (options: ConfigType<'visualizer'>) => { document.addEventListener( 'audioCanPlay', (e) => { - const video = document.querySelector('video') as (HTMLVideoElement & { captureStream(): MediaStream; }); - const visualizerContainer = document.querySelector('#player') as HTMLElement; + const video = document.querySelector('video'); + if (!video) { + return; + } - let canvas = document.querySelector('#visualizer') as HTMLCanvasElement; + const visualizerContainer = document.querySelector('#player'); + if (!visualizerContainer) { + return; + } + + let canvas = document.querySelector('#visualizer'); if (!canvas) { canvas = document.createElement('canvas'); canvas.id = 'visualizer'; canvas.style.position = 'absolute'; canvas.style.background = 'black'; - visualizerContainer.append(canvas); + visualizerContainer?.append(canvas); } const resizeCanvas = () => { - canvas.width = visualizerContainer.clientWidth; - canvas.height = visualizerContainer.clientHeight; + if (canvas) { + canvas.width = visualizerContainer.clientWidth; + canvas.height = visualizerContainer.clientHeight; + } }; resizeCanvas(); diff --git a/preload.ts b/preload.ts index d14e23db..9cbc11d7 100644 --- a/preload.ts +++ b/preload.ts @@ -150,7 +150,7 @@ function onApiLoaded() { // Navigate to "Starting page" const startingPage: string = config.get('options.startingPage'); if (startingPage && startingPages[startingPage]) { - ($('ytmusic-app') as YouTubeMusicAppElement)?.navigate_(startingPages[startingPage]); + $('ytmusic-app')?.navigate_(startingPages[startingPage]); } // Remove upgrade button diff --git a/providers/prompt-custom-titlebar.ts b/providers/prompt-custom-titlebar.ts index 3a66afe2..cfcdad1a 100644 --- a/providers/prompt-custom-titlebar.ts +++ b/providers/prompt-custom-titlebar.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-unresolved import { Titlebar, Color } from 'custom-electron-titlebar'; export default () => { @@ -7,8 +8,10 @@ export default () => { maximizable: false, menu: undefined, }); - const mainStyle = (document.querySelector('#container') as HTMLElement)!.style; - mainStyle.width = '100%'; - mainStyle.position = 'fixed'; - mainStyle.border = 'unset'; + const mainStyle = document.querySelector('#container')?.style; + if (mainStyle) { + mainStyle.width = '100%'; + mainStyle.position = 'fixed'; + mainStyle.border = 'unset'; + } }; diff --git a/providers/song-info-front.ts b/providers/song-info-front.ts index 602d6d49..0848a9b9 100644 --- a/providers/song-info-front.ts +++ b/providers/song-info-front.ts @@ -9,8 +9,8 @@ import { GetState } from '../types/datahost-get-state'; let songInfo: SongInfo = {} as SongInfo; export const getSongInfo = () => songInfo; -const $ = (s: string): E => document.querySelector(s) as E; -const $$ = (s: string): E[] => Array.from(document.querySelectorAll(s)); +const $ = (s: string): E | null => document.querySelector(s); +const $$ = (s: string): NodeListOf => document.querySelectorAll(s); ipcRenderer.on('update-song-info', async (_, extractedSongInfo: string) => { songInfo = JSON.parse(extractedSongInfo) as SongInfo; @@ -21,35 +21,54 @@ ipcRenderer.on('update-song-info', async (_, extractedSongInfo: string) => { const srcChangedEvent = new CustomEvent('srcChanged'); export const setupSeekedListener = singleton(() => { - $('video')?.addEventListener('seeked', (v) => ipcRenderer.send('seeked', (v.target as HTMLVideoElement).currentTime)); + $('video')?.addEventListener('seeked', (v) => { + if (v.target instanceof HTMLVideoElement) { + ipcRenderer.send('seeked', v.target.currentTime); + } + }); }); export const setupTimeChangedListener = singleton(() => { const progressObserver = new MutationObserver((mutations) => { - const target = mutations[0].target as HTMLInputElement; - ipcRenderer.send('timeChanged', target.value); - songInfo.elapsedSeconds = Number(target.value); + for (const mutation of mutations) { + const target = mutation.target as Node & { value: string }; + ipcRenderer.send('timeChanged', target.value); + songInfo.elapsedSeconds = Number(target.value); + } }); - progressObserver.observe($('#progress-bar'), { attributeFilter: ['value'] }); + const progressBar = $('#progress-bar'); + if (progressBar) { + progressObserver.observe(progressBar, { attributeFilter: ['value'] }); + } }); export const setupRepeatChangedListener = singleton(() => { const repeatObserver = new MutationObserver((mutations) => { - // provided by YouTube music - // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access - ipcRenderer.send('repeatChanged', ((mutations[0].target as any).__dataHost.getState() as GetState).queue.repeatMode); + // provided by YouTube Music + ipcRenderer.send( + 'repeatChanged', + (mutations[0].target as Node & { + __dataHost: { + getState: () => GetState; + } + }).__dataHost.getState().queue.repeatMode, + ); }); repeatObserver.observe($('#right-controls .repeat')!, { attributeFilter: ['title'] }); // Emit the initial value as well; as it's persistent between launches. - // provided by YouTube music - // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unnecessary-type-assertion - ipcRenderer.send('repeatChanged', (($('ytmusic-player-bar') as any).getState() as GetState).queue.repeatMode); + // provided by YouTube Music + ipcRenderer.send( + 'repeatChanged', + $ GetState; + }>('ytmusic-player-bar')?.GetState().queue.repeatMode, + ); }); export const setupVolumeChangedListener = singleton((api: YoutubePlayer) => { - $('video').addEventListener('volumechange', () => { + $('video')?.addEventListener('volumechange', () => { ipcRenderer.send('volumeChanged', api.getVolume()); }); // Emit the initial value as well; as it's persistent between launches. @@ -75,10 +94,10 @@ export default () => { }); const playPausedHandler = (e: Event, status: string) => { - if (Math.round((e.target as HTMLVideoElement).currentTime) > 0) { + if (e.target instanceof HTMLVideoElement && Math.round(e.target.currentTime) > 0) { ipcRenderer.send('playPaused', { isPaused: status === 'pause', - elapsedSeconds: Math.floor((e.target as HTMLVideoElement).currentTime), + elapsedSeconds: Math.floor(e.target.currentTime), }); } }; @@ -94,10 +113,10 @@ export default () => { return; } const video = $('video'); - video.dispatchEvent(srcChangedEvent); + video?.dispatchEvent(srcChangedEvent); for (const status of ['playing', 'pause'] as const) { // for fix issue that pause event not fired - video.addEventListener(status, playPausedHandlers[status]); + video?.addEventListener(status, playPausedHandlers[status]); } setTimeout(sendSongInfo, 200); }); @@ -110,12 +129,12 @@ export default () => { function sendSongInfo() { const data = apiEvent.detail.getPlayerResponse(); - data.videoDetails.album = $$( - '.byline.ytmusic-player-bar > .yt-simple-endpoint', - ).find((e) => - e.href?.includes('browse/FEmusic_library_privately_owned_release') - || e.href?.includes('browse/MPREb'), - )?.textContent; + for (const e of $$('.byline.ytmusic-player-bar > .yt-simple-endpoint')) { + if (e.href?.includes('browse/FEmusic_library_privately_owned_release') || e.href?.includes('browse/MPREb')) { + data.videoDetails.album = e.textContent; + break; + } + } data.videoDetails.elapsedSeconds = 0; data.videoDetails.isPaused = false; diff --git a/tsconfig.json b/tsconfig.json index 18fce570..a53e985a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ESNext", - "lib": ["dom", "es2022"], + "lib": ["dom", "dom.iterable", "es2022"], "module": "CommonJS", "allowSyntheticDefaultImports": true, "esModuleInterop": true,