This commit is contained in:
JellyBrick
2024-01-16 20:34:00 +09:00
parent 7c404ba2ea
commit 26de7f940e
3 changed files with 65 additions and 50 deletions

View File

@ -32,7 +32,7 @@ export const onRendererLoad = ({
let unregister: (() => void) | null = null; let unregister: (() => void) | null = null;
on('update-song-info', (extractedSongInfo: SongInfo) => { on('ytmd:update-song-info', (extractedSongInfo: SongInfo) => {
unregister?.(); unregister?.();
setTimeout(async () => { setTimeout(async () => {

View File

@ -10,10 +10,7 @@ import type { VideoDataChanged } from '@/types/video-data-changed';
let songInfo: SongInfo = {} as SongInfo; let songInfo: SongInfo = {} as SongInfo;
export const getSongInfo = () => songInfo; export const getSongInfo = () => songInfo;
const $ = <E extends Element = Element>(s: string): E | null => window.ipcRenderer.on('ytmd:update-song-info', (_, extractedSongInfo: SongInfo) => {
document.querySelector<E>(s);
window.ipcRenderer.on('update-song-info', (_, extractedSongInfo: SongInfo) => {
songInfo = extractedSongInfo; songInfo = extractedSongInfo;
}); });
@ -21,7 +18,7 @@ window.ipcRenderer.on('update-song-info', (_, extractedSongInfo: SongInfo) => {
const srcChangedEvent = new CustomEvent('ytmd:src-changed'); const srcChangedEvent = new CustomEvent('ytmd:src-changed');
export const setupSeekedListener = singleton(() => { export const setupSeekedListener = singleton(() => {
$('video')?.addEventListener('seeked', (v) => { document.querySelector('video')?.addEventListener('seeked', (v) => {
if (v.target instanceof HTMLVideoElement) { if (v.target instanceof HTMLVideoElement) {
window.ipcRenderer.send('ytmd:seeked', v.target.currentTime); window.ipcRenderer.send('ytmd:seeked', v.target.currentTime);
} }
@ -36,7 +33,7 @@ export const setupTimeChangedListener = singleton(() => {
songInfo.elapsedSeconds = Number(target.value); songInfo.elapsedSeconds = Number(target.value);
} }
}); });
const progressBar = $('#progress-bar'); const progressBar = document.querySelector('#progress-bar');
if (progressBar) { if (progressBar) {
progressObserver.observe(progressBar, { attributeFilter: ['value'] }); progressObserver.observe(progressBar, { attributeFilter: ['value'] });
} }
@ -56,7 +53,7 @@ export const setupRepeatChangedListener = singleton(() => {
).__dataHost.getState().queue.repeatMode, ).__dataHost.getState().queue.repeatMode,
); );
}); });
repeatObserver.observe($('#right-controls .repeat')!, { repeatObserver.observe(document.querySelector('#right-controls .repeat')!, {
attributeFilter: ['title'], attributeFilter: ['title'],
}); });
@ -64,7 +61,7 @@ export const setupRepeatChangedListener = singleton(() => {
// provided by YouTube Music // provided by YouTube Music
window.ipcRenderer.send( window.ipcRenderer.send(
'ytmd:repeat-changed', 'ytmd:repeat-changed',
$< document.querySelector<
HTMLElement & { HTMLElement & {
getState: () => GetState; getState: () => GetState;
} }
@ -73,7 +70,7 @@ export const setupRepeatChangedListener = singleton(() => {
}); });
export const setupVolumeChangedListener = singleton((api: YoutubePlayer) => { export const setupVolumeChangedListener = singleton((api: YoutubePlayer) => {
$('video')?.addEventListener('volumechange', () => { document.querySelector('video')?.addEventListener('volumechange', () => {
window.ipcRenderer.send('ytmd:volume-changed', api.getVolume()); window.ipcRenderer.send('ytmd:volume-changed', api.getVolume());
}); });
// Emit the initial value as well; as it's persistent between launches. // Emit the initial value as well; as it's persistent between launches.
@ -134,7 +131,7 @@ export default (api: YoutubePlayer) => {
waitingEvent.delete(videoData.videoId); waitingEvent.delete(videoData.videoId);
sendSongInfo(videoData); sendSongInfo(videoData);
} else if (name === 'dataloaded') { } else if (name === 'dataloaded') {
const video = $<HTMLVideoElement>('video'); const video = document.querySelector<HTMLVideoElement>('video');
video?.dispatchEvent(srcChangedEvent); video?.dispatchEvent(srcChangedEvent);
for (const status of ['playing', 'pause'] as const) { for (const status of ['playing', 'pause'] as const) {
@ -146,9 +143,12 @@ export default (api: YoutubePlayer) => {
} }
}); });
const video = $('video')!; const video = document.querySelector('video');
for (const status of ['playing', 'pause'] as const) {
video.addEventListener(status, playPausedHandlers[status]); if (video) {
for (const status of ['playing', 'pause'] as const) {
video.addEventListener(status, playPausedHandlers[status]);
}
} }
function sendSongInfo(videoData: VideoDataChangeValue) { function sendSongInfo(videoData: VideoDataChangeValue) {

View File

@ -1,7 +1,8 @@
import { BrowserWindow, ipcMain, nativeImage, net } from 'electron'; import { BrowserWindow, ipcMain, nativeImage, net } from 'electron';
import { cache } from './decorators'; import { Mutex } from 'async-mutex';
import { cache } from './decorators';
import config from '@/config'; import config from '@/config';
import type { GetPlayerResponse } from '@/types/get-player-response'; import type { GetPlayerResponse } from '@/types/get-player-response';
@ -22,23 +23,6 @@ export interface SongInfo {
playlistId?: string; playlistId?: string;
} }
// Fill songInfo with empty values
export const songInfo: SongInfo = {
title: '',
artist: '',
views: 0,
uploadDate: '',
imageSrc: '',
image: null,
isPaused: undefined,
songDuration: 0,
elapsedSeconds: 0,
url: '',
album: undefined,
videoId: '',
playlistId: '',
};
// Grab the native image using the src // Grab the native image using the src
export const getImage = cache( export const getImage = cache(
async (src: string): Promise<Electron.NativeImage> => { async (src: string): Promise<Electron.NativeImage> => {
@ -57,11 +41,28 @@ export const getImage = cache(
const handleData = async ( const handleData = async (
data: GetPlayerResponse, data: GetPlayerResponse,
win: Electron.BrowserWindow, win: Electron.BrowserWindow,
) => { ): Promise<SongInfo | null> => {
if (!data) { if (!data) {
return; return null;
} }
// Fill songInfo with empty values
const songInfo: SongInfo = {
title: '',
artist: '',
views: 0,
uploadDate: '',
imageSrc: '',
image: null,
isPaused: undefined,
songDuration: 0,
elapsedSeconds: 0,
url: '',
album: undefined,
videoId: '',
playlistId: '',
} satisfies SongInfo;
const microformat = data.microformat?.microformatDataRenderer; const microformat = data.microformat?.microformatDataRenderer;
if (microformat) { if (microformat) {
songInfo.uploadDate = microformat.uploadDate; songInfo.uploadDate = microformat.uploadDate;
@ -87,8 +88,10 @@ const handleData = async (
songInfo.imageSrc = thumbnails.at(-1)?.url.split('?')[0]; songInfo.imageSrc = thumbnails.at(-1)?.url.split('?')[0];
if (songInfo.imageSrc) songInfo.image = await getImage(songInfo.imageSrc); if (songInfo.imageSrc) songInfo.image = await getImage(songInfo.imageSrc);
win.webContents.send('update-song-info', songInfo); win.webContents.send('ytmd:update-song-info', songInfo);
} }
return songInfo;
}; };
// This variable will be filled with the callbacks once they register // This variable will be filled with the callbacks once they register
@ -100,35 +103,47 @@ const registerCallback = (callback: SongInfoCallback) => {
callbacks.add(callback); callbacks.add(callback);
}; };
let handlingData = false;
const registerProvider = (win: BrowserWindow) => { const registerProvider = (win: BrowserWindow) => {
const dataMutex = new Mutex();
let songInfo: SongInfo | null = null;
// This will be called when the song-info-front finds a new request with song data // This will be called when the song-info-front finds a new request with song data
ipcMain.on('ytmd:video-src-changed', async (_, data: GetPlayerResponse) => { ipcMain.on('ytmd:video-src-changed', async (_, data: GetPlayerResponse) => {
handlingData = true; const tempSongInfo = await dataMutex.runExclusive<SongInfo | null>(async () => {
await handleData(data, win); songInfo = await handleData(data, win);
handlingData = false; return songInfo;
for (const c of callbacks) { });
c(songInfo, 'ytmd:video-src-changed');
if (tempSongInfo) {
for (const c of callbacks) {
c(tempSongInfo, 'ytmd:video-src-changed');
}
} }
}); });
ipcMain.on( ipcMain.on(
'ytmd:play-or-paused', 'ytmd:play-or-paused',
( async (
_, _,
{ {
isPaused, isPaused,
elapsedSeconds, elapsedSeconds,
}: { isPaused: boolean; elapsedSeconds: number }, }: { isPaused: boolean; elapsedSeconds: number },
) => { ) => {
songInfo.isPaused = isPaused; const tempSongInfo = await dataMutex.runExclusive<SongInfo | null>(() => {
songInfo.elapsedSeconds = elapsedSeconds; if (!songInfo) {
if (handlingData) { return null;
return; }
}
for (const c of callbacks) { songInfo.isPaused = isPaused;
c(songInfo, 'ytmd:play-or-paused'); songInfo.elapsedSeconds = elapsedSeconds;
return songInfo;
});
if (tempSongInfo) {
for (const c of callbacks) {
c(tempSongInfo, 'ytmd:play-or-paused');
}
} }
}, },
); );