From 652208aa5701c6d1b29028d0889caadc9bfcfe15 Mon Sep 17 00:00:00 2001 From: 0d0 <0d0acre@esiliati.org> Date: Tue, 25 Feb 2025 17:06:41 +0100 Subject: [PATCH] it's cool that we have format --- src/hooks.server.ts | 8 +++--- src/lib/common/supportedFormats.json | 10 +++---- src/lib/server/helpers.ts | 24 ++++++++--------- src/lib/server/ytdlp.ts | 31 ++++++++++----------- src/routes/+page.svelte | 40 ++++++++++++++++------------ src/routes/download/+server.ts | 16 ++++++----- 6 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/hooks.server.ts b/src/hooks.server.ts index d9757b1..adfbff4 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,7 +1,7 @@ -import { logger } from "$lib/server/helpers"; +import { logger } from '$lib/server/helpers'; export async function handle({ event, resolve }) { logger.info(`Received ${event.request.method} request: ${event.url}`); - - return await resolve(event);; -} \ No newline at end of file + + return await resolve(event); +} diff --git a/src/lib/common/supportedFormats.json b/src/lib/common/supportedFormats.json index 103cca1..cd304cb 100644 --- a/src/lib/common/supportedFormats.json +++ b/src/lib/common/supportedFormats.json @@ -1,6 +1,6 @@ { - "mp3": "audio/mpeg", - "opus": "audio/ogg", - "wav": "audio/wav", - "mp4": "video/mp4" -} \ No newline at end of file + "mp3": "audio/mpeg", + "opus": "audio/ogg", + "wav": "audio/wav", + "mp4": "video/mp4" +} diff --git a/src/lib/server/helpers.ts b/src/lib/server/helpers.ts index 7474981..1b86056 100644 --- a/src/lib/server/helpers.ts +++ b/src/lib/server/helpers.ts @@ -2,18 +2,18 @@ import formats from '$lib/common/supportedFormats.json'; import winston from 'winston'; export const logger = winston.createLogger({ - level: 'debug', - format: winston.format.json(), - transports: [new winston.transports.Console()], + level: 'debug', + format: winston.format.json(), + transports: [new winston.transports.Console()] }); -const formatMime = new Map(Object.entries(formats)) +const formatMime = new Map(Object.entries(formats)); export const isURLValid = (url: string) => { - try { - new URL(url) - } catch { - return false - } + try { + new URL(url); + } catch { + return false; + } - return true; -} -export const mimeTypeMap = formatMime; \ No newline at end of file + return true; +}; +export const mimeTypeMap = formatMime; diff --git a/src/lib/server/ytdlp.ts b/src/lib/server/ytdlp.ts index 94b79c9..0ff15fa 100644 --- a/src/lib/server/ytdlp.ts +++ b/src/lib/server/ytdlp.ts @@ -25,45 +25,46 @@ export async function getYouTubeMetadata(link: string) { */ export function streamYouTube(link: string, format: string): ReadableStream { logger.debug(`Starting to stream: ${link}`); - const mimeType: string | undefined = mimeTypeMap.get(format) + const mimeType: string | undefined = mimeTypeMap.get(format); if (!mimeType) { - throw new Error("Unsupported format"); + throw new Error('Unsupported format'); } logger.debug(`Given format is compatible: ${mimeType}`); return new ReadableStream({ start(controller) { - const args = [ - '--no-write-thumbnail', - '-o', - '-', - ].filter(Boolean); + const args = ['--no-write-thumbnail', '-o', '-'].filter(Boolean); if (mimeType?.includes('audio')) { - args.push(...['--extract-audio', '--embed-metadata', '--embed-thumbnail', '--audio-format', format]) + args.push( + ...['--extract-audio', '--embed-metadata', '--embed-thumbnail', '--audio-format', format] + ); } else if (mimeType.includes('video')) { - args.push(...['--embed-metadata', '--embed-thumbnail', '--format', format]) + args.push(...['--embed-metadata', '--embed-thumbnail', '--format', format]); } - const cmd = `${YTDLP_PATH} ${args.join(' ')} ${link}` + const cmd = `${YTDLP_PATH} ${args.join(' ')} ${link}`; logger.debug(`Running: ${cmd}`); - const process = spawn(YTDLP_PATH, [...args, link], { cwd: "/tmp", stdio: ['ignore', 'pipe', 'pipe'] }); + const process = spawn(YTDLP_PATH, [...args, link], { + cwd: '/tmp', + stdio: ['ignore', 'pipe', 'pipe'] + }); process.stdout.on('data', (chunk) => { try { - controller.enqueue(chunk) + controller.enqueue(chunk); } catch (ex) { - process.kill() + process.kill(); } }); process.stderr.on('data', (chunk) => logger.debug(chunk.toString())); process.stdout.on('end', () => { try { - controller.close() + controller.close(); } catch (ex) { - logger.error(ex) + logger.error(ex); } }); process.stdout.on('error', (err) => { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e0bf85c..702625f 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -24,21 +24,21 @@ }; onMount(() => { - document.cookie = 'downloading=0' + document.cookie = 'downloading=0'; }); const readLogs = () => { logId = setInterval(() => { - logs += "We're downloading
" - }, 2000) - } + logs += "We're downloading
"; + }, 2000); + }; const onClick = () => { let checkIterations = 0; link = ''; downloading = true; - document.cookie = 'downloading=1' - - readLogs() + document.cookie = 'downloading=1'; + + readLogs(); const id = setInterval(() => { if (document.cookie.includes('downloading=0') || checkIterations > 3) { @@ -72,8 +72,7 @@ searchParams.append('link', link); searchParams.append('format', format); - if (metadata) - searchParams.append('metadata', "1"); + if (metadata) searchParams.append('metadata', '1'); href = `/download?${searchParams.toString()}`; }; @@ -88,12 +87,18 @@ } }); +
- -
+
{@html logs}
@@ -182,7 +187,9 @@
- + - Click here for the source code @@ -237,10 +244,10 @@ font-family: 'Press Start 2P', cursive; } - @media (max-height: 1000px) { + @media (max-height: 1000px) { * { font-size: 10px; - } + } } @media (min-width: 1024px) { * { @@ -248,14 +255,13 @@ } } - #loader { display: none; } #loader.downloading { display: grid; justify-items: center; - align-items: center; + align-items: center; } .not-available { text-decoration-line: line-through; diff --git a/src/routes/download/+server.ts b/src/routes/download/+server.ts index c2ffb42..30d94a9 100644 --- a/src/routes/download/+server.ts +++ b/src/routes/download/+server.ts @@ -30,9 +30,12 @@ const validateRequest = (url: URL) => { logger.debug(`Request is valid`); return { - link, format, source, metadata - } -} + link, + format, + source, + metadata + }; +}; export const GET: RequestHandler = async ({ url }) => { const { format, source, metadata, link } = validateRequest(url); let filename = `you-clicked-no-metadata-so-i-cant-put-a-correct-name.${format}`; @@ -46,7 +49,7 @@ export const GET: RequestHandler = async ({ url }) => { const safeTitle = `${uploader} - ${title}`; filename = `${safeTitle}.${format}`; } catch (err) { - logger.error(err) + logger.error(err); logger.error('Error fetching metadata:'); throw error(500, 'Failed to fetch video metadata'); } @@ -56,14 +59,13 @@ export const GET: RequestHandler = async ({ url }) => { // Stream video/audio return new Response(streamYouTube(link, format), { headers: { - 'Content-Type': "text/event-stream", + 'Content-Type': 'text/event-stream', 'Content-Disposition': `attachment; filename="${filename}"`, 'Set-Cookie': 'downloading=0' } }); - } catch (err) { - logger.error(err) + logger.error(err); logger.error('Filed to stream file'); throw error(500, 'Failed to stream file'); }