mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-16 20:52:06 +00:00
fix: fix #1621
This commit is contained in:
@ -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 () => {
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user