-
-
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); | |
})(); |
This is sick! But is it still working? I don't see the download button showing up.
You need to go on a detail page of a song https://suno.com/song/1a042042-b5d5-43e3-8b50-965d1ec0e303 and hard refresh ( I don't support the fake loading) it was also a glitch with the orginal script I just update it. You should see:
@dansleboby
I am not experienced in any of this, but have downloaded Tampermonkey and added the above code. When I do a hard reset nothing is happening. Can someone please help me figure this out?
@dansleboby I was able to get it to work, It is downloading the SRT file, but one word per line, Is there a way to change this so that it will export a line of the song at a time?
love this code, just build in a switch that you can also go for lines (not only words) when using .srt format.
When using lines (lyrics) commands like [Intro] [Outro] etc are removed.
You can go with this .srt file directly on youtube subtitles!
// ==UserScript==
// @name Suno Aligned Words Fetcher with Auth
// @namespace http://tampermonkey.net/
// @Version 1.3
// @description Fetch aligned words with auth and add a button under the image on Suno pages.
// @author dansleboby + Dschi
// @match https://suno.com/song/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const file_type = "srt"; // lrc ou srt
const use_lyrics = true;
// 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();
console.log("response json", data);
if (data && data.aligned_words) {
if(use_lyrics && file_type == "srt"){
console.log('aligned lyrics:', data.aligned_lyrics);
return data.aligned_lyrics;
}
else{
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`;
if(use_lyrics){
srtContent += `${wordObj.text.replace(/\[.*?\]/g, '')}\n\n`;
}
else{
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;
}
console.log("fetching song", songId);
// Fetch aligned words and add the button
fetchAlignedWords(songId, sessionToken).then((alignedWords) => {
if (alignedWords) {
addButton(imageSrcPattern, alignedWords);
}
});
}
console.log("start suno.ai checking");
setTimeout(function() { main(); }, 5000);
})();
Go on https://suno.com/song/ with the song you want and after 5 sec you will see a button to download the file.