91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
export type SearchItem = {
|
|
id: string;
|
|
media_type: string;
|
|
title: string;
|
|
artist: string;
|
|
cover?: string | null;
|
|
preview_url?: string | null;
|
|
desc?: string;
|
|
};
|
|
|
|
export default function DownloadDialog({ item, onClose }: { item: SearchItem; onClose: () => void }) {
|
|
const [quality, setQuality] = useState(2);
|
|
const [codec, setCodec] = useState("");
|
|
const [sending, setSending] = useState(false);
|
|
const [msg, setMsg] = useState("");
|
|
|
|
useEffect(() => {
|
|
fetch("/api/config/ui")
|
|
.then((r) => r.json())
|
|
.then((b) => {
|
|
if (typeof b?.deezer?.quality === "number") setQuality(b.deezer.quality);
|
|
if (b?.conversion?.enabled && typeof b.conversion.codec === "string") setCodec(b.conversion.codec);
|
|
})
|
|
.catch(() => {});
|
|
}, []);
|
|
|
|
async function start() {
|
|
setSending(true);
|
|
setMsg("");
|
|
try {
|
|
const r = await fetch("/api/downloads", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
source: "deezer",
|
|
media_type: item.media_type,
|
|
id: item.id,
|
|
quality,
|
|
codec,
|
|
}),
|
|
});
|
|
if (!r.ok) throw new Error(`Start fehlgeschlagen (${r.status}).`);
|
|
const body = await r.json();
|
|
setMsg(`Angelegt: ${body.job_id} — siehe Queue.`);
|
|
} catch (e: unknown) {
|
|
setMsg(e instanceof Error ? e.message : "Fehler.");
|
|
} finally {
|
|
setSending(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div
|
|
style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.65)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 100 }}
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="card"
|
|
style={{ minWidth: 320, maxWidth: 420, width: "90%", padding: 20, background: "var(--surface-2)" }}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<h3 style={{ marginTop: 0 }}>{item.title}</h3>
|
|
<p className="muted" style={{ marginTop: 0 }}>{item.artist}</p>
|
|
<label>
|
|
Qualität (0=MP3 128, 1=MP3 320, 2=FLAC)
|
|
<select value={quality} onChange={(e) => setQuality(Number(e.target.value))} className="select" style={{ margin: "6px 0 12px" }}>
|
|
<option value={0}>0 — MP3 128</option>
|
|
<option value={1}>1 — MP3 320</option>
|
|
<option value={2}>2 — FLAC</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Codec (optional, z. B. MP3/FLAC/OGG)
|
|
<input value={codec} onChange={(e) => setCodec(e.target.value)} placeholder="Leer = kein Convert" className="input" style={{ margin: "6px 0 12px" }} />
|
|
</label>
|
|
<div style={{ display: "flex", gap: 8 }}>
|
|
<button onClick={start} disabled={sending} className="btn btn-primary" style={{ flex: 1 }}>
|
|
{sending ? "…" : "Download starten"}
|
|
</button>
|
|
<button onClick={onClose} className="btn">Schließen</button>
|
|
</div>
|
|
{msg && <p>{msg}</p>}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|