-
-
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); | |
| })(); |
great script but right now still not working
i edited again the script above (last post from yesterday) to fix a problem with new Parameters added to the URI ...
tested with this song:
https://suno.com/song/5924013e-5253-42a9-b478-87da3a0d0ed8?sh=oOrVwmQz77HZ7FLL
(after 5seconds you see the button at the cover image)
got this to work, you need to ensure you enable userscripts Q209: Permission to execute userscripts
It seems that the script is no longer capable to deliver SRT for lines. It is ignoring line structure and splitting lines by unknown logic. Anyone is facing the same problem?
UPD: I tried to generate SRT for the old song which already has SRT in my archive made with a script which dschibait kindly shared with us. I compared an SRT generated month ago with one I made today and the issue is clearly visible. I suppose the SUNO changed algorithms somehow so the last word from the reference line was moved to the next SRT block, so subs are not such accurate they were before. I suppose it is problem on the SUNO side, apparently they changed algorhytms and aligned_lyrics request now giving us the crooked line structure. However timecodes for words are still accurate.
Idk if the script is updated or something, but it works great for me. Now it's an lrc file instead of srt but that's great.
@WorlockM is it works well for line by line subs or you're using word by word option? Can you share your code please if the former?
It works good for words indeed, but lines are messed up since the August. The API querry "data.aligned_lyrics" returns an incorrect response to be more specific.
Here is an example of what I'm providing the Suno with:
I don't want to set the world on fire
I just want to start a flame in your heart.
And here how it is returning lines. Suno splits them for some reason and with unknown pattern.
I don't wat to set
the world on fire
I just want to start
a flame in your
heart.
@WorlockM is it works well for line by line subs or you're using word by word option? Can you share your code please if the former? It works good for words indeed, but lines are messed up since the August. The API querry "data.aligned_lyrics" returns an incorrect response to be more specific. Here is an example of what I'm providing the Suno with:
I don't want to set the world on fire I just want to start a flame in your heart.And here how it is returning lines. Suno splits them for some reason and with unknown pattern.
I don't wat to set the world on fire I just want to start a flame in your heart.
Ah ok, I got it word by word indeed.
great script but right now still not working