Skip to content

Instantly share code, notes, and snippets.

@yodaluca23
Last active July 19, 2024 20:09
Show Gist options
  • Save yodaluca23/5465128b2c622c95f790fb93e3532433 to your computer and use it in GitHub Desktop.
Save yodaluca23/5465128b2c622c95f790fb93e3532433 to your computer and use it in GitHub Desktop.
Ultimate Lyrics fetcher, gets lyrics from Beautiful Lyrics API, using Spotify SongID, uses Spotify API, with hacky ways to get token, to match song and artist to ID.
import requests
from bs4 import BeautifulSoup
import urllib.parse
import re
import json
def get_bearer_token():
fetch_url = "https://open.spotify.com"
response = requests.get(fetch_url)
response.raise_for_status() # Check if the request was successful
html_content = response.text
soup = BeautifulSoup(html_content, 'html.parser')
session_element = soup.find(id="session")
session_html = session_element.get_text()
tokens = json.loads(session_html)
access_token = tokens['accessToken']
return access_token
def search_spotify(artist, song):
token = get_bearer_token()
print("")
print("Token:")
print (token)
print("")
url = f'https://api.spotify.com/v1/search?query=artist%3A+{artist}+track%3A+{song}&type=track&offset=0&limit=1'
headers = {
'Authorization': f'Bearer {token}'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
if data['tracks']['items']:
href = data['tracks']['items'][0]['href']
match = re.search(r'tracks/([a-zA-Z0-9]+)', href)
if match:
song_id = match.group(1)
return song_id
else:
raise ValueError("Song ID not found in the href.")
else:
raise ValueError("No tracks found for the given artist and song.")
else:
raise Exception(f"Spotify API request failed with status code {response.status_code}")
def fetch_lyrics(track_id):
url = f'https://beautiful-lyrics.socalifornian.live/lyrics/{track_id}'
headers = {
'authorization': 'Bearer litterallyAnythingCanGoHereItJustTakesItLOL'
}
response = requests.get(url, headers=headers)
if response.status_code == 200 and response.headers.get('content-length') != '0':
return response.json()
return None
def convert_to_lrc_timestamp(timestamp):
minutes = int(timestamp // 60)
seconds = timestamp % 60
return f"{minutes:02}:{seconds:05.2f}"
def parse_lyrics(data, useA2):
lyrics = []
if data['Type'] == 'Line':
for item in data['Content']:
if item['Type'] == 'Vocal':
line = item['Text']
timestamp = convert_to_lrc_timestamp(item['StartTime'])
line = f"[{timestamp}] " + line
lyrics.append(line.strip())
if 'Background' in item:
print("This song has Background with Type Line, I was not able to find this in testing so I don't know the structure, please report this song, so I may add support for it.")
elif data['Type'] == 'Syllable':
if useA2:
for item in data['Content']:
if item['Type'] == 'Vocal':
syllables = item['Lead']['Syllables']
line = ''
timestamp = convert_to_lrc_timestamp(item['Lead']['StartTime'])
for syllable in syllables:
syllable_text = syllable['Text']
syllable_timestamp = convert_to_lrc_timestamp(syllable['StartTime'])
if syllable['IsPartOfWord']:
line += syllable_text
else:
line += f" <{syllable_timestamp}> {syllable_text}"
line = f"[{timestamp}]" + line
lyrics.append(line.strip())
if 'Background' in item:
for bg in item['Background']:
syllables = bg['Syllables']
line = ''
timestamp = convert_to_lrc_timestamp(bg['StartTime'])
for index, syllable in enumerate(syllables):
syllable_text = syllable['Text']
syllable_timestamp = convert_to_lrc_timestamp(syllable['StartTime'])
if syllable['IsPartOfWord']:
if index == 0:
line += f"({syllable_text}"
elif index == len(syllables) - 1:
line += f"{syllable_text})"
else:
line += syllable_text
else:
if index == 0:
line += f" <{syllable_timestamp}> ({syllable_text}"
elif index == len(syllables) - 1:
line += f" <{syllable_timestamp}> {syllable_text})"
else:
line += f" <{syllable_timestamp}> {syllable_text}"
line = f"[{timestamp}]" + line
lyrics.append(line.strip())
else:
for item in data['Content']:
if item['Type'] == 'Vocal':
line = ''.join([
f"{syllable['Text']}{' ' if not syllable['IsPartOfWord'] else ''}"
for syllable in item['Lead']['Syllables']
])
timestamp = item['Lead']['StartTime']
line = f"[{convert_to_lrc_timestamp(timestamp)}]" + f" {line}"
lyrics.append(line.strip())
if 'Background' in item:
for bg in item['Background']:
line = ''.join([
f"{syllable['Text']}{' ' if not syllable['IsPartOfWord'] else ''}"
for syllable in bg['Syllables']
])
timestamp = item['Background'][0]['StartTime']
line = f"[{convert_to_lrc_timestamp(timestamp)}]" + f" ({line.rstrip()})"
lyrics.append(line.strip())
return lyrics
def print_lyrics(lyrics):
for line in lyrics:
print(line)
def main():
artist = input("Enter Artist Name: ").strip()
song = input("Enter Song Title: ").strip()
useA2_input = input("Should use A2 extension (Enhanced LRC format) if available? ")
if useA2_input.lower() in ['true', '1', 't', 'y', 'yes']:
useA2 = True
elif useA2_input.lower() in ['false', '0', 'f', 'n', 'no']:
useA2 = False
else:
raise ValueError("Invalid input. Please enter True or False.")
# Search Spotify and get track ID
track_id = search_spotify(artist, song)
if not track_id:
print("No track found on Spotify.")
return
# Fetch lyrics using track ID
data = fetch_lyrics(track_id)
if not data:
print("No lyrics found for this track.")
return
# Parse and print lyrics
lyrics = parse_lyrics(data, useA2)
print("")
print("Lyrics:")
print("")
print_lyrics(lyrics)
print("")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment