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;
on('update-song-info', (extractedSongInfo: SongInfo) => {
on('ytmd:update-song-info', (extractedSongInfo: SongInfo) => {
unregister?.();
setTimeout(async () => {

View File

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

View File

@ -1,7 +1,8 @@
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 type { GetPlayerResponse } from '@/types/get-player-response';
@ -22,23 +23,6 @@ export interface SongInfo {
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
export const getImage = cache(
async (src: string): Promise<Electron.NativeImage> => {
@ -57,11 +41,28 @@ export const getImage = cache(
const handleData = async (
data: GetPlayerResponse,
win: Electron.BrowserWindow,
) => {
): Promise<SongInfo | null> => {
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;
if (microformat) {
songInfo.uploadDate = microformat.uploadDate;
@ -87,8 +88,10 @@ const handleData = async (
songInfo.imageSrc = thumbnails.at(-1)?.url.split('?')[0];
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
@ -100,35 +103,47 @@ const registerCallback = (callback: SongInfoCallback) => {
callbacks.add(callback);
};
let handlingData = false;
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
ipcMain.on('ytmd:video-src-changed', async (_, data: GetPlayerResponse) => {
handlingData = true;
await handleData(data, win);
handlingData = false;
for (const c of callbacks) {
c(songInfo, 'ytmd:video-src-changed');
const tempSongInfo = await dataMutex.runExclusive<SongInfo | null>(async () => {
songInfo = await handleData(data, win);
return songInfo;
});
if (tempSongInfo) {
for (const c of callbacks) {
c(tempSongInfo, 'ytmd:video-src-changed');
}
}
});
ipcMain.on(
'ytmd:play-or-paused',
(
async (
_,
{
isPaused,
elapsedSeconds,
}: { isPaused: boolean; elapsedSeconds: number },
) => {
songInfo.isPaused = isPaused;
songInfo.elapsedSeconds = elapsedSeconds;
if (handlingData) {
return;
}
const tempSongInfo = await dataMutex.runExclusive<SongInfo | null>(() => {
if (!songInfo) {
return null;
}
for (const c of callbacks) {
c(songInfo, 'ytmd:play-or-paused');
songInfo.isPaused = isPaused;
songInfo.elapsedSeconds = elapsedSeconds;
return songInfo;
});
if (tempSongInfo) {
for (const c of callbacks) {
c(tempSongInfo, 'ytmd:play-or-paused');
}
}
},
);