Skip to content

Instantly share code, notes, and snippets.

@pckv
Last active March 12, 2025 09:03
Show Gist options
  • Save pckv/ea2e8e72f6a8f327fb204296266c3dcf to your computer and use it in GitHub Desktop.
Save pckv/ea2e8e72f6a8f327fb204296266c3dcf to your computer and use it in GitHub Desktop.
Lyrical-nonsense Copy Userscript
// ==UserScript==
// @name Lyrical-nonsense Copy
// @namespace http://tampermonkey.net/
// @version 0.3
// @description Enables copying in lyric text and adds a copy lyrics button to each tab
// @author pckv
// @match https://www.lyrical-nonsense.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=lyrical-nonsense.com
// @grant none
// ==/UserScript==
(function () {
"use strict";
function addUserSelectToLyrics() {
// Add the CSS definition if it doesn't exist
if (
!document.head.querySelector("style").innerHTML.includes(".allow-select")
) {
// Add a new css class definition to the page
const style = document.createElement("style");
style.innerHTML = `
.allow-select {
user-select: text !important;
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
-khtml-user-select: text !important;
-webkit-touch-callout: unset !important;
}
`;
document.head.appendChild(style);
}
// Make all text inside lyrics containers selectable
const elementsWithLyrics = document.querySelectorAll(".olyrictext");
elementsWithLyrics.forEach((element) => {
element.classList.add("allow-select");
});
}
function copyLyricsToClipboard(parentId) {
// Find lyrics inside the parent element
const lyricsParentElement = document.querySelector(
`#${parentId} .olyrictext`
);
// Get the lyrics as a string
let lyrics = Array.from(lyricsParentElement.children)
.map((element) => element.innerText)
.join("\n");
// Remove any duplicate empty lines
lyrics = lyrics.replace(/\n\n\n/g, "\n\n");
// Remove any leading or trailing whitespace from each line
lyrics = lyrics
.split("\n")
.map((line) => line.trim())
.join("\n");
// Copy the lyrics to the clipboard
navigator.clipboard.writeText(lyrics);
}
function addCopyLyricsToClipboardButton(parentId) {
// Skip if the button already exists
if (document.querySelector(`#${parentId} .copy-lyrics-button`)) {
return;
}
// Find the theme selector button
const themeSelector = document.querySelector(
`#${parentId} .theme-selector2`
);
if (!themeSelector) {
return;
}
// Create the copy lyrics button
const copyButton = document.createElement("button");
copyButton.classList.add("lyric-control-container", "copy-lyrics-button");
copyButton.innerHTML = "Copy Lyrics";
copyButton.style.backgroundColor = "white";
copyButton.addEventListener("click", () => {
copyLyricsToClipboard(parentId);
// Change the color of the button to indicate that the lyrics have been copied
copyButton.style.backgroundColor = "#4CAF50";
// Transition the color back to the original color
setTimeout(() => {
copyButton.style.backgroundColor = "white";
}, 2500);
});
// Add the button to the DOM after the theme selector button
themeSelector.after(copyButton);
}
function addCopyLyricsToClipboardButtonToAllTabs() {
// Find all lyrics tabs
const tabDivs = document.querySelector(".contentsblock");
const tabs = Array.from(tabDivs.children).map((element) => element.id);
// Add the copy lyrics button to each tab
tabs.forEach((tab) => {
// If there are sub-tabs, add the button to each sub-tab
// This is used for the translations tab
const subTabDivs = document.querySelector(`#${tab} .subcontentsblock`);
if (subTabDivs) {
const subTabs = Array.from(subTabDivs.children).map(
(element) => element.id
);
subTabs.forEach((subTab) => {
addCopyLyricsToClipboardButton(subTab);
});
return;
}
// Otherwise, add the button to the tab
addCopyLyricsToClipboardButton(tab);
});
}
function removeHighlightAndShareWrapper() {
// Remove the highlight and share wrapper if it exists
const highlightAndShareWrapper = document.querySelector(
".highlight-and-share-wrapper"
);
if (highlightAndShareWrapper) {
highlightAndShareWrapper.remove();
}
}
// Create a MutationObserver to watch for changes in the DOM to
// always apply the script to new elements
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length > 0) {
addUserSelectToLyrics();
addCopyLyricsToClipboardButtonToAllTabs();
removeHighlightAndShareWrapper();
}
});
});
// Start observing the entire document for changes
observer.observe(document.body, { childList: true, subtree: true });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment