cvzi / genius-lyrics-userscript Goto Github PK
View Code? Open in Web Editor NEWA userscript library to show lyrics from genius.com on other websites
License: GNU General Public License v3.0
A userscript library to show lyrics from genius.com on other websites
License: GNU General Public License v3.0
@cyfung1031
Someone found a song where the lyrics_state is not "complete" but "unreleased" (cvzi/Spotify-Genius-Lyrics-userscript#4)
https://www.youtube.com/watch?v=DteiePMlI6M
https://genius.com/Omik-k-kamikaze-lyrics
Genius page says:
"Lyrics for this song have yet to be released. Please check back once the song has been released."
The statement is wrong, because the song was released over a year ago.
But now I remember that I have seen these several times with songs that were released long ago. So it is a common problem.
There is also "lyrics_updated_at": null
which usually is a unix timestamp. Probably someone has just added the song to genius.com without adding the lyrics.
The Genius API result:
{
"meta":
{
"status": 200
},
"response":
{
"sections":
[
{
"type": "song",
"hits":
[
{
"highlights": [],
"index": "song",
"type": "song",
"result":
{
"_type": "song",
"annotation_count": 0,
"api_path": "/songs/6493820",
"artist_names": "Omik K",
"full_title": "Kamikaze by Omik K",
"header_image_thumbnail_url": "https://images.genius.com/5afb277516caa3497f893c03334e3705.300x300x1.jpg",
"header_image_url": "https://images.genius.com/5afb277516caa3497f893c03334e3705.1000x1000x1.jpg",
"id": 6493820,
"instrumental": false,
"language": null,
"lyrics_owner_id": 6479473,
"lyrics_state": "unreleased",
"lyrics_updated_at": null,
"path": "/Omik-k-kamikaze-lyrics",
"pyongs_count": null,
"relationships_index_url": "https://genius.com/Omik-k-kamikaze-sample",
"release_date_components":
{
"year": 2021,
"month": 2,
"day": 12
},
"release_date_for_display": "February 12, 2021",
"release_date_with_abbreviated_month_for_display": "Feb. 12, 2021",
"song_art_image_thumbnail_url": "https://images.genius.com/5afb277516caa3497f893c03334e3705.300x300x1.jpg",
"song_art_image_url": "https://images.genius.com/5afb277516caa3497f893c03334e3705.1000x1000x1.jpg",
"stats":
{
"unreviewed_annotations": 0,
"hot": false,
"pageviews": 322
},
"title": "Kamikaze",
"title_with_featured": "Kamikaze",
"updated_by_human_at": 1613442088,
"url": "https://genius.com/Omik-k-kamikaze-lyrics",
"featured_artists": [],
"primary_artist":
{
"_type": "artist",
"api_path": "/artists/52390",
"header_image_url": "https://images.genius.com/a704c4574fe9d60cb7f3aec0f23cbaaa.480x720x1.jpg",
"id": 52390,
"image_url": "https://images.genius.com/a704c4574fe9d60cb7f3aec0f23cbaaa.480x720x1.jpg",
"index_character": "o",
"is_meme_verified": false,
"is_verified": false,
"name": "Omik K",
"slug": "Omik-k",
"url": "https://genius.com/artists/Omik-k"
}
}
}
]
}
],
"next_page": 2
}
}
(( Example: YouTube Music ))
I have not really checked what is the latest AlertModal, I just notice that, when I search in YouTube Music, there is an alert modal.
Since this alert modal does not have the focus, it does not cancel the input field focus, and no blocking of keyEvents.
then if i pressed enter more than one time, there will be multiple alertmodal shown, and need to click ok ok ok.
I think this is a new bug although not 100% sure.
the overlay in the backdrop does not block pointer events.
it just blur and cursor can click the links behind.
i thought it can close the dialog but it just move me to somewhere without closing the dialog.
UX experience is a bit scary now...
Edge/Chrome 114 is already published as stable version.
The key feature in Chrome 114 is Side Panel (extension API) which allows developers to create a side panel in the browser.
Genius Lyrics might consider using this feature in the future.
There is discrepancy in License Declaration.
In UserScript's field, 2020 is mentioned.
// @copyright 2020, cuzi (https://github.com/cvzi)
Inside GeniusLyrics, 2019 is mentioned in both top GPL text and option dialog footer.
Copyright (C) 2019 cuzi ([email protected])
div.innerHTML = `<p style="font-size:15px;">
Powered by <a style="font-size:15px;" target="_blank" href="https://github.com/cvzi/genius-lyrics-userscript/">GeniusLyrics.js</a>, Copyright © 2019 <a style="font-size:15px;" href="mailto:[email protected]">cuzi</a>.
<br>Licensed under the GNU General Public License v3.0</p>`
I've noticed that you put this script as a library script on OUJS. i.e. the // ==UserLibrary==
metadata block and of course the .js only extension.
If you have a specific reason why you are using @grant
in it I'd like to know please.
Eventually this script is not going to be served... so it is important to understand your reasoning.
Thanks for the look,
OUJS Admin
go to a page that cannot do auto lyrics
e.g. youtube: https://www.youtube.com/watch?v=aLTQmyIGoXg
e.g. youtube music: https://music.youtube.com/watch?v=aLTQmyIGoXg
e.g. spotify: https://open.spotify.com/track/59qKtAOHPMdqGaVoUVAbPc
Manual click the button to search any lyrics
select from the results.
once the lyrics is shown, open option dialog
change the theme
then the loaded lyrics will disappear.
Greasemonkey can't load scripts in iframes
greasemonkey/greasemonkey#2574
Firefox and Tampermonkey seems to work fine.
Options are always the default values. Refresh the pages and the values will reset to default.
Both checker boxes and select boxes.
I have disabled all other extensions and userscripts.
This issue only happens in YouTube Music Genius Lyrics & YouTube Genius Lyrics.
Not happen in Spotify Genius Lyrics.
@cvzi is it due to the recent updates?
Actually when I first touched these codes, I don't understand why it is https://www.youtube.com/robots.txt
.
For interactions between top frame and iframe, we can just use postMessage
to do.
If we go for https://genius.com/robots.txt
, we can use the native fetch API to do which is exactly the same implementation in single-page application (SPA).
https://genius.com/robots.txt
https://genius.com/robots.txt
is loaded, postMessage
to top frame to ask for what you wanthttps://genius.com/robots.txt
do his job (same-origin, like SPA). I have done before that location href can be also faked with history API replaceState
, and the REFERER sent in native fetch can be changed tooRecently, i switched to Brave+ViolentMonkey, and i cannot fetch the lyrics too. (but the reason is different, the solution might require to change the communication between iframe and top frame).
If you just create a Iframe with the same origin as the top window, what is the point of creating iframe? You can just use GM.xmlHttpRequest in your top frame.
I noted that
My other Userscripts that use this library:
https://github.com/cvzi/Bandcamp-script-deluxe-edition
is mentioned in the README. I did not read its code but I guess such a big change might break something there?
innerHTML
to textContent
innerHTML
to elmBuild
const elmBuild = (tag, ...contents) => {
/** @type {HTMLElement} */
const elm = typeof tag === 'string' ? document.createElement(tag) : tag;
for (content of contents) {
if (!content || typeof content !== 'object' || (content instanceof Node)) {
elm.append(content)
} else if (content.length > 0) {
elm.appendChild(elmBuild(...content))
} else if (content.style) {
Object.assign(elm.style, content.style);
} else if (content.classList) {
elm.classList.add(...content.classList)
} else if (content.attr) {
for (const [attr, val] of Object.entries(content.attr)) elm.setAttribute(attr, val);
} else {
Object.assign(elm, content)
}
}
return elm;
}
const elmBuildNS = (tag, ...contents) => {
const ns = 'http://www.w3.org/2000/svg'
/** @type {Element} */
const elm = typeof tag === 'string' ? document.createElementNS(ns, tag) : tag;
for (content of contents) {
if (!content || typeof content !== 'object' || (content instanceof Node)) {
elm.append(content)
} else if (content.length > 0) {
elm.appendChild(elmBuildNS(...content))
} else if (content.style) {
Object.assign(elm.style, content.style);
} else if (content.classList) {
elm.classList.add(...content.classList)
} else if (content.attr) {
for (const [attr, val] of Object.entries(content.attr)) elm.setAttributeNS(ns, attr, val);
} else {
Object.assign(elm, content)
}
}
return elm;
}
Coding Example
elmBuild(container,
['h2',
'This script only works in ',
['a', {
'target': '_blank',
'href': 'https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/'
}, 'Tampermonkey'],
],
'Greasemonkey is no longer supported because of this ',
['a', {
'target': '_blank',
'href': 'https://github.com/greasemonkey/greasemonkey/issues/2574'
}, 'bug greasemonkey/issues/2574'],
' in Greasemonkey.',
)
YouTube Music Genius now is generating "[Report Only] This document requires 'TrustedScript' assignment." in Edge/Chrome 112.
Not only the "getTrueWindow" method. This message appears for the change of innerHTML.
Seems it is still working limitedly, but not as good as before.
In Chrome 111+ it's possible to show an always-on-top window with HTML content, previously only a video could be shown like this. This could be used to show the lyrics even if Youtube/Spotify tab is in the background.
A website that uses this:
https://lazy-guy.github.io/tomodoro/index.html
https://greasyfork.org/en/scripts/377439-spotify-genius-lyrics/discussions/251399
hit.result.language
is removed in the latest genius ?if (hit.result.language === 'romanization') {
isGeniusTranslationLike
is not working since hit.result.language
detection failure2000
3007
TODO
hit.result.language
isGeniusTranslationLike
due to the omitted field hit.result.language
lz-string
aims for store data in localStorage
with compression which is fast and reliable. It is based on LZW and with the study of localStorage characteristics.
I just found it recently and this is what we want.
Demo Here: https://raw.githack.com/cvzi/genius-lyrics-userscript/master/references/lz-string-demo.html
Goto https://www.youtube.com/watch?v=JMzDWbqROT4
Search (Auto/Manual) "Girls Dead Monster All Song"
I see this variable in script but seems not really used.
obsoleted? can just remove this variable in all scripts?
getTrueWindow shall use the window inside iframe to ensure the APIs are native.
Assign to myself.
function freshWindowFromIframe () {
const iframe = document.body.appendChild(document.createElement('iframe'))
iframe.style.display = 'none'
return iframe.contentWindow
}
const setTimeout = function (a, b) {
if (window.setTimeout.toString().indexOf('[native code]') !== -1) {
return window.setTimeout(a, b)
}
if (top.setTimeout.toString().indexOf('[native code]') !== -1) {
return top.setTimeout(a, b)
}
if (!cleanWindow && document.body) {
cleanWindow = freshWindowFromIframe()
}
if (cleanWindow) {
return cleanWindow.setTimeout(a, b)
} else {
return window.setTimeout(a, b)
}
}
const setInterval = function (a, b) {
if (window.setInterval.toString().indexOf('[native code]') !== -1) {
return window.setInterval(a, b)
}
if (top.setInterval.toString().indexOf('[native code]') !== -1) {
return top.setInterval(a, b)
}
if (!cleanWindow && document.body) {
cleanWindow = freshWindowFromIframe()
}
if (cleanWindow) {
return cleanWindow.setInterval(a, b)
} else {
return window.setInterval(a, b)
}
}
const clearTimeout = function (a, b) {
if (window.clearTimeout.toString().indexOf('[native code]') !== -1) {
return window.clearTimeout(a, b)
}
if (top.clearTimeout.toString().indexOf('[native code]') !== -1) {
return top.clearTimeout(a, b)
}
if (!cleanWindow && document.body) {
cleanWindow = freshWindowFromIframe()
}
if (cleanWindow) {
return cleanWindow.clearTimeout(a, b)
} else {
return window.clearTimeout(a, b)
}
}
const clearInterval = function (a, b) {
if (window.clearInterval.toString().indexOf('[native code]') !== -1) {
return window.clearInterval(a, b)
}
if (top.clearInterval.toString().indexOf('[native code]') !== -1) {
return top.clearInterval(a, b)
}
if (!cleanWindow && document.body) {
cleanWindow = freshWindowFromIframe()
}
if (cleanWindow) {
return cleanWindow.clearInterval(a, b)
} else {
return window.clearInterval(a, b)
}
}
I cannot notify any declaration of setTimeout
, setInterval
, clearTimeout
, and clearInterval
in the script. What is the purpose of these coding?
I saw that document.getElementById('lyrics').scrollIntoView()
is used in the script.
However, if iframe.contentWindow.postMessage({ iAm: custom.scriptName, type: 'scrollLyrics', position: positionFraction }, '*')
is called, the positionFraction
from 0.0
to 1.0
is not referred to the location of #lyrics
(it seems refer to the scrollHeight of the entire iframe)
I have noticed that there are similar but different implementations in 4 themes. (like clear / remove / hide the elements)
As the recent scrollIntoView change is in "Genius React". How about other themes? deprecated?
Which theme you are using in Spotify?
The new genius page design will break the Youtube script and parts of the Spotify script
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.