-
-
Save dansleboby/b8dacd07ed09dfcd851f7f42f6594136 to your computer and use it in GitHub Desktop.
| // ==UserScript== | |
| // @name Suno Aligned Words Fetcher with Auth | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.2 | |
| // @description Fetch aligned words with auth and add a button under the image on Suno pages. | |
| // @author Your Name | |
| // @match https://suno.com/song/* | |
| // @grant none | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| const file_type = "lrc"; // lrc ou srt | |
| // Helper function to get the value of a cookie by name | |
| function getCookie(name) { | |
| const value = `; ${document.cookie}`; | |
| const parts = value.split(`; ${name}=`); | |
| if (parts.length === 2) return parts.pop().split(';').shift(); | |
| } | |
| // Helper function to fetch aligned words data with Bearer token | |
| async function fetchAlignedWords(songId, token) { | |
| const apiUrl = `https://studio-api.prod.suno.com/api/gen/${songId}/aligned_lyrics/v2/`; | |
| try { | |
| const response = await fetch(apiUrl, { | |
| method: 'GET', | |
| headers: { | |
| 'Authorization': `Bearer ${token}`, | |
| 'Content-Type': 'application/json' | |
| } | |
| }); | |
| const data = await response.json(); | |
| if (data && data.aligned_words) { | |
| console.log('Aligned words:', data.aligned_words); | |
| return data.aligned_words; | |
| } else { | |
| console.error('No aligned words found.'); | |
| } | |
| } catch (error) { | |
| console.error('Error fetching aligned words:', error); | |
| } | |
| } | |
| // Function to add a button under the image | |
| function addButton(imageSrc, alignedWords) { | |
| const imageElements = document.querySelectorAll(`img[src*="${imageSrc}"].w-full.h-full`); | |
| console.log(imageSrc, imageElements); | |
| imageElements.forEach(function(imageElement, k) { | |
| console.log(k, imageElement); | |
| if (imageElement) { | |
| const button = document.createElement('button'); | |
| button.innerText = 'Download '+file_type; | |
| button.style.marginTop = '10px'; | |
| button.style.zIndex = '9999'; | |
| button.style.position = 'absolute'; | |
| button.style.bottom = '0'; | |
| button.style.left = '0'; | |
| button.style.right = '0'; | |
| button.style.background = 'gray'; | |
| button.style.borderRadius = '5px'; | |
| button.style.padding = '10px 6px'; | |
| button.addEventListener('click', () => { | |
| const srtContent = file_type === 'srt' ? convertToSRT(alignedWords) : convertToLRC(alignedWords); | |
| const blob = new Blob([srtContent], { type: 'text/'+file_type }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'aligned_words.'+file_type; | |
| a.click(); | |
| URL.revokeObjectURL(url); // Clean up the URL object | |
| }); | |
| imageElement.parentNode.appendChild(button); | |
| } else { | |
| console.error('Image not found.'); | |
| } | |
| }); | |
| } | |
| // Function to convert aligned words to SRT format | |
| function convertToSRT(alignedWords) { | |
| let srtContent = ''; | |
| alignedWords.forEach((wordObj, index) => { | |
| const startTime = formatTime(wordObj.start_s); | |
| const endTime = formatTime(wordObj.end_s); | |
| srtContent += `${index + 1}\n`; | |
| srtContent += `${startTime} --> ${endTime}\n`; | |
| srtContent += `${wordObj.word}\n\n`; | |
| }); | |
| return srtContent; | |
| } | |
| // Helper function to format time into SRT format (HH:MM:SS,MS) | |
| function formatTime(seconds) { | |
| const date = new Date(0); | |
| date.setMilliseconds(seconds * 1000); // Convert seconds to milliseconds | |
| const hours = String(date.getUTCHours()).padStart(2, '0'); | |
| const minutes = String(date.getUTCMinutes()).padStart(2, '0'); | |
| const secs = String(date.getUTCSeconds()).padStart(2, '0'); | |
| const milliseconds = String(date.getUTCMilliseconds()).padStart(3, '0'); | |
| return `${hours}:${minutes}:${secs},${milliseconds}`; | |
| } | |
| // Function to convert aligned words to LRC format | |
| function convertToLRC(alignedWords) { | |
| let lrcContent = ''; | |
| alignedWords.forEach(wordObj => { | |
| const time = formatLrcTime(wordObj.start_s); | |
| lrcContent += `${time}${wordObj.word}\n`; | |
| }); | |
| return lrcContent; | |
| } | |
| // Helper function to format time into LRC format [mm:ss.xx] | |
| function formatLrcTime(seconds) { | |
| const date = new Date(0); | |
| date.setMilliseconds(seconds * 1000); // Convert seconds to milliseconds | |
| const minutes = String(date.getUTCMinutes()).padStart(2, '0'); | |
| const secs = String(date.getUTCSeconds()).padStart(2, '0'); | |
| const hundredths = String(Math.floor(date.getUTCMilliseconds() / 10)).padStart(2, '0'); // Convert milliseconds to hundredths of a second | |
| return `[${minutes}:${secs}.${hundredths}]`; | |
| } | |
| // Main function to run the script | |
| function main() { | |
| const urlParts = window.location.href.split('/'); | |
| const songId = urlParts[urlParts.length - 1]; // Get song ID from URL | |
| const imageSrcPattern = songId; | |
| // Get the token from the cookie | |
| const sessionToken = getCookie('__session'); | |
| if (!sessionToken) { | |
| console.error('Session token not found in cookies.'); | |
| return; | |
| } | |
| // Fetch aligned words and add the button | |
| fetchAlignedWords(songId, sessionToken).then((alignedWords) => { | |
| if (alignedWords) { | |
| addButton(imageSrcPattern, alignedWords); | |
| } | |
| }); | |
| } | |
| setTimeout(function() { main(); }, 5000); | |
| })(); |
It is but at the same time I can't blame them for shuffling things, because they didn't explicitly released this functionality for public usage, maybe they're using this data for their internal needs which we don't know about, while I 100% aggree that SRT feature definitely should be included to a paid plan.
I tried making SRTs using AI tools as well but the main problem is that the AI is struggling to understand the words being sung, especially the names and moreover, when it comes to different languages if you're doing localisations for ad campaings it goes even worse while you can't check fast if the text is correct, so their own SRTs are gold. So while their word by word SRTs are quite precise it is pretty feasible to rearrange the SRT according to your own line structure on your side.
gemini, upload mp3 file, ask to download srt file
its sad, that suno likes to shuffle things up ... i use the paid version, i didnt see any solution /feature for this - i mean, it would make sense when there is a function to download these somehow in a free or paid version, but so, it seems like suno devs like to troll.
i mean, the stems are crapp, i also tried to get a .srt file by converting it with Whisper... seems like, its a bigger feature request when i see the activity on this thread here.
@sunodevs: maybe dont bringing effort into protection, you could implement that feature...