mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +00:00
add featured artists to playlist page
+ fetch album bio on raising bottom container
This commit is contained in:
@@ -64,8 +64,12 @@ def get_album():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
album.duration = 0
|
album.duration = 0
|
||||||
|
|
||||||
if (album.count == 1 and tracks[0].title == album.title
|
if (
|
||||||
and tracks[0].tracknumber == 1 and tracks[0].disknumber == 1):
|
album.count == 1
|
||||||
|
and tracks[0].title == album.title
|
||||||
|
and tracks[0].tracknumber == 1
|
||||||
|
and tracks[0].disknumber == 1
|
||||||
|
):
|
||||||
album.is_single = True
|
album.is_single = True
|
||||||
|
|
||||||
return {"tracks": tracks, "info": album}
|
return {"tracks": tracks, "info": album}
|
||||||
@@ -109,12 +113,6 @@ def get_albumartists():
|
|||||||
if artist not in artists:
|
if artist not in artists:
|
||||||
artists.append(artist)
|
artists.append(artist)
|
||||||
|
|
||||||
final_artists = []
|
final_artists = [models.Artist(a) for a in artists]
|
||||||
for artist in artists:
|
|
||||||
artist_obj = {
|
|
||||||
"name": artist,
|
|
||||||
"image": helpers.create_safe_name(artist) + ".webp",
|
|
||||||
}
|
|
||||||
final_artists.append(artist_obj)
|
|
||||||
|
|
||||||
return {"artists": final_artists}
|
return {"artists": final_artists}
|
||||||
|
|||||||
+11
-10
@@ -3,7 +3,6 @@ Contains all the playlist routes.
|
|||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from app import api
|
|
||||||
from app import exceptions
|
from app import exceptions
|
||||||
from app import instances
|
from app import instances
|
||||||
from app import models
|
from app import models
|
||||||
@@ -17,8 +16,8 @@ from flask import request
|
|||||||
|
|
||||||
playlist_bp = Blueprint("playlist", __name__, url_prefix="/")
|
playlist_bp = Blueprint("playlist", __name__, url_prefix="/")
|
||||||
|
|
||||||
PlaylistExists = exceptions.PlaylistExists
|
PlaylistExists = exceptions.PlaylistExistsError
|
||||||
TrackExistsInPlaylist = exceptions.TrackExistsInPlaylist
|
TrackExistsInPlaylist = exceptions.TrackExistsInPlaylistError
|
||||||
|
|
||||||
|
|
||||||
@playlist_bp.route("/playlists", methods=["GET"])
|
@playlist_bp.route("/playlists", methods=["GET"])
|
||||||
@@ -28,8 +27,7 @@ def get_all_playlists():
|
|||||||
dbplaylists = [models.Playlist(p) for p in dbplaylists]
|
dbplaylists = [models.Playlist(p) for p in dbplaylists]
|
||||||
|
|
||||||
playlists = [
|
playlists = [
|
||||||
serializer.Playlist(p, construct_last_updated=False)
|
serializer.Playlist(p, construct_last_updated=False) for p in dbplaylists
|
||||||
for p in dbplaylists
|
|
||||||
]
|
]
|
||||||
playlists.sort(
|
playlists.sort(
|
||||||
key=lambda p: datetime.strptime(p.lastUpdated, "%Y-%m-%d %H:%M:%S"),
|
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)
|
image_, thumb_ = playlistlib.save_p_image(image, playlistid)
|
||||||
playlist["image"] = image_
|
playlist["image"] = image_
|
||||||
playlist["thumb"] = thumb_
|
playlist["thumb"] = thumb_
|
||||||
|
|
||||||
else:
|
else:
|
||||||
playlist["image"] = p.image.split("/")[-1]
|
playlist["image"] = p.image.split("/")[-1]
|
||||||
playlist["thumb"] = p.thumb.split("/")[-1]
|
playlist["thumb"] = p.thumb.split("/")[-1]
|
||||||
@@ -136,7 +133,11 @@ def update_playlist(playlistid: str):
|
|||||||
return {"msg": "Something shady happened"}, 500
|
return {"msg": "Something shady happened"}, 500
|
||||||
|
|
||||||
|
|
||||||
# @playlist_bp.route("/playlist/<playlist_id>/info")
|
@playlist_bp.route("/playlist/artists", methods=["POST"])
|
||||||
# def get_playlist_track(playlist_id: str):
|
def get_playlist_artists():
|
||||||
# tracks = playlistlib.get_playlist_tracks(playlist_id)
|
data = request.get_json()
|
||||||
# return {"data": tracks}
|
|
||||||
|
pid = data["pid"]
|
||||||
|
artists = playlistlib.GetPlaylistArtists(pid)()
|
||||||
|
|
||||||
|
return {"data": artists}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
class TrackExistsInPlaylist(Exception):
|
class TrackExistsInPlaylistError(Exception):
|
||||||
"""
|
"""
|
||||||
Exception raised when a track is already in a playlist.
|
Exception raised when a track is already in a playlist.
|
||||||
"""
|
"""
|
||||||
@@ -6,7 +6,7 @@ class TrackExistsInPlaylist(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PlaylistExists(Exception):
|
class PlaylistExistsError(Exception):
|
||||||
"""
|
"""
|
||||||
Exception raised when a playlist already exists.
|
Exception raised when a playlist already exists.
|
||||||
"""
|
"""
|
||||||
|
|||||||
+11
-15
@@ -28,23 +28,18 @@ def run_checks():
|
|||||||
Checks for new songs every 5 minutes.
|
Checks for new songs every 5 minutes.
|
||||||
"""
|
"""
|
||||||
ValidateAlbumThumbs()
|
ValidateAlbumThumbs()
|
||||||
|
ValidatePlaylistThumbs()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
trackslib.validate_tracks()
|
trackslib.validate_tracks()
|
||||||
|
|
||||||
Populate()
|
Populate()
|
||||||
CreateAlbums()
|
CreateAlbums()
|
||||||
|
ProcessAlbumColors()
|
||||||
|
|
||||||
if helpers.Ping()():
|
if helpers.Ping()():
|
||||||
CheckArtistImages()()
|
CheckArtistImages()()
|
||||||
|
|
||||||
@helpers.background
|
|
||||||
def process_album_colors():
|
|
||||||
ProcessAlbumColors()
|
|
||||||
|
|
||||||
ValidatePlaylistThumbs()
|
|
||||||
process_album_colors()
|
|
||||||
|
|
||||||
time.sleep(300)
|
time.sleep(300)
|
||||||
|
|
||||||
|
|
||||||
@@ -79,7 +74,6 @@ class getArtistImage:
|
|||||||
|
|
||||||
|
|
||||||
class useImageDownloader:
|
class useImageDownloader:
|
||||||
|
|
||||||
def __init__(self, url: str, dest: str) -> None:
|
def __init__(self, url: str, dest: str) -> None:
|
||||||
self.url = url
|
self.url = url
|
||||||
self.dest = dest
|
self.dest = dest
|
||||||
@@ -96,10 +90,8 @@ class useImageDownloader:
|
|||||||
|
|
||||||
|
|
||||||
class CheckArtistImages:
|
class CheckArtistImages:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.artists: list[str] = []
|
self.artists: list[str] = []
|
||||||
print("Checking for artist images")
|
|
||||||
log.info("Checking artist images")
|
log.info("Checking artist images")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -121,8 +113,12 @@ class CheckArtistImages:
|
|||||||
:param artistname: The artist name
|
:param artistname: The artist name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
img_path = (settings.APP_DIR + "/images/artists/" +
|
img_path = (
|
||||||
helpers.create_safe_name(artistname) + ".webp")
|
settings.APP_DIR
|
||||||
|
+ "/images/artists/"
|
||||||
|
+ helpers.create_safe_name(artistname)
|
||||||
|
+ ".webp"
|
||||||
|
)
|
||||||
|
|
||||||
if cls.check_if_exists(img_path):
|
if cls.check_if_exists(img_path):
|
||||||
return "exists"
|
return "exists"
|
||||||
@@ -149,7 +145,8 @@ def fetch_album_bio(title: str, albumartist: str) -> str | None:
|
|||||||
Returns the album bio for a given album.
|
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(
|
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:
|
try:
|
||||||
response = requests.get(last_fm_url)
|
response = requests.get(last_fm_url)
|
||||||
@@ -158,8 +155,7 @@ def fetch_album_bio(title: str, albumartist: str) -> str | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bio = data["album"]["wiki"]["summary"].split(
|
bio = data["album"]["wiki"]["summary"].split('<a href="https://www.last.fm/')[0]
|
||||||
'<a href="https://www.last.fm/')[0]
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
bio = None
|
bio = None
|
||||||
|
|
||||||
|
|||||||
+9
-15
@@ -4,13 +4,11 @@ This module contains mini functions for the server.
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict
|
from typing import Dict, List, Set
|
||||||
from typing import List
|
|
||||||
from typing import Set
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from app import instances
|
|
||||||
from app import models
|
from app import instances, models
|
||||||
|
|
||||||
|
|
||||||
def background(func):
|
def background(func):
|
||||||
@@ -51,7 +49,6 @@ def run_fast_scandir(__dir: str, full=False) -> Dict[List[str], List[str]]:
|
|||||||
|
|
||||||
|
|
||||||
class RemoveDuplicates:
|
class RemoveDuplicates:
|
||||||
|
|
||||||
def __init__(self, tracklist: List[models.Track]) -> None:
|
def __init__(self, tracklist: List[models.Track]) -> None:
|
||||||
self.tracklist = tracklist
|
self.tracklist = tracklist
|
||||||
|
|
||||||
@@ -73,13 +70,12 @@ def is_valid_file(filename: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def create_album_hash(title: str, artist: str) -> str:
|
def create_hash(*args: List[str]) -> str:
|
||||||
"""
|
"""
|
||||||
Creates a simple hash for an album
|
Creates a simple hash for an album
|
||||||
"""
|
"""
|
||||||
lower = (title + artist).replace(" ", "").lower()
|
string = "".join(a for a in args).replace(" ", "")
|
||||||
hash = "".join([i for i in lower if i.isalnum()])
|
return "".join([i for i in string if i.isalnum()]).lower()
|
||||||
return hash
|
|
||||||
|
|
||||||
|
|
||||||
def create_new_date():
|
def create_new_date():
|
||||||
@@ -92,7 +88,7 @@ def create_safe_name(name: str) -> str:
|
|||||||
"""
|
"""
|
||||||
Creates a url-safe name from a name.
|
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:
|
class UseBisection:
|
||||||
@@ -103,8 +99,7 @@ class UseBisection:
|
|||||||
items.
|
items.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, source: List, search_from: str,
|
def __init__(self, source: List, search_from: str, queries: List[str]) -> None:
|
||||||
queries: List[str]) -> None:
|
|
||||||
self.source_list = source
|
self.source_list = source
|
||||||
self.queries_list = queries
|
self.queries_list = queries
|
||||||
self.attr = search_from
|
self.attr = search_from
|
||||||
@@ -134,7 +129,6 @@ class UseBisection:
|
|||||||
|
|
||||||
|
|
||||||
class Get:
|
class Get:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_tracks() -> List[models.Track]:
|
def get_all_tracks() -> List[models.Track]:
|
||||||
"""
|
"""
|
||||||
@@ -157,7 +151,7 @@ class Get:
|
|||||||
|
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
for artist in track.artists:
|
for artist in track.artists:
|
||||||
artists.add(artist.lower())
|
artists.add(artist)
|
||||||
|
|
||||||
return artists
|
return artists
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ from PIL import Image
|
|||||||
from PIL import ImageSequence
|
from PIL import ImageSequence
|
||||||
from werkzeug import datastructures
|
from werkzeug import datastructures
|
||||||
|
|
||||||
TrackExistsInPlaylist = exceptions.TrackExistsInPlaylist
|
|
||||||
|
TrackExistsInPlaylist = exceptions.TrackExistsInPlaylistError
|
||||||
|
|
||||||
logg = get_logger()
|
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
|
Creates a 250 x 250 thumbnail from a playlist image
|
||||||
"""
|
"""
|
||||||
thumb_path = "thumb_" + img_path
|
thumb_path = "thumb_" + img_path
|
||||||
full_thumb_path = os.path.join(settings.APP_DIR, "images", "playlists",
|
full_thumb_path = os.path.join(settings.APP_DIR, "images", "playlists", thumb_path)
|
||||||
thumb_path)
|
|
||||||
|
|
||||||
aspect_ratio = image.width / image.height
|
aspect_ratio = image.width / image.height
|
||||||
|
|
||||||
@@ -71,13 +71,11 @@ def save_p_image(file: datastructures.FileStorage, pid: str):
|
|||||||
"""
|
"""
|
||||||
img = Image.open(file)
|
img = Image.open(file)
|
||||||
|
|
||||||
random_str = "".join(
|
random_str = "".join(random.choices(string.ascii_letters + string.digits, k=5))
|
||||||
random.choices(string.ascii_letters + string.digits, k=5))
|
|
||||||
|
|
||||||
img_path = pid + str(random_str) + ".webp"
|
img_path = pid + str(random_str) + ".webp"
|
||||||
|
|
||||||
full_img_path = os.path.join(settings.APP_DIR, "images", "playlists",
|
full_img_path = os.path.join(settings.APP_DIR, "images", "playlists", img_path)
|
||||||
img_path)
|
|
||||||
|
|
||||||
if file.content_type == "image/gif":
|
if file.content_type == "image/gif":
|
||||||
frames = []
|
frames = []
|
||||||
@@ -140,3 +138,23 @@ def create_playlist_tracks(playlist_tracks: List) -> List[models.Track]:
|
|||||||
tracks.append(models.Track(track))
|
tracks.append(models.Track(track))
|
||||||
|
|
||||||
return tracks
|
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]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import List
|
|||||||
|
|
||||||
from app import instances
|
from app import instances
|
||||||
from app import settings
|
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 Get
|
||||||
from app.helpers import run_fast_scandir
|
from app.helpers import run_fast_scandir
|
||||||
from app.helpers import UseBisection
|
from app.helpers import UseBisection
|
||||||
@@ -51,7 +51,7 @@ class Populate:
|
|||||||
tags = get_tags(file)
|
tags = get_tags(file)
|
||||||
|
|
||||||
if tags is not None:
|
if tags is not None:
|
||||||
hash = create_album_hash(tags["album"], tags["albumartist"])
|
hash = create_hash(tags["album"], tags["albumartist"])
|
||||||
tags["albumhash"] = hash
|
tags["albumhash"] = hash
|
||||||
self.tagged_tracks.append(tags)
|
self.tagged_tracks.append(tags)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from app import instances
|
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.lib.taglib import get_tags
|
||||||
from app.logger import get_logger
|
from app.logger import get_logger
|
||||||
from watchdog.events import PatternMatchingEventHandler
|
from watchdog.events import PatternMatchingEventHandler
|
||||||
@@ -53,7 +53,7 @@ def add_track(filepath: str) -> None:
|
|||||||
tags = get_tags(filepath)
|
tags = get_tags(filepath)
|
||||||
|
|
||||||
if tags is not None:
|
if tags is not None:
|
||||||
hash = create_album_hash(tags["album"], tags["albumartist"])
|
hash = create_hash(tags["album"], tags["albumartist"])
|
||||||
tags["albumhash"] = hash
|
tags["albumhash"] = hash
|
||||||
instances.tracks_instance.insert_song(tags)
|
instances.tracks_instance.insert_song(tags)
|
||||||
|
|
||||||
@@ -82,21 +82,19 @@ class Handler(PatternMatchingEventHandler):
|
|||||||
"""
|
"""
|
||||||
Fired when a supported file is created.
|
Fired when a supported file is created.
|
||||||
"""
|
"""
|
||||||
print("🔵 created +++")
|
|
||||||
self.files_to_process.append(event.src_path)
|
self.files_to_process.append(event.src_path)
|
||||||
|
|
||||||
def on_deleted(self, event):
|
def on_deleted(self, event):
|
||||||
"""
|
"""
|
||||||
Fired when a delete event occurs on a supported file.
|
Fired when a delete event occurs on a supported file.
|
||||||
"""
|
"""
|
||||||
print("🔴 deleted ---")
|
|
||||||
remove_track(event.src_path)
|
remove_track(event.src_path)
|
||||||
|
|
||||||
def on_moved(self, event):
|
def on_moved(self, event):
|
||||||
"""
|
"""
|
||||||
Fired when a move event occurs on a supported file.
|
Fired when a move event occurs on a supported file.
|
||||||
"""
|
"""
|
||||||
print("🔘 moved -->")
|
|
||||||
tr = "share/Trash"
|
tr = "share/Trash"
|
||||||
|
|
||||||
if tr in event.dest_path:
|
if tr in event.dest_path:
|
||||||
@@ -114,7 +112,6 @@ class Handler(PatternMatchingEventHandler):
|
|||||||
"""
|
"""
|
||||||
Fired when a created file is closed.
|
Fired when a created file is closed.
|
||||||
"""
|
"""
|
||||||
print("⚫ closed ~~~")
|
|
||||||
try:
|
try:
|
||||||
self.files_to_process.remove(event.src_path)
|
self.files_to_process.remove(event.src_path)
|
||||||
add_track(event.src_path)
|
add_track(event.src_path)
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ class Track:
|
|||||||
self.image = tags["albumhash"] + ".webp"
|
self.image = tags["albumhash"] + ".webp"
|
||||||
self.tracknumber = int(tags["tracknumber"])
|
self.tracknumber = int(tags["tracknumber"])
|
||||||
|
|
||||||
self.uniq_hash = self.create_unique_hash("".join(self.artists),
|
self.uniq_hash = helpers.create_hash(
|
||||||
self.album, self.title)
|
"".join(self.artists), self.album, self.title
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_unique_hash(*args):
|
def create_unique_hash(*args):
|
||||||
@@ -64,14 +65,12 @@ class Artist:
|
|||||||
Artist class
|
Artist class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
artistid: str
|
|
||||||
name: str
|
name: str
|
||||||
image: str
|
image: str
|
||||||
|
|
||||||
def __init__(self, tags):
|
def __init__(self, name: str):
|
||||||
self.artistid = tags["_id"]["$oid"]
|
self.name = name
|
||||||
self.name = tags["name"]
|
self.image = helpers.create_safe_name(name) + ".webp"
|
||||||
self.image = tags["image"]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Artist } from "./../../interfaces";
|
||||||
import { Playlist, Track } from "../../interfaces";
|
import { Playlist, Track } from "../../interfaces";
|
||||||
import { Notification, NotifType } from "../../stores/notification";
|
import { Notification, NotifType } from "../../stores/notification";
|
||||||
import state from "../state";
|
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<Artist[]>} A promise that resolves to an array of artists.
|
||||||
|
*/
|
||||||
|
export async function getPlaylistArtists(pid: string): Promise<Artist[]> {
|
||||||
|
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 {
|
export {
|
||||||
createNewPlaylist,
|
createNewPlaylist,
|
||||||
getAllPlaylists,
|
getAllPlaylists,
|
||||||
|
|||||||
@@ -28,14 +28,23 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import useVisibility from "../../composables/useVisibility";
|
import useVisibility from "@/composables/useVisibility";
|
||||||
import useNavStore from "../../stores/nav";
|
import useNavStore from "@/stores/nav";
|
||||||
|
import { onBeforeRouteUpdate, RouteParams, useRoute } from "vue-router";
|
||||||
|
|
||||||
const nav = useNavStore();
|
const nav = useNavStore();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
/**
|
||||||
|
* Called when the bottom container is raised.
|
||||||
|
*/
|
||||||
|
onBottomRaised?: (routeparams?: RouteParams) => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
let elem: HTMLElement = null;
|
let elem: HTMLElement = null;
|
||||||
let classlist: DOMTokenList = null;
|
let classlist: DOMTokenList = null;
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
const apheader = ref<HTMLElement>(null);
|
const apheader = ref<HTMLElement>(null);
|
||||||
const apbottomcontainer = ref(null);
|
const apbottomcontainer = ref(null);
|
||||||
const bottomContainerRaised = ref(false);
|
const bottomContainerRaised = ref(false);
|
||||||
@@ -45,6 +54,12 @@ onMounted(() => {
|
|||||||
classlist = elem.classList;
|
classlist = elem.classList;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeRouteUpdate((to) => {
|
||||||
|
if (bottomContainerRaised.value) {
|
||||||
|
props.onBottomRaised(to.params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function handleVisibilityState(state: boolean) {
|
function handleVisibilityState(state: boolean) {
|
||||||
resetBottomPadding();
|
resetBottomPadding();
|
||||||
|
|
||||||
@@ -59,14 +74,19 @@ function resetBottomPadding() {
|
|||||||
classlist.remove("addbottompadding");
|
classlist.remove("addbottompadding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bottomRaisedCallbackExecuted = false;
|
||||||
|
|
||||||
function toggleBottom() {
|
function toggleBottom() {
|
||||||
bottomContainerRaised.value = !bottomContainerRaised.value;
|
bottomContainerRaised.value = !bottomContainerRaised.value;
|
||||||
|
|
||||||
if (bottomContainerRaised.value) {
|
if (bottomContainerRaised.value) {
|
||||||
classlist.add("addbottompadding");
|
classlist.add("addbottompadding");
|
||||||
|
if (!bottomRaisedCallbackExecuted) {
|
||||||
|
bottomRaisedCallbackExecuted = true;
|
||||||
|
props.onBottomRaised(route.params);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem.scrollTop == 0) {
|
if (elem.scrollTop == 0) {
|
||||||
classlist.remove("addbottompadding");
|
classlist.remove("addbottompadding");
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,6 @@ const routes = [
|
|||||||
state.loading.value = true;
|
state.loading.value = true;
|
||||||
await useAStore().fetchTracksAndArtists(to.params.hash);
|
await useAStore().fetchTracksAndArtists(to.params.hash);
|
||||||
state.loading.value = false;
|
state.loading.value = false;
|
||||||
useAStore().fetchBio(to.params.hash);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
|
import { Artist } from "./../../interfaces";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { getPlaylist } from "../../composables/pages/playlists";
|
import {
|
||||||
|
getPlaylist,
|
||||||
|
getPlaylistArtists,
|
||||||
|
} from "../../composables/pages/playlists";
|
||||||
import { Track, Playlist } from "../../interfaces";
|
import { Track, Playlist } from "../../interfaces";
|
||||||
|
|
||||||
export default defineStore("playlist-tracks", {
|
export default defineStore("playlist-tracks", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
info: <Playlist>{},
|
info: <Playlist>{},
|
||||||
tracks: <Track[]>[],
|
tracks: <Track[]>[],
|
||||||
|
artists: <Artist[]>[],
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
/**
|
/**
|
||||||
@@ -18,6 +23,10 @@ export default defineStore("playlist-tracks", {
|
|||||||
this.info = playlist.info;
|
this.info = playlist.info;
|
||||||
this.tracks = playlist.tracks;
|
this.tracks = playlist.tracks;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async fetchArtists(playlistid: string) {
|
||||||
|
this.artists = await getPlaylistArtists(playlistid);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Updates the playlist header info. This is used when the playlist is
|
* Updates the playlist header info. This is used when the playlist is
|
||||||
* updated.
|
* updated.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page :onBottomRaised="fetchAlbumBio">
|
||||||
<template #header>
|
<template #header>
|
||||||
<Header :album="album.info" />
|
<Header :album="album.info" />
|
||||||
</template>
|
</template>
|
||||||
@@ -13,18 +13,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeRouteUpdate, RouteLocationNormalized } from "vue-router";
|
import { onBeforeRouteUpdate, RouteLocationNormalized, RouteParams } from "vue-router";
|
||||||
import useAStore from "@/stores/pages/album";
|
import useAStore from "@/stores/pages/album";
|
||||||
|
|
||||||
import Page from "../layouts/HeaderContentBottom.vue";
|
import Page from "@/layouts/HeaderContentBottom.vue";
|
||||||
import Header from "./Header.vue";
|
import Header from "./Header.vue";
|
||||||
import Content from "./Content.vue";
|
import Content from "./Content.vue";
|
||||||
import Bottom from "./Bottom.vue";
|
import Bottom from "./Bottom.vue";
|
||||||
|
import { onBeforeUnmount } from "vue";
|
||||||
|
|
||||||
const album = useAStore();
|
const album = useAStore();
|
||||||
|
|
||||||
|
function fetchAlbumBio(params: RouteParams) {
|
||||||
|
album.fetchBio(params.hash.toString());
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeRouteUpdate(async (to: RouteLocationNormalized) => {
|
onBeforeRouteUpdate(async (to: RouteLocationNormalized) => {
|
||||||
await album.fetchTracksAndArtists(to.params.hash.toString());
|
await album.fetchTracksAndArtists(to.params.hash.toString());
|
||||||
album.fetchBio(to.params.hash.toString());
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,21 +12,29 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #bottom>
|
<template #bottom>
|
||||||
<FeaturedArtists :artists="[]" />
|
<FeaturedArtists :artists="playlist.artists" />
|
||||||
</template>
|
</template>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Page from "../layouts/HeaderContentBottom.vue";
|
import Page from "@/layouts/HeaderContentBottom.vue";
|
||||||
|
|
||||||
import Header from "@/components/PlaylistView/Header.vue";
|
import Header from "@/components/PlaylistView/Header.vue";
|
||||||
import Content from "./Content.vue";
|
import Content from "./Content.vue";
|
||||||
import FeaturedArtists from "@/components/PlaylistView/FeaturedArtists.vue";
|
import FeaturedArtists from "@/components/PlaylistView/FeaturedArtists.vue";
|
||||||
|
|
||||||
import usePTrackStore from "@/stores/pages/playlist";
|
import usePTrackStore from "@/stores/pages/playlist";
|
||||||
|
import { onBeforeUnmount, onMounted } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
const playlist = usePTrackStore();
|
const playlist = usePTrackStore();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
playlist.fetchArtists(route.params.pid as string);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss"></style>
|
<style lang="scss"></style>
|
||||||
|
|||||||
Reference in New Issue
Block a user