mirror of
https://github.com/markuryy/shark.git
synced 2025-12-15 12:41:02 +00:00
feat: music-metadata
This commit is contained in:
147
src/routes/playlists/[name]/+page.svelte
Normal file
147
src/routes/playlists/[name]/+page.svelte
Normal file
@@ -0,0 +1,147 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { settings, loadSettings } from '$lib/stores/settings';
|
||||
import { scanPlaylists } from '$lib/library/scanner';
|
||||
import { loadPlaylistTracks, type PlaylistWithTracks } from '$lib/library/playlist';
|
||||
import type { Track } from '$lib/types/track';
|
||||
|
||||
let playlistData = $state<PlaylistWithTracks | null>(null);
|
||||
let loading = $state(true);
|
||||
let error = $state<string | null>(null);
|
||||
|
||||
let playlistName = $derived(decodeURIComponent($page.params.name));
|
||||
|
||||
onMount(async () => {
|
||||
await loadSettings();
|
||||
await loadPlaylist();
|
||||
});
|
||||
|
||||
async function loadPlaylist() {
|
||||
loading = true;
|
||||
error = null;
|
||||
|
||||
if (!$settings.playlistsFolder || !$settings.musicFolder) {
|
||||
error = 'Playlists or music folder not configured. Please set them in Settings.';
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Find the playlist file
|
||||
const playlists = await scanPlaylists($settings.playlistsFolder);
|
||||
const playlist = playlists.find(p => p.name === playlistName);
|
||||
|
||||
if (!playlist) {
|
||||
error = `Playlist "${playlistName}" not found.`;
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load tracks from the playlist file
|
||||
playlistData = await loadPlaylistTracks(
|
||||
playlist.path,
|
||||
playlist.name,
|
||||
$settings.musicFolder
|
||||
);
|
||||
|
||||
loading = false;
|
||||
} catch (e) {
|
||||
error = 'Error loading playlist: ' + (e as Error).message;
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h2>{playlistName}</h2>
|
||||
|
||||
{#if loading}
|
||||
<p>Loading playlist...</p>
|
||||
{:else if error}
|
||||
<p class="error">{error}</p>
|
||||
{:else if playlistData && playlistData.tracks.length === 0}
|
||||
<p>No tracks in this playlist.</p>
|
||||
{:else if playlistData}
|
||||
<section class="playlist-content">
|
||||
<p class="track-count">{playlistData.tracks.length} track{playlistData.tracks.length !== 1 ? 's' : ''}</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;">#</th>
|
||||
<th>Title</th>
|
||||
<th>Artist</th>
|
||||
<th>Album</th>
|
||||
<th>Duration</th>
|
||||
<th>Format</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each playlistData.tracks as track, index}
|
||||
<tr>
|
||||
<td class="track-number">{index + 1}</td>
|
||||
<td>{track.metadata.title || track.filename}</td>
|
||||
<td>{track.metadata.artist || '—'}</td>
|
||||
<td class="album">{track.metadata.album || '—'}</td>
|
||||
<td class="duration">
|
||||
{#if track.metadata.duration}
|
||||
{Math.floor(track.metadata.duration / 60)}:{String(Math.floor(track.metadata.duration % 60)).padStart(2, '0')}
|
||||
{:else}
|
||||
—
|
||||
{/if}
|
||||
</td>
|
||||
<td class="format">{track.format.toUpperCase()}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.playlist-content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.track-count {
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.7;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.track-number {
|
||||
text-align: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.album {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.duration {
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.format {
|
||||
font-family: monospace;
|
||||
font-size: 0.85em;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user