Add audio format option to download form and update API for audio downloads
This commit is contained in:
@@ -38,6 +38,7 @@ const streamOnly = import.meta.env.STREAM_ONLY === "true";
|
||||
<option value="mp4" selected
|
||||
>{t(Astro, "downloadForm.formatMp4")}</option
|
||||
>
|
||||
<option value="audio">{t(Astro, "downloadForm.formatAudio")}</option>
|
||||
<option value="best">{t(Astro, "downloadForm.formatBest")}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"urlPlaceholder": "https://www.youtube.com/watch?v=...",
|
||||
"formatLabel": "Format",
|
||||
"formatMp4": "MP4 (empfohlen - beste Kompatibilität)",
|
||||
"formatAudio": "Nur Audio (MP3/M4A)",
|
||||
"formatBest": "Bestes verfügbares Format",
|
||||
"downloadInProgress": "Download läuft...",
|
||||
"downloadSuccessful": "Download erfolgreich!",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"urlPlaceholder": "https://www.youtube.com/watch?v=...",
|
||||
"formatLabel": "Format",
|
||||
"formatMp4": "MP4 (recommended - best compatibility)",
|
||||
"formatAudio": "Audio only (MP3/M4A)",
|
||||
"formatBest": "Best available format",
|
||||
"downloadInProgress": "Download in progress...",
|
||||
"downloadSuccessful": "Download successful!",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { getSession, isLoginEnabled } from "../../lib/session";
|
||||
import { tApi } from "../../lib/i18n";
|
||||
import { mkdir, unlink, readFile } from "node:fs/promises";
|
||||
import { mkdir, unlink, readFile, readdir } from "node:fs/promises";
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -59,13 +59,24 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
// Zuerst Video-Informationen abrufen, um den Dateinamen zu erhalten
|
||||
const videoInfo = await ytDlpWrap.getVideoInfo(url);
|
||||
const title = videoInfo.title || "Video";
|
||||
const ext = format === "mp4" ? "mp4" : videoInfo.ext || "mp4";
|
||||
|
||||
// Dateiendung bestimmen
|
||||
let ext = "mp4";
|
||||
if (format === "audio") {
|
||||
ext = "mp3"; // yt-dlp wird zu MP3 konvertieren
|
||||
} else if (format === "mp4") {
|
||||
ext = "mp4";
|
||||
} else {
|
||||
ext = videoInfo.ext || "mp4";
|
||||
}
|
||||
const filename = `${title}.${ext}`;
|
||||
|
||||
// Format-String für yt-dlp
|
||||
const formatString =
|
||||
format === "mp4"
|
||||
? "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo[ext=mp4]+bestaudio/best[ext=mp4]/best"
|
||||
: format === "audio"
|
||||
? "bestaudio[ext=m4a]/bestaudio[ext=mp3]/bestaudio"
|
||||
: "best";
|
||||
|
||||
if (streamOnly) {
|
||||
@@ -88,6 +99,9 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
|
||||
if (format === "mp4") {
|
||||
execArgs.push("--merge-output-format", "mp4");
|
||||
} else if (format === "audio") {
|
||||
// Für Audio: Konvertiere zu MP3
|
||||
execArgs.push("--extract-audio", "--audio-format", "mp3");
|
||||
}
|
||||
|
||||
ytDlpWrap
|
||||
@@ -113,11 +127,17 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
});
|
||||
|
||||
// Stream als Response zurückgeben
|
||||
let contentType = "application/octet-stream";
|
||||
if (format === "mp4") {
|
||||
contentType = "video/mp4";
|
||||
} else if (format === "audio") {
|
||||
contentType = "audio/mpeg"; // MP3
|
||||
}
|
||||
|
||||
return new Response(fileBuffer, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type":
|
||||
format === "mp4" ? "video/mp4" : "application/octet-stream",
|
||||
"Content-Type": contentType,
|
||||
"Content-Disposition": `attachment; filename="${encodeURIComponent(
|
||||
filename
|
||||
)}"`,
|
||||
@@ -172,6 +192,9 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
|
||||
if (format === "mp4") {
|
||||
execArgs.push("--merge-output-format", "mp4");
|
||||
} else if (format === "audio") {
|
||||
// Für Audio: Konvertiere zu MP3
|
||||
execArgs.push("--extract-audio", "--audio-format", "mp3");
|
||||
}
|
||||
|
||||
ytDlpWrap
|
||||
@@ -197,10 +220,21 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
});
|
||||
|
||||
// Dateiname aus dem tatsächlichen Output-Pfad extrahieren
|
||||
const actualFilename = path.join(downloadDir, `${title}.${ext}`);
|
||||
// Für Audio: yt-dlp erstellt .mp3 Datei
|
||||
const actualExt = format === "audio" ? "mp3" : ext;
|
||||
const actualFilename = path.join(downloadDir, `${title}.${actualExt}`);
|
||||
let finalFilename = filename;
|
||||
if (existsSync(actualFilename)) {
|
||||
finalFilename = path.basename(actualFilename);
|
||||
} else if (format === "audio") {
|
||||
// Fallback: Suche nach .mp3 Datei mit möglicherweise anderem Namen
|
||||
const files = await readdir(downloadDir);
|
||||
const mp3File = files.find(
|
||||
(f: string) => f.endsWith(".mp3") && f.includes(title)
|
||||
);
|
||||
if (mp3File) {
|
||||
finalFilename = mp3File;
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(
|
||||
|
||||
Reference in New Issue
Block a user