diff --git a/app/api/lyrics.py b/app/api/lyrics.py index 9fedf35..c7d6147 100644 --- a/app/api/lyrics.py +++ b/app/api/lyrics.py @@ -1,6 +1,11 @@ from flask import Blueprint, request -from app.lib.lyrics import get_lyrics, check_lyrics_file, get_lyrics_from_duplicates +from app.lib.lyrics import ( + get_lyrics, + check_lyrics_file, + get_lyrics_from_duplicates, + get_lyrics_from_tags, +) api = Blueprint("lyrics", __name__, url_prefix="") @@ -18,15 +23,19 @@ def send_lyrics(): if filepath is None or trackhash is None: return {"error": "No filepath or trackhash provided"}, 400 - lyrics = get_lyrics(filepath) + is_synced = True + lyrics, copyright = get_lyrics(filepath) - if lyrics is None: - lyrics = get_lyrics_from_duplicates(trackhash, filepath) + if not lyrics: + lyrics, copyright = get_lyrics_from_duplicates(trackhash, filepath) - if lyrics is None: + if not lyrics: + lyrics, is_synced, copyright = get_lyrics_from_tags(filepath) + + if not lyrics: return {"error": "No lyrics found"}, 204 - return {"lyrics": lyrics}, 200 + return {"lyrics": lyrics, "synced": is_synced, "copyright": copyright}, 200 @api.route("/lyrics/check", methods=["POST"]) @@ -39,9 +48,11 @@ def check_lyrics(): if filepath is None or trackhash is None: return {"error": "No filepath or trackhash provided"}, 400 - exists, filepath = check_lyrics_file(filepath, trackhash) + exists = check_lyrics_file(filepath, trackhash) if exists: - return {"filepath": filepath}, 200 + return {"exists": exists}, 200 - return {"filepath": None} + exists = get_lyrics_from_tags(filepath, just_check=True) + + return {"exists": exists}, 200 diff --git a/app/lib/lyrics.py b/app/lib/lyrics.py index acc14f5..8d495a8 100644 --- a/app/lib/lyrics.py +++ b/app/lib/lyrics.py @@ -1,7 +1,11 @@ from pathlib import Path +from tinytag import TinyTag + from app.store.tracks import TrackStore filepath = "/home/cwilvx/Music/Editor's Pick/Bad Day 😢/6 Dogs - Crying in the Rarri.m4a" +# filepath = "/home/cwilvx/Music/90s/Ballads/All-4-One - I Swear.mp3" +filepath = "/home/cwilvx/Music/Afrobeats/Kabusa Oriental Choir/Kabusa Oriental Choir - Bandana.m4a" def split_line(line: str): @@ -22,19 +26,33 @@ def convert_to_milliseconds(time: str): return int(milliseconds) +def format_synced_lyrics(lines: list[str]): + """ + Formats synced lyrics into a list of dicts + """ + lyrics = [] + + for line in lines: + # if line starts with [ and ends with ] .ie. ID3 tag, skip it + if line.startswith("[") and line.endswith("]"): + continue + + # if line does not start with [ skip it + if not line.startswith("["): + continue + + time, lyric = split_line(line) + milliseconds = convert_to_milliseconds(time) + + lyrics.append({"time": milliseconds, "text": lyric}) + + return lyrics + + def get_lyrics_from_lrc(filepath: str): with open(filepath, mode="r") as file: lines = (f.removesuffix("\n") for f in file.readlines()) - - lyrics = [] - - for line in lines: - time, lyric = split_line(line) - milliseconds = convert_to_milliseconds(time) - - lyrics.append({"time": milliseconds, "text": lyric}) - - return lyrics + return format_synced_lyrics(lines) def get_lyrics_file_rel_to_track(filepath: str): @@ -66,9 +84,12 @@ def get_lyrics(track_path: str): lyrics_path = get_lyrics_file_rel_to_track(track_path) if lyrics_path: - return get_lyrics_from_lrc(lyrics_path) + lyrics = get_lyrics_from_lrc(lyrics_path) + copyright = get_extras(track_path, ["copyright"]) + + return lyrics, copyright[0] else: - return None + return None, "" def get_lyrics_from_duplicates(trackhash: str, filepath: str): @@ -78,23 +99,69 @@ def get_lyrics_from_duplicates(trackhash: str, filepath: str): for track in TrackStore.tracks: if track.trackhash == trackhash and track.filepath != filepath: - lyrics = get_lyrics(track.filepath) + lyrics, copyright = get_lyrics(track.filepath) if lyrics: - return lyrics + return lyrics, copyright + + return None, "" def check_lyrics_file(filepath: str, trackhash: str): lyrics_exists = check_lyrics_file_rel_to_track(filepath) if lyrics_exists: - return True, filepath + return True for track in TrackStore.tracks: if track.trackhash == trackhash and track.filepath != filepath: lyrics_exists = check_lyrics_file_rel_to_track(track.filepath) if lyrics_exists: - return True, track.filepath + return True - return False, None + return False + + +def test_is_synced(lyrics: list[str]): + # try to split lines and get milliseconds + # if any passes, return True + + for line in lyrics: + time, _ = split_line(line) + milliseconds = convert_to_milliseconds(time) + + if milliseconds != 0: + return True + + return False + + +def get_extras(filepath: str, keys: list[str]): + tags = TinyTag.get(filepath) + extras = tags.extra + + return [extras.get(key, "").strip() for key in keys] + + +def get_lyrics_from_tags(filepath: str, just_check: bool = False): + """ + Gets the lyrics from the tags of the track + """ + lyrics, copyright = get_extras(filepath, ["lyrics", "copyright"]) + lyrics = lyrics.replace("engdesc", "") + exists = bool(lyrics.replace("\n", "").strip()) + + if just_check: + return exists + + if not exists: + return None, False, "" + + lines = lyrics.split("\n") + synced = test_is_synced(lines[:15]) + + if synced: + return format_synced_lyrics(lines), synced, copyright + + return lines, synced, copyright diff --git a/build.sh b/build.sh index 0206a46..fc39774 100755 --- a/build.sh +++ b/build.sh @@ -4,5 +4,6 @@ cd ../swingmusic-client yarn build --outDir ../swingmusic/client + cd ../swingmusic poetry run python manage.py --build \ No newline at end of file