// ==UserScript== // @name Youtube Genius Lyrics // @description Show lyrics/songtexts from genius.com on Youtube next to music videos // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt // @copyright 2020, cuzi (https://github.com/cvzi) // @author cuzi // @supportURL https://github.com/cvzi/Youtube-Genius-Lyrics-userscript/issues // @version 8 // @require https://greasyfork.org/scripts/406698-geniuslyrics/code/GeniusLyrics.js?version=825578 // @grant GM.xmlHttpRequest // @grant GM.setValue // @grant GM.getValue // @grant unsafeWindow // @connect genius.com // @include https://www.youtube.com/* // @include https://music.youtube.com/* // @namespace https://greasyfork.org/users/20068 // @downloadURL none // ==/UserScript== /* global GM, genius, unsafeWindow, geniusLyrics */ // eslint-disable-line no-unused-vars 'use strict' var genius const musicKeywords = [ 'music', 'musik', 'album', 'single', 'hiphop', 'hip-hop', 'hip hop', 'rap', 'rnb', 'r\'n\'n', 'r&b', 'dance', 'reggae', 'folk', 'indie', 'metal', 'pop', 'punk', 'rock' ] const musicDescriptors = [ 'Music video', 'Composer', 'Lyricist', 'full track', 'vevo.ly', 'Provided to YouTube by ', 'Columbia Records', 'Universal Music Group', 'Warner Music Group', 'Sony Music' ] function addCss () { // Spotify document.head.appendChild(document.createElement('style')).innerHTML = ` #myconfigwin39457845 { z-index:2060 !important; } #lyricscontainer { position:fixed; right:0px; margin:0px; padding:0px; background:white; z-index:2001; font-size:1.4rem; border:none; border-radius:none; } .lyricsiframe { opacity:0.1; transition:opacity 2s; margin:0px; padding:0px; } .lyricsnavbar { font-size : 0.7em; text-align:right; padding-right:10px; background:#fafafa; } .lyricsnavbar span,.lyricsnavbar a:link,.lyricsnavbar a:visited { color:#606060; text-decoration:none; transition:color 400ms; } .lyricsnavbar a:hover,.lyricsnavbar span:hover { color:#9026e0; text-decoration:none; } .loadingspinner { color:black; font-size:1em; line-height:2.5em; } .loadingspinnerholder { z-index:2050; background-color:white; position:absolute; top:56px; right:100px; cursor:progress } .lorem {padding:10px 0px 0px 15px; font-size: 1.4rem;line-height: 2.2rem;letter-spacing: 0.3rem;} .lorem .white {background:white;color:white} .lorem .gray {background:rgb(204, 204, 204);color:rgb(204, 204, 204)} ` } function calcContainerWidthTop () { let w const upnext = document.getElementById('upnext') const playlist = document.querySelector('ytd-playlist-panel-renderer#playlist') const video = document.querySelector('ytd-watch-flexy div#primary video') if (upnext && upnext.getBoundingClientRect().left > 0) { w = window.innerWidth - upnext.getBoundingClientRect().left - 5 } else if (playlist && playlist.getBoundingClientRect().left > 0) { w = window.innerWidth - playlist.getBoundingClientRect().left - 5 } else if (video) { w = window.innerWidth - 1.02 * video.getClientRects()[0].right } else { w = window.innerWidth * 0.45 } w = Math.min(window.innerWidth * 0.75, w) const top = document.getElementById('masthead-container').clientHeight return [w, top] } function setFrameDimensions (container, iframe) { const bar = container.querySelector('.lyricsnavbar') const width = iframe.style.width = container.clientWidth - 1 + 'px' const height = iframe.style.height = window.innerHeight - bar.clientHeight - document.getElementById('masthead-container').clientHeight + 'px' if (genius.option.themeKey === 'spotify') { iframe.style.backgroundColor = 'black' } else { iframe.style.backgroundColor = '' } return [width, height] } function onResize () { window.setTimeout(function () { genius.option.resizeOnNextRun = true }, 200) } function resize () { const container = document.getElementById('lyricscontainer') const iframe = document.getElementById('lyricsiframe') if (!container) { return } const [w, top] = calcContainerWidthTop() container.style.top = top + 'px' container.style.width = w + 'px' if (iframe) { setFrameDimensions(container, iframe) } } function getCleanLyricsContainer () { let container const [w, top] = calcContainerWidthTop() if (!document.getElementById('lyricscontainer')) { container = document.createElement('div') container.id = 'lyricscontainer' document.body.appendChild(container) } else { container = document.getElementById('lyricscontainer') container.innerHTML = '' } container.style = '' container.style.top = top + 'px' container.style.width = w + 'px' return document.getElementById('lyricscontainer') } function hideLyrics () { document.querySelectorAll('.loadingspinner').forEach((spinner) => spinner.remove()) if (document.getElementById('lyricscontainer')) { document.getElementById('lyricscontainer').remove() } addLyricsButton() } var checkFullscreenIV function addLyricsButton () { if (document.getElementById('showlyricsbutton')) { return } const top = calcContainerWidthTop()[1] const b = document.createElement('div') b.setAttribute('id', 'showlyricsbutton') b.setAttribute('style', 'position:absolute;top:' + (top + 2) + 'px;right:0px;color:#ffff64;cursor:pointer;background:black;border-radius:50%;margin:auto;text-align:center;font-size:15px;line-height:15px;') b.setAttribute('title', 'Load lyrics from genius.com') b.appendChild(document.createTextNode('๐ ')) b.addEventListener('click', function onShowLyricsButtonClick () { genius.option.autoShow = true // Temporarily enable showing lyrics automatically on song change genius.iv.main = window.setInterval(main, 2000) addLyrics(true) }) document.body.appendChild(b) window.clearInterval(checkFullscreenIV) checkFullscreenIV = window.setInterval(function () { if (document.getElementById('showlyricsbutton')) { document.getElementById('showlyricsbutton').style.display = document.fullscreenElement ? 'none' : 'block' } }, 1000) } var lastVideoId = null function addLyrics (force, beLessSpecific) { const h1 = document.querySelector('#content ytd-watch-flexy:not([hidden]) #container .title') if (!h1 || !document.querySelector('ytd-watch-flexy div#primary video')) { // Not a video page or video page not visible hideLyrics() return } let isMusic = false const videoTitle = h1.textContent.toLowerCase() if (videoTitle.indexOf('official video') !== -1 || videoTitle.indexOf('music video') !== -1 || videoTitle.indexOf('audio') !== -1) { isMusic = true } if (videoTitle.match(/.+\s+[-โ]\s+.+/)) { isMusic = true } let videoDetails try { videoDetails = JSON.parse(unsafeWindow.document.querySelector('ytd-app').__data.data.player.args.player_response).videoDetails } catch (e) { videoDetails = { keywords: [], shortDescription: '' } } if (!videoDetails.keywords) { videoDetails.keywords = [] } if ('videoId' in videoDetails) { if (lastVideoId === videoDetails.videoId + genius.option.themeKey && document.getElementById('lyricscontainer')) { // Same video id and same theme and lyrics are showing -> stop here return } else { lastVideoId = videoDetails.videoId + genius.option.themeKey } } else { lastVideoId = null } const keywords = videoDetails.keywords.join('').toLowerCase() for (let i = 0; i < musicKeywords.length; i++) { if (keywords.indexOf(musicKeywords[i]) !== -1) { isMusic = true break } } for (let i = 0; i < musicDescriptors.length; i++) { if (videoDetails.shortDescription.indexOf(musicDescriptors[i]) !== -1) { isMusic = true break } } if (!isMusic) { hideLyrics() return } let songArtists let songTitle = videoTitle.replace(/\(.+?\)/, '') songTitle = songTitle.replace(/\[.+?\]/, '') songTitle = songTitle.replace(/official\s*music\s*video/, '') songTitle = songTitle.replace(/official\s*video/, '') songTitle = songTitle.replace(/music\s*video/, '') songTitle = songTitle.replace(/video/, '') songTitle = songTitle.replace(/music/, '') songTitle = songTitle.replace(/exclusive\s*-?/, '') songTitle = songTitle.trim() // Pattern: Artist - Song title songTitle = songTitle.split(/\s+[-โ]\s+/) if (songTitle.length === 1) { // Pattern: Artist | Song title const m = songTitle[0].match(/(.+?)\s*\|\s*(.+)/) if (m) { songTitle = [m[1], m[2]] } } if (songTitle.length === 1) { // Pattern: Artist "Song title" const m = songTitle[0].match(/(.+?)\s*["โโ'`ยด*]+(.+)["โโ'`ยด*]+/) if (m) { songTitle = [m[1], m[2]] } } if (songTitle.length === 1) { // Pattern: Songtitle by Artist const m = songTitle[0].match(/(.+?)\s+by\s+(.+)/) if (m) { songTitle = [m[2], m[1]] } } if (songTitle.length === 1 && 'author' in videoDetails) { // Fallback to video author name songArtists = videoDetails.author.toLowerCase() songArtists = songArtists.replace(/vevo/, '') songArtists = songArtists.replace(/official/, '') songArtists = songArtists.replace(/music/, '') songArtists = songArtists.replace(/band/, '') songArtists = songArtists.replace(/-\s*topic/, '') } else { songArtists = songTitle.shift().trim() } const songArtistsArr = songArtists.split(',').map(s => s.trim()) songTitle = songTitle.join(' - ').trim() songTitle = songTitle.replace('"', '').replace('[', '').replace(']', '').replace('(', '').replace(')', '').replace('|', '') songTitle = songTitle.replace(/\W+$/, '') songTitle = songTitle.replace(/^\W+/, '') songTitle = songTitle.trim() let feat = songTitle.indexOf(' feat') if (feat !== -1) { songTitle = songTitle.substring(0, feat).trim() } feat = songTitle.indexOf(' ft') if (feat !== -1) { songTitle = songTitle.substring(0, feat).trim() } const musicIsPlaying = document.querySelector('.ytp-play-button.ytp-button').title.indexOf('Pause') !== -1 genius.f.loadLyrics(force, beLessSpecific, songTitle, songArtistsArr, musicIsPlaying) } function showSearchField (query) { const b = getCleanLyricsContainer() b.style.border = '1px solid black' b.style.borderRadius = '3px' b.style.padding = '5px' b.appendChild(document.createTextNode('Search genius.com: ')) b.style.paddingRight = '15px' const input = b.appendChild(document.createElement('input')) input.className = 'SearchInputBox__input' input.placeholder = 'Search genius.com...' const span = b.appendChild(document.createElement('span')) span.style = 'cursor:pointer' span.appendChild(document.createTextNode(' \uD83D\uDD0D')) if (query) { input.value = query } else if (genius.current.artists) { input.value = genius.current.artists } input.addEventListener('change', function onSearchLyricsButtonClick () { if (input.value) { genius.f.searchByQuery(input.value, b) } }) input.addEventListener('keyup', function onSearchLyricsKeyUp (ev) { if (ev.keyCode === 13) { ev.preventDefault() if (input.value) { genius.f.searchByQuery(input.value, b) } } }) span.addEventListener('click', function onSearchLyricsKeyUp (ev) { if (input.value) { genius.f.searchByQuery(input.value, b) } }) document.body.appendChild(b) input.focus() } function listSongs (hits, container, query) { if (!container) { container = getCleanLyricsContainer() } // Back to search button const backToSearchButton = document.createElement('a') backToSearchButton.href = '#' backToSearchButton.appendChild(document.createTextNode('Back to search')) backToSearchButton.addEventListener('click', function backToSearchButtonClick (ev) { ev.preventDefault() if (query) { showSearchField(query) } else if (genius.current.artists) { showSearchField(genius.current.artists + ' ' + genius.current.title) } else { showSearchField() } }) const separator = document.createElement('span') separator.setAttribute('class', 'second-line-separator') separator.setAttribute('style', 'padding:0px 3px') separator.appendChild(document.createTextNode('โข')) // Hide button const hideButton = document.createElement('a') hideButton.href = '#' hideButton.appendChild(document.createTextNode('Hide')) hideButton.addEventListener('click', function hideButtonClick (ev) { ev.preventDefault() hideLyrics() }) // List search results const trackhtml = '