Skip to content

Instantly share code, notes, and snippets.

@yodaluca23
Last active July 19, 2024 06:23
Show Gist options
  • Save yodaluca23/a9e8e9ef249c2b6d4dce2f3be0f23aa2 to your computer and use it in GitHub Desktop.
Save yodaluca23/a9e8e9ef249c2b6d4dce2f3be0f23aa2 to your computer and use it in GitHub Desktop.
Fetch .lrc files for all songs in directory, from MusixMatch.
# To get a MusixMatch token, download the app
# Go to settings (Top right corner) > Scroll all the way down > click "Get help" > click "Copy debug info"
# [UserToken] is your API key.
# After this finishes, you can fill in the rest from libLRC database using https://github.com/tranxuanthang/lrcget
import os
import requests
import json
import re
# Function to load configuration from config.txt
def load_config():
if os.path.exists('config.txt'):
with open('config.txt', 'r') as config_file:
config = json.load(config_file)
return config.get('token'), config.get('naming_format')
return None, None
# Function to save configuration to config.txt
def save_config(token, naming_format):
with open('config.txt', 'w') as config_file:
json.dump({'token': token, 'naming_format': naming_format}, config_file)
# Load token and naming format from config.txt if it exists
token, naming_format = load_config()
if not token:
token = input("Paste your MusixMatch token: ")
if not naming_format:
naming_format = input("Enter the naming format (use %A for artist and %T for title): ")
# Save token and naming format to config.txt
save_config(token, naming_format)
# Ask the user if they want to override existing files
override_existing = input("Do you want to override existing files? (yes/no): ").strip().lower() == 'yes'
# List of supported file extensions
supported_extensions = [
".3gp", ".aa", ".aac", ".aax", ".act", ".aiff", ".alac", ".amr", ".ape", ".au", ".awb", ".dss",
".dvf", ".flac", ".gsm", ".iklax", ".ivs", ".m4a", ".m4b", ".m4p", ".mmf", ".movpkg", ".mp3",
".mpc", ".msv", ".nmf", ".ogg", ".oga", ".mogg", ".opus", ".ra", ".rm", ".raw", ".rf64",
".sln", ".tta", ".voc", ".vox", ".wav", ".wma", ".wv", ".webm", ".8svx", ".cda"
]
def extract_artist_and_song(filename, naming_format):
# Add "." to properly extract ending thing
naming_format = naming_format + "."
# Define the placeholders and their corresponding regex patterns
placeholders = {
'%A': '(?P<artist>.+?)',
'%T': '(?P<title>.+?)'
}
# Escape special regex characters in the naming format
escaped_format = re.escape(naming_format)
# Replace the placeholders in the escaped naming format with regex patterns
for placeholder, pattern in placeholders.items():
escaped_format = escaped_format.replace(re.escape(placeholder), pattern)
# Create a regex pattern from the modified naming format
pattern = re.compile(escaped_format)
# Match the pattern with the filename
match = pattern.match(filename)
# If a match is found, extract the artist and title
if match:
artist = match.group('artist')
title = match.group('title')
return artist.strip(), title.strip()
else:
filename = filename.split('.')[0]
print(f"The filename '{filename}' does not match the naming format '{naming_format}'")
artist = "rhiugwbeguhwouehgouqhefoqnfobnqoefhouqehgoiuhqeoughqeouhgouq"
title = "gfrwouehgowgoeqjhoghjewojgoirwdjgoiswrjdoifjqoiefjoqefgqrogjoiwrjgoesrjgjedo"
return artist.strip(), title.strip()
def fetch_and_save_lyrics(filename, artist, title, token, override_existing):
lrc_filename = os.path.splitext(filename)[0] + '.lrc'
if not override_existing and os.path.exists(lrc_filename):
filename = filename.split('.')[0]
print(f"Lyrics for {filename} already exist, skipping")
return
url = f'https://apic.musixmatch.com/ws/1.1/macro.subtitles.get?subtitle_format=lrc&q_track={title}&usertoken={token}&q_artist={artist}&app_id=mac-ios-v2.0'
response = requests.get(url)
data = response.json()
try:
lyrics_body = data['message']['body']['macro_calls']['track.subtitles.get']['message']['body']['subtitle_list'][0]['subtitle']['subtitle_body']
if is_lyrics_blank(lyrics_body):
raise ValueError("Time synced lyrics are blank or contain only whitespace.")
save_lyrics(lrc_filename, lyrics_body, True, filename)
except:
filename = filename.split('.')[0]
print(f"Could not find time synced lyrics for {filename}, trying non-time synced")
try:
lyrics_body = data['message']['body']['macro_calls']['track.lyrics.get']['message']['body']['lyrics']['lyrics_body']
if is_lyrics_blank(lyrics_body):
raise ValueError("Non-time synced lyrics are blank or contain only whitespace.")
save_lyrics(lrc_filename, lyrics_body, False, filename)
except:
filename = filename.split('.')[0]
print(f"Could not save lyrics for {filename}")
def is_lyrics_blank(lyrics_body):
return not lyrics_body.strip()
def save_lyrics(lrc_filename, lyrics_body, is_time_synced, filename):
lyrics_body = lyrics_body.replace("\\n", "\n")
with open(lrc_filename, 'w') as lrc_file:
lrc_file.write(lyrics_body)
if is_time_synced:
filename = filename.split('.')[0]
print(f"Saved time-synced lyrics for {filename}")
else:
filename = filename.split('.')[0]
print(f"Saved non-time-synced lyrics for {filename}")
# Scan the current directory for supported audio files
for item in os.listdir('.'):
if any(item.endswith(ext) for ext in supported_extensions):
artist, title = extract_artist_and_song(item, naming_format)
if artist and title:
fetch_and_save_lyrics(item, artist, title, token, override_existing)
else:
item = item.split('.')[0]
print(f"Could not extract artist and title from {item}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment