mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-07-29 14:12:21 +00:00
major refactors
- add album page store - show loaders in beforeEnter guards - show bitrate on now playing card - etc
This commit is contained in:
parent
0c1e792839
commit
dbb27734fe
@ -7,6 +7,7 @@ from app import api
|
|||||||
from app import helpers, cache
|
from app import helpers, cache
|
||||||
from app import functions
|
from app import functions
|
||||||
from app.lib import albumslib, trackslib
|
from app.lib import albumslib, trackslib
|
||||||
|
|
||||||
album_bp = Blueprint("album", __name__, url_prefix="")
|
album_bp = Blueprint("album", __name__, url_prefix="")
|
||||||
|
|
||||||
|
|
||||||
@ -44,12 +45,18 @@ def get_album_tracks():
|
|||||||
return {"songs": songs, "info": album}
|
return {"songs": songs, "info": album}
|
||||||
|
|
||||||
|
|
||||||
@album_bp.route("/album/<title>/<artist>/bio")
|
@album_bp.route("/album/bio", methods=["POST"])
|
||||||
@cache.cached()
|
def get_album_bio():
|
||||||
def get_album_bio(title, artist):
|
|
||||||
"""Returns the album bio for the given album."""
|
"""Returns the album bio for the given album."""
|
||||||
bio = functions.get_album_bio(title, artist)
|
data = request.get_json()
|
||||||
return {"bio": bio}, 200
|
print(data)
|
||||||
|
|
||||||
|
bio = functions.get_album_bio(data["album"], data["albumartist"])
|
||||||
|
|
||||||
|
if bio is not None:
|
||||||
|
return {"bio": bio}
|
||||||
|
else:
|
||||||
|
return {"bio": "No bio found."}, 404
|
||||||
|
|
||||||
|
|
||||||
@album_bp.route("/album/artists", methods=["POST"])
|
@album_bp.route("/album/artists", methods=["POST"])
|
||||||
|
@ -21,6 +21,7 @@ def get_all_playlists():
|
|||||||
playlists = []
|
playlists = []
|
||||||
|
|
||||||
for pl in ppp:
|
for pl in ppp:
|
||||||
|
pl.count = len(pl.tracks)
|
||||||
pl.tracks = []
|
pl.tracks = []
|
||||||
playlists.append(pl)
|
playlists.append(pl)
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ def create_playlist():
|
|||||||
|
|
||||||
playlist = {
|
playlist = {
|
||||||
"name": data["name"],
|
"name": data["name"],
|
||||||
"description": [],
|
"description": "",
|
||||||
"tracks": [],
|
"tracks": [],
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"lastUpdated": 0,
|
"lastUpdated": 0,
|
||||||
@ -74,6 +75,7 @@ def add_track_to_playlist(playlist_id: str):
|
|||||||
def get_single_p_info(playlist_id: str):
|
def get_single_p_info(playlist_id: str):
|
||||||
for p in api.PLAYLISTS:
|
for p in api.PLAYLISTS:
|
||||||
if p.playlistid == playlist_id:
|
if p.playlistid == playlist_id:
|
||||||
|
p.count = len(p.tracks)
|
||||||
return {"data": p}
|
return {"data": p}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ Contains all the track routes.
|
|||||||
|
|
||||||
from flask import Blueprint, send_file
|
from flask import Blueprint, send_file
|
||||||
|
|
||||||
from app import instances
|
from app import instances, api
|
||||||
|
|
||||||
track_bp = Blueprint("track", __name__, url_prefix="/")
|
track_bp = Blueprint("track", __name__, url_prefix="/")
|
||||||
|
|
||||||
@ -15,7 +15,11 @@ def send_track_file(trackid):
|
|||||||
Returns an audio file that matches the passed id to the client.
|
Returns an audio file that matches the passed id to the client.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
filepath = instances.songs_instance.get_song_by_id(trackid)["filepath"]
|
filepath = [
|
||||||
|
file["filepath"]
|
||||||
|
for file in api.PRE_TRACKS
|
||||||
|
if file["_id"]["$oid"] == trackid
|
||||||
|
][0]
|
||||||
return send_file(filepath, mimetype="audio/mp3")
|
return send_file(filepath, mimetype="audio/mp3")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return "File not found", 404
|
return "File not found", 404
|
||||||
|
@ -147,7 +147,7 @@ def use_defaults() -> str:
|
|||||||
"""
|
"""
|
||||||
Returns a path to a random image in the defaults directory.
|
Returns a path to a random image in the defaults directory.
|
||||||
"""
|
"""
|
||||||
path = str(random.randint(0, 10)) + ".webp"
|
path = "defaults/" + str(random.randint(0, 20)) + ".webp"
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
@ -266,14 +266,14 @@ def parse_album_artist_tag(audio):
|
|||||||
return albumartist
|
return albumartist
|
||||||
|
|
||||||
|
|
||||||
def parse_album_tag(audio):
|
def parse_album_tag(audio, full_path: str):
|
||||||
"""
|
"""
|
||||||
Parses the album tag from an audio file.
|
Parses the album tag from an audio file.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
album = audio["album"][0]
|
album = audio["album"][0]
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
album = "Unknown"
|
album = full_path.split("/")[-1]
|
||||||
|
|
||||||
return album
|
return album
|
||||||
|
|
||||||
@ -339,7 +339,7 @@ def get_tags(fullpath: str) -> dict:
|
|||||||
"artists": parse_artist_tag(audio),
|
"artists": parse_artist_tag(audio),
|
||||||
"title": parse_title_tag(audio, fullpath),
|
"title": parse_title_tag(audio, fullpath),
|
||||||
"albumartist": parse_album_artist_tag(audio),
|
"albumartist": parse_album_artist_tag(audio),
|
||||||
"album": parse_album_tag(audio),
|
"album": parse_album_tag(audio, fullpath),
|
||||||
"genre": parse_genre_tag(audio),
|
"genre": parse_genre_tag(audio),
|
||||||
"date": parse_date_tag(audio)[:4],
|
"date": parse_date_tag(audio)[:4],
|
||||||
"tracknumber": parse_track_number(audio),
|
"tracknumber": parse_track_number(audio),
|
||||||
@ -365,16 +365,13 @@ def get_album_bio(title: str, albumartist: str):
|
|||||||
response = requests.get(last_fm_url)
|
response = requests.get(last_fm_url)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
except:
|
except:
|
||||||
return "None"
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bio = data["album"]["wiki"]["summary"].split('<a href="https://www.last.fm/')[0]
|
bio = data["album"]["wiki"]["summary"].split('<a href="https://www.last.fm/')[0]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
bio = None
|
bio = None
|
||||||
|
|
||||||
if bio is None:
|
|
||||||
return "None"
|
|
||||||
|
|
||||||
return bio
|
return bio
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,3 +54,4 @@ def get_track_by_id(trackid: str) -> models.Track:
|
|||||||
for track in api.TRACKS:
|
for track in api.TRACKS:
|
||||||
if track.trackid == trackid:
|
if track.trackid == trackid:
|
||||||
return track
|
return track
|
||||||
|
|
||||||
|
@ -60,19 +60,24 @@ def add_track(filepath: str) -> None:
|
|||||||
api.TRACKS.append(models.Track(tags))
|
api.TRACKS.append(models.Track(tags))
|
||||||
|
|
||||||
folder = folderslib.create_folder(tags["folder"])
|
folder = folderslib.create_folder(tags["folder"])
|
||||||
|
print(f"💙💙 {tags['folder']}")
|
||||||
|
print(folder)
|
||||||
|
|
||||||
if folder not in api.FOLDERS:
|
if folder not in api.FOLDERS:
|
||||||
api.FOLDERS.append(folder)
|
api.FOLDERS.append(folder)
|
||||||
|
print(f"added folder {folder.path}")
|
||||||
|
|
||||||
|
|
||||||
def remove_track(filepath: str) -> None:
|
def remove_track(filepath: str) -> None:
|
||||||
"""
|
"""
|
||||||
Removes a track from the music dict.
|
Removes a track from the music dict.
|
||||||
"""
|
"""
|
||||||
print(filepath)
|
fpath = filepath.split("/")[-1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
trackid = instances.songs_instance.get_song_by_path(filepath)["_id"]["$oid"]
|
trackid = instances.songs_instance.get_song_by_path(filepath)["_id"]["$oid"]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
print(f"💙 Watchdog Error: Error removing track {filepath} TypeError")
|
||||||
return
|
return
|
||||||
|
|
||||||
instances.songs_instance.remove_song_by_id(trackid)
|
instances.songs_instance.remove_song_by_id(trackid)
|
||||||
@ -81,6 +86,10 @@ def remove_track(filepath: str) -> None:
|
|||||||
if track.trackid == trackid:
|
if track.trackid == trackid:
|
||||||
api.TRACKS.remove(track)
|
api.TRACKS.remove(track)
|
||||||
|
|
||||||
|
for folder in api.FOLDERS:
|
||||||
|
if folder.path == filepath.replace(fpath, ""):
|
||||||
|
api.FOLDERS.remove(folder)
|
||||||
|
|
||||||
|
|
||||||
class Handler(PatternMatchingEventHandler):
|
class Handler(PatternMatchingEventHandler):
|
||||||
files_to_process = []
|
files_to_process = []
|
||||||
|
@ -69,6 +69,16 @@ class Album:
|
|||||||
self.image = settings.IMG_THUMB_URI + tags["image"]
|
self.image = settings.IMG_THUMB_URI + tags["image"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_p_track(ptrack):
|
||||||
|
for track in api.TRACKS:
|
||||||
|
if (
|
||||||
|
track.title == ptrack["title"]
|
||||||
|
and track.artists == ptrack["artists"]
|
||||||
|
and ptrack["album"] == track.album
|
||||||
|
):
|
||||||
|
return track
|
||||||
|
|
||||||
|
|
||||||
def create_playlist_tracks(playlist_tracks: List) -> List[Track]:
|
def create_playlist_tracks(playlist_tracks: List) -> List[Track]:
|
||||||
"""
|
"""
|
||||||
Creates a list of model.Track objects from a list of playlist track dicts.
|
Creates a list of model.Track objects from a list of playlist track dicts.
|
||||||
@ -76,12 +86,8 @@ def create_playlist_tracks(playlist_tracks: List) -> List[Track]:
|
|||||||
tracks: List[Track] = []
|
tracks: List[Track] = []
|
||||||
|
|
||||||
for t in playlist_tracks:
|
for t in playlist_tracks:
|
||||||
for track in api.TRACKS:
|
track = get_p_track(t)
|
||||||
if (
|
if track is not None:
|
||||||
track.title == t["title"]
|
|
||||||
and track.artists == t["artists"]
|
|
||||||
and track.album == t["album"]
|
|
||||||
):
|
|
||||||
tracks.append(track)
|
tracks.append(track)
|
||||||
|
|
||||||
return tracks
|
return tracks
|
||||||
@ -96,8 +102,8 @@ class Playlist:
|
|||||||
description: str
|
description: str
|
||||||
image: str
|
image: str
|
||||||
tracks: List[Track]
|
tracks: List[Track]
|
||||||
count: int
|
|
||||||
lastUpdated: int
|
lastUpdated: int
|
||||||
|
count: int = 0
|
||||||
"""A list of track objects in the playlist"""
|
"""A list of track objects in the playlist"""
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
@ -106,11 +112,9 @@ class Playlist:
|
|||||||
self.description = data["description"]
|
self.description = data["description"]
|
||||||
self.image = ""
|
self.image = ""
|
||||||
self.tracks = create_playlist_tracks(data["tracks"])
|
self.tracks = create_playlist_tracks(data["tracks"])
|
||||||
self.count = len(data["tracks"])
|
|
||||||
self.lastUpdated = data["lastUpdated"]
|
self.lastUpdated = data["lastUpdated"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Folder:
|
class Folder:
|
||||||
name: str
|
name: str
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
/home/cwilvx/.cache/pypoetry/virtualenvs/musicx_server-jsG71GtA-py3.8/bin/python manage.py
|
|
||||||
|
ppath=$(poetry run which python)
|
||||||
|
|
||||||
|
$ppath manage.py
|
||||||
|
|
||||||
|
|
||||||
#python manage.py
|
#python manage.py
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
:song="song"
|
:song="song"
|
||||||
:index="index + 1"
|
:index="index + 1"
|
||||||
@updateQueue="updateQueue"
|
@updateQueue="updateQueue"
|
||||||
@loadAlbum="loadAlbum"
|
:isPlaying="queue.playing"
|
||||||
|
:isCurrent="queue.current.trackid == song.trackid"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -33,7 +34,6 @@ import { useRoute } from "vue-router";
|
|||||||
|
|
||||||
import SongItem from "../shared/SongItem.vue";
|
import SongItem from "../shared/SongItem.vue";
|
||||||
|
|
||||||
import routeLoader from "../../composables/routeLoader.js";
|
|
||||||
import state from "../../composables/state";
|
import state from "../../composables/state";
|
||||||
import useQStore from "../../stores/queue";
|
import useQStore from "../../stores/queue";
|
||||||
import { Track } from "../../interfaces";
|
import { Track } from "../../interfaces";
|
||||||
@ -43,28 +43,29 @@ const queue = useQStore();
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
tracks: Track[];
|
tracks: Track[];
|
||||||
path?: string;
|
path?: string;
|
||||||
|
pname?: string;
|
||||||
|
playlistid?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let route = useRoute().name;
|
let route = useRoute().name;
|
||||||
console.log(route);
|
|
||||||
const search_query = state.search_query;
|
const search_query = state.search_query;
|
||||||
|
|
||||||
function updateQueue(song: Track) {
|
function updateQueue(track: Track) {
|
||||||
|
|
||||||
switch (route) {
|
switch (route) {
|
||||||
// check which route the play request come from
|
// check which route the play request come from
|
||||||
case "FolderView":
|
case "FolderView":
|
||||||
queue.playFromFolder(props.path, props.tracks, song);
|
queue.playFromFolder(props.path, props.tracks);
|
||||||
|
queue.play(track);
|
||||||
break;
|
break;
|
||||||
case "AlbumView":
|
case "AlbumView":
|
||||||
|
queue.playFromAlbum(track.album, track.albumartist, props.tracks);
|
||||||
|
queue.play(track);
|
||||||
|
break;
|
||||||
|
case "PlaylistView":
|
||||||
|
queue.playFromPlaylist(props.pname, props.playlistid, props.tracks);
|
||||||
|
queue.play(track);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// perks.updateQueue(song, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadAlbum(title, albumartist) {
|
|
||||||
routeLoader.toAlbum(title, albumartist);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,6 +2,20 @@
|
|||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
<div>
|
<div>
|
||||||
|
<div class="art">
|
||||||
|
<div
|
||||||
|
class="l-image image rounded"
|
||||||
|
:style="{
|
||||||
|
backgroundImage: `url("${track.image}")`,
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div id="bitrate">
|
||||||
|
<span v-if="track.bitrate > 330"
|
||||||
|
>FLAC • {{ track.bitrate }}</span
|
||||||
|
>
|
||||||
|
<span v-else>MP3 | {{ track.bitrate }}</span>
|
||||||
|
</div>
|
||||||
<div class="title ellip">{{ props.track.title }}</div>
|
<div class="title ellip">{{ props.track.title }}</div>
|
||||||
<div class="separator no-border"></div>
|
<div class="separator no-border"></div>
|
||||||
<div class="artists ellip" v-if="props.track.artists[0] !== ''">
|
<div class="artists ellip" v-if="props.track.artists[0] !== ''">
|
||||||
|
@ -4,15 +4,6 @@
|
|||||||
<div class="button menu image rounded"></div>
|
<div class="button menu image rounded"></div>
|
||||||
<div class="separator no-border"></div>
|
<div class="separator no-border"></div>
|
||||||
<div>
|
<div>
|
||||||
<div class="art">
|
|
||||||
<div
|
|
||||||
class="l-image image rounded"
|
|
||||||
:style="{
|
|
||||||
backgroundImage: `url("${queue.current.image}")`,
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<div class="separator no-border"></div>
|
|
||||||
<SongCard :track="queue.current" />
|
<SongCard :track="queue.current" />
|
||||||
<Progress :seek="queue.seek" :pos="queue.current_time" />
|
<Progress :seek="queue.seek" :pos="queue.current_time" />
|
||||||
<HotKeys
|
<HotKeys
|
||||||
@ -81,6 +72,7 @@ const queue = useQStore();
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
margin-bottom: $small;
|
||||||
|
|
||||||
.l-image {
|
.l-image {
|
||||||
height: 12rem;
|
height: 12rem;
|
||||||
@ -88,6 +80,18 @@ const queue = useQStore();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#bitrate {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
width: max-content;
|
||||||
|
padding: 0.2rem;
|
||||||
|
top: 13.25rem;
|
||||||
|
left: 1.5rem;
|
||||||
|
background-color: $black;
|
||||||
|
border-radius: $smaller;
|
||||||
|
box-shadow: 0rem 0rem 1rem rgba(0, 0, 0, 0.438);
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,13 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.f-artists {
|
.f-artists {
|
||||||
height: 15.5em;
|
height: 14.5em;
|
||||||
width: calc(100%);
|
width: calc(100%);
|
||||||
padding: $small;
|
padding: $small;
|
||||||
|
padding-bottom: 0;
|
||||||
border-radius: $small;
|
border-radius: $small;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background: linear-gradient(58deg, $gray 0%, rgba(5, 0, 7, 0.5) 100%);
|
background: linear-gradient(0deg, transparent, $black);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -75,7 +76,8 @@ export default {
|
|||||||
.headin {
|
.headin {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
display: flex;
|
// border: solid;
|
||||||
|
margin-left: $small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-header">
|
<div class="p-header">
|
||||||
<div class="carddd circular">
|
<div class="carddd">
|
||||||
<div class="type">❤ Playlist</div>
|
<div class="type">❤ Playlist</div>
|
||||||
<div class="title ellip">{{ props.info.name }}</div>
|
<div class="title ellip">{{ props.info.name }}</div>
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
{{ props.info.desc[0] }}
|
{{ props.info.desc[0] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="duration rounded">4 Tracks • 3 Hours</div>
|
<div class="duration">4 Tracks • 3 Hours</div>
|
||||||
<div class="btns">
|
<div class="btns">
|
||||||
<PlayBtnRect />
|
<PlayBtnRect />
|
||||||
</div>
|
</div>
|
||||||
@ -77,6 +77,7 @@ const props = defineProps<{
|
|||||||
width: 25rem;
|
width: 25rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: rgba(5, 4, 4, 0.829);
|
background-color: rgba(5, 4, 4, 0.829);
|
||||||
|
border-radius: .75rem;
|
||||||
|
|
||||||
.type {
|
.type {
|
||||||
color: rgba(255, 255, 255, 0.692);
|
color: rgba(255, 255, 255, 0.692);
|
||||||
@ -85,6 +86,7 @@ const props = defineProps<{
|
|||||||
.title {
|
.title {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desc {
|
.desc {
|
||||||
@ -105,6 +107,7 @@ const props = defineProps<{
|
|||||||
padding: $smaller;
|
padding: $smaller;
|
||||||
border: solid 1px $gray1;
|
border: solid 1px $gray1;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
border-radius: $smaller;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btns {
|
.btns {
|
||||||
|
@ -14,9 +14,11 @@
|
|||||||
<p class="title ellip">{{ queue.next.title }}</p>
|
<p class="title ellip">{{ queue.next.title }}</p>
|
||||||
<hr />
|
<hr />
|
||||||
<p class="artist ellip">
|
<p class="artist ellip">
|
||||||
<span v-for="artist in putCommas(queue.next.artists)" :key="artist">{{
|
<span
|
||||||
artist
|
v-for="artist in putCommas(queue.next.artists)"
|
||||||
}}</span>
|
:key="artist"
|
||||||
|
>{{ artist }}</span
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -37,10 +39,11 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import perks from "../../composables/perks.js";
|
import perks from "../../composables/perks.js";
|
||||||
import { ref } from "@vue/reactivity";
|
|
||||||
import TrackItem from "../shared/TrackItem.vue";
|
import TrackItem from "../shared/TrackItem.vue";
|
||||||
import useQStore from "../../stores/queue";
|
import useQStore from "../../stores/queue";
|
||||||
import { Track } from "../../interfaces.js";
|
import { Track } from "../../interfaces.js";
|
||||||
|
import { onBeforeMount } from "vue";
|
||||||
|
|
||||||
const queue = useQStore();
|
const queue = useQStore();
|
||||||
|
|
||||||
const putCommas = perks.putCommas;
|
const putCommas = perks.putCommas;
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
v-for="track in props.tracks"
|
v-for="track in props.tracks"
|
||||||
:key="track.trackid"
|
:key="track.trackid"
|
||||||
:track="track"
|
:track="track"
|
||||||
|
:isPlaying="queue.playing"
|
||||||
|
:isCurrent="queue.current.trackid == track.trackid"
|
||||||
/>
|
/>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -19,9 +21,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import LoadMore from "./LoadMore.vue";
|
import LoadMore from "./LoadMore.vue";
|
||||||
import TrackItem from "../shared/TrackItem.vue";
|
import TrackItem from "../shared/TrackItem.vue";
|
||||||
|
import useQStore from "../../stores/queue";
|
||||||
|
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
|
const queue = useQStore();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tracks: {
|
tracks: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="image rounded"></div>
|
<div class="image rounded"></div>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<div class="name ellip">{{ props.playlist.name }}</div>
|
<div class="name ellip">{{ props.playlist.name }}</div>
|
||||||
<div class="count">N Tracks</div>
|
<div class="count">{{ props.playlist.count }} Tracks</div>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
@ -23,21 +23,35 @@ const props = defineProps<{
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.p-card {
|
.p-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: $gray;
|
background: $gray;
|
||||||
padding: $small;
|
padding: $small;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $accent;
|
||||||
|
.bottom > .count {
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
height: 10rem;
|
height: 10rem;
|
||||||
background-image: url("../../assets/images/eggs.jpg");
|
background-image: url("../../assets/images/eggs.jpg");
|
||||||
|
background-size: auto 10rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
margin-top: $small;
|
margin-top: $small;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
font-size: $medium;
|
font-size: $medium;
|
||||||
color: $red;
|
color: $indigo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="songlist-item rounded"
|
class="songlist-item rounded"
|
||||||
:class="[
|
:class="[{ current: props.isCurrent }, { 'context-on': context_on }]"
|
||||||
{ current: current.trackid === props.song.trackid },
|
|
||||||
{ 'context-on': context_on },
|
|
||||||
]"
|
|
||||||
@dblclick="emitUpdate(props.song)"
|
@dblclick="emitUpdate(props.song)"
|
||||||
@contextmenu="showContextMenu"
|
@contextmenu="showContextMenu"
|
||||||
>
|
>
|
||||||
@ -17,8 +14,8 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="now-playing-track image"
|
class="now-playing-track image"
|
||||||
v-if="current.trackid === props.song.trackid"
|
v-if="props.isPlaying && props.isCurrent"
|
||||||
:class="{ active: is_playing, not_active: !is_playing }"
|
:class="{ active: isPlaying, not_active: !isPlaying }"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<div @click="emitUpdate(props.song)">
|
<div @click="emitUpdate(props.song)">
|
||||||
@ -46,14 +43,20 @@
|
|||||||
<span class="artist">{{ props.song.albumartist }}</span>
|
<span class="artist">{{ props.song.albumartist }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="song-album">
|
<router-link
|
||||||
<div
|
class="song-album"
|
||||||
class="album ellip"
|
:to="{
|
||||||
@click="emitLoadAlbum(props.song.album, props.song.albumartist)"
|
name: 'AlbumView',
|
||||||
|
params: {
|
||||||
|
album: props.song.album,
|
||||||
|
artist: props.song.albumartist,
|
||||||
|
},
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
|
<div class="album ellip">
|
||||||
{{ props.song.album }}
|
{{ props.song.album }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</router-link>
|
||||||
<div class="song-duration">
|
<div class="song-duration">
|
||||||
{{ perks.formatSeconds(props.song.length) }}
|
{{ perks.formatSeconds(props.song.length) }}
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +65,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import perks from "../../composables/perks.js";
|
import perks from "../../composables/perks.js";
|
||||||
import state from "../../composables/state";
|
|
||||||
import useContextStore from "../../stores/context";
|
import useContextStore from "../../stores/context";
|
||||||
import useModalStore from "../../stores/modal";
|
import useModalStore from "../../stores/modal";
|
||||||
|
|
||||||
@ -72,7 +74,6 @@ import { Track } from "../../interfaces.js";
|
|||||||
|
|
||||||
const contextStore = useContextStore();
|
const contextStore = useContextStore();
|
||||||
const modalStore = useModalStore();
|
const modalStore = useModalStore();
|
||||||
|
|
||||||
const context_on = ref(false);
|
const context_on = ref(false);
|
||||||
|
|
||||||
const showContextMenu = (e: Event) => {
|
const showContextMenu = (e: Event) => {
|
||||||
@ -92,23 +93,17 @@ const showContextMenu = (e: Event) => {
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
song: Track;
|
song: Track;
|
||||||
index: Number;
|
index: Number;
|
||||||
|
isPlaying: Boolean;
|
||||||
|
isCurrent: Boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "updateQueue", song: Track): void;
|
(e: "updateQueue", song: Track): void;
|
||||||
(e: "loadAlbum", album: string, artist: string): void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function emitUpdate(track: Track) {
|
function emitUpdate(track: Track) {
|
||||||
emit("updateQueue", track);
|
emit("updateQueue", track);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitLoadAlbum(title: string, artist: string) {
|
|
||||||
emit("loadAlbum", title, artist);
|
|
||||||
}
|
|
||||||
|
|
||||||
const is_playing = state.is_playing;
|
|
||||||
const current = state.current;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import state from "./state";
|
|
||||||
|
|
||||||
const getAlbumTracks = async (album, artist) => {
|
|
||||||
let data = {};
|
|
||||||
|
|
||||||
await axios
|
|
||||||
.post(state.settings.uri + "/album/tracks", {
|
|
||||||
album: album,
|
|
||||||
artist: artist,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
data = res.data;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAlbumArtists = async (album, artist) => {
|
|
||||||
let artists = [];
|
|
||||||
|
|
||||||
await axios
|
|
||||||
.post(state.settings.uri + "/album/artists", {
|
|
||||||
album: album,
|
|
||||||
artist: artist,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
artists = res.data.artists;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return artists;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAlbumBio = async (name, artist) => {
|
|
||||||
const res = await fetch(
|
|
||||||
state.settings.uri +
|
|
||||||
"/album/" +
|
|
||||||
encodeURIComponent(name.replaceAll("/", "|")) +
|
|
||||||
"/" +
|
|
||||||
encodeURIComponent(artist.replaceAll("/", "|")) +
|
|
||||||
"/bio"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const message = `An error has occurred: ${res.status}`;
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
return data.bio;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getAlbumTracks,
|
|
||||||
getAlbumArtists,
|
|
||||||
getAlbumBio,
|
|
||||||
};
|
|
65
src/composables/album.ts
Normal file
65
src/composables/album.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import state from "./state";
|
||||||
|
import { AlbumInfo, Track } from "../interfaces";
|
||||||
|
|
||||||
|
const getAlbumTracks = async (album: string, artist: string) => {
|
||||||
|
let data = {
|
||||||
|
info: <AlbumInfo>{},
|
||||||
|
tracks: <Track[]>[],
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.post(state.settings.uri + "/album/tracks", {
|
||||||
|
album: album,
|
||||||
|
artist: artist,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
data.info = res.data.info;
|
||||||
|
data.tracks = res.data.songs;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAlbumArtists = async (album, artist) => {
|
||||||
|
let artists = [];
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.post(state.settings.uri + "/album/artists", {
|
||||||
|
album: album,
|
||||||
|
artist: artist,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
artists = res.data.artists;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return artists;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAlbumBio = async (album: string, albumartist: string) => {
|
||||||
|
let bio = null;
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.post(state.settings.uri + "/album/bio", {
|
||||||
|
album: album,
|
||||||
|
albumartist: albumartist,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
bio = res.data.bio;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.response.status === 404) {
|
||||||
|
bio = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return bio;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getAlbumTracks, getAlbumArtists, getAlbumBio };
|
@ -1,47 +0,0 @@
|
|||||||
import Router from "@/router";
|
|
||||||
|
|
||||||
import album from "./album.js";
|
|
||||||
import state from "./state";
|
|
||||||
|
|
||||||
async function toAlbum(title, artist) {
|
|
||||||
console.log("routing to album");
|
|
||||||
|
|
||||||
state.loading.value = true;
|
|
||||||
await album
|
|
||||||
.getAlbumTracks(title, artist)
|
|
||||||
.then((data) => {
|
|
||||||
state.album.tracklist = data.songs;
|
|
||||||
state.album.info = data.info;
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
await album.getAlbumArtists(title, artist).then((data) => {
|
|
||||||
state.album.artists = data;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
album.getAlbumBio(title, artist).then((data) => {
|
|
||||||
if (data === "None") {
|
|
||||||
state.album.bio = null;
|
|
||||||
} else {
|
|
||||||
state.album.bio = data;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
Router.push({
|
|
||||||
name: "AlbumView",
|
|
||||||
params: {
|
|
||||||
album: title,
|
|
||||||
artist: artist,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
state.loading.value = false;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
toAlbum,
|
|
||||||
};
|
|
@ -53,6 +53,8 @@ interface Playlist {
|
|||||||
description?: string;
|
description?: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
tracks?: Track[];
|
tracks?: Track[];
|
||||||
|
count?: number;
|
||||||
|
lastUpdated?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Notif {
|
interface Notif {
|
||||||
|
@ -13,6 +13,8 @@ import SettingsView from "../views/SettingsView.vue";
|
|||||||
import usePStore from "../stores/playlists";
|
import usePStore from "../stores/playlists";
|
||||||
import usePTrackStore from "../stores/p.ptracks";
|
import usePTrackStore from "../stores/p.ptracks";
|
||||||
import useFStore from "../stores/folder";
|
import useFStore from "../stores/folder";
|
||||||
|
import useAStore from "../stores/album";
|
||||||
|
import state from "../composables/state";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -25,8 +27,9 @@ const routes = [
|
|||||||
name: "FolderView",
|
name: "FolderView",
|
||||||
component: FolderView,
|
component: FolderView,
|
||||||
beforeEnter: async (to) => {
|
beforeEnter: async (to) => {
|
||||||
console.log("beforeEnter")
|
state.loading.value = true;
|
||||||
await useFStore().fetchAll(to.params.path);
|
await useFStore().fetchAll(to.params.path);
|
||||||
|
state.loading.value = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -38,7 +41,9 @@ const routes = [
|
|||||||
name: "Playlists",
|
name: "Playlists",
|
||||||
component: Playlists,
|
component: Playlists,
|
||||||
beforeEnter: async () => {
|
beforeEnter: async () => {
|
||||||
|
state.loading.value = true;
|
||||||
await usePStore().fetchAll();
|
await usePStore().fetchAll();
|
||||||
|
state.loading.value = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -46,7 +51,9 @@ const routes = [
|
|||||||
name: "PlaylistView",
|
name: "PlaylistView",
|
||||||
component: PlaylistView,
|
component: PlaylistView,
|
||||||
beforeEnter: async (to) => {
|
beforeEnter: async (to) => {
|
||||||
|
state.loading.value = true;
|
||||||
await usePTrackStore().fetchAll(to.params.pid);
|
await usePTrackStore().fetchAll(to.params.pid);
|
||||||
|
state.loading.value = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -58,6 +65,15 @@ const routes = [
|
|||||||
path: "/albums/:album/:artist",
|
path: "/albums/:album/:artist",
|
||||||
name: "AlbumView",
|
name: "AlbumView",
|
||||||
component: AlbumView,
|
component: AlbumView,
|
||||||
|
beforeEnter: async (to) => {
|
||||||
|
state.loading.value = true;
|
||||||
|
await useAStore().fetchTracksAndArtists(
|
||||||
|
to.params.album,
|
||||||
|
to.params.artist
|
||||||
|
);
|
||||||
|
state.loading.value = false;
|
||||||
|
useAStore().fetchBio(to.params.album, to.params.artist);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/artists",
|
path: "/artists",
|
||||||
|
31
src/stores/album.ts
Normal file
31
src/stores/album.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { Track, Artist, AlbumInfo } from "../interfaces";
|
||||||
|
import {
|
||||||
|
getAlbumTracks,
|
||||||
|
getAlbumArtists,
|
||||||
|
getAlbumBio,
|
||||||
|
} from "../composables/album";
|
||||||
|
|
||||||
|
export default defineStore("album", {
|
||||||
|
state: () => ({
|
||||||
|
info: <AlbumInfo>{},
|
||||||
|
tracks: <Track[]>[],
|
||||||
|
artists: <Artist[]>[],
|
||||||
|
bio: null,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async fetchTracksAndArtists(title: string, albumartist: string) {
|
||||||
|
const tracks = await getAlbumTracks(title, albumartist);
|
||||||
|
const artists = await getAlbumArtists(title, albumartist);
|
||||||
|
|
||||||
|
this.tracks = tracks.tracks;
|
||||||
|
this.info = tracks.info;
|
||||||
|
this.artists = artists;
|
||||||
|
},
|
||||||
|
fetchBio(title: string, albumartist: string) {
|
||||||
|
getAlbumBio(title, albumartist).then((bio) => {
|
||||||
|
this.bio = bio;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -64,17 +64,18 @@ export default defineStore("Queue", {
|
|||||||
progressElem: HTMLElement,
|
progressElem: HTMLElement,
|
||||||
audio: new Audio(),
|
audio: new Audio(),
|
||||||
current: <Track>{},
|
current: <Track>{},
|
||||||
playing: false,
|
|
||||||
current_time: 0,
|
|
||||||
next: <Track>{},
|
next: <Track>{},
|
||||||
prev: <Track>{},
|
prev: <Track>{},
|
||||||
|
playing: false,
|
||||||
|
current_time: 0,
|
||||||
from: <fromFolder>{} || <fromAlbum>{} || <fromPlaylist>{},
|
from: <fromFolder>{} || <fromAlbum>{} || <fromPlaylist>{},
|
||||||
tracks: <Track[]>[],
|
tracks: <Track[]>[defaultTrack],
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
play(track: Track) {
|
play(track: Track) {
|
||||||
const uri = state.settings.uri + "/file/" + track.trackid;
|
const uri = state.settings.uri + "/file/" + track.trackid;
|
||||||
const elem = document.getElementById("progress");
|
const elem = document.getElementById("progress");
|
||||||
|
this.updateCurrent(track);
|
||||||
|
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
this.audio.src = uri;
|
this.audio.src = uri;
|
||||||
@ -82,8 +83,8 @@ export default defineStore("Queue", {
|
|||||||
this.audio.onerror = reject;
|
this.audio.onerror = reject;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.updateCurrent(track);
|
|
||||||
this.audio.play().then(() => {
|
this.audio.play().then(() => {
|
||||||
|
|
||||||
this.playing = true;
|
this.playing = true;
|
||||||
notif(track, this.playPause, this.playNext, this.playPrev);
|
notif(track, this.playPause, this.playNext, this.playPrev);
|
||||||
|
|
||||||
@ -172,49 +173,37 @@ export default defineStore("Queue", {
|
|||||||
this.prev = this.tracks[index - 1];
|
this.prev = this.tracks[index - 1];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setNewQueue(current: Track, tracklist: Track[]) {
|
setNewQueue(tracklist: Track[]) {
|
||||||
this.play(current);
|
|
||||||
|
|
||||||
if (this.tracks !== tracklist) {
|
if (this.tracks !== tracklist) {
|
||||||
this.tracks = tracklist;
|
this.tracks = tracklist;
|
||||||
addQToLocalStorage(this.from, this.tracks);
|
addQToLocalStorage(this.from, this.tracks);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
playFromFolder(fpath: string, tracks: Track[], current: Track) {
|
playFromFolder(fpath: string, tracks: Track[]) {
|
||||||
|
this.setNewQueue(tracks);
|
||||||
|
|
||||||
this.from = <fromFolder>{
|
this.from = <fromFolder>{
|
||||||
type: FromOptions.folder,
|
type: FromOptions.folder,
|
||||||
path: fpath,
|
path: fpath,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setNewQueue(current, tracks);
|
|
||||||
},
|
},
|
||||||
playFromAlbum(
|
playFromAlbum(aname: string, albumartist: string, tracks: Track[]) {
|
||||||
aname: string,
|
this.setNewQueue(tracks);
|
||||||
albumartist: string,
|
|
||||||
tracks: Track[],
|
|
||||||
current: Track
|
|
||||||
) {
|
|
||||||
this.from = <fromAlbum>{
|
this.from = <fromAlbum>{
|
||||||
type: FromOptions.album,
|
type: FromOptions.album,
|
||||||
name: aname,
|
name: aname,
|
||||||
albumartist: albumartist,
|
albumartist: albumartist,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setNewQueue(current, tracks);
|
|
||||||
},
|
},
|
||||||
playFromPlaylist(
|
playFromPlaylist(pname: string, pid: string, tracks: Track[]) {
|
||||||
pname: string,
|
this.setNewQueue(tracks);
|
||||||
pid: string,
|
|
||||||
tracks: Track[],
|
|
||||||
current: Track
|
|
||||||
) {
|
|
||||||
this.from = <fromPlaylist>{
|
this.from = <fromPlaylist>{
|
||||||
type: FromOptions.playlist,
|
type: FromOptions.playlist,
|
||||||
name: pname,
|
name: pname,
|
||||||
playlistid: pid,
|
playlistid: pid,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setNewQueue(current, tracks);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,47 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="al-view rounded">
|
<div class="al-view rounded">
|
||||||
<div>
|
<div>
|
||||||
<Header :album_info="state.album.info" />
|
<Header :album_info="album.info" />
|
||||||
</div>
|
</div>
|
||||||
<div class="separator" id="av-sep"></div>
|
<div class="separator" id="av-sep"></div>
|
||||||
<div class="songs rounded">
|
<div class="songs rounded">
|
||||||
<SongList :songs="state.album.tracklist" />
|
<SongList :tracks="album.tracks" />
|
||||||
</div>
|
</div>
|
||||||
<div class="separator" id="av-sep"></div>
|
<div class="separator" id="av-sep"></div>
|
||||||
<FeaturedArtists :artists="state.album.artists" />
|
<FeaturedArtists :artists="album.artists" />
|
||||||
<div v-if="state.album.bio">
|
<div v-if="album.bio">
|
||||||
<div class="separator" id="av-sep"></div>
|
<div class="separator" id="av-sep"></div>
|
||||||
<AlbumBio :bio="state.album.bio" v-if="state.album.bio" />
|
<AlbumBio :bio="album.bio" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { onMounted } from "@vue/runtime-core";
|
|
||||||
import { watch } from "vue";
|
|
||||||
import Header from "../components/AlbumView/Header.vue";
|
import Header from "../components/AlbumView/Header.vue";
|
||||||
import AlbumBio from "../components/AlbumView/AlbumBio.vue";
|
import AlbumBio from "../components/AlbumView/AlbumBio.vue";
|
||||||
|
|
||||||
import SongList from "../components/FolderView/SongList.vue";
|
import SongList from "../components/FolderView/SongList.vue";
|
||||||
import FeaturedArtists from "../components/PlaylistView/FeaturedArtists.vue";
|
import FeaturedArtists from "../components/PlaylistView/FeaturedArtists.vue";
|
||||||
|
|
||||||
import state from "@/composables/state";
|
import useAStore from "../stores/album";
|
||||||
import routeLoader from "@/composables/routeLoader.js";
|
import { onBeforeRouteUpdate } from "vue-router";
|
||||||
|
|
||||||
const route = useRoute();
|
const album = useAStore();
|
||||||
|
|
||||||
onMounted(() => {
|
onBeforeRouteUpdate(async (to) => {
|
||||||
routeLoader.toAlbum(route.params.album, route.params.artist);
|
await album.fetchTracksAndArtists(
|
||||||
|
to.params.album.toString(),
|
||||||
watch(
|
to.params.artist.toString()
|
||||||
() => route.params,
|
|
||||||
() => {
|
|
||||||
if (route.name === "AlbumView") {
|
|
||||||
routeLoader.toAlbum(route.params.album, route.params.artist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
album.fetchBio(to.params.album.toString(), to.params.artist.toString());
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -4,7 +4,11 @@
|
|||||||
<div class="separator no-border"></div>
|
<div class="separator no-border"></div>
|
||||||
|
|
||||||
<div class="songlist rounded">
|
<div class="songlist rounded">
|
||||||
<SongList :songs="playlist.tracks" />
|
<SongList
|
||||||
|
:tracks="playlist.tracks"
|
||||||
|
:pname="info.name"
|
||||||
|
:playlistid="playlist.playlistid"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator no-border"></div>
|
<div class="separator no-border"></div>
|
||||||
<FeaturedArtists />
|
<FeaturedArtists />
|
||||||
@ -41,7 +45,7 @@ const info = {
|
|||||||
}
|
}
|
||||||
.songlist {
|
.songlist {
|
||||||
padding: $small;
|
padding: $small;
|
||||||
min-height: 100%;
|
min-height: calc(100% - 30rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user