Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 87 additions & 9 deletions YoutubeMusicGeniusLyrics.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/* 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'

const SCRIPT_NAME = 'Youtube Music Genius Lyrics'
let lyricsDisplayState = 'hidden'
let lyricsWidth = '40%'
let resizeRequested = false

const elmBuild = (tag, ...contents) => {
/** @type {HTMLElement} */
Expand Down Expand Up @@ -188,7 +187,7 @@ function setFrameDimensions (container, iframe) {

function onResize () {
window.setTimeout(function () {
resizeRequested = true
document.body.dispatchEvent(new CustomEvent('genius-resize-requested'))
}, 200)
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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 () {
Expand Down Expand Up @@ -705,6 +782,7 @@ const genius = geniusLyrics({
emptyURL: 'https://music.youtube.com/robots.txt',
config: [configLyricsWidth],
main,
setupMain,
addCss,
listSongs,
showSearchField,
Expand Down
Loading