import { spawn } from 'child_process' import { existsSync, readdirSync, statSync } from 'fs' import path from 'path' import { prisma } from '@/lib/prisma' import { buildYtdlpArgs } from '@/lib/ytdlp' import { createToken } from '@/lib/token' import { config } from '../config/app.config' function resolveYtdlpBin(): string { const local = path.join(config.BIN_DIR, 'yt-dlp') return existsSync(local) ? local : 'yt-dlp' } export async function processDownload(downloadId: string): Promise { const download = await prisma.download.findUnique({ where: { id: downloadId } }) if (!download) return const args = buildYtdlpArgs({ url: download.url, uuid: download.uuid, format: download.format, quality: download.quality, subtitles: download.subtitles, extraArgs: download.extraArgs, }) let stderr = '' await new Promise((resolve) => { const proc = spawn(resolveYtdlpBin(), args) proc.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString() }) proc.on('close', async (code) => { if (code === 0) { const files = readdirSync(config.STORAGE_PATH).filter((f) => f.startsWith(download.uuid) ) const relFile = files[0] ?? null const filePath = relFile ? path.join(config.STORAGE_PATH, relFile) : null const fileSize = filePath ? BigInt(statSync(filePath).size) : null await prisma.download.update({ where: { id: downloadId }, data: { status: 'DONE', filePath, fileName: relFile, fileSize, completedAt: new Date(), }, }) await createToken(downloadId) } else { await prisma.download.update({ where: { id: downloadId }, data: { status: 'FAILED', errorMsg: stderr || `yt-dlp exited with code ${code}`, completedAt: new Date(), }, }) } resolve() }) }) }