From dbb27734fe8966e515759e487f28ac6ef289f99c Mon Sep 17 00:00:00 2001 From: geoffrey45 Date: Sun, 3 Apr 2022 01:03:32 +0300 Subject: [PATCH] major refactors - add album page store - show loaders in beforeEnter guards - show bitrate on now playing card - etc --- server/app/api/album.py | 17 +++-- server/app/api/playlist.py | 4 +- server/app/api/track.py | 8 ++- server/app/functions.py | 13 ++-- server/app/lib/trackslib.py | 1 + server/app/lib/watchdoge.py | 11 +++- server/app/models.py | 24 ++++--- server/start.sh | 5 +- src/components/FolderView/SongList.vue | 25 +++---- src/components/LeftSidebar/SongCard.vue | 14 ++++ src/components/LeftSidebar/nowPlaying.vue | 22 ++++--- .../PlaylistView/FeaturedArtists.vue | 8 ++- src/components/PlaylistView/Header.vue | 7 +- src/components/RightSideBar/Queue.vue | 11 ++-- src/components/Search/TracksGrid.vue | 5 +- src/components/playlists/PlaylistCard.vue | 20 +++++- src/components/shared/SongItem.vue | 39 +++++------ src/composables/album.js | 63 ------------------ src/composables/album.ts | 65 +++++++++++++++++++ src/composables/routeLoader.js | 47 -------------- src/interfaces.ts | 2 + src/router/index.js | 18 ++++- src/stores/album.ts | 31 +++++++++ src/stores/queue.ts | 41 +++++------- src/views/AlbumView.vue | 36 ++++------ src/views/PlaylistView.vue | 8 ++- 26 files changed, 300 insertions(+), 245 deletions(-) delete mode 100644 src/composables/album.js create mode 100644 src/composables/album.ts delete mode 100644 src/composables/routeLoader.js create mode 100644 src/stores/album.ts diff --git a/server/app/api/album.py b/server/app/api/album.py index af052de..abc4d06 100644 --- a/server/app/api/album.py +++ b/server/app/api/album.py @@ -7,6 +7,7 @@ from app import api from app import helpers, cache from app import functions from app.lib import albumslib, trackslib + album_bp = Blueprint("album", __name__, url_prefix="") @@ -44,12 +45,18 @@ def get_album_tracks(): return {"songs": songs, "info": album} -@album_bp.route("/album//<artist>/bio") -@cache.cached() -def get_album_bio(title, artist): +@album_bp.route("/album/bio", methods=["POST"]) +def get_album_bio(): """Returns the album bio for the given album.""" - bio = functions.get_album_bio(title, artist) - return {"bio": bio}, 200 + data = request.get_json() + 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"]) diff --git a/server/app/api/playlist.py b/server/app/api/playlist.py index 0846262..41cf8b7 100644 --- a/server/app/api/playlist.py +++ b/server/app/api/playlist.py @@ -21,6 +21,7 @@ def get_all_playlists(): playlists = [] for pl in ppp: + pl.count = len(pl.tracks) pl.tracks = [] playlists.append(pl) @@ -33,7 +34,7 @@ def create_playlist(): playlist = { "name": data["name"], - "description": [], + "description": "", "tracks": [], "count": 0, "lastUpdated": 0, @@ -74,6 +75,7 @@ def add_track_to_playlist(playlist_id: str): def get_single_p_info(playlist_id: str): for p in api.PLAYLISTS: if p.playlistid == playlist_id: + p.count = len(p.tracks) return {"data": p} diff --git a/server/app/api/track.py b/server/app/api/track.py index e0cdbe0..e4f5759 100644 --- a/server/app/api/track.py +++ b/server/app/api/track.py @@ -4,7 +4,7 @@ Contains all the track routes. from flask import Blueprint, send_file -from app import instances +from app import instances, api 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. """ 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") except FileNotFoundError: return "File not found", 404 diff --git a/server/app/functions.py b/server/app/functions.py index 2759da5..f884806 100644 --- a/server/app/functions.py +++ b/server/app/functions.py @@ -147,7 +147,7 @@ def use_defaults() -> str: """ 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 @@ -266,14 +266,14 @@ def parse_album_artist_tag(audio): return albumartist -def parse_album_tag(audio): +def parse_album_tag(audio, full_path: str): """ Parses the album tag from an audio file. """ try: album = audio["album"][0] except (KeyError, IndexError): - album = "Unknown" + album = full_path.split("/")[-1] return album @@ -339,7 +339,7 @@ def get_tags(fullpath: str) -> dict: "artists": parse_artist_tag(audio), "title": parse_title_tag(audio, fullpath), "albumartist": parse_album_artist_tag(audio), - "album": parse_album_tag(audio), + "album": parse_album_tag(audio, fullpath), "genre": parse_genre_tag(audio), "date": parse_date_tag(audio)[:4], "tracknumber": parse_track_number(audio), @@ -365,16 +365,13 @@ def get_album_bio(title: str, albumartist: str): response = requests.get(last_fm_url) data = response.json() except: - return "None" + return None try: bio = data["album"]["wiki"]["summary"].split('<a href="https://www.last.fm/')[0] except KeyError: bio = None - if bio is None: - return "None" - return bio diff --git a/server/app/lib/trackslib.py b/server/app/lib/trackslib.py index 5f7a0ec..fc40043 100644 --- a/server/app/lib/trackslib.py +++ b/server/app/lib/trackslib.py @@ -54,3 +54,4 @@ def get_track_by_id(trackid: str) -> models.Track: for track in api.TRACKS: if track.trackid == trackid: return track + diff --git a/server/app/lib/watchdoge.py b/server/app/lib/watchdoge.py index e1590e2..8c44c1a 100644 --- a/server/app/lib/watchdoge.py +++ b/server/app/lib/watchdoge.py @@ -60,19 +60,24 @@ def add_track(filepath: str) -> None: api.TRACKS.append(models.Track(tags)) folder = folderslib.create_folder(tags["folder"]) + print(f"💙💙 {tags['folder']}") + print(folder) if folder not in api.FOLDERS: api.FOLDERS.append(folder) + print(f"added folder {folder.path}") def remove_track(filepath: str) -> None: """ Removes a track from the music dict. """ - print(filepath) + fpath = filepath.split("/")[-1] + try: trackid = instances.songs_instance.get_song_by_path(filepath)["_id"]["$oid"] except TypeError: + print(f"💙 Watchdog Error: Error removing track {filepath} TypeError") return instances.songs_instance.remove_song_by_id(trackid) @@ -81,6 +86,10 @@ def remove_track(filepath: str) -> None: if track.trackid == trackid: api.TRACKS.remove(track) + for folder in api.FOLDERS: + if folder.path == filepath.replace(fpath, ""): + api.FOLDERS.remove(folder) + class Handler(PatternMatchingEventHandler): files_to_process = [] diff --git a/server/app/models.py b/server/app/models.py index 22e639d..d00f468 100644 --- a/server/app/models.py +++ b/server/app/models.py @@ -69,6 +69,16 @@ class Album: 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]: """ Creates a list of model.Track objects from a list of playlist track dicts. @@ -76,13 +86,9 @@ def create_playlist_tracks(playlist_tracks: List) -> List[Track]: tracks: List[Track] = [] for t in playlist_tracks: - for track in api.TRACKS: - if ( - track.title == t["title"] - and track.artists == t["artists"] - and track.album == t["album"] - ): - tracks.append(track) + track = get_p_track(t) + if track is not None: + tracks.append(track) return tracks @@ -96,8 +102,8 @@ class Playlist: description: str image: str tracks: List[Track] - count: int lastUpdated: int + count: int = 0 """A list of track objects in the playlist""" def __init__(self, data): @@ -106,11 +112,9 @@ class Playlist: self.description = data["description"] self.image = "" self.tracks = create_playlist_tracks(data["tracks"]) - self.count = len(data["tracks"]) self.lastUpdated = data["lastUpdated"] - @dataclass class Folder: name: str diff --git a/server/start.sh b/server/start.sh index aaa7431..a509bcc 100755 --- a/server/start.sh +++ b/server/start.sh @@ -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 diff --git a/src/components/FolderView/SongList.vue b/src/components/FolderView/SongList.vue index ed02d72..2e802f2 100644 --- a/src/components/FolderView/SongList.vue +++ b/src/components/FolderView/SongList.vue @@ -15,7 +15,8 @@ :song="song" :index="index + 1" @updateQueue="updateQueue" - @loadAlbum="loadAlbum" + :isPlaying="queue.playing" + :isCurrent="queue.current.trackid == song.trackid" /> </div> </div> @@ -33,7 +34,6 @@ import { useRoute } from "vue-router"; import SongItem from "../shared/SongItem.vue"; -import routeLoader from "../../composables/routeLoader.js"; import state from "../../composables/state"; import useQStore from "../../stores/queue"; import { Track } from "../../interfaces"; @@ -43,28 +43,29 @@ const queue = useQStore(); const props = defineProps<{ tracks: Track[]; path?: string; + pname?: string; + playlistid?: string; }>(); let route = useRoute().name; -console.log(route); const search_query = state.search_query; -function updateQueue(song: Track) { - +function updateQueue(track: Track) { switch (route) { // check which route the play request come from case "FolderView": - queue.playFromFolder(props.path, props.tracks, song); + queue.playFromFolder(props.path, props.tracks); + queue.play(track); break; 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; } - - // perks.updateQueue(song, type); -} - -function loadAlbum(title, albumartist) { - routeLoader.toAlbum(title, albumartist); } </script> diff --git a/src/components/LeftSidebar/SongCard.vue b/src/components/LeftSidebar/SongCard.vue index 5e51321..c13fd8d 100644 --- a/src/components/LeftSidebar/SongCard.vue +++ b/src/components/LeftSidebar/SongCard.vue @@ -2,6 +2,20 @@ <div class="info"> <div class="desc"> <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="separator no-border"></div> <div class="artists ellip" v-if="props.track.artists[0] !== ''"> diff --git a/src/components/LeftSidebar/nowPlaying.vue b/src/components/LeftSidebar/nowPlaying.vue index 5b8b3e4..27d19a2 100644 --- a/src/components/LeftSidebar/nowPlaying.vue +++ b/src/components/LeftSidebar/nowPlaying.vue @@ -4,15 +4,6 @@ <div class="button menu image rounded"></div> <div class="separator no-border"></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" /> <Progress :seek="queue.seek" :pos="queue.current_time" /> <HotKeys @@ -81,6 +72,7 @@ const queue = useQStore(); width: 100%; display: grid; place-items: center; + margin-bottom: $small; .l-image { 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 { font-weight: 900; } diff --git a/src/components/PlaylistView/FeaturedArtists.vue b/src/components/PlaylistView/FeaturedArtists.vue index de8d027..ec36dd5 100644 --- a/src/components/PlaylistView/FeaturedArtists.vue +++ b/src/components/PlaylistView/FeaturedArtists.vue @@ -58,12 +58,13 @@ export default { <style lang="scss"> .f-artists { - height: 15.5em; + height: 14.5em; width: calc(100%); padding: $small; + padding-bottom: 0; border-radius: $small; user-select: none; - background: linear-gradient(58deg, $gray 0%, rgba(5, 0, 7, 0.5) 100%); + background: linear-gradient(0deg, transparent, $black); position: relative; .header { @@ -75,7 +76,8 @@ export default { .headin { font-size: 1.5rem; font-weight: 900; - display: flex; + // border: solid; + margin-left: $small; } } } diff --git a/src/components/PlaylistView/Header.vue b/src/components/PlaylistView/Header.vue index fec517d..16c0adc 100644 --- a/src/components/PlaylistView/Header.vue +++ b/src/components/PlaylistView/Header.vue @@ -1,12 +1,12 @@ <template> <div class="p-header"> - <div class="carddd circular"> + <div class="carddd"> <div class="type">❤ Playlist</div> <div class="title ellip">{{ props.info.name }}</div> <div class="desc"> {{ props.info.desc[0] }} </div> - <div class="duration rounded">4 Tracks • 3 Hours</div> + <div class="duration">4 Tracks • 3 Hours</div> <div class="btns"> <PlayBtnRect /> </div> @@ -77,6 +77,7 @@ const props = defineProps<{ width: 25rem; padding: 1rem; background-color: rgba(5, 4, 4, 0.829); + border-radius: .75rem; .type { color: rgba(255, 255, 255, 0.692); @@ -85,6 +86,7 @@ const props = defineProps<{ .title { font-size: 2.5rem; font-weight: 900; + text-transform: capitalize; } .desc { @@ -105,6 +107,7 @@ const props = defineProps<{ padding: $smaller; border: solid 1px $gray1; user-select: none; + border-radius: $smaller; } .btns { diff --git a/src/components/RightSideBar/Queue.vue b/src/components/RightSideBar/Queue.vue index 016e75e..226662b 100644 --- a/src/components/RightSideBar/Queue.vue +++ b/src/components/RightSideBar/Queue.vue @@ -14,9 +14,11 @@ <p class="title ellip">{{ queue.next.title }}</p> <hr /> <p class="artist ellip"> - <span v-for="artist in putCommas(queue.next.artists)" :key="artist">{{ - artist - }}</span> + <span + v-for="artist in putCommas(queue.next.artists)" + :key="artist" + >{{ artist }}</span + > </p> </div> </div> @@ -37,10 +39,11 @@ <script setup lang="ts"> import perks from "../../composables/perks.js"; -import { ref } from "@vue/reactivity"; import TrackItem from "../shared/TrackItem.vue"; import useQStore from "../../stores/queue"; import { Track } from "../../interfaces.js"; +import { onBeforeMount } from "vue"; + const queue = useQStore(); const putCommas = perks.putCommas; diff --git a/src/components/Search/TracksGrid.vue b/src/components/Search/TracksGrid.vue index 77696ea..028ea5d 100644 --- a/src/components/Search/TracksGrid.vue +++ b/src/components/Search/TracksGrid.vue @@ -8,6 +8,8 @@ v-for="track in props.tracks" :key="track.trackid" :track="track" + :isPlaying="queue.playing" + :isCurrent="queue.current.trackid == track.trackid" /> </tbody> </table> @@ -19,9 +21,10 @@ <script setup> import LoadMore from "./LoadMore.vue"; import TrackItem from "../shared/TrackItem.vue"; +import useQStore from "../../stores/queue"; let counter = 0; - +const queue = useQStore(); const props = defineProps({ tracks: { type: Object, diff --git a/src/components/playlists/PlaylistCard.vue b/src/components/playlists/PlaylistCard.vue index bbd5eb9..b0b468b 100644 --- a/src/components/playlists/PlaylistCard.vue +++ b/src/components/playlists/PlaylistCard.vue @@ -7,7 +7,7 @@ <div class="image rounded"></div> <div class="bottom"> <div class="name ellip">{{ props.playlist.name }}</div> - <div class="count">N Tracks</div> + <div class="count">{{ props.playlist.count }} Tracks</div> </div> </router-link> </template> @@ -23,21 +23,35 @@ const props = defineProps<{ <style lang="scss"> .p-card { width: 100%; - background-color: $gray; + background: $gray; padding: $small; + transition: all 0.2s ease; + + &:hover { + background-color: $accent; + .bottom > .count { + color: $white; + } + } .image { min-width: 100%; height: 10rem; background-image: url("../../assets/images/eggs.jpg"); + background-size: auto 10rem; + transition: all 0.2s ease; } .bottom { margin-top: $small; + .name { + text-transform: capitalize; + } + .count { font-size: $medium; - color: $red; + color: $indigo; } } } diff --git a/src/components/shared/SongItem.vue b/src/components/shared/SongItem.vue index 5ee73af..883ae3a 100644 --- a/src/components/shared/SongItem.vue +++ b/src/components/shared/SongItem.vue @@ -1,10 +1,7 @@ <template> <div class="songlist-item rounded" - :class="[ - { current: current.trackid === props.song.trackid }, - { 'context-on': context_on }, - ]" + :class="[{ current: props.isCurrent }, { 'context-on': context_on }]" @dblclick="emitUpdate(props.song)" @contextmenu="showContextMenu" > @@ -17,8 +14,8 @@ > <div class="now-playing-track image" - v-if="current.trackid === props.song.trackid" - :class="{ active: is_playing, not_active: !is_playing }" + v-if="props.isPlaying && props.isCurrent" + :class="{ active: isPlaying, not_active: !isPlaying }" ></div> </div> <div @click="emitUpdate(props.song)"> @@ -46,14 +43,20 @@ <span class="artist">{{ props.song.albumartist }}</span> </div> </div> - <div class="song-album"> - <div - class="album ellip" - @click="emitLoadAlbum(props.song.album, props.song.albumartist)" - > + <router-link + class="song-album" + :to="{ + name: 'AlbumView', + params: { + album: props.song.album, + artist: props.song.albumartist, + }, + }" + > + <div class="album ellip"> {{ props.song.album }} </div> - </div> + </router-link> <div class="song-duration"> {{ perks.formatSeconds(props.song.length) }} </div> @@ -62,7 +65,6 @@ <script setup lang="ts"> import perks from "../../composables/perks.js"; -import state from "../../composables/state"; import useContextStore from "../../stores/context"; import useModalStore from "../../stores/modal"; @@ -72,7 +74,6 @@ import { Track } from "../../interfaces.js"; const contextStore = useContextStore(); const modalStore = useModalStore(); - const context_on = ref(false); const showContextMenu = (e: Event) => { @@ -92,23 +93,17 @@ const showContextMenu = (e: Event) => { const props = defineProps<{ song: Track; index: Number; + isPlaying: Boolean; + isCurrent: Boolean; }>(); const emit = defineEmits<{ (e: "updateQueue", song: Track): void; - (e: "loadAlbum", album: string, artist: string): void; }>(); function emitUpdate(track: 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> <style lang="scss"> diff --git a/src/composables/album.js b/src/composables/album.js deleted file mode 100644 index 4f1872b..0000000 --- a/src/composables/album.js +++ /dev/null @@ -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, -}; diff --git a/src/composables/album.ts b/src/composables/album.ts new file mode 100644 index 0000000..2d412f9 --- /dev/null +++ b/src/composables/album.ts @@ -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 }; diff --git a/src/composables/routeLoader.js b/src/composables/routeLoader.js deleted file mode 100644 index cea2a1f..0000000 --- a/src/composables/routeLoader.js +++ /dev/null @@ -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, -}; diff --git a/src/interfaces.ts b/src/interfaces.ts index d2c71cf..254208b 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -53,6 +53,8 @@ interface Playlist { description?: string; image?: string; tracks?: Track[]; + count?: number; + lastUpdated?: number; } interface Notif { diff --git a/src/router/index.js b/src/router/index.js index 2262f7a..e1e0ca4 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -13,6 +13,8 @@ import SettingsView from "../views/SettingsView.vue"; import usePStore from "../stores/playlists"; import usePTrackStore from "../stores/p.ptracks"; import useFStore from "../stores/folder"; +import useAStore from "../stores/album"; +import state from "../composables/state"; const routes = [ { @@ -25,8 +27,9 @@ const routes = [ name: "FolderView", component: FolderView, beforeEnter: async (to) => { - console.log("beforeEnter") + state.loading.value = true; await useFStore().fetchAll(to.params.path); + state.loading.value = false; }, }, { @@ -38,7 +41,9 @@ const routes = [ name: "Playlists", component: Playlists, beforeEnter: async () => { + state.loading.value = true; await usePStore().fetchAll(); + state.loading.value = false; }, }, { @@ -46,7 +51,9 @@ const routes = [ name: "PlaylistView", component: PlaylistView, beforeEnter: async (to) => { + state.loading.value = true; await usePTrackStore().fetchAll(to.params.pid); + state.loading.value = false; }, }, { @@ -58,6 +65,15 @@ const routes = [ path: "/albums/:album/:artist", name: "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", diff --git a/src/stores/album.ts b/src/stores/album.ts new file mode 100644 index 0000000..6a519eb --- /dev/null +++ b/src/stores/album.ts @@ -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; + }); + }, + }, +}); diff --git a/src/stores/queue.ts b/src/stores/queue.ts index 3d56fcd..acc617e 100644 --- a/src/stores/queue.ts +++ b/src/stores/queue.ts @@ -64,17 +64,18 @@ export default defineStore("Queue", { progressElem: HTMLElement, audio: new Audio(), current: <Track>{}, - playing: false, - current_time: 0, next: <Track>{}, prev: <Track>{}, + playing: false, + current_time: 0, from: <fromFolder>{} || <fromAlbum>{} || <fromPlaylist>{}, - tracks: <Track[]>[], + tracks: <Track[]>[defaultTrack], }), actions: { play(track: Track) { const uri = state.settings.uri + "/file/" + track.trackid; const elem = document.getElementById("progress"); + this.updateCurrent(track); new Promise((resolve, reject) => { this.audio.src = uri; @@ -82,8 +83,8 @@ export default defineStore("Queue", { this.audio.onerror = reject; }) .then(() => { - this.updateCurrent(track); this.audio.play().then(() => { + this.playing = true; notif(track, this.playPause, this.playNext, this.playPrev); @@ -172,49 +173,37 @@ export default defineStore("Queue", { this.prev = this.tracks[index - 1]; } }, - setNewQueue(current: Track, tracklist: Track[]) { - this.play(current); - + setNewQueue(tracklist: Track[]) { if (this.tracks !== tracklist) { this.tracks = tracklist; addQToLocalStorage(this.from, this.tracks); } }, - playFromFolder(fpath: string, tracks: Track[], current: Track) { + playFromFolder(fpath: string, tracks: Track[]) { + this.setNewQueue(tracks); + this.from = <fromFolder>{ type: FromOptions.folder, path: fpath, }; - - this.setNewQueue(current, tracks); }, - playFromAlbum( - aname: string, - albumartist: string, - tracks: Track[], - current: Track - ) { + playFromAlbum(aname: string, albumartist: string, tracks: Track[]) { + this.setNewQueue(tracks); + this.from = <fromAlbum>{ type: FromOptions.album, name: aname, albumartist: albumartist, }; - - this.setNewQueue(current, tracks); }, - playFromPlaylist( - pname: string, - pid: string, - tracks: Track[], - current: Track - ) { + playFromPlaylist(pname: string, pid: string, tracks: Track[]) { + this.setNewQueue(tracks); + this.from = <fromPlaylist>{ type: FromOptions.playlist, name: pname, playlistid: pid, }; - - this.setNewQueue(current, tracks); }, }, }); diff --git a/src/views/AlbumView.vue b/src/views/AlbumView.vue index e55439f..a2a0569 100644 --- a/src/views/AlbumView.vue +++ b/src/views/AlbumView.vue @@ -1,47 +1,39 @@ <template> <div class="al-view rounded"> <div> - <Header :album_info="state.album.info" /> + <Header :album_info="album.info" /> </div> <div class="separator" id="av-sep"></div> <div class="songs rounded"> - <SongList :songs="state.album.tracklist" /> + <SongList :tracks="album.tracks" /> </div> <div class="separator" id="av-sep"></div> - <FeaturedArtists :artists="state.album.artists" /> - <div v-if="state.album.bio"> + <FeaturedArtists :artists="album.artists" /> + <div v-if="album.bio"> <div class="separator" id="av-sep"></div> - <AlbumBio :bio="state.album.bio" v-if="state.album.bio" /> + <AlbumBio :bio="album.bio" /> </div> </div> </template> -<script setup> -import { useRoute } from "vue-router"; -import { onMounted } from "@vue/runtime-core"; -import { watch } from "vue"; +<script setup lang="ts"> import Header from "../components/AlbumView/Header.vue"; import AlbumBio from "../components/AlbumView/AlbumBio.vue"; import SongList from "../components/FolderView/SongList.vue"; import FeaturedArtists from "../components/PlaylistView/FeaturedArtists.vue"; -import state from "@/composables/state"; -import routeLoader from "@/composables/routeLoader.js"; +import useAStore from "../stores/album"; +import { onBeforeRouteUpdate } from "vue-router"; -const route = useRoute(); +const album = useAStore(); -onMounted(() => { - routeLoader.toAlbum(route.params.album, route.params.artist); - - watch( - () => route.params, - () => { - if (route.name === "AlbumView") { - routeLoader.toAlbum(route.params.album, route.params.artist); - } - } +onBeforeRouteUpdate(async (to) => { + await album.fetchTracksAndArtists( + to.params.album.toString(), + to.params.artist.toString() ); + album.fetchBio(to.params.album.toString(), to.params.artist.toString()); }); </script> diff --git a/src/views/PlaylistView.vue b/src/views/PlaylistView.vue index 20ce981..242ca54 100644 --- a/src/views/PlaylistView.vue +++ b/src/views/PlaylistView.vue @@ -4,7 +4,11 @@ <div class="separator no-border"></div> <div class="songlist rounded"> - <SongList :songs="playlist.tracks" /> + <SongList + :tracks="playlist.tracks" + :pname="info.name" + :playlistid="playlist.playlistid" + /> </div> <div class="separator no-border"></div> <FeaturedArtists /> @@ -41,7 +45,7 @@ const info = { } .songlist { padding: $small; - min-height: 100%; + min-height: calc(100% - 30rem); } } </style>