dl.emersa.it/src/routes/download/+server.ts
2025-02-07 20:46:33 +01:00

91 lines
2.7 KiB
TypeScript

import { DOWNLOAD_PATH } from '$env/static/private';
import { redirect } from '@sveltejs/kit';
import ytdl from 'youtube-dl-exec';
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { spawn } from 'child_process';
/**
* Fetch YouTube metadata (title, uploader/artist)
*/
async function getYouTubeMetadata(link: string) {
return await ytdl(link, {
dumpSingleJson: true,
noCheckCertificates: true,
noWarnings: true,
preferFreeFormats: true
});
}
/**
* Streams the YouTube video/audio using youtube-dl-exec
*/
function streamYouTube(link: string, format: string): ReadableStream<Uint8Array> {
return new ReadableStream({
start(controller) {
const args = [
'-o',
'-',
format === 'mp3' ? '--embed-metadata' : '',
'--format',
format === 'mp3' ? 'bestaudio' : 'best',
'--audio-format',
format === 'mp3' ? 'mp3' : '',
'--no-playlist'
].filter(Boolean);
const process = spawn('yt-dlp', [...args, link], { stdio: ['ignore', 'pipe', 'ignore'] });
process.stdout.on('data', (chunk) => controller.enqueue(chunk));
process.stdout.on('end', () => controller.close());
process.stdout.on('error', (err) => {
console.error('Stream error:', err);
controller.error(err);
});
}
});
}
/**
* Sanitize filename by removing unsafe characters
*/
function sanitizeFilename(name: string): string {
return name.replace(/[\/:*?"<>|]/g, '').trim();
}
export const GET: RequestHandler = async ({ url }) => {
// Get query params
const link = url.searchParams.get('link');
const format = url.searchParams.get('format'); // mp3, mp4
const source = url.searchParams.get('source'); // youtube or spotify
// Validate input
if (!link || !format || !source) {
throw error(400, 'Missing required query parameters: link, format, or source');
}
if (source !== 'youtube') {
throw error(400, 'Currently, only YouTube is supported');
}
try {
// Fetch metadata for filename
const metadata = await getYouTubeMetadata(link);
const { title, uploader } = metadata;
const safeTitle = sanitizeFilename(`${uploader} - ${title}`);
const filename = `${safeTitle}.${format}`;
console.log(filename)
// Stream video/audio
return new Response(streamYouTube(link, format), {
headers: {
'Content-Type': format === 'mp3' ? 'audio/mpeg' : 'video/mp4',
'Content-Disposition': `attachment; filename="${filename}"`
}
});
} catch (err) {
console.error('Error fetching metadata:', err);
throw error(500, 'Failed to fetch video metadata');
}
};