diff --git a/server/app/api/album.py b/server/app/api/album.py index 2f22f702..e80acc46 100644 --- a/server/app/api/album.py +++ b/server/app/api/album.py @@ -64,8 +64,12 @@ def get_album(): except AttributeError: album.duration = 0 - if (album.count == 1 and tracks[0].title == album.title - and tracks[0].tracknumber == 1 and tracks[0].disknumber == 1): + if ( + album.count == 1 + and tracks[0].title == album.title + and tracks[0].tracknumber == 1 + and tracks[0].disknumber == 1 + ): album.is_single = True return {"tracks": tracks, "info": album} @@ -109,12 +113,6 @@ def get_albumartists(): if artist not in artists: artists.append(artist) - final_artists = [] - for artist in artists: - artist_obj = { - "name": artist, - "image": helpers.create_safe_name(artist) + ".webp", - } - final_artists.append(artist_obj) + final_artists = [models.Artist(a) for a in artists] return {"artists": final_artists} diff --git a/server/app/api/playlist.py b/server/app/api/playlist.py index c26eb0df..d6fd19e1 100644 --- a/server/app/api/playlist.py +++ b/server/app/api/playlist.py @@ -3,7 +3,6 @@ Contains all the playlist routes. """ from datetime import datetime -from app import api from app import exceptions from app import instances from app import models @@ -17,8 +16,8 @@ from flask import request playlist_bp = Blueprint("playlist", __name__, url_prefix="/") -PlaylistExists = exceptions.PlaylistExists -TrackExistsInPlaylist = exceptions.TrackExistsInPlaylist +PlaylistExists = exceptions.PlaylistExistsError +TrackExistsInPlaylist = exceptions.TrackExistsInPlaylistError @playlist_bp.route("/playlists", methods=["GET"]) @@ -28,8 +27,7 @@ def get_all_playlists(): dbplaylists = [models.Playlist(p) for p in dbplaylists] playlists = [ - serializer.Playlist(p, construct_last_updated=False) - for p in dbplaylists + serializer.Playlist(p, construct_last_updated=False) for p in dbplaylists ] playlists.sort( key=lambda p: datetime.strptime(p.lastUpdated, "%Y-%m-%d %H:%M:%S"), @@ -121,7 +119,6 @@ def update_playlist(playlistid: str): 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] @@ -136,7 +133,11 @@ def update_playlist(playlistid: str): return {"msg": "Something shady happened"}, 500 -# @playlist_bp.route("/playlist//info") -# def get_playlist_track(playlist_id: str): -# tracks = playlistlib.get_playlist_tracks(playlist_id) -# return {"data": tracks} +@playlist_bp.route("/playlist/artists", methods=["POST"]) +def get_playlist_artists(): + data = request.get_json() + + pid = data["pid"] + artists = playlistlib.GetPlaylistArtists(pid)() + + return {"data": artists} diff --git a/server/app/exceptions.py b/server/app/exceptions.py index 2777466a..6e774e9d 100644 --- a/server/app/exceptions.py +++ b/server/app/exceptions.py @@ -1,4 +1,4 @@ -class TrackExistsInPlaylist(Exception): +class TrackExistsInPlaylistError(Exception): """ Exception raised when a track is already in a playlist. """ @@ -6,7 +6,7 @@ class TrackExistsInPlaylist(Exception): pass -class PlaylistExists(Exception): +class PlaylistExistsError(Exception): """ Exception raised when a playlist already exists. """ diff --git a/server/app/functions.py b/server/app/functions.py index b9293de3..116d9745 100644 --- a/server/app/functions.py +++ b/server/app/functions.py @@ -28,23 +28,18 @@ def run_checks(): Checks for new songs every 5 minutes. """ ValidateAlbumThumbs() + ValidatePlaylistThumbs() while True: trackslib.validate_tracks() Populate() CreateAlbums() + ProcessAlbumColors() if helpers.Ping()(): CheckArtistImages()() - @helpers.background - def process_album_colors(): - ProcessAlbumColors() - - ValidatePlaylistThumbs() - process_album_colors() - time.sleep(300) @@ -79,7 +74,6 @@ class getArtistImage: class useImageDownloader: - def __init__(self, url: str, dest: str) -> None: self.url = url self.dest = dest @@ -96,10 +90,8 @@ class useImageDownloader: class CheckArtistImages: - def __init__(self): self.artists: list[str] = [] - print("Checking for artist images") log.info("Checking artist images") @staticmethod @@ -121,8 +113,12 @@ class CheckArtistImages: :param artistname: The artist name """ - img_path = (settings.APP_DIR + "/images/artists/" + - helpers.create_safe_name(artistname) + ".webp") + img_path = ( + settings.APP_DIR + + "/images/artists/" + + helpers.create_safe_name(artistname) + + ".webp" + ) if cls.check_if_exists(img_path): return "exists" @@ -149,7 +145,8 @@ def fetch_album_bio(title: str, albumartist: str) -> str | None: Returns the album bio for a given album. """ last_fm_url = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={}&artist={}&album={}&format=json".format( - settings.LAST_FM_API_KEY, albumartist, title) + settings.LAST_FM_API_KEY, albumartist, title + ) try: response = requests.get(last_fm_url) @@ -158,8 +155,7 @@ def fetch_album_bio(title: str, albumartist: str) -> str | None: return None try: - bio = data["album"]["wiki"]["summary"].split( - ' str: """ Creates a url-safe name from a name. """ - return "".join([i for i in name if i.isalnum()]) + return "".join([i for i in name if i.isalnum()]).lower() class UseBisection: @@ -103,8 +99,7 @@ class UseBisection: items. """ - def __init__(self, source: List, search_from: str, - queries: List[str]) -> None: + def __init__(self, source: List, search_from: str, queries: List[str]) -> None: self.source_list = source self.queries_list = queries self.attr = search_from @@ -134,7 +129,6 @@ class UseBisection: class Get: - @staticmethod def get_all_tracks() -> List[models.Track]: """ @@ -157,7 +151,7 @@ class Get: for track in tracks: for artist in track.artists: - artists.add(artist.lower()) + artists.add(artist) return artists diff --git a/server/app/lib/playlistlib.py b/server/app/lib/playlistlib.py index 95fda111..3cd896bf 100644 --- a/server/app/lib/playlistlib.py +++ b/server/app/lib/playlistlib.py @@ -18,7 +18,8 @@ from PIL import Image from PIL import ImageSequence from werkzeug import datastructures -TrackExistsInPlaylist = exceptions.TrackExistsInPlaylist + +TrackExistsInPlaylist = exceptions.TrackExistsInPlaylistError logg = get_logger() @@ -52,8 +53,7 @@ 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) + full_thumb_path = os.path.join(settings.APP_DIR, "images", "playlists", thumb_path) aspect_ratio = image.width / image.height @@ -71,13 +71,11 @@ def save_p_image(file: datastructures.FileStorage, pid: str): """ img = Image.open(file) - random_str = "".join( - random.choices(string.ascii_letters + string.digits, k=5)) + random_str = "".join(random.choices(string.ascii_letters + string.digits, k=5)) img_path = pid + str(random_str) + ".webp" - full_img_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 = [] @@ -140,3 +138,23 @@ def create_playlist_tracks(playlist_tracks: List) -> List[models.Track]: tracks.append(models.Track(track)) return tracks + + +class GetPlaylistArtists: + """ + Returns a list of artists from a list of playlist tracks. + """ + + def __init__(self, pid: str) -> None: + self.pid = pid + p = instances.playlist_instance.get_playlist_by_id(self.pid) + self.tracks = create_playlist_tracks(p["pre_tracks"]) + + def __call__(self): + artists = set() + + for t in self.tracks: + for a in t.artists: + artists.add(a) + + return [models.Artist(a) for a in artists] diff --git a/server/app/lib/populate.py b/server/app/lib/populate.py index c6ee9400..811b0787 100644 --- a/server/app/lib/populate.py +++ b/server/app/lib/populate.py @@ -5,7 +5,7 @@ from typing import List from app import instances from app import settings -from app.helpers import create_album_hash +from app.helpers import create_hash from app.helpers import Get from app.helpers import run_fast_scandir from app.helpers import UseBisection @@ -51,7 +51,7 @@ class Populate: tags = get_tags(file) if tags is not None: - hash = create_album_hash(tags["album"], tags["albumartist"]) + hash = create_hash(tags["album"], tags["albumartist"]) tags["albumhash"] = hash self.tagged_tracks.append(tags) diff --git a/server/app/lib/watchdoge.py b/server/app/lib/watchdoge.py index 50731a6d..366aea5e 100644 --- a/server/app/lib/watchdoge.py +++ b/server/app/lib/watchdoge.py @@ -5,7 +5,7 @@ import os import time from app import instances -from app.helpers import create_album_hash +from app.helpers import create_hash from app.lib.taglib import get_tags from app.logger import get_logger from watchdog.events import PatternMatchingEventHandler @@ -53,7 +53,7 @@ def add_track(filepath: str) -> None: tags = get_tags(filepath) if tags is not None: - hash = create_album_hash(tags["album"], tags["albumartist"]) + hash = create_hash(tags["album"], tags["albumartist"]) tags["albumhash"] = hash instances.tracks_instance.insert_song(tags) @@ -82,21 +82,19 @@ class Handler(PatternMatchingEventHandler): """ Fired when a supported file is created. """ - print("🔵 created +++") self.files_to_process.append(event.src_path) def on_deleted(self, event): """ Fired when a delete event occurs on a supported file. """ - print("🔴 deleted ---") + remove_track(event.src_path) def on_moved(self, event): """ Fired when a move event occurs on a supported file. """ - print("🔘 moved -->") tr = "share/Trash" if tr in event.dest_path: @@ -114,7 +112,6 @@ class Handler(PatternMatchingEventHandler): """ Fired when a created file is closed. """ - print("⚫ closed ~~~") try: self.files_to_process.remove(event.src_path) add_track(event.src_path) diff --git a/server/app/models.py b/server/app/models.py index 974e4b36..797ec1dd 100644 --- a/server/app/models.py +++ b/server/app/models.py @@ -49,8 +49,9 @@ class Track: self.image = tags["albumhash"] + ".webp" self.tracknumber = int(tags["tracknumber"]) - self.uniq_hash = self.create_unique_hash("".join(self.artists), - self.album, self.title) + self.uniq_hash = helpers.create_hash( + "".join(self.artists), self.album, self.title + ) @staticmethod def create_unique_hash(*args): @@ -64,14 +65,12 @@ class Artist: Artist class """ - artistid: str name: str image: str - def __init__(self, tags): - self.artistid = tags["_id"]["$oid"] - self.name = tags["name"] - self.image = tags["image"] + def __init__(self, name: str): + self.name = name + self.image = helpers.create_safe_name(name) + ".webp" @dataclass diff --git a/src/composables/pages/playlists.ts b/src/composables/pages/playlists.ts index 2fb663ba..5f45d6b1 100644 --- a/src/composables/pages/playlists.ts +++ b/src/composables/pages/playlists.ts @@ -1,3 +1,4 @@ +import { Artist } from "./../../interfaces"; import { Playlist, Track } from "../../interfaces"; import { Notification, NotifType } from "../../stores/notification"; import state from "../state"; @@ -120,6 +121,32 @@ async function updatePlaylist(pid: string, playlist: FormData, pStore: any) { } } +/** + * Gets the artists in a playlist. + * @param pid The playlist id to fetch tracks for. + * @returns {Promise} A promise that resolves to an array of artists. + */ +export async function getPlaylistArtists(pid: string): Promise { + const uri = state.settings.uri + "/playlist/artists"; + + const { data, error } = await useAxios({ + url: uri, + props: { + pid: pid, + }, + }); + + if (error) { + new Notification("Something funny happened!", NotifType.Error); + } + + if (data) { + return data.data as Artist[]; + } + + return []; +} + export { createNewPlaylist, getAllPlaylists, diff --git a/src/views/layouts/HeaderContentBottom.vue b/src/layouts/HeaderContentBottom.vue similarity index 82% rename from src/views/layouts/HeaderContentBottom.vue rename to src/layouts/HeaderContentBottom.vue index 2ed3f208..e9e3c2a0 100644 --- a/src/views/layouts/HeaderContentBottom.vue +++ b/src/layouts/HeaderContentBottom.vue @@ -28,14 +28,23 @@ diff --git a/src/views/playlist/index.vue b/src/views/playlist/index.vue index 7a3b8298..cff33863 100644 --- a/src/views/playlist/index.vue +++ b/src/views/playlist/index.vue @@ -12,21 +12,29 @@ />