diff --git a/src/providers/song-info-front.ts b/src/providers/song-info-front.ts index 0d1cbd3a..0c308c70 100644 --- a/src/providers/song-info-front.ts +++ b/src/providers/song-info-front.ts @@ -5,6 +5,7 @@ import { getImage, SongInfo } from './song-info'; import { YoutubePlayer } from '../types/youtube-player'; import { GetState } from '../types/datahost-get-state'; +import { VideoDataChangeValue } from '../types/player-api-events'; let songInfo: SongInfo = {} as SongInfo; export const getSongInfo = () => songInfo; @@ -107,18 +108,22 @@ export default () => { pause: (e: Event) => playPausedHandler(e, 'pause'), }; + const waitingEvent = new Set(); // Name = "dataloaded" and abit later "dataupdated" - apiEvent.detail.addEventListener('videodatachange', (name: string) => { - if (name !== 'dataloaded') { - return; - } - const video = $('video'); - video?.dispatchEvent(srcChangedEvent); + apiEvent.detail.addEventListener('videodatachange', (name: string, videoData) => { + if (name === 'dataupdated' && waitingEvent.has(videoData.videoId)) { + waitingEvent.delete(videoData.videoId); + sendSongInfo(videoData); + } else if (name === 'dataloaded') { + const video = $('video'); + video?.dispatchEvent(srcChangedEvent); - for (const status of ['playing', 'pause'] as const) { // for fix issue that pause event not fired - video?.addEventListener(status, playPausedHandlers[status]); + for (const status of ['playing', 'pause'] as const) { // for fix issue that pause event not fired + video?.addEventListener(status, playPausedHandlers[status]); + } + + waitingEvent.add(videoData.videoId); } - setTimeout(sendSongInfo, 200); }); const video = $('video')!; @@ -126,16 +131,10 @@ export default () => { video.addEventListener(status, playPausedHandlers[status]); } - function sendSongInfo() { + function sendSongInfo(videoData: VideoDataChangeValue) { const data = apiEvent.detail.getPlayerResponse(); - for (const e of $$('.byline.ytmusic-player-bar > .yt-simple-endpoint')) { - if (e.href?.includes('browse/FEmusic_library_privately_owned_release') || e.href?.includes('browse/MPREb')) { - data.videoDetails.album = e.textContent; - break; - } - } - + data.videoDetails.album = videoData?.Hd?.playerOverlays?.playerOverlayRenderer?.browserMediaSession?.browserMediaSessionRenderer?.album.runs?.at(0)?.text; data.videoDetails.elapsedSeconds = 0; data.videoDetails.isPaused = false; ipcRenderer.send('video-src-changed', data); diff --git a/src/types/player-api-events.ts b/src/types/player-api-events.ts index af05c82b..afca99ae 100644 --- a/src/types/player-api-events.ts +++ b/src/types/player-api-events.ts @@ -1,15 +1,262 @@ +export interface AlbumDetails { + responseContext: ResponseContext; + contents: Contents; + currentVideoEndpoint: CurrentVideoEndpoint; + trackingParams: string; + playerOverlays: PlayerOverlays; + videoReporting: VideoReporting; +} + +export interface Contents { + singleColumnMusicWatchNextResultsRenderer: SingleColumnMusicWatchNextResultsRenderer; +} + +export interface SingleColumnMusicWatchNextResultsRenderer { + tabbedRenderer: TabbedRenderer; +} + +export interface TabbedRenderer { + watchNextTabbedResultsRenderer: WatchNextTabbedResultsRenderer; +} + +export interface WatchNextTabbedResultsRenderer { + tabs: Tab[]; +} + +export interface Tab { + tabRenderer: TabRenderer; +} + +export interface TabRenderer { + title: string; + content?: Content; + trackingParams: string; + endpoint?: Endpoint; +} + +export interface Content { + musicQueueRenderer: MusicQueueRenderer; +} + +export interface MusicQueueRenderer { + hack: boolean; +} + +export interface Endpoint { + clickTrackingParams: string; + browseEndpoint: BrowseEndpoint; +} + +export interface BrowseEndpoint { + browseId: string; + browseEndpointContextSupportedConfigs: BrowseEndpointContextSupportedConfigs; +} + +export interface BrowseEndpointContextSupportedConfigs { + browseEndpointContextMusicConfig: BrowseEndpointContextMusicConfig; +} + +export interface BrowseEndpointContextMusicConfig { + pageType: string; +} + +export interface CurrentVideoEndpoint { + clickTrackingParams: string; + watchEndpoint: WatchEndpoint; +} + +export interface WatchEndpoint { + videoId: string; + playlistId: string; + index: number; + playlistSetVideoId: string; + loggingContext: LoggingContext; +} + +export interface LoggingContext { + vssLoggingContext: VssLoggingContext; +} + +export interface VssLoggingContext { + serializedContextData: string; +} + +export interface PlayerOverlays { + playerOverlayRenderer: PlayerOverlayRenderer; +} + +export interface PlayerOverlayRenderer { + actions: PlayerOverlayRendererAction[]; + browserMediaSession: BrowserMediaSession; +} + +export interface PlayerOverlayRendererAction { + likeButtonRenderer: LikeButtonRenderer; +} + +export interface LikeButtonRenderer { + target: Target; + likeStatus: string; + trackingParams: string; + likesAllowed: boolean; + serviceEndpoints: ServiceEndpoint[]; +} + +export interface ServiceEndpoint { + clickTrackingParams: string; + likeEndpoint: LikeEndpoint; +} + +export interface LikeEndpoint { + status: string; + target: Target; + actions?: LikeEndpointAction[]; + likeParams?: string; + dislikeParams?: string; + removeLikeParams?: string; +} + +export interface LikeEndpointAction { + clickTrackingParams: string; + musicLibraryStatusUpdateCommand: MusicLibraryStatusUpdateCommand; +} + +export interface MusicLibraryStatusUpdateCommand { + libraryStatus: string; + addToLibraryFeedbackToken: string; +} + +export interface Target { + videoId: string; +} + +export interface BrowserMediaSession { + browserMediaSessionRenderer: BrowserMediaSessionRenderer; +} + +export interface BrowserMediaSessionRenderer { + album: Title; + thumbnailDetails: ThumbnailDetails; +} + +export interface Title { + runs: TitleRun[]; +} + +export interface TitleRun { + text: string; +} + +export interface ThumbnailDetails { + thumbnails: Thumbnail[]; +} + +export interface Thumbnail { + url: string; + width: number; + height: number; +} + +export interface ResponseContext { + serviceTrackingParams: ServiceTrackingParam[]; +} + +export interface ServiceTrackingParam { + service: string; + params: Param[]; +} + +export interface Param { + key: string; + value: string; +} + +export interface VideoReporting { + reportFormModalRenderer: ReportFormModalRenderer; +} + +export interface ReportFormModalRenderer { + optionsSupportedRenderers: OptionsSupportedRenderers; + trackingParams: string; + title: Title; + submitButton: Button; + cancelButton: Button; + footer: Footer; +} + +export interface Button { + buttonRenderer: ButtonRenderer; +} + +export interface ButtonRenderer { + style: string; + isDisabled: boolean; + text: Title; + trackingParams: string; +} + +export interface Footer { + runs: FooterRun[]; +} + +export interface FooterRun { + text: string; + navigationEndpoint?: NavigationEndpoint; +} + +export interface NavigationEndpoint { + clickTrackingParams: string; + urlEndpoint: URLEndpoint; +} + +export interface URLEndpoint { + url: string; +} + +export interface OptionsSupportedRenderers { + optionsRenderer: OptionsRenderer; +} + +export interface OptionsRenderer { + items: Item[]; + trackingParams: string; +} + +export interface Item { + optionSelectableItemRenderer: OptionSelectableItemRenderer; +} + +export interface OptionSelectableItemRenderer { + text: Title; + trackingParams: string; + submitEndpoint: SubmitEndpoint; +} + +export interface SubmitEndpoint { + clickTrackingParams: string; + flagEndpoint: FlagEndpoint; +} + +export interface FlagEndpoint { + flagAction: string; +} + +export type VideoDataChangeValue = Record & { + videoId: string + title: string + author: string + + Hd?: AlbumDetails + + playlistId: string + isUpcoming: boolean + loading: boolean + + lengthSeconds: number +}; + export interface PlayerAPIEvents { videodatachange: { - value: Record & { - videoId: string - title: string - author: string - - playlistId: string - isUpcoming: boolean - loading: boolean - - lengthSeconds: number - } + value: VideoDataChangeValue } & ({ name: 'dataloaded' } | { name: 'dataupdated ' }) }