Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[info] create a csv of all song of a user set as public? #6

Open
spupuz opened this issue Jul 27, 2024 · 37 comments
Open

[info] create a csv of all song of a user set as public? #6

spupuz opened this issue Jul 27, 2024 · 37 comments

Comments

@spupuz
Copy link

spupuz commented Jul 27, 2024

is it possible to create and csv file with all song created and present in a user profile with these api?

@kuhnchris
Copy link

Would be interesting to know currently it looks like it just dumps all the data directly into their HTML, for example querying "https://suno.com/profile/liltingafrobeats036" gives us this in the HTML reply:
image
The only interacting with any API i see in the dev tools is a increase of the play counter...

BUT! There seems to be another API, if you hit the buttons for sorting on the page we get: https://studio-api.suno.ai/api/profiles/liltingafrobeats036?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at

Would that help you? It should contain all the necessary info of a user's songs profile, like profile, name, handle, no. of songs, clips and even the playlists, for example SirBitesAlot: https://studio-api.suno.ai/api/profiles/sirbitesalot?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at
image

Writing a wrapper for this should be fairly trivial as it seems to contain most of the Clip data this API already uses for the "api/feed" endpoint anyways...

So, technically, yes, it is possible to create a csv file with the data, but there is no api function for it yet, implementing one would be rather easy given the data above.

@spupuz
Copy link
Author

spupuz commented Jul 28, 2024

thanks i'm not a code but i'll try to play around that, i would like to dump at least some info of my profile in a csv and update it mainly name and url and possibly only for "public" songs. (not for trash)

@spupuz
Copy link
Author

spupuz commented Jul 28, 2024

Would be interesting to know currently it looks like it just dumps all the data directly into their HTML, for example querying "https://suno.com/profile/liltingafrobeats036" gives us this in the HTML reply: image The only interacting with any API i see in the dev tools is a increase of the play counter...

BUT! There seems to be another API, if you hit the buttons for sorting on the page we get: https://studio-api.suno.ai/api/profiles/liltingafrobeats036?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at

Would that help you? It should contain all the necessary info of a user's songs profile, like profile, name, handle, no. of songs, clips and even the playlists, for example SirBitesAlot: https://studio-api.suno.ai/api/profiles/sirbitesalot?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at image

Writing a wrapper for this should be fairly trivial as it seems to contain most of the Clip data this API already uses for the "api/feed" endpoint anyways...

So, technically, yes, it is possible to create a csv file with the data, but there is no api function for it yet, implementing one would be rather easy given the data above.

ok then i was able

i'll post there just for any one that could be interested:

import requests
import csv
import math

base_url = "https://studio-api.suno.ai/api/profiles/sirbitesalot"
clips_per_page = 20

response = requests.get(f"{base_url}?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at")
data = response.json()

total_clips = data['num_total_clips']
total_pages = math.ceil(total_clips / clips_per_page)

songs_data = []
song_base_url = "https://suno.com/song/"

for page in range(1, total_pages + 1):
    url = f"{base_url}?page={page}&playlists_sort_by=upvote_count&clips_sort_by=created_at"
    response = requests.get(url)
    data = response.json()
    
    for clip in data.get('clips', []):
        title = clip.get('title')
        clip_id = clip.get('id')
        song_url = song_base_url + clip_id
        play_count = clip.get('play_count', 0)
        upvote_count = clip.get('upvote_count', 0)
        is_public = clip.get('is_public', False)
        major_model_version = clip.get('major_model_version', '')
        
        songs_data.append([title, song_url, play_count, upvote_count, is_public, major_model_version])

# Sort the songs_data list by upvote count (descending order)
songs_data.sort(key=lambda x: x[3], reverse=True)

csv_filename = "all_songs_list_sorted.csv"
with open(csv_filename, mode='w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(['Song Title', 'URL', 'Play Count', 'Upvote Count', 'Is Public', 'Major Model Version'])
    writer.writerows(songs_data)

print(f"CSV file '{csv_filename}' has been created with all {total_clips} songs, sorted by upvote count.")

@kuhnchris
Copy link

Hello, great that you managed to get it to work! :-)

@Malith-Rukshan
Copy link
Owner

Would be interesting to know currently it looks like it just dumps all the data directly into their HTML, for example querying "https://suno.com/profile/liltingafrobeats036" gives us this in the HTML reply: image The only interacting with any API i see in the dev tools is a increase of the play counter...
BUT! There seems to be another API, if you hit the buttons for sorting on the page we get: https://studio-api.suno.ai/api/profiles/liltingafrobeats036?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at
Would that help you? It should contain all the necessary info of a user's songs profile, like profile, name, handle, no. of songs, clips and even the playlists, for example SirBitesAlot: https://studio-api.suno.ai/api/profiles/sirbitesalot?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at image
Writing a wrapper for this should be fairly trivial as it seems to contain most of the Clip data this API already uses for the "api/feed" endpoint anyways...
So, technically, yes, it is possible to create a csv file with the data, but there is no api function for it yet, implementing one would be rather easy given the data above.

ok then i was able

i'll post there just for any one that could be interested:

import requests
import csv
import math

base_url = "https://studio-api.suno.ai/api/profiles/sirbitesalot"
clips_per_page = 20

response = requests.get(f"{base_url}?page=1&playlists_sort_by=upvote_count&clips_sort_by=created_at")
data = response.json()

total_clips = data['num_total_clips']
total_pages = math.ceil(total_clips / clips_per_page)

songs_data = []
song_base_url = "https://suno.com/song/"

for page in range(1, total_pages + 1):
    url = f"{base_url}?page={page}&playlists_sort_by=upvote_count&clips_sort_by=created_at"
    response = requests.get(url)
    data = response.json()
    
    for clip in data.get('clips', []):
        title = clip.get('title')
        clip_id = clip.get('id')
        song_url = song_base_url + clip_id
        play_count = clip.get('play_count', 0)
        upvote_count = clip.get('upvote_count', 0)
        is_public = clip.get('is_public', False)
        major_model_version = clip.get('major_model_version', '')
        
        songs_data.append([title, song_url, play_count, upvote_count, is_public, major_model_version])

# Sort the songs_data list by upvote count (descending order)
songs_data.sort(key=lambda x: x[3], reverse=True)

csv_filename = "all_songs_list_sorted.csv"
with open(csv_filename, mode='w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(['Song Title', 'URL', 'Play Count', 'Upvote Count', 'Is Public', 'Major Model Version'])
    writer.writerows(songs_data)

print(f"CSV file '{csv_filename}' has been created with all {total_clips} songs, sorted by upvote count.")

Thanks for sharing 🤝

@spupuz
Copy link
Author

spupuz commented Aug 2, 2024

Pu play around I created a containerized app to Monitor my songs

Now the question is: is there a way to monitor the trending songs of the homepage?

@spupuz
Copy link
Author

spupuz commented Aug 2, 2024

Screenshot_20240802_072222_Chrome

@kuhnchris
Copy link

"Trending" seems to be just a special playlist with the id "07653cdf-8f72-430e-847f-9ab8ac05af40" on the mainpage.

https://studio-api.suno.ai/api/playlist/07653cdf-8f72-430e-847f-9ab8ac05af40/?page=0

image

@spupuz
Copy link
Author

spupuz commented Aug 2, 2024

"Trending" seems to be just a special playlist with the id "07653cdf-8f72-430e-847f-9ab8ac05af40" on the mainpage.

https://studio-api.suno.ai/api/playlist/07653cdf-8f72-430e-847f-9ab8ac05af40/?page=0

image

in fact i have found this https://studio-api.suno.ai/api/trending/

@kuhnchris
Copy link

Even better, thanks for looping this info back.
@Malith-Rukshan should we try to add those 2 endpoints (get_profile and get_trending) to the API as another PR?

@spupuz
Copy link
Author

spupuz commented Aug 11, 2024

Ssems that trending list url I provided is not properly updated and can create discrepancies the play list you gave me may be is more precise will try to check against this

@spupuz
Copy link
Author

spupuz commented Aug 20, 2024

do you guys know if it's possible to look at a user profile and find followers? i want to extract data of new follower name in a week??

@spupuz
Copy link
Author

spupuz commented Aug 20, 2024

ok got it:

https://studio-api.suno.ai/api/profiles/followers?page=1

but it says not authorized, how can i be authorized?

cookie? token??

any idea?

@kuhnchris
Copy link

Hello,

you can only get your own followers, as it seems. And yes, to do so you need to have the cookie/authentication enabled, so if you add this code to your suno.py you should be able to fetch it using the library (this is just quick and dirty, one should add a model for it, etc. but this is just a quick PoC to see if this fulfills your need. :-) )
image

  def get_followers(self):
        self._keep_alive()
        logger.info("Get followers...")
        response = self.client.get(
            f"{Suno.BASE_URL}/api/profiles/followers")
        logger.debug(response.text)

        self._check_for_error(response)

        if response.status_code != 200:
            logger.error("Get followers failed.")
            raise Exception(f"Error response: {response.text}")
        
        followers = response.json()["profiles"]
        return followers

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

Not worried about hot to get them but how to authenticate and how to integrate authentication into the script.

@kuhnchris
Copy link

Yeah you need to use the cookie:
image

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

Can you post without sensible data I text format? I'm not using this Suno library in fact up to now since operation I'm doing are not supported by it

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

I'm not a real Coder so I have doubts 😂 but I have done a lot maybe I'll post a video of what I'm doing.

@kuhnchris
Copy link

kuhnchris commented Aug 21, 2024

Basically we use do this:

Suno(
      cookie='__client_uat=aa; __client=bb; ajs_anonymous_id=cc; _cfuvid=dd; __cf_bm=ee; mp_ff_mixpanel=gg',
      model_version=ModelVersions.CHIRP_V3_5)

All it does is sending the cookie from the SUNO website as the "Cookies:" Header in the request, based on that we get a "Authorization: bearer ..." token, and with that you just call the API url endpoints.
image

So, basically,

  • grab cookie (go to suno.com in browser and use F12 to check cookie),
  • query the clerk javascript to get a session id (v1/client?_clerk_js_version=...),
  • get a jwt token (/v1/client/sessions/{session_id}/tokens?_clerk_js_version=...),
  • use that as Authentication for the api endpoint.

Hope that helps a little, else let us know if I can try to explain it differently. :-)

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

I'll try so then I have to authenticate with your library api and use it Un the script correct?

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

Another question why if I open

https://studio-api.suno.ai/api/profiles/followers?page=1

From a browser where I'm already authenticated to suno in got:

{"detail": "Unauthorized"}

Shouldn't be able to use it?

@kuhnchris
Copy link

kuhnchris commented Aug 21, 2024

No, because the browser sends an AJAX request in the website (basically it opens the URL with other things in the header) - you can see that by pressing F12 while being on the Suno page and if you click on the Network tab (and then go to "Followers") you should see the /followers?page=1 request, if you go to "Request headers" you see there is a Auth bearer + alot of data, that is basically what is being sent everytime you click on that, and that is what your browser doesn't send if you just click on that link - it's done via javascript on the suno.com page :-)

I'll try so then I have to authenticate with your library api and use it Un the script correct?

Either that, or roll your own login + the 3 requests, basically, the important thing is: you need the JWT token, and with that token you can access the followers API page

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

No, because the browser sends an AJAX request in the website (basically it opens the URL with other things in the header) - you can see that by pressing F12 while being on the Suno page and if you click on the Network tab (and then go to "Followers") you should see the /followers?page=1 request, if you go to "Request headers" you see there is a Auth bearer + alot of data, that is basically what is being sent everytime you click on that, and that is what your browser doesn't send if you just click on that link - it's done via javascript on the suno.com page :-)

I'll try so then I have to authenticate with your library api and use it Un the script correct?

Either that, or roll your own login + the 3 requests, basically, the important thing is: you need the JWT token, and with that token you can access the followers API page

i have to integrate your api into my script correct? can't do it in different way correct?

@kuhnchris
Copy link

No you don't, those are regular requests. calls, I gave you the generic way of doing it, you can use the library, then you do not have to do it per requests (just use Suno (...) as I wrote a couple of posts above).
I can write you the boiled down non-library version later today if you struggle with extracting the code from the library.

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

i can use the library too but not sure if it is already covering trending list, or follower... just that
i'm not an expert coder and i'm not using python to generate songs, more interested in metrics and statistics :) trending and more over.

@spupuz
Copy link
Author

spupuz commented Aug 21, 2024

No you don't, those are regular requests. calls, I gave you the generic way of doing it, you can use the library, then you do not have to do it per requests (just use Suno (...) as I wrote a couple of posts above). I can write you the boiled down non-library version later today if you struggle with extracting the code from the library.

anyway if you are so kind to draft me some sort of code for extract follower with auth i will be really glad

@kuhnchris
Copy link

So, the stripped down version is here: https://gist.github.com/kuhnchris/443c5e69da2dd327a0b74c8f18ce0770
Don't forget to get your cookie from suno.com and set the "cookie = " variable on top
image

@spupuz
Copy link
Author

spupuz commented Aug 22, 2024

So, the stripped down version is here: https://gist.github.com/kuhnchris/443c5e69da2dd327a0b74c8f18ce0770 Don't forget to get your cookie from suno.com and set the "cookie = " variable on top image

thank you su much integrated the code perfectly! :) and i'ts working

@spupuz
Copy link
Author

spupuz commented Aug 23, 2024

they just changed trending list here is the new trending data structure:

https://studio-api.suno.ai/api/discover/

@kuhnchris
Copy link

Yeah i noticed, they also added some search filters (like by language, weekly, monthly, all time, etc.) so we'll probabbly have to check how to refactor it

@spupuz
Copy link
Author

spupuz commented Aug 23, 2024

i have no time for now going in holiday tomorrow will see in 2 weeks i suppose, old trending playlist is still available and working and updated

@kuhnchris
Copy link

Alright, enjoy your holidays! :-)

@spupuz
Copy link
Author

spupuz commented Aug 23, 2024

can you share your suno or discord nick so i can follow :) don't want to chat here :)

@spupuz
Copy link
Author

spupuz commented Sep 2, 2024

Alright, enjoy your holidays! :-)

did you figure out something on the new tranding?
Have you a discord account?

@spupuz
Copy link
Author

spupuz commented Oct 4, 2024

do you know guys how to check the status of song that can be mine or from other via command?

i'm able to set or unset with this command:

with a post command

url = f"https://studio-api.prod.suno.com/api/gen/{song_id}/update_reaction_type/"
payload = {"reaction_type": "like"}

    response = requests.post(url, json=payload, headers=headers)

@spupuz
Copy link
Author

spupuz commented Oct 4, 2024

do you know guys how to check the status of like of a song that can be mine or from other via command?

i'm able to set or unset like with this command:

with a post command

url = f"https://studio-api.prod.suno.com/api/gen/{song_id}/update_reaction_type/"
payload = {"reaction_type": "like"}

    response = requests.post(url, json=payload, headers=headers)

but unable to check before what is the status

@kuhnchris
Copy link

kuhnchris commented Oct 5, 2024

As already talked on discord, we need to grab the data from the RSC (react server component), which we then can parse, it's a bit of a hacky way but given the Repo we are in right now, it would be something like:

def get_someone_else_song(self, song_id: str) -> str:
        self._keep_alive()  # Ensure session is active
        logger.info("Getting Song Info...")
        response = self.client.get(
            f"https://suno.com/song/{song_id}?_rsc=asdf))  # Call API

        self._check_for_error(response)
        for f in response.text.splitlines():
            if "clip" in f:
                jsonPart = f.split(":",1)[1]
                for r in json.loads(jsonPart):
                    if r != None and "clip" in r:
                        return r

And making sure that the headers container RSC: 1:

 headers = {
            # Generate a random User-agent for requests
            'User-Agent': generate_fake_useragent(),
            'Cookie': cookie,
            'RSC': "1"
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants