mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
fix: fix an issue with videodatachangefired timing
This commit is contained in:
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
2
src/reset.d.ts
vendored
@ -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 {
|
||||||
|
|||||||
6
src/types/video-data-changed.ts
Normal file
6
src/types/video-data-changed.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import type { VideoDataChangeValue } from '@/types/player-api-events';
|
||||||
|
|
||||||
|
export interface VideoDataChanged {
|
||||||
|
name: string;
|
||||||
|
videoData?: VideoDataChangeValue;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user