Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Udio Lyrics Auto Downloader (with prompt + tags + created-at + author)
- // @namespace http://tampermonkey.net/
- // @version 2.1
- // @description Auto-download lyrics from Udio song pages, with title, optional prompt, tags, created-at, and author. Uses URL ID in filename, waits for lyrics, shows indicator, and polls URL for SPA navigation.
- // @author Microsoft Copilot
- // @match https://www.udio.com/*
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- function sanitizeFilename(name) {
- return name.replace(/[\\/:*?"<>|]/g, "_");
- }
- function showIndicator(message, success = true) {
- const indicator = document.createElement("div");
- indicator.textContent = message;
- indicator.style.position = "fixed";
- indicator.style.top = "20px";
- indicator.style.left = "50%";
- indicator.style.transform = "translateX(-50%)";
- indicator.style.padding = "12px 20px";
- indicator.style.fontSize = "18px";
- indicator.style.fontWeight = "bold";
- indicator.style.color = success ? "#fff" : "#000";
- indicator.style.background = success ? "green" : "red";
- indicator.style.border = "3px solid black";
- indicator.style.zIndex = "99999";
- indicator.style.borderRadius = "6px";
- indicator.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)";
- indicator.style.opacity = "1";
- indicator.style.transition = "opacity 1s ease-out";
- document.body.appendChild(indicator);
- setTimeout(() => {
- indicator.style.opacity = "0";
- setTimeout(() => indicator.remove(), 1000);
- }, 3000);
- }
- function waitForLyricsAndDownload(maxWaitMs = 15000, intervalMs = 300) {
- const start = Date.now();
- const check = () => {
- const titleEls = document.querySelectorAll("h1.truncate-2-lines");
- const lyricCandidates = Array.from(document.querySelectorAll("pre.whitespace-pre-wrap"))
- .map(el => el.textContent.trim())
- .filter(text => text.length > 0);
- if (titleEls.length === 1 && lyricCandidates.length >= 1) {
- const title = titleEls[0].textContent.trim();
- const lyrics = lyricCandidates[0];
- // Optional: Prompt
- let promptLine = "";
- const promptSpan = Array.from(document.querySelectorAll("span"))
- .map(el => el.textContent.trim())
- .find(text => text.startsWith("Prompt:"));
- if (promptSpan) {
- promptLine = promptSpan;
- }
- // Optional: Tags
- const tags = Array.from(document.querySelectorAll("a[href^='/tags/'] span"))
- .map(el => el.textContent.trim())
- .filter(t => t.length > 0);
- let tagsLine = "";
- if (tags.length > 0) {
- tagsLine = "Tags: " + tags.join(", ");
- }
- // Optional: Created at
- let createdLine = "";
- const createdDiv = Array.from(document.querySelectorAll("div[title^='Created at ']"))
- .map(el => el.getAttribute("title"))
- .find(t => t && t.startsWith("Created at "));
- if (createdDiv) {
- createdLine = createdDiv;
- }
- // Optional: Author from meta og:title
- let authorLine = "";
- const meta = document.querySelector("meta[property='og:title']");
- if (meta) {
- const content = meta.getAttribute("content") || "";
- const parts = content.split(" - ");
- if (parts.length > 1) {
- authorLine = "Author: " + parts[0].trim();
- }
- }
- // Build content
- let content = `Title: ${title}\n`;
- if (authorLine) content += `${authorLine}\n`;
- if (promptLine) content += `${promptLine}\n`;
- if (tagsLine) content += `${tagsLine}\n`;
- if (createdLine) content += `${createdLine}\n`;
- content += `\n${lyrics}`;
- // Save file
- const blob = new Blob([content], { type: "text/plain" });
- const url = URL.createObjectURL(blob);
- const safeTitle = sanitizeFilename(title);
- const pathParts = window.location.pathname.split("/");
- const uniqueId = pathParts[pathParts.length - 1];
- const filename = `${safeTitle} [${uniqueId}].txt`;
- const a = document.createElement("a");
- a.href = url;
- a.download = filename;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- console.log("Saved:", filename);
- showIndicator("✅ Lyrics Saved", true);
- return;
- }
- if (Date.now() - start < maxWaitMs) {
- setTimeout(check, intervalMs);
- } else {
- showIndicator("❌ Lyrics not loaded", false);
- }
- };
- check();
- }
- // Poll for URL changes every half second
- let lastUrl = location.href;
- setInterval(() => {
- const currentUrl = location.href;
- if (currentUrl !== lastUrl) {
- lastUrl = currentUrl;
- if (/^https:\/\/www\.udio\.com\/songs\//.test(currentUrl)) {
- waitForLyricsAndDownload();
- }
- }
- }, 500);
- // Also run immediately if we land directly on a song page
- if (/^https:\/\/www\.udio\.com\/songs\//.test(location.href)) {
- waitForLyricsAndDownload();
- }
- })();
Advertisement
Add Comment
Please, Sign In to add comment