feat: music-metadata

This commit is contained in:
2025-09-30 20:45:01 -04:00
parent b5d14a71d6
commit b990d721c2
5 changed files with 420 additions and 1 deletions

View 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>