Files
shelfless/src/components/detail/FilesTab.tsx
Scarriffle 83d8b7b99d Initial commit: Shelfless – alternative Audiobookshelf frontend
React + Vite + TypeScript SPA covering the full ABS feature set (library
browsing, item detail, metadata/cover editing, podcasts, player with session
sync, admin: users/libraries/scanner/server settings). Dev uses a dynamic
CORS proxy; production is served by server/index.mjs (static + reverse proxy
to ABS_URL). Includes systemd unit and installer under deploy/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 20:23:04 +02:00

41 lines
1.6 KiB
TypeScript

import { FileAudio } from 'lucide-react'
import { formatBytes, formatDuration } from '@/lib/format'
import type { AudioFile, Book, LibraryItem, Podcast } from '@/types/abs'
function collectAudioFiles(item: LibraryItem): AudioFile[] {
if (item.mediaType === 'book') return (item.media as Book).audioFiles ?? []
const eps = (item.media as Podcast).episodes ?? []
return eps.map((e) => e.audioFile).filter((f): f is AudioFile => !!f)
}
export function FilesTab({ item }: { item: LibraryItem }) {
const files = collectAudioFiles(item)
if (files.length === 0) {
return <p className="text-sm text-text-muted">Keine Audiodateien vorhanden.</p>
}
return (
<div className="overflow-hidden rounded-lg border border-border">
{files.map((f, i) => (
<div
key={`${f.ino}-${i}`}
className="flex items-center gap-3 border-b border-border px-3 py-2.5 last:border-b-0"
>
<FileAudio size={18} className="shrink-0 text-text-muted" />
<div className="min-w-0 flex-1">
<p className="truncate text-sm text-text">{f.metadata.filename}</p>
<p className="text-xs text-text-muted">
{f.codec?.toUpperCase()} {f.bitRate ? `· ${Math.round(f.bitRate / 1000)} kbps` : ''}
</p>
</div>
<span className="tnum shrink-0 text-xs text-text-muted">{formatDuration(f.duration)}</span>
<span className="tnum hidden shrink-0 text-xs text-text-muted sm:block">
{formatBytes(f.metadata.size)}
</span>
</div>
))}
</div>
)
}