convert flacs to aac for streaming
This commit is contained in:
100
src/app/admin/music/_components/ConvertToStreamButton.tsx
Normal file
100
src/app/admin/music/_components/ConvertToStreamButton.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from "react";
|
||||
import { AudioLines, Loader2, RefreshCw } from "lucide-react";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { trpc } from "~/app/_trpc/Client";
|
||||
import { useUploadThing } from "~/lib/uploadthing";
|
||||
import { transcodeToAac } from "~/lib/ffmpeg/transcode";
|
||||
import { toast } from "sonner";
|
||||
import type { RouterOutputs } from "~/server/routers/_app";
|
||||
import type { IterableElement } from "type-fest";
|
||||
|
||||
export default function ConvertToStreamButton(props: {
|
||||
track: IterableElement<RouterOutputs['music']['list']>;
|
||||
}) {
|
||||
const { track } = props;
|
||||
const utils = trpc.useUtils();
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [stage, setStage] = useState("");
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
const setStream = trpc.music.setStream.useMutation({
|
||||
onSuccess: () => utils.music.list.invalidate(),
|
||||
});
|
||||
|
||||
const { startUpload } = useUploadThing("musicUploader");
|
||||
|
||||
async function handleConvert() {
|
||||
setBusy(true);
|
||||
setProgress(0);
|
||||
let currentStage = "Loading ffmpeg";
|
||||
const goto = (s: string) => {
|
||||
currentStage = s;
|
||||
setStage(s);
|
||||
};
|
||||
try {
|
||||
goto("Transcoding");
|
||||
const file = await transcodeToAac({
|
||||
sourceUrl: track.fileUrl,
|
||||
outputName: `${track.title || "track"}.m4a`,
|
||||
onProgress: setProgress,
|
||||
});
|
||||
|
||||
goto("Uploading");
|
||||
const uploaded = await startUpload([file]);
|
||||
const res = uploaded?.[0];
|
||||
if (!res) throw new Error("Upload returned no file");
|
||||
|
||||
goto("Saving");
|
||||
await setStream.mutateAsync({
|
||||
id: track.id,
|
||||
streamUrl: res.serverData.fileUrl,
|
||||
streamKey: res.serverData.fileKey,
|
||||
streamName: res.serverData.fileName,
|
||||
});
|
||||
toast("Streaming version saved");
|
||||
} catch (e) {
|
||||
console.error("[ConvertToStream] failed during", currentStage, e);
|
||||
const detail =
|
||||
e instanceof Error
|
||||
? e.message
|
||||
: typeof e === "string"
|
||||
? e
|
||||
: (() => {
|
||||
try {
|
||||
return JSON.stringify(e);
|
||||
} catch {
|
||||
return String(e);
|
||||
}
|
||||
})();
|
||||
toast(`Conversion failed (${currentStage}): ${detail || "see console for details"}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
setStage("");
|
||||
setProgress(0);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button type="button" variant="outline" size="sm" disabled={busy} onClick={handleConvert}>
|
||||
{busy ? (
|
||||
<Loader2 className="animate-spin" />
|
||||
) : track.streamUrl ? (
|
||||
<RefreshCw />
|
||||
) : (
|
||||
<AudioLines />
|
||||
)}
|
||||
{busy
|
||||
? `${stage}${stage === "Transcoding" && progress ? ` ${Math.round(progress * 100)}%` : "…"}`
|
||||
: track.streamUrl
|
||||
? "Re-generate stream"
|
||||
: "Generate stream (AAC)"}
|
||||
</Button>
|
||||
{track.streamUrl && !busy && (
|
||||
<span className="text-xs text-muted-foreground">Streaming version ready</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user