diff --git a/server/app/api/playlist.py b/server/app/api/playlist.py index 64b08b7..8a25373 100644 --- a/server/app/api/playlist.py +++ b/server/app/api/playlist.py @@ -40,6 +40,7 @@ def create_playlist(): "pre_tracks": [], "lastUpdated": data["lastUpdated"], "image": "", + "thumb": "", } try: @@ -100,15 +101,20 @@ def update_playlist(playlistid: str): "description": str(data.get("description").strip()), "lastUpdated": str(data.get("lastUpdated")), "image": None, + "thumb": None, } for p in api.PLAYLISTS: if p.playlistid == playlistid: if image: - playlist["image"] = playlistlib.save_p_image(image, playlistid) + image_, thumb_ = playlistlib.save_p_image(image, playlistid) + playlist["image"] = image_ + playlist["thumb"] = thumb_ + else: playlist["image"] = p.image.split("/")[-1] + playlist["thumb"] = p.thumb.split("/")[-1] p.update_playlist(playlist) instances.playlist_instance.update_playlist(playlistid, playlist) diff --git a/server/app/api/track.py b/server/app/api/track.py index 661d4bf..ad60637 100644 --- a/server/app/api/track.py +++ b/server/app/api/track.py @@ -19,7 +19,7 @@ def send_track_file(trackid): file["filepath"] for file in api.PRE_TRACKS if file["_id"]["$oid"] == trackid ][0] - except (FileNotFoundError, IndexError): + except (FileNotFoundError, IndexError) as e: return "File not found", 404 return send_file(filepath, mimetype="audio/mp3") diff --git a/server/app/db/albums.py b/server/app/db/albums.py index 92c56b8..885ea45 100644 --- a/server/app/db/albums.py +++ b/server/app/db/albums.py @@ -1,8 +1,7 @@ """ -This file contains the Album class for interacting with +This file contains the Album class for interacting with album documents in MongoDB. """ - from app import db from bson import ObjectId @@ -16,16 +15,21 @@ class Albums(db.Mongo): """ def __init__(self): - super(Albums, self).__init__("ALBUMS") - self.collection = self.db["ALBUMS"] + super(Albums, self).__init__("ALICE_ALBUMS") + self.collection = self.db["ALL_ALBUMS"] def insert_album(self, album: dict) -> None: """ Inserts a new album object into the database. """ return self.collection.update_one( - {"album": album["album"], "artist": album["artist"]}, - {"$set": album}, + { + "album": album["album"], + "artist": album["artist"] + }, + { + "$set": album + }, upsert=True, ).upserted_id diff --git a/server/app/db/artists.py b/server/app/db/artists.py index 6fa1054..4f82428 100644 --- a/server/app/db/artists.py +++ b/server/app/db/artists.py @@ -1,7 +1,6 @@ """ This file contains the Artists class for interacting with artist documents in MongoDB. """ - from app import db from bson import ObjectId @@ -12,16 +11,17 @@ class Artists(db.Mongo): """ def __init__(self): - super(Artists, self).__init__("ALL_ARTISTS") - self.collection = self.db["THEM_ARTISTS"] + super(Artists, self).__init__("ALICE_ARTISTS") + self.collection = self.db["ALL_ARTISTS"] def insert_artist(self, artist_obj: dict) -> None: """ Inserts an artist into the database. """ - self.collection.update_one( - artist_obj, {"$set": artist_obj}, upsert=True - ).upserted_id + self.collection.update_one(artist_obj, { + "$set": artist_obj + }, + upsert=True).upserted_id def get_all_artists(self) -> list: """ diff --git a/server/app/db/playlists.py b/server/app/db/playlists.py index ca08106..4e076fa 100644 --- a/server/app/db/playlists.py +++ b/server/app/db/playlists.py @@ -15,8 +15,8 @@ class Playlists(db.Mongo): """ def __init__(self): - super(Playlists, self).__init__("PLAYLISTS") - self.collection = self.db["PLAYLISTS"] + super(Playlists, self).__init__("ALICE_PLAYLISTS") + self.collection = self.db["ALL_PLAYLISTS"] def insert_playlist(self, playlist: dict) -> None: """ diff --git a/server/app/db/trackcolors.py b/server/app/db/trackcolors.py index 75ad4c0..fc52c4c 100644 --- a/server/app/db/trackcolors.py +++ b/server/app/db/trackcolors.py @@ -1,7 +1,6 @@ """ This file contains the TrackColors class for interacting with Track colors documents in MongoDB. """ - from app import db @@ -11,7 +10,7 @@ class TrackColors(db.Mongo): """ def __init__(self): - super(TrackColors, self).__init__("TRACK_COLORS") + super(TrackColors, self).__init__("ALICE_TRACK_COLORS") self.collection = self.db["TRACK_COLORS"] def insert_track_color(self, track_color: dict) -> None: @@ -19,8 +18,12 @@ class TrackColors(db.Mongo): Inserts a new track object into the database. """ return self.collection.update_one( - {"filepath": track_color["filepath"]}, - {"$set": track_color}, + { + "filepath": track_color["filepath"] + }, + { + "$set": track_color + }, upsert=True, ).upserted_id diff --git a/server/app/db/tracks.py b/server/app/db/tracks.py index 8b70b56..1dbcdca 100644 --- a/server/app/db/tracks.py +++ b/server/app/db/tracks.py @@ -1,7 +1,6 @@ """ This file contains the AllSongs class for interacting with track documents in MongoDB. """ - from app import db from bson import ObjectId @@ -15,8 +14,8 @@ class AllSongs(db.Mongo): """ def __init__(self): - super(AllSongs, self).__init__("ALL_SONGS") - self.collection = self.db["ALL_SONGS"] + super(AllSongs, self).__init__("ALICE_MUSIC_TRACKS") + self.collection = self.db["ALL_TRACKS"] # def drop_db(self): # self.collection.drop() @@ -25,9 +24,12 @@ class AllSongs(db.Mongo): """ Inserts a new track object into the database. """ - return self.collection.update_one( - {"filepath": song_obj["filepath"]}, {"$set": song_obj}, upsert=True - ).upserted_id + return self.collection.update_one({ + "filepath": song_obj["filepath"] + }, { + "$set": song_obj + }, + upsert=True).upserted_id def get_all_songs(self) -> list: """ @@ -53,22 +55,33 @@ class AllSongs(db.Mongo): """ Returns all the songs matching the albums in the query params (using regex). """ - songs = self.collection.find({"album": {"$regex": query, "$options": "i"}}) + songs = self.collection.find( + {"album": { + "$regex": query, + "$options": "i" + }}) return convert_many(songs) def search_songs_by_artist(self, query: str) -> list: """ Returns all the songs matching the artists in the query params. """ - songs = self.collection.find({"artists": {"$regex": query, "$options": "i"}}) + songs = self.collection.find( + {"artists": { + "$regex": query, + "$options": "i" + }}) return convert_many(songs) def find_song_by_title(self, query: str) -> list: """ Finds all the tracks matching the title in the query params. """ - self.collection.create_index([("title", db.pymongo.TEXT)]) - song = self.collection.find({"title": {"$regex": query, "$options": "i"}}) + song = self.collection.find( + {"title": { + "$regex": query, + "$options": "i" + }}) return convert_many(song) def find_songs_by_album(self, name: str, artist: str) -> list: @@ -82,7 +95,9 @@ class AllSongs(db.Mongo): """ Returns a sorted list of all the tracks exactly matching the folder in the query params """ - songs = self.collection.find({"folder": query}).sort("title", db.pymongo.ASCENDING) + songs = self.collection.find({ + "folder": query + }).sort("title", db.pymongo.ASCENDING) return convert_many(songs) def find_songs_by_folder_og(self, query: str) -> list: @@ -104,8 +119,10 @@ class AllSongs(db.Mongo): Returns a list of all the tracks containing the albumartist in the query params. """ songs = self.collection.find( - {"albumartist": {"$regex": query, "$options": "i"}} - ) + {"albumartist": { + "$regex": query, + "$options": "i" + }}) return convert_many(songs) def get_song_by_path(self, path: str) -> dict: diff --git a/server/app/lib/folderslib.py b/server/app/lib/folderslib.py index 3343ae5..1035dd3 100644 --- a/server/app/lib/folderslib.py +++ b/server/app/lib/folderslib.py @@ -1,6 +1,9 @@ import time from typing import List -from app import api, helpers, models + +from app import api +from app import helpers +from app import models from progress.bar import Bar @@ -58,7 +61,6 @@ def get_subdirs(foldername: str) -> List[models.Folder]: if str1 is not None: subdirs.add(foldername + "/" + str1) - return [create_folder(dir) for dir in subdirs] diff --git a/server/app/lib/playlistlib.py b/server/app/lib/playlistlib.py index f95e70b..bbc8b7b 100644 --- a/server/app/lib/playlistlib.py +++ b/server/app/lib/playlistlib.py @@ -61,6 +61,26 @@ def create_all_playlists(): _bar.next() _bar.finish() + validate_images() + + +def create_thumbnail(image: any, img_path: str) -> str: + """ + Creates a 250 x 250 thumbnail from a playlist image + """ + thumb_path = "thumb_" + img_path + full_thumb_path = os.path.join(settings.APP_DIR, "images", "playlists", + thumb_path) + + aspect_ratio = image.width / image.height + + new_w = round(250 * aspect_ratio) + + thumb = image.resize((new_w, 250), Image.ANTIALIAS) + thumb.save(full_thumb_path, "webp") + + return thumb_path + def save_p_image(file: datastructures.FileStorage, pid: str): """ @@ -72,7 +92,9 @@ def save_p_image(file: datastructures.FileStorage, pid: str): random.choices(string.ascii_letters + string.digits, k=5)) img_path = pid + str(random_str) + ".webp" - full_path = os.path.join(settings.APP_DIR, "images", "playlists", img_path) + + full_img_path = os.path.join(settings.APP_DIR, "images", "playlists", + img_path) if file.content_type == "image/gif": frames = [] @@ -80,9 +102,33 @@ def save_p_image(file: datastructures.FileStorage, pid: str): for frame in ImageSequence.Iterator(img): frames.append(frame.copy()) - frames[0].save(full_path, save_all=True, append_images=frames[1:]) - return img_path + frames[0].save(full_img_path, save_all=True, append_images=frames[1:]) + thumb_path = create_thumbnail(img, img_path=img_path) - img.save(full_path, "webp") + return img_path, thumb_path - return img_path + img.save(full_img_path, "webp") + thumb_path = create_thumbnail(img, img_path=img_path) + + return img_path, thumb_path + + +def validate_images(): + """ + Removes all unused images in the images/playlists folder. + """ + images = [] + + for playlist in api.PLAYLISTS: + if playlist.image: + img_path = playlist.image.split("/")[-1] + thumb_path = playlist.thumb.split("/")[-1] + + images.append(img_path) + images.append(thumb_path) + + p_path = os.path.join(settings.APP_DIR, "images", "playlists") + + for image in os.listdir(p_path): + if image not in images: + os.remove(os.path.join(p_path, image)) diff --git a/server/app/models.py b/server/app/models.py index fa4f5dc..e59a29d 100644 --- a/server/app/models.py +++ b/server/app/models.py @@ -103,6 +103,7 @@ class Playlist: _pre_tracks: list = field(init=False, repr=False) lastUpdated: int image: str + thumb: str description: str = "" count: int = 0 """A list of track objects in the playlist""" @@ -112,6 +113,7 @@ class Playlist: self.name = data["name"] self.description = data["description"] self.image = self.create_img_link(data["image"]) + self.thumb = self.create_img_link(data["thumb"]) self._pre_tracks = data["pre_tracks"] self.tracks = [] self.lastUpdated = data["lastUpdated"] @@ -149,6 +151,7 @@ class Playlist: if data["image"]: self.image = self.create_img_link(data["image"]) + self.thumb = self.create_img_link(data["thumb"]) @dataclass diff --git a/server/app/patches.py b/server/app/patches.py new file mode 100644 index 0000000..caf4314 --- /dev/null +++ b/server/app/patches.py @@ -0,0 +1,3 @@ +""" +This module contains patch functions to modify existing data in the database. +""" diff --git a/server/app/serializer.py b/server/app/serializer.py index 37874fa..2e3bf78 100644 --- a/server/app/serializer.py +++ b/server/app/serializer.py @@ -58,6 +58,7 @@ class Playlist: playlistid: str name: str image: str + thumb: str lastUpdated: int description: str count: int = 0 @@ -68,6 +69,7 @@ class Playlist: self.playlistid = p.playlistid self.name = p.name self.image = p.image + self.thumb = p.thumb self.lastUpdated = p.lastUpdated self.description = p.description self.count = p.count diff --git a/src/App.vue b/src/App.vue index 1720a31..aa01e0f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -103,5 +103,6 @@ app_dom.addEventListener("click", (e) => { padding: 0 $small; display: grid; grid-template-rows: 1fr; + margin-top: $small; } diff --git a/src/components/AlbumView/Header.vue b/src/components/AlbumView/Header.vue index ab5c9b5..a9ba96a 100644 --- a/src/components/AlbumView/Header.vue +++ b/src/components/AlbumView/Header.vue @@ -44,7 +44,7 @@ const props = defineProps<{ .a-header { display: grid; - grid-template-columns: 13rem 1fr; + grid-template-columns: 15rem 1fr; padding: 1rem; height: 100%; background-color: $gray4; @@ -58,8 +58,8 @@ const props = defineProps<{ align-items: flex-end; .image { - width: 12rem; - height: 12rem; + width: 14rem; + height: 14rem; } } diff --git a/src/components/FolderView/Header.vue b/src/components/FolderView/Header.vue deleted file mode 100644 index 55e8560..0000000 --- a/src/components/FolderView/Header.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - diff --git a/src/components/RightSideBar/queue/playingFrom.vue b/src/components/RightSideBar/queue/playingFrom.vue index 0e84a30..f56bd05 100644 --- a/src/components/RightSideBar/queue/playingFrom.vue +++ b/src/components/RightSideBar/queue/playingFrom.vue @@ -1,9 +1,8 @@ @@ -10,30 +11,31 @@ #new-playlist-card { display: grid; place-items: center; - background-color: $black; position: relative; cursor: pointer; .gradient { position: absolute; - width: calc(100% - 2rem); - height: 10rem; - top: 1rem; + width: calc(100% - 1.5rem); + top: 0.75rem; background-image: linear-gradient(37deg, $red, $blue); background-size: 100%; - transition: all .5s ease-in-out; + transition: all 0.5s ease-in-out; + aspect-ratio: 1; } .image { background-image: url("../../assets/icons/plus.svg"); background-size: 5rem; z-index: 1; - transition: all .5s ease-in-out; + transition: all 0.5s ease-in-out; + background-color: transparent; + margin-bottom: $small; } &:hover { .gradient { - background-size: 30rem; + background-size: 300rem; } .image { transform: rotate(270deg); diff --git a/src/components/playlists/PlaylistCard.vue b/src/components/playlists/PlaylistCard.vue index 931868e..f79cf57 100644 --- a/src/components/playlists/PlaylistCard.vue +++ b/src/components/playlists/PlaylistCard.vue @@ -10,7 +10,7 @@
@@ -37,21 +37,21 @@ import Option from "../shared/Option.vue"; const props = defineProps<{ playlist: Playlist; }>(); - diff --git a/src/views/Playlists.vue b/src/views/Playlists.vue index 299dc59..b29cda3 100644 --- a/src/views/Playlists.vue +++ b/src/views/Playlists.vue @@ -22,15 +22,15 @@ const pStore = usePStore();