diff --git a/website/app/api/playlist/[playlistId]/thumbnail/route.ts b/website/app/api/playlist/[playlistId]/thumbnail/route.ts new file mode 100644 index 0000000..0601df0 --- /dev/null +++ b/website/app/api/playlist/[playlistId]/thumbnail/route.ts @@ -0,0 +1,40 @@ +import { getDB } from "@/util/db"; +import { NextResponse } from "next/server"; + +// GET /api/playlist/[playlistId]/thumbnail +// get a playlist's thumbnail + +export async function GET(request: Request, { params }: { params: { playlistId: string } }) { + const playlistId = params.playlistId; + + // check if valid id + if (isNaN(+playlistId)) { + return NextResponse.json({ error: "invalid playlist id" }, { status: 400 }); + } + + const conn = await getDB(); + + let tracks: any; + try { + tracks = await conn.query(` + SELECT track.id FROM playlist + INNER JOIN playlist_tracks ON playlist_tracks.playlist_id = $1 + INNER JOIN track ON playlist_tracks.track_id = track.id + LIMIT 1`, + [playlistId]); + + if (tracks.rowCount < 1) { + // if there are no songs, just use a default image + return await fetch(`https://github.com/google/material-design-icons/blob/master/png/av/library_music/materialiconsoutlined/48dp/2x/outline_library_music_black_48dp.png?raw=true`); + } + } catch (e) { + await conn.end(); + return NextResponse.json({ error: "invalid playlist id" }, { status: 400 }); + } + + await conn.end(); + + const trackId = tracks.rows[0].id; + + return await fetch(`http://${process.env.FILESTORE_HOST}:${process.env.FILESTORE_PORT}/track-thumbnail/${trackId}`); +} \ No newline at end of file diff --git a/website/app/api/playlist/[playlistId]/tracks/route.ts b/website/app/api/playlist/[playlistId]/tracks/route.ts index 7b17561..3851f4d 100644 --- a/website/app/api/playlist/[playlistId]/tracks/route.ts +++ b/website/app/api/playlist/[playlistId]/tracks/route.ts @@ -8,7 +8,7 @@ import { getAPITrack } from "@/util/models/track"; import { NextResponse } from "next/server"; // GET /api/playlist/[playlistId]/tracks -// get an playlist's track list +// get a playlist's track list export async function GET(request: Request, { params }: { params: { albumId: string } }) { const albumId = params.albumId; diff --git a/website/app/collection/playlists/[playlistId]/page.tsx b/website/app/collection/playlists/[playlistId]/page.tsx index 75064cc..3412fe9 100644 --- a/website/app/collection/playlists/[playlistId]/page.tsx +++ b/website/app/collection/playlists/[playlistId]/page.tsx @@ -31,13 +31,9 @@ export default function CollectionPlaylistPage({params} : {params: {playlistId: const playlistId = params.playlistId; - const [playlists, setPlaylists] = useState([]); const [playlist, setPlaylist] = useState(); const [tracks, setTracks] = useState([] as APITrack[]); const [alerts, setAlerts] = useState([] as AlertEntry[]); - const [showCreateModal, setShowCreateModal] = useState(false); - const [newPlaylistName, setNewPlaylistName] = useState(''); - const [newPlaylistDescription, setNewPlaylistDescription] = useState(''); useEffect(() => { // wait for credentials to be loaded @@ -82,29 +78,6 @@ export default function CollectionPlaylistPage({params} : {params: {playlistId: const totalTime = tracks.reduce((acc, track) => acc + track.audio_length, 0); - const handleCreatePlaylist = () => { - setShowCreateModal(true); - }; - - const handleCloseModal = () => { - setShowCreateModal(false); - setNewPlaylistName(''); - setNewPlaylistDescription(''); - }; - - const handleCreateButtonClick = async () => { - try { - const data = { name: newPlaylistName, description: newPlaylistDescription }; - const response = await apiPostCreatePlaylist(playlistId, data); - const newPlaylist = response.data; - setPlaylists([...playlists, newPlaylist]); - handleCloseModal(); - } catch (error) { - console.error('Error creating playlist:', error); - // Handle error and show error message - } - }; - return (
@@ -122,37 +95,10 @@ export default function CollectionPlaylistPage({params} : {params: {playlistId: {tracks.length} songs • Total duration: {formatDuration(totalTime)}
- - - {/* Create Playlist Modal */} - -
- Create New Playlist - setNewPlaylistName(e.target.value)} - fullWidth - margin="normal" - /> - setNewPlaylistDescription(e.target.value)} - fullWidth - margin="normal" - /> - -
-
); } diff --git a/website/app/collection/playlists/page.tsx b/website/app/collection/playlists/page.tsx index 3da6ee7..ddcfb93 100644 --- a/website/app/collection/playlists/page.tsx +++ b/website/app/collection/playlists/page.tsx @@ -1,12 +1,12 @@ 'use client' import { useEffect, useState } from "react"; -import { apiGetCollectionPlaylists } from "@/components/apiclient"; +import { apiGetCollectionPlaylists, apiPostCreatePlaylist } from "@/components/apiclient"; import { useLoginStateContext } from "@/components/loginstateprovider"; import { useRouter } from "next/navigation"; import { APIPlaylist } from "@/util/models/playlist"; import AlertComponent, { AlertEntry } from "@/components/alerts"; -import { Typography, Grid } from "@mui/material"; +import { Typography, Grid, Modal, TextField, Button } from "@mui/material"; import PlaylistCard from "@/components/playlistCard"; export default function CollectionPlaylistsPage() { @@ -16,6 +16,33 @@ export default function CollectionPlaylistsPage() { const [playlists, setPlaylists] = useState([] as APIPlaylist[]); const [alerts, setAlerts] = useState([] as AlertEntry[]); + const [showCreateModal, setShowCreateModal] = useState(false); + const [newPlaylistName, setNewPlaylistName] = useState(''); + const [newPlaylistDescription, setNewPlaylistDescription] = useState(''); + + const handleCreatePlaylist = () => { + setShowCreateModal(true); + }; + + const handleCloseModal = () => { + setShowCreateModal(false); + setNewPlaylistName(''); + setNewPlaylistDescription(''); + }; + + const handleCreateButtonClick = async () => { + try { + const data = { name: newPlaylistName, description: newPlaylistDescription }; + const response = await apiPostCreatePlaylist(loginState.loggedInUserUuid, data); + const newPlaylist = response.data; + setPlaylists([...playlists, newPlaylist]); + handleCloseModal(); + } catch (error) { + console.error('Error creating playlist:', error); + // Handle error and show error message + } + }; + useEffect(() => { // wait for credentials to be loaded if (!loginState.loggedInStateValid) { @@ -46,9 +73,37 @@ export default function CollectionPlaylistsPage() { return ( - + + {/* Create Playlist Modal */} + +
+ Create New Playlist + setNewPlaylistName(e.target.value)} + fullWidth + margin="normal" + /> + setNewPlaylistDescription(e.target.value)} + fullWidth + margin="normal" + /> + +
+
+ Playlists + + {playlists.map((playlist) => { diff --git a/website/components/apiclient.tsx b/website/components/apiclient.tsx index 61d3f6e..3d661db 100644 --- a/website/components/apiclient.tsx +++ b/website/components/apiclient.tsx @@ -123,8 +123,8 @@ export function apiGetPlaylistTracks(playlistId: string) { }); } -export function apiPostCreatePlaylist(playlistId: string, data: { name: string, description: string }) { - return axios.post(`/api/collection/${playlistId}/playlists`, data); +export function apiPostCreatePlaylist(accountUuid: string, data: { name: string, description: string }) { + return axios.post(`/api/collection/${accountUuid}/playlists`, data); } export function apiGetCollectionGenres(accountUuid: string) {