From cc6552cb949e418d88c678772ef4e7b5bfc8644b Mon Sep 17 00:00:00 2001 From: mungai-njoroge Date: Mon, 19 Jun 2023 21:49:13 +0300 Subject: [PATCH] check if track exists in db before sending file --- .gitignore | 1 + app/api/favorites.py | 24 +++++++++--------------- app/api/track.py | 14 +++++++++++--- app/arg_handler.py | 4 +++- app/db/sqlite/albums.py | 7 ++++++- app/db/sqlite/artists.py | 3 +++ app/db/sqlite/favorite.py | 8 +++++++- app/db/sqlite/migrations.py | 16 +++++++++++++--- app/db/sqlite/playlists.py | 5 +++++ app/db/sqlite/settings.py | 1 + app/lib/folderslib.py | 2 +- app/lib/watchdogg.py | 9 +++++++++ app/models/track.py | 4 ++-- app/print_help.py | 2 ++ app/serializers/favorites_serializer.py | 5 +++-- app/settings.py | 2 +- app/utils/parsers.py | 20 +++++++++++++++++++- 17 files changed, 96 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 9164181..b6d42b4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ encoderx.py dist build client +.gitignore diff --git a/app/api/favorites.py b/app/api/favorites.py index e293184..39aa96f 100644 --- a/app/api/favorites.py +++ b/app/api/favorites.py @@ -185,20 +185,13 @@ def get_all_favorites(): artists = remove_none(artists) recents = [] - first_n = favs[:album_limit] + # first_n = favs - for fav in first_n: - if fav[2] == FavType.track: - try: - track = [t for t in tracks if t.trackhash == fav[1]][0] - recents.append({ - "type": "track", - "item": recent_fav_track_serializer(track) - }) - except IndexError: - pass + for fav in favs: + if len(recents) >= largest: + break - elif fav[2] == FavType.album: + if fav[2] == FavType.album: try: album = [a for a in albums if a.albumhash == fav[1]][0] recents.append({ @@ -206,8 +199,9 @@ def get_all_favorites(): "item": recent_fav_album_serializer(album) }) except IndexError: - pass - elif fav[2] == FavType.artist: + continue + + if fav[2] == FavType.artist: try: artist = [a for a in artists if a.artisthash == fav[1]][0] recents.append({ @@ -215,7 +209,7 @@ def get_all_favorites(): "item": recent_fav_artist_serializer(artist) }) except IndexError: - pass + continue return { "recents": recents[:album_limit], diff --git a/app/api/track.py b/app/api/track.py index 129bba9..1377f9e 100644 --- a/app/api/track.py +++ b/app/api/track.py @@ -24,9 +24,17 @@ def send_track_file(trackhash: str): filepath = request.args.get("filepath") - if filepath is not None and os.path.exists(filepath): - audio_type = get_mime(filepath) - return send_file(filepath, mimetype=audio_type) + if filepath is not None: + try: + track = TrackStore.get_tracks_by_filepaths([filepath])[0] + except IndexError: + track = None + + track_exists = track is not None and os.path.exists(track.filepath) + + if track_exists: + audio_type = get_mime(filepath) + return send_file(filepath, mimetype=audio_type) if trackhash is None: return msg, 404 diff --git a/app/arg_handler.py b/app/arg_handler.py index 556baae..c89b4b0 100644 --- a/app/arg_handler.py +++ b/app/arg_handler.py @@ -151,7 +151,9 @@ class HandleArgs: @staticmethod def handle_periodic_scan_interval(): if any((a in ARGS for a in ALLARGS.periodic_scan_interval.value)): - index = [ARGS.index(a) for a in ALLARGS.periodic_scan_interval.value if a in ARGS][0] + index = [ + ARGS.index(a) for a in ALLARGS.periodic_scan_interval.value if a in ARGS + ][0] try: interval = ARGS[index + 1] diff --git a/app/db/sqlite/albums.py b/app/db/sqlite/albums.py index 49d2bd3..fdf08e6 100644 --- a/app/db/sqlite/albums.py +++ b/app/db/sqlite/albums.py @@ -17,13 +17,17 @@ class SQLiteAlbumMethods: """ cur.execute(sql, (albumhash, colors)) - return cur.lastrowid + lastrowid = cur.lastrowid + cur.close() + + return lastrowid @classmethod def get_all_albums(cls): with SQLiteManager() as cur: cur.execute("SELECT * FROM albums") albums = cur.fetchall() + cur.close() if albums is not None: return albums @@ -35,6 +39,7 @@ class SQLiteAlbumMethods: with SQLiteManager() as cur: cur.execute("SELECT * FROM albums WHERE albumartist=?", (albumartist,)) albums = cur.fetchall() + cur.close() if albums is not None: return tuples_to_albums(albums) diff --git a/app/db/sqlite/artists.py b/app/db/sqlite/artists.py index 96618bb..ed7f9cb 100644 --- a/app/db/sqlite/artists.py +++ b/app/db/sqlite/artists.py @@ -21,6 +21,7 @@ class SQLiteArtistMethods: """ colors = json.dumps(colors) cur.execute(sql, (artisthash, colors)) + cur.close() @staticmethod def get_all_artists(): @@ -34,3 +35,5 @@ class SQLiteArtistMethods: for artist in cur.fetchall(): yield artist + + cur.close() diff --git a/app/db/sqlite/favorite.py b/app/db/sqlite/favorite.py index b7c9368..2db2036 100644 --- a/app/db/sqlite/favorite.py +++ b/app/db/sqlite/favorite.py @@ -14,6 +14,7 @@ class SQLiteFavoriteMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, (itemhash, fav_type)) items = cur.fetchall() + cur.close() return len(items) > 0 @classmethod @@ -28,6 +29,7 @@ class SQLiteFavoriteMethods: sql = """INSERT INTO favorites(type, hash) VALUES(?,?)""" with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, (fav_type, fav_hash)) + cur.close() @classmethod def get_all(cls) -> list[tuple]: @@ -38,6 +40,7 @@ class SQLiteFavoriteMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql) favs = cur.fetchall() + cur.close() return [fav for fav in favs if fav[1] != ""] @classmethod @@ -48,7 +51,9 @@ class SQLiteFavoriteMethods: sql = """SELECT * FROM favorites WHERE type = ?""" with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, (fav_type,)) - return cur.fetchall() + all_favs = cur.fetchall() + cur.close() + return all_favs @classmethod def get_fav_tracks(cls) -> list[tuple]: @@ -80,3 +85,4 @@ class SQLiteFavoriteMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, (fav_hash, fav_type)) + cur.close() diff --git a/app/db/sqlite/migrations.py b/app/db/sqlite/migrations.py index 71a0865..8ece4ea 100644 --- a/app/db/sqlite/migrations.py +++ b/app/db/sqlite/migrations.py @@ -21,7 +21,10 @@ class MigrationManager: """ with SQLiteManager() as cur: cur.execute(cls.all_get_sql) - return int(cur.fetchone()[1]) + ver = int(cur.fetchone()[1]) + cur.close() + + return ver @classmethod def get_maindb_postinit_version(cls) -> int: @@ -30,7 +33,10 @@ class MigrationManager: """ with SQLiteManager() as cur: cur.execute(cls.all_get_sql) - return int(cur.fetchone()[2]) + ver = int(cur.fetchone()[2]) + cur.close() + + return ver @classmethod def get_userdatadb_postinit_version(cls) -> int: @@ -39,7 +45,10 @@ class MigrationManager: """ with SQLiteManager(userdata_db=True) as cur: cur.execute(cls.all_get_sql) - return cur.fetchone()[2] + ver = cur.fetchone()[2] + cur.close() + + return ver # 👇 Setters 👇 @classmethod @@ -49,6 +58,7 @@ class MigrationManager: """ with SQLiteManager() as cur: cur.execute(cls.pre_init_set_sql, (version,)) + cur.close() @classmethod def set_maindb_postinit_version(cls, version: int): diff --git a/app/db/sqlite/playlists.py b/app/db/sqlite/playlists.py index 52f7d28..68bd2d5 100644 --- a/app/db/sqlite/playlists.py +++ b/app/db/sqlite/playlists.py @@ -31,6 +31,7 @@ class SQLitePlaylistMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, playlist) pid = cur.lastrowid + cur.close() p_tuple = (pid, *playlist.values()) return tuple_to_playlist(p_tuple) @@ -43,6 +44,7 @@ class SQLitePlaylistMethods: cur.execute(sql, (name,)) data = cur.fetchone() + cur.close() if data is not None: return tuple_to_playlist(data) @@ -57,6 +59,7 @@ class SQLitePlaylistMethods: cur.execute(sql, (name,)) data = cur.fetchone() + cur.close() return int(data[0]) @@ -65,6 +68,7 @@ class SQLitePlaylistMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute("SELECT * FROM playlists") playlists = cur.fetchall() + cur.close() if playlists is not None: return tuples_to_playlists(playlists) @@ -79,6 +83,7 @@ class SQLitePlaylistMethods: cur.execute(sql, (playlist_id,)) data = cur.fetchone() + cur.close() if data is not None: return tuple_to_playlist(data) diff --git a/app/db/sqlite/settings.py b/app/db/sqlite/settings.py index 5e5ee46..4cc9604 100644 --- a/app/db/sqlite/settings.py +++ b/app/db/sqlite/settings.py @@ -18,6 +18,7 @@ class SettingsSQLMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql) dirs = cur.fetchall() + cur.close() dirs = [_dir[0] for _dir in dirs] return [win_replace_slash(d) for d in dirs] diff --git a/app/lib/folderslib.py b/app/lib/folderslib.py index d1a5a22..e7c3734 100644 --- a/app/lib/folderslib.py +++ b/app/lib/folderslib.py @@ -32,7 +32,7 @@ def get_folders(paths: list[str]): for track in TrackStore.tracks: for path in paths: - if track.filepath.startswith(path): + if track.folder.startswith(path): count_dict[path] += 1 folders = [{"path": path, "count": count_dict[path]} for path in paths] diff --git a/app/lib/watchdogg.py b/app/lib/watchdogg.py index 5c4341a..93625be 100644 --- a/app/lib/watchdogg.py +++ b/app/lib/watchdogg.py @@ -131,6 +131,11 @@ def add_track(filepath: str) -> None: Then creates the folder, album and artist objects for the added track and adds them to the store. """ + # remove the track if it already exists + TrackStore.remove_track_by_filepath(filepath) + db.remove_track_by_filepath(filepath) + + # add the track to the database and store. tags = get_tags(filepath) if tags is None: @@ -253,7 +258,11 @@ class Handler(PatternMatchingEventHandler): if os.path.getsize(event.src_path) > 0: path = self.get_abs_path(event.src_path) add_track(path) + except FileNotFoundError: + # file was closed and deleted. + pass except ValueError: + # file was removed from the list by another event handler. pass def on_modified(self, event): diff --git a/app/models/track.py b/app/models/track.py index 519b906..ff4f519 100644 --- a/app/models/track.py +++ b/app/models/track.py @@ -42,7 +42,7 @@ class Track: self.og_album = self.album if self.artist is not None: - artists = split_artists(self.artist) + artists = split_artists(self.artist, with_and=True) new_title = self.title if get_flag(ParserFlags.EXTRACT_FEAT): @@ -71,7 +71,7 @@ class Track: self.artist_hashes = "-".join(create_hash(a, decode=True) for a in artists) self.artist = [ArtistMinimal(a) for a in artists] - albumartists = split_artists(self.albumartist) + albumartists = split_artists(self.albumartist, with_and=True) self.albumartist = [ArtistMinimal(a) for a in albumartists] self.filetype = self.filepath.rsplit(".", maxsplit=1)[-1] diff --git a/app/print_help.py b/app/print_help.py index b00921a..8f8e605 100644 --- a/app/print_help.py +++ b/app/print_help.py @@ -5,6 +5,8 @@ args = ALLARGS HELP_MESSAGE = f""" Usage: swingmusic [options] + Swing Music is a beautiful, self-hosted music player for your local audio files. Like a cooler Spotify ... but bring your own music. + Options: {', '.join(args.help.value)}: Show this help message {', '.join(args.version.value)}: Show the app version diff --git a/app/serializers/favorites_serializer.py b/app/serializers/favorites_serializer.py index bbf02f3..c9f65e3 100644 --- a/app/serializers/favorites_serializer.py +++ b/app/serializers/favorites_serializer.py @@ -1,4 +1,4 @@ -from app.models import Track, Album, Artist +from app.models import Album, Artist, Track def recent_fav_track_serializer(track: Track) -> dict: @@ -21,8 +21,9 @@ def recent_fav_album_serializer(album: Album) -> dict: """ return { "image": album.image, - "title": album.title, + "title": album.og_title, "albumhash": album.albumhash, + "artist": album.albumartists[0].name, "colors": album.colors, } diff --git a/app/settings.py b/app/settings.py index e462b98..df38dc4 100644 --- a/app/settings.py +++ b/app/settings.py @@ -8,7 +8,7 @@ join = os.path.join class Release: - APP_VERSION = "v1.2.1" + APP_VERSION = "1.3.0.beta" class Paths: diff --git a/app/utils/parsers.py b/app/utils/parsers.py index ff6155c..1d91d95 100644 --- a/app/utils/parsers.py +++ b/app/utils/parsers.py @@ -6,7 +6,7 @@ def split_artists(src: str, with_and: bool = False): """ Splits a string of artists into a list of artists. """ - exp = r"\s*(?: and |&|,|;)\s*" if with_and else r"\s*[,;]\s*" + exp = r"\s*(?: and |&|,|;|/)\s*" if with_and else r"\s*[,;]\s*" artists = re.split(exp, src) return [a.strip() for a in artists] @@ -245,3 +245,21 @@ def clean_title(title: str) -> str: # # if "-" in title: # return remove_hyphen_remasters(title) + + +# Traceback (most recent call last): +# File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner +# self.run() +# File "/usr/lib/python3.10/threading.py", line 953, in run +# self._target(*self._args, **self._kwargs) +# File "/home/cwilvx/code/swingmusic/app/periodic_scan.py", line 29, in run_periodic_scans +# Populate(key=get_random_str()) +# File "/home/cwilvx/code/swingmusic/app/lib/populate.py", line 74, in __init__ +# self.tag_untagged(untagged, key) +# File "/home/cwilvx/code/swingmusic/app/lib/populate.py", line 123, in tag_untagged +# insert_many_tracks(tagged_tracks) +# File "/home/cwilvx/code/swingmusic/app/db/sqlite/tracks.py", line 54, in insert_many_tracks +# cls.insert_one_track(track, cur) +# File "/home/cwilvx/code/swingmusic/app/db/sqlite/tracks.py", line 45, in insert_one_track +# cur.execute(sql, track) +# sqlite3.IntegrityError: UNIQUE constraint failed: tracks.filepath