Skip to content

Commit

Permalink
feat: Update version and add helper functions for Suno songs
Browse files Browse the repository at this point in the history
- Update version to "0.5.0" and various project information
- Add helper function for downloading songs from Suno
- Modify functions to handle cases with audio URLs
- Improve downloading capability to download multiple songs from a list of URLs
- Remove unnecessary variable from the `save_songs` function
- Improve file naming convention to include song ID
- Update `get_songs` function to handle errors and use new helper functions
  • Loading branch information
yihong0618 committed Apr 10, 2024
1 parent e8bd251 commit 5684769
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 34 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name="suno_songs",
version="0.4.1",
version="0.5.0",
author="yihong0618",
author_email="[email protected]",
description="High quality songs generation by suno.ai. Reverse engineered API.",
Expand Down
74 changes: 41 additions & 33 deletions suno/suno.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def _parse_lyrics(self, data: dict) -> Tuple[str, str]:
return song_name, lyrics

def _fetch_songs_metadata(self, ids):
self.song_info_dict["song_url_list"] = []
id1, id2 = ids[:2]
url = f"https://studio-api.suno.ai/api/feed/?ids={id1}%2C{id2}"
response = self.session.get(url, impersonate=browser_version)
Expand All @@ -163,16 +164,15 @@ def _fetch_songs_metadata(self, ids):
# renew now data
self.now_data = data
try:
for d in data:
# only get one url for now TODO: See if possible for both urls
# and early return
if audio_url := d.get("audio_url"):
if all(d.get("audio_url") for d in data):
for d in data:
song_name, lyric = self._parse_lyrics(d)
self.song_info_dict["song_name"] = song_name
self.song_info_dict["lyric"] = lyric
self.song_info_dict["song_url"] = audio_url
return True
return False
self.song_info_dict["song_url_list"].append(d.get("audio_url"))
# for support old api
self.song_info_dict["song_url"] = d.get("audio_url")
return True
except Exception as e:
print(e)
# since we only get the music_id is ok
Expand All @@ -182,6 +182,10 @@ def _fetch_songs_metadata(self, ids):
song_name, lyric = self._parse_lyrics(self.now_data[0])
self.song_info_dict["song_name"] = song_name
self.song_info_dict["lyric"] = lyric
self.song_info_dict["song_url_list"] = [
f"https://audiopipe.suno.ai/?item_id={id1}",
f"https://audiopipe.suno.ai/?item_id={id2}",
]
self.song_info_dict["song_url"] = (
f"https://audiopipe.suno.ai/?item_id={id1}"
)
Expand Down Expand Up @@ -247,6 +251,28 @@ def get_songs(
# keep the song info dict as old api
return self.song_info_dict

def _download_suno_song(self, link: str, song_id: str, output_dir: str) -> None:
song_name = self.song_info_dict["song_name"]
lyric = self.song_info_dict["lyric"]
response = rget(link, allow_redirects=False, stream=True)
if response.status_code != 200:
raise Exception("Could not download song")
# save response to file
print(f"Downloading song... {song_id}")
with open(os.path.join(output_dir, f"suno_{song_id}.mp3"), "wb") as output_file:
for chunk in response.iter_content(chunk_size=1024):
# If the chunk is not empty, write it to the file.
if chunk:
output_file.write(chunk)
if not song_name:
song_name = "Untitled"
with open(
os.path.join(output_dir, f"{song_name.replace(' ', '_')}.lrc"),
"w",
encoding="utf-8",
) as lyric_file:
lyric_file.write(f"{song_name}\n\n{lyric}")

def save_songs(
self,
prompt: str,
Expand All @@ -256,7 +282,6 @@ def save_songs(
make_instrumental: bool = False,
is_custom: bool = False,
) -> None:
mp3_index = 0
try:
self.get_songs(
prompt,
Expand All @@ -265,37 +290,20 @@ def save_songs(
is_custom=is_custom,
make_instrumental=make_instrumental,
) # make the info dict
song_name = self.song_info_dict["song_name"]
lyric = self.song_info_dict["lyric"]
link = self.song_info_dict["song_url"]
link_list = self.song_info_dict["song_url_list"]
except Exception as e:
print(e)
raise
with contextlib.suppress(FileExistsError):
os.mkdir(output_dir)
print()
while os.path.exists(os.path.join(output_dir, f"suno_{mp3_index}.mp3")):
mp3_index += 1
print(link)
response = rget(link, allow_redirects=False, stream=True)
if response.status_code != 200:
raise Exception("Could not download song")
# save response to file
with open(
os.path.join(output_dir, f"suno_{mp3_index + 1}.mp3"), "wb"
) as output_file:
for chunk in response.iter_content(chunk_size=1024):
# If the chunk is not empty, write it to the file.
if chunk:
output_file.write(chunk)
if not song_name:
song_name = "Untitled"
with open(
os.path.join(output_dir, f"{song_name.replace(' ', '_')}.lrc"),
"w",
encoding="utf-8",
) as lyric_file:
lyric_file.write(f"{song_name}\n\n{lyric}")
print(link_list)
for link in link_list:
if link.endswith(".mp3"):
mp3_id = link.split("/")[-1][:-4]
else:
mp3_id = link.split("=")[-1]
self._download_suno_song(link, mp3_id, output_dir)


def main():
Expand Down

0 comments on commit 5684769

Please sign in to comment.