Download lib/ffmpeg.ts from d3evil4/scrape: direct link, hf CLI and curl.
- Browser
- Download file 2.5 kB
-
https://huggingface.co/spaces/d3evil4/scrape/resolve/main/lib/ffmpeg.ts
- Command line
-
hf download hf://spaces/d3evil4/scrape/lib/ffmpeg.ts
-
curl -L -o ffmpeg.ts https://huggingface.co/spaces/d3evil4/scrape/resolve/main/lib/ffmpeg.ts
2.5 kB
| import { execFile } from "child_process"; | |
| import { promisify } from "util"; | |
| import ffmpegStatic from "ffmpeg-static"; | |
| import ffprobeStatic from "ffprobe-static"; | |
| import type { VideoMeta } from "@/types"; | |
| const execFileAsync = promisify(execFile); | |
| export function getFfmpegPath(): string { | |
| if (!ffmpegStatic) throw new Error("ffmpeg-static not available"); | |
| return ffmpegStatic; | |
| } | |
| export function getFfprobePath(): string { | |
| return ffprobeStatic.path; | |
| } | |
| function parseFps(rate: string): number { | |
| if (!rate || rate === "0/0") return 30; | |
| const [num, den] = rate.split("/").map(Number); | |
| if (!den) return 30; | |
| return Math.round((num / den) * 100) / 100; | |
| } | |
| export async function extractVideoMeta(videoFile: string): Promise<VideoMeta> { | |
| const ffprobe = getFfprobePath(); | |
| const { stdout } = await execFileAsync(ffprobe, [ | |
| "-v", "error", | |
| "-select_streams", "v:0", | |
| "-show_entries", "stream=width,height,r_frame_rate,duration", | |
| "-show_entries", "format=duration", | |
| "-of", "json", | |
| videoFile, | |
| ]); | |
| const data = JSON.parse(stdout); | |
| const stream = data.streams?.[0] ?? {}; | |
| const formatDuration = Number(data.format?.duration ?? 0); | |
| const streamDuration = Number(stream.duration ?? 0); | |
| const duration = streamDuration || formatDuration || 0; | |
| return { | |
| width: Number(stream.width ?? 1920), | |
| height: Number(stream.height ?? 1080), | |
| duration, | |
| fps: parseFps(stream.r_frame_rate ?? "30/1"), | |
| }; | |
| } | |
| export async function runFfmpeg(args: string[]): Promise<void> { | |
| await execFileAsync(getFfmpegPath(), args); | |
| } | |
| export async function extractFrames( | |
| inputVideo: string, | |
| outputPattern: string, | |
| fps: number, | |
| width: number, | |
| ): Promise<void> { | |
| await runFfmpeg([ | |
| "-i", inputVideo, | |
| "-vf", `fps=${fps},scale=${width}:-1:flags=lanczos`, | |
| "-q:v", "2", | |
| "-y", | |
| outputPattern, | |
| ]); | |
| } | |
| export async function encodeVideo( | |
| inputVideo: string, | |
| framePattern: string, | |
| outputVideo: string, | |
| fps: number, | |
| audioFile?: string, | |
| ): Promise<void> { | |
| const args = [ | |
| "-framerate", String(fps), | |
| "-i", framePattern, | |
| ]; | |
| if (audioFile) { | |
| args.push("-i", audioFile, "-map", "0:v", "-map", "1:a"); | |
| } else { | |
| args.push("-i", inputVideo, "-map", "0:v", "-map", "1:a?"); | |
| } | |
| args.push( | |
| "-c:v", "libx264", | |
| "-preset", "fast", | |
| "-crf", "23", | |
| "-pix_fmt", "yuv420p", | |
| "-c:a", "aac", | |
| "-b:a", "128k", | |
| "-movflags", "+faststart", | |
| "-shortest", | |
| "-y", | |
| outputVideo, | |
| ); | |
| await runFfmpeg(args); | |
| } | |