diff --git a/YoutubeMusicGeniusLyrics.user.js b/YoutubeMusicGeniusLyrics.user.js index 174e358..ac4bc85 100644 --- a/YoutubeMusicGeniusLyrics.user.js +++ b/YoutubeMusicGeniusLyrics.user.js @@ -42,7 +42,7 @@ along with this program. If not, see . */ -/* global GM, genius, geniusLyrics, GM_addValueChangeListener */ // eslint-disable-line no-unused-vars +/* global GM, genius, geniusLyrics, GM_addValueChangeListener, HTMLMediaElement, MutationObserver */ // eslint-disable-line no-unused-vars /* jshint asi: true, esversion: 8 */ 'use strict' @@ -50,7 +50,6 @@ const SCRIPT_NAME = 'Youtube Music Genius Lyrics' let lyricsDisplayState = 'hidden' let lyricsWidth = '40%' -let resizeRequested = false const elmBuild = (tag, ...contents) => { /** @type {HTMLElement} */ @@ -188,7 +187,7 @@ function setFrameDimensions (container, iframe) { function onResize () { window.setTimeout(function () { - resizeRequested = true + document.body.dispatchEvent(new CustomEvent('genius-resize-requested')) }, 200) } @@ -239,8 +238,16 @@ function getCleanLyricsContainer () { } function getSongInfoNodes () { - const titleNode = document.querySelector('.ytmusic-player-bar .title.ytmusic-player-bar') - const artistNodes = document.querySelectorAll('.ytmusic-player-bar.subtitle a[href*="channel/"]') + let playerBars = [...document.querySelectorAll('ytmusic-player-bar.ytmusic-app')].filter(e => !e.closest('[hidden]') && !e.closest('[disabled]')) + if (playerBars.length === 0) playerBars = [...document.querySelectorAll('ytmusic-player-bar')].filter(e => !e.closest('[hidden]') && !e.closest('[disabled]')) + let titleNode = null + let artistNodes = [] + if (playerBars.length === 1) { + const playerBar = playerBars[0] + const key = '__shady_native_querySelector' in playerBar && typeof playerBar.__shady_native_querySelector === 'function' && typeof playerBar.__shady_native_querySelectorAll === 'function' ? '__shady_native_querySelector' : 'querySelector' + titleNode = playerBar[key]('.title.ytmusic-player-bar') + artistNodes = [...playerBar[`${key}All`]('.ytmusic-player-bar.subtitle a[href*="channel/"]')] + } return { titleNode, artistNodes, @@ -314,11 +321,23 @@ function addLyrics (force, beLessSpecific) { songTitle = genius.f.cleanUpSongTitle(songTitle) const video = getYoutubeMainVideo() + console.log('debug: Youtube Music Genius Lyrics - getYoutubeMainVideo()', video) const musicIsPlaying = video && !video.paused genius.f.loadLyrics(force, beLessSpecific, songTitle, songArtistsArr, musicIsPlaying) } function getYoutubeMainVideo () { + const activeMedia_ = activeMedia + if (activeMedia_) { + const moviePlayer = activeMedia_.closest('#movie_player') + const mediaList = moviePlayer ? moviePlayer.querySelectorAll('audio, video') : null + if (mediaList && mediaList.length === 1 && mediaList[0] === activeMedia_) { + return activeMedia_ + } + if (activeMedia_.classList.contains('html5-main-video')) { + return activeMedia_ + } + } let video = document.querySelector('#movie_player video[src]') if (video !== null) { return video @@ -661,10 +680,25 @@ function configLyricsWidth (div) { input.addEventListener('change', onChange) } -function main () { - GM.getValue('lyricswidth', '40%').then(function (v) { - lyricsWidth = v - if (getSongInfoNodes().isSongQueuedOrPlaying) { +const getNodeHTML = (e) => { + if (e) { + return e.__shady_native_innerHTML || e.innerHTML || '' + } + return '' +} +let activeMedia = null +async function setupMain () { + let resizeRequested = false + lyricsWidth = await GM.getValue('lyricswidth', '40%') + let runid = 0 + let lastNodeString = '' + const mutationObserver = new MutationObserver(() => { + const songInfoNodes = getSongInfoNodes() + const nodeString = `${(getNodeHTML(songInfoNodes?.titleNode) || '')}|${(songInfoNodes?.artistNodes?.map(e => getNodeHTML(e))?.join(',') || '')}` + if (lastNodeString === nodeString) return + lastNodeString = nodeString + if (nodeString.length > 1 && songInfoNodes.isSongQueuedOrPlaying) { + console.log('debug: Youtube Music Genius Lyrics - Song Info', songInfoNodes, nodeString) if (genius.option.autoShow) { addLyrics() } else { @@ -676,6 +710,49 @@ function main () { } } }) + const onMediaChanged_ = (runid_) => { + if (runid_ !== runid) return + const songInfoNodes = getSongInfoNodes() + const titleNode = songInfoNodes?.titleNode + if (titleNode) { + mutationObserver.observe(titleNode, { attributes: true, childList: true, subtree: true, characterData: true, attributeFilter: ['media-changed-at', 'title'] }) + titleNode.setAttribute('media-changed-at', Date.now()) + } else { + activeMedia = null + } + } + + const onMediaChanged = (evt) => { + const target = evt?.target + if (!(target instanceof HTMLMediaElement)) return + if (runid > 1e9) runid = 9 + const runid_ = ++runid + activeMedia = target + Promise.resolve(runid_).then(onMediaChanged_).catch(console.warn) + } + + const onResizeRequested = (evt) => { + if (runid > 1e9) runid = 9 + const runid_ = ++runid + lastNodeString = '' + resizeRequested = true + Promise.resolve(runid_).then(onMediaChanged_).catch(console.warn) + } + + document.addEventListener('durationchange', onMediaChanged, true) + document.addEventListener('loadedmetadata', onMediaChanged, true) + document.addEventListener('canplay', onMediaChanged, true) + document.addEventListener('canplaythrough', onMediaChanged, true) + document.addEventListener('emptied', onMediaChanged, true) + document.addEventListener('abort', onMediaChanged, true) + document.addEventListener('error', onMediaChanged, true) + document.addEventListener('ended', onMediaChanged, true) + document.addEventListener('genius-resize-requested', onResizeRequested, true) + Promise.resolve(++runid).then(onMediaChanged_) +} + +function main () { + // do nothing } function styleIframeContent () { @@ -705,6 +782,7 @@ const genius = geniusLyrics({ emptyURL: 'https://music.youtube.com/robots.txt', config: [configLyricsWidth], main, + setupMain, addCss, listSongs, showSearchField,