diff --git a/extension/src/controllers/video-data-sync-controller.ts b/extension/src/controllers/video-data-sync-controller.ts index babbdf2e..ecbb2f3e 100644 --- a/extension/src/controllers/video-data-sync-controller.ts +++ b/extension/src/controllers/video-data-sync-controller.ts @@ -1,6 +1,5 @@ import { ConfirmedVideoDataSubtitleTrack, - ExtensionSyncMessage, OpenAsbplayerSettingsMessage, SerializedSubtitleFile, VideoData, diff --git a/extension/src/pages.json b/extension/src/pages.json index bf189ab6..8ee1e2a7 100644 --- a/extension/src/pages.json +++ b/extension/src/pages.json @@ -11,7 +11,7 @@ { "host": "(m|www)\\.youtube\\.com", "script": "youtube-page.js", - "path": "watch", + "path": "watch|shorts", "autoSync": { "enabled": true } diff --git a/extension/src/pages/youtube-page.ts b/extension/src/pages/youtube-page.ts index 5b2c5d86..c6980172 100644 --- a/extension/src/pages/youtube-page.ts +++ b/extension/src/pages/youtube-page.ts @@ -28,10 +28,20 @@ if (window.trustedTypes !== undefined) { }); } +const adaptYtTrack = (track: any) => { + return trackFromDef({ + label: `${track.languageCode} - ${track.name?.simpleText ?? track.name?.runs?.[0]?.text}`, + language: track.languageCode.toLowerCase(), + url: track.baseUrl, + extension: 'ytxml', + }); +}; + document.addEventListener( 'asbplayer-get-synced-data', async () => { const response: VideoData = { error: '', basename: '', subtitles: [] }; + const initialHref = window.location.href; try { const playerContext = await fetch(window.location.href) @@ -80,14 +90,7 @@ document.addEventListener( response.basename = playerContext.videoDetails?.title || document.title; response.subtitles = (playerContext?.captions?.playerCaptionsTracklistRenderer?.captionTracks || []).map( - (track: any) => { - return trackFromDef({ - label: `${track.languageCode} - ${track.name?.simpleText ?? track.name?.runs?.[0]?.text}`, - language: track.languageCode.toLowerCase(), - url: track.baseUrl, - extension: 'ytxml', - }); - } + adaptYtTrack ); } catch (error) { if (error instanceof Error) { @@ -96,12 +99,64 @@ document.addEventListener( response.error = String(error); } } finally { + if (initialHref === window.location.href) { + document.dispatchEvent( + new CustomEvent('asbplayer-synced-data', { + detail: response, + }) + ); + } + } + }, + false +); + +const dataByVideoId: { [key: string]: VideoData } = {}; +let lastVideoIdDispatched: string | undefined; + +setInterval(() => { + for (const videoId of Object.keys(dataByVideoId)) { + if (lastVideoIdDispatched !== videoId && window.location.pathname.includes(videoId)) { document.dispatchEvent( new CustomEvent('asbplayer-synced-data', { - detail: response, + detail: dataByVideoId[videoId], }) ); + lastVideoIdDispatched = videoId; } - }, - false -); + } +}, 500); + +const originalParse = JSON.parse; + +// Hijack JSON in order to detect subtitle changes delivered asynchronously +JSON.parse = function () { + // @ts-ignore + const value = originalParse.apply(this, arguments); + const tracks = value?.captions?.playerCaptionsTracklistRenderer?.captionTracks; + + if (typeof tracks === 'object' && Array.isArray(tracks) && typeof value?.videoDetails?.videoId === 'string') { + const videoId = value.videoDetails.videoId; + const subtitles = tracks.map(adaptYtTrack); + const basename = value.videoDetails?.title || document.title; + dataByVideoId[videoId] = { + subtitles, + basename, + error: '', + }; + + if (location.pathname.includes(videoId)) { + document.dispatchEvent( + new CustomEvent('asbplayer-synced-data', { + detail: { + subtitles, + basename, + error: '', + }, + }) + ); + } + } + + return value; +};