From 326d080e2c3bd68e3960073b4df405157524c1fc Mon Sep 17 00:00:00 2001 From: Tudorel Oprisan Date: Fri, 20 Dec 2024 15:57:02 +0000 Subject: [PATCH] Albums are loaded correctly --- .../src/components/ArtistList.svelte | 176 +++++++++++++++--- subsonic-ui-svelte/src/lib/api.ts | 65 +++++-- 2 files changed, 203 insertions(+), 38 deletions(-) diff --git a/subsonic-ui-svelte/src/components/ArtistList.svelte b/subsonic-ui-svelte/src/components/ArtistList.svelte index db56bf5..317a04c 100644 --- a/subsonic-ui-svelte/src/components/ArtistList.svelte +++ b/subsonic-ui-svelte/src/components/ArtistList.svelte @@ -15,26 +15,142 @@ artists: Artist[]; }; + type Album = { + id: string; + title: string; + artist: string; + coverArtUrl: string; + }; + let artistGroups: GroupedArtists[] = []; + let albums: Album[] = []; + let selectedArtistId: string | null = null; let error: string | null = null; + async function fetchAlbumsForArtist(id: string) { + try { + const response = await fetchFromAPI("getArtist", { id }); + const albums = response["subsonic-response"]?.artist?.album || []; + + // Fetch the cover art for each album + for (const album of albums) { + const coverArtUrl = await fetchCoverArt(album.id); + album.coverArtUrl = coverArtUrl; // Add the full URL to the album object + } + + return albums; + } catch (err) { + error = (err as Error).message; + return []; + } + } + + async function fetchCoverArt(albumId: string) { + try { + const blob = await fetchFromAPI("getCoverArt", { + id: albumId, + size: "150", + }); + + // Convert the Blob into a URL that can be used in an tag + const coverArtUrl = URL.createObjectURL(blob); + + return coverArtUrl; + } catch (err) { + console.error("Error fetching cover art:", err); + return ""; + } + } + + let showAlbums = false; + + const toggleView = () => { + showAlbums = !showAlbums; + }; + onMount(async () => { try { const data = await fetchFromAPI("getArtists", {}); - artistGroups = (data["subsonic-response"]?.artists?.index || []).map((group: any) => ({ + artistGroups = ( + data["subsonic-response"]?.artists?.index || [] + ).map((group: any) => ({ name: group.name, - artists: group.artist || [] + artists: group.artist || [], })); } catch (err) { error = (err as Error).message; } }); + + async function handleArtistClick(artistId: string) { + selectedArtistId = artistId; + albums = await fetchAlbumsForArtist(artistId); + albums.length > 0 && (showAlbums = true); + } +{#if error} +

Error: {error}

+{:else if artistGroups.length === 0} +

Loading artists...

+{:else} +
+ + {#if showAlbums} + {#if selectedArtistId && albums.length > 0} +

Albums for {albums[0].artist}

+
+ {#each albums as album} +
+ {#if album.coverArtUrl} + {album.title} + {/if} +

{album.title}

+
+ {/each} +
+ {/if} + {:else} + {#each artistGroups as group} +
+

{group.name}

+ +
+ {#each group.artists as artist} + + {/each} +
+
+ {/each} + {/if} +
+{/if} + -{#if error} -

Error: {error}

-{:else if artistGroups.length === 0} -

Loading artists...

-{:else} -
- {#each artistGroups as group} -
-

{group.name}

-
- {#each group.artists as artist} -
- {artist.name} -

{artist.name}

-

{artist.albumCount} album(s)

-
- {/each} -
-
- {/each} -
-{/if} + .album-list { + display: grid; + grid-template-columns: repeat( + auto-fill, + minmax(150px, 1fr) + ); /* Flexible column layout */ + gap: 1rem; /* Space between items */ + justify-content: center; + padding: 1rem; + } + + .album-card { + text-align: center; + color: black; + border: 1px solid #ccc; + border-radius: 8px; + padding: 1rem; + background-color: #f9f9f9; + } + + .album-card img { + width: 100%; /* Makes the image responsive within the card */ + height: auto; + border-radius: 4px; + object-fit: cover; /* Ensures the image covers the area without distortion */ + } + diff --git a/subsonic-ui-svelte/src/lib/api.ts b/subsonic-ui-svelte/src/lib/api.ts index 0b74831..0c3db26 100644 --- a/subsonic-ui-svelte/src/lib/api.ts +++ b/subsonic-ui-svelte/src/lib/api.ts @@ -3,22 +3,65 @@ const API_VERSION = "1.16.1"; // Replace with the Subsonic API version you're us const API_PASSWORD = "xWPciqjr5VSVDvVS0sAH9L8gKEQm42"; // Replace with your actual token const API_CLIENT = "SvelteApp"; +// export async function fetchFromAPI(endpoint: string, params: Record) { +// const url = new URL(`${API_URL}/${endpoint}`); +// url.search = new URLSearchParams({ +// ...params, +// v: API_VERSION, +// c: API_CLIENT, +// f: "json", // Always request JSON format +// u: "drome", // Replace with your username +// p: API_PASSWORD +// //t: API_TOKEN, // Your token +// //s: "salt", // Optional if you're using salted tokens +// }).toString(); + + +// const response = await fetch(url.toString()); +// if (!response.ok) { +// throw new Error(`API request failed: ${response.statusText}`); +// } +// return response.json(); +// } + + export async function fetchFromAPI(endpoint: string, params: Record) { const url = new URL(`${API_URL}/${endpoint}`); - url.search = new URLSearchParams({ - ...params, - v: API_VERSION, - c: API_CLIENT, - f: "json", // Always request JSON format - u: "drome", // Replace with your username - p: API_PASSWORD - //t: API_TOKEN, // Your token - //s: "salt", // Optional if you're using salted tokens - }).toString(); + // If we're requesting an image (cover art), we don't need f: "json" + if (endpoint === "getCoverArt") { + // Add the parameters for the cover art request, but exclude f: "json" + url.search = new URLSearchParams({ + ...params, + v: API_VERSION, + c: API_CLIENT, + u: "drome", // Replace with your username + p: API_PASSWORD, + // Optionally you can add other params like size if needed + }).toString(); + } else { + // For JSON requests, we add f: "json" + url.search = new URLSearchParams({ + ...params, + v: API_VERSION, + c: API_CLIENT, + f: "json", // Always request JSON format + u: "drome", // Replace with your username + p: API_PASSWORD + }).toString(); + } + + // Make the fetch request const response = await fetch(url.toString()); if (!response.ok) { throw new Error(`API request failed: ${response.statusText}`); } - return response.json(); + + // If it's a request for JSON, return the parsed JSON + if (endpoint !== "getCoverArt") { + return response.json(); + } + + // If it's a request for cover art, return the raw image data as a Blob + return response.blob(); }