fix: fix an issue with videodatachangefired timing

This commit is contained in:
JellyBrick
2023-11-30 00:45:15 +09:00
parent 4c0cce89ee
commit d78da237fc
5 changed files with 59 additions and 26 deletions

View File

@ -4,6 +4,8 @@ import style from './style.css?inline';
import { createPlugin } from '@/utils'; import { createPlugin } from '@/utils';
import type { VideoDataChanged } from '@/types/video-data-changed';
export default createPlugin({ export default createPlugin({
name: 'Album Color Theme', name: 'Album Color Theme',
restartNeeded: true, restartNeeded: true,
@ -110,8 +112,8 @@ export default createPlugin({
onPlayerApiReady(playerApi) { onPlayerApiReady(playerApi) {
const fastAverageColor = new FastAverageColor(); const fastAverageColor = new FastAverageColor();
playerApi.addEventListener('videodatachange', (name: string) => { document.addEventListener('videodatachange', (event: CustomEvent<VideoDataChanged>) => {
if (name === 'dataloaded') { if (event.detail.name === 'dataloaded') {
const playerResponse = playerApi.getPlayerResponse(); const playerResponse = playerApi.getPlayerResponse();
const thumbnail = playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); const thumbnail = playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0);
if (thumbnail) { if (thumbnail) {

View File

@ -1,5 +1,6 @@
import { createPlugin } from '@/utils'; import { createPlugin } from '@/utils';
import type { VideoDataChanged } from '@/types/video-data-changed';
import type { YoutubePlayer } from '@/types/youtube-player'; import type { YoutubePlayer } from '@/types/youtube-player';
export type DisableAutoPlayPluginConfig = { export type DisableAutoPlayPluginConfig = {
@ -13,7 +14,7 @@ export default createPlugin<
{ {
config: DisableAutoPlayPluginConfig | null; config: DisableAutoPlayPluginConfig | null;
api: YoutubePlayer | null; api: YoutubePlayer | null;
eventListener: (name: string) => void; eventListener: (event: CustomEvent<VideoDataChanged>) => void;
timeUpdateListener: (e: Event) => void; timeUpdateListener: (e: Event) => void;
}, },
DisableAutoPlayPluginConfig DisableAutoPlayPluginConfig
@ -44,12 +45,12 @@ export default createPlugin<
renderer: { renderer: {
config: null, config: null,
api: null, api: null,
eventListener(name: string) { eventListener(event: CustomEvent<VideoDataChanged>) {
if (this.config?.applyOnce) { if (this.config?.applyOnce) {
this.api?.removeEventListener('videodatachange', this.eventListener); document.removeEventListener('videodatachange', this.eventListener);
} }
if (name === 'dataloaded') { if (event.detail.name === 'dataloaded') {
this.api?.pauseVideo(); this.api?.pauseVideo();
document.querySelector<HTMLVideoElement>('video')?.addEventListener('timeupdate', this.timeUpdateListener, { once: true }); document.querySelector<HTMLVideoElement>('video')?.addEventListener('timeupdate', this.timeUpdateListener, { once: true });
} }
@ -65,10 +66,10 @@ export default createPlugin<
onPlayerApiReady(api) { onPlayerApiReady(api) {
this.api = api; this.api = api;
api.addEventListener('videodatachange', this.eventListener); document.addEventListener('videodatachange', this.eventListener);
}, },
stop() { stop() {
this.api?.removeEventListener('videodatachange', this.eventListener); document.removeEventListener('videodatachange', this.eventListener);
}, },
onConfigChange(newConfig) { onConfigChange(newConfig) {
this.config = newConfig; this.config = newConfig;

View File

@ -13,28 +13,48 @@ import type { PluginConfig } from '@/types/plugins';
import type { YoutubePlayer } from '@/types/youtube-player'; import type { YoutubePlayer } from '@/types/youtube-player';
let api: (Element & YoutubePlayer) | null = null; let api: (Element & YoutubePlayer) | null = null;
let isPluginLoaded = false;
let isApiLoaded = false;
let firstDataLoaded = false;
const observer = new MutationObserver(() => {
const playerApi = document.querySelector<Element & YoutubePlayer>('#movie_player');
if (playerApi) {
observer.disconnect();
// Inject song-info provider
setupSongInfo(playerApi);
const dataLoadedListener = (name: string) => {
if (!firstDataLoaded && name === 'dataloaded') {
firstDataLoaded = true;
playerApi.removeEventListener('videodatachange', dataLoadedListener);
}
};
playerApi.addEventListener('videodatachange', dataLoadedListener);
if (isPluginLoaded && !isApiLoaded) {
api = playerApi;
isApiLoaded = true;
onApiLoaded();
}
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
async function listenForApiLoad() { async function listenForApiLoad() {
if (!isApiLoaded) {
api = document.querySelector('#movie_player'); api = document.querySelector('#movie_player');
if (api) { if (api) {
await onApiLoaded(); await onApiLoaded();
return; return;
} }
const observer = new MutationObserver(() => {
api = document.querySelector('#movie_player');
if (api) {
observer.disconnect();
onApiLoaded();
} }
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
} }
interface YouTubeMusicAppElement extends HTMLElement { interface YouTubeMusicAppElement extends HTMLElement {
@ -45,9 +65,6 @@ async function onApiLoaded() {
window.ipcRenderer.on('seekTo', (_, t: number) => api!.seekTo(t)); window.ipcRenderer.on('seekTo', (_, t: number) => api!.seekTo(t));
window.ipcRenderer.on('seekBy', (_, t: number) => api!.seekBy(t)); window.ipcRenderer.on('seekBy', (_, t: number) => api!.seekBy(t));
// Inject song-info provider
setupSongInfo(api!);
const video = document.querySelector('video')!; const video = document.querySelector('video')!;
const audioContext = new AudioContext(); const audioContext = new AudioContext();
const audioSource = audioContext.createMediaElementSource(video); const audioSource = audioContext.createMediaElementSource(video);
@ -59,6 +76,10 @@ async function onApiLoaded() {
} }
} }
if (firstDataLoaded) {
document.dispatchEvent(new CustomEvent('videodatachange', { detail: { name: 'dataloaded' } }));
}
const audioCanPlayEventDispatcher = () => { const audioCanPlayEventDispatcher = () => {
document.dispatchEvent( document.dispatchEvent(
new CustomEvent('audioCanPlay', { new CustomEvent('audioCanPlay', {
@ -128,6 +149,7 @@ async function onApiLoaded() {
(async () => { (async () => {
await loadAllRendererPlugins(); await loadAllRendererPlugins();
isPluginLoaded = true;
window.ipcRenderer.on( window.ipcRenderer.on(
'plugin:unload', 'plugin:unload',

2
src/reset.d.ts vendored
View File

@ -4,6 +4,7 @@ import type { ipcRenderer as electronIpcRenderer } from 'electron';
import type is from 'electron-is'; import type is from 'electron-is';
import type config from './config'; import type config from './config';
import type { VideoDataChanged } from '@/types/video-data-changed';
declare global { declare global {
interface Compressor { interface Compressor {
@ -13,6 +14,7 @@ declare global {
interface DocumentEventMap { interface DocumentEventMap {
'audioCanPlay': CustomEvent<Compressor>; 'audioCanPlay': CustomEvent<Compressor>;
'videodatachange': CustomEvent<VideoDataChanged>;
} }
interface Window { interface Window {

View File

@ -0,0 +1,6 @@
import type { VideoDataChangeValue } from '@/types/player-api-events';
export interface VideoDataChanged {
name: string;
videoData?: VideoDataChangeValue;
}