Write uploads to disk so ffmpeg can seek properly

This commit is contained in:
Alex Gleason 2025-03-01 20:59:14 -06:00
parent c2e6e10a3a
commit 5168ccd748
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
5 changed files with 28 additions and 18 deletions

View file

@ -64,9 +64,12 @@ export async function uploadFile(
} }
if (needsTranscode) { if (needsTranscode) {
const stream = transcodeVideo(file.stream(), { ffmpegPath }); const tmp = new URL('file://' + await Deno.makeTempFile());
await Deno.writeFile(tmp, file.stream());
const stream = transcodeVideo(tmp, { ffmpegPath });
const transcoded = await new Response(stream).bytes(); const transcoded = await new Response(stream).bytes();
file = new File([transcoded], file.name, { type: 'video/mp4' }); file = new File([transcoded], file.name, { type: 'video/mp4' });
await Deno.remove(tmp);
} }
} }
perf.mark('transcode-end'); perf.mark('transcode-end');
@ -108,7 +111,10 @@ export async function uploadFile(
const { width, height } = video; const { width, height } = video;
try { try {
const bytes = await extractVideoFrame(file.stream(), '00:00:01', { ffmpegPath }); const tmp = new URL('file://' + await Deno.makeTempFile());
await Deno.writeFile(tmp, file.stream());
const bytes = await extractVideoFrame(tmp, '00:00:01', { ffmpegPath });
await Deno.remove(tmp);
const [[, url]] = await uploader.upload(new File([bytes], 'thumb.jpg', { type: 'image/jpeg' }), { signal }); const [[, url]] = await uploader.upload(new File([bytes], 'thumb.jpg', { type: 'image/jpeg' }), { signal });
if (!image) { if (!image) {

View file

@ -92,7 +92,7 @@ export function analyzeFile(
opts?: { ffprobePath?: string | URL }, opts?: { ffprobePath?: string | URL },
): Promise<AnalyzeResult> { ): Promise<AnalyzeResult> {
const stream = ffprobe(input, { const stream = ffprobe(input, {
'v': 'error', 'loglevel': 'fatal',
'show_streams': '', 'show_streams': '',
'show_format': '', 'show_format': '',
'of': 'json', 'of': 'json',

View file

@ -1,7 +1,9 @@
import { ffmpeg } from './ffmpeg.ts'; import { ffmpeg } from './ffmpeg.ts';
const uri = new URL('./buckbunny.mp4', import.meta.url);
Deno.test('ffmpeg', async () => { Deno.test('ffmpeg', async () => {
await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url)); await using file = await Deno.open(uri);
const output = ffmpeg(file.readable, { const output = ffmpeg(file.readable, {
'c:v': 'libx264', 'c:v': 'libx264',
@ -12,11 +14,11 @@ Deno.test('ffmpeg', async () => {
}); });
await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true }); await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true });
await Deno.writeFile(new URL('./tmp/buckbunny-transcoded.mp4', import.meta.url), output); await Deno.writeFile(new URL('./tmp/transcoded-1.mp4', import.meta.url), output);
}); });
Deno.test('ffmpeg from file', async () => { Deno.test('ffmpeg from file URI', async () => {
const output = ffmpeg(new URL('./buckbunny.mp4', import.meta.url), { const output = ffmpeg(uri, {
'c:v': 'libx264', 'c:v': 'libx264',
'preset': 'veryfast', 'preset': 'veryfast',
'loglevel': 'fatal', 'loglevel': 'fatal',
@ -25,5 +27,5 @@ Deno.test('ffmpeg from file', async () => {
}); });
await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true }); await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true });
await Deno.writeFile(new URL('./tmp/buckbunny-transcoded-fromfile.mp4', import.meta.url), output); await Deno.writeFile(new URL('./tmp/transcoded-2.mp4', import.meta.url), output);
}); });

View file

@ -1,9 +1,11 @@
import { assertEquals } from '@std/assert'; import { assertObjectMatch } from '@std/assert';
import { ffprobe } from './ffprobe.ts'; import { ffprobe } from './ffprobe.ts';
Deno.test('ffprobe', async () => { const uri = new URL('./buckbunny.mp4', import.meta.url);
await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url));
Deno.test('ffprobe from ReadableStream', async () => {
await using file = await Deno.open(uri);
const stream = ffprobe(file.readable, { const stream = ffprobe(file.readable, {
'v': 'error', 'v': 'error',
@ -14,12 +16,10 @@ Deno.test('ffprobe', async () => {
const { streams: [dimensions] } = await new Response(stream).json(); const { streams: [dimensions] } = await new Response(stream).json();
assertEquals(dimensions, { width: 1920, height: 1080 }); assertObjectMatch(dimensions, { width: 1920, height: 1080 });
}); });
Deno.test('ffprobe from file', async () => { Deno.test('ffprobe from file URI', async () => {
const uri = new URL('./buckbunny.mp4', import.meta.url);
const stream = ffprobe(uri, { const stream = ffprobe(uri, {
'v': 'error', 'v': 'error',
'select_streams': 'v:0', 'select_streams': 'v:0',
@ -29,5 +29,5 @@ Deno.test('ffprobe from file', async () => {
const { streams: [dimensions] } = await new Response(stream).json(); const { streams: [dimensions] } = await new Response(stream).json();
assertEquals(dimensions, { width: 1920, height: 1080 }); assertObjectMatch(dimensions, { width: 1920, height: 1080 });
}); });

View file

@ -1,10 +1,12 @@
import { extractVideoFrame } from './frame.ts'; import { extractVideoFrame } from './frame.ts';
const uri = new URL('./buckbunny.mp4', import.meta.url);
Deno.test('extractVideoFrame', async () => { Deno.test('extractVideoFrame', async () => {
await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url)); await using file = await Deno.open(uri);
const result = await extractVideoFrame(file.readable); const result = await extractVideoFrame(file.readable);
await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true }); await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true });
await Deno.writeFile(new URL('./tmp/buckbunny-poster.jpg', import.meta.url), result); await Deno.writeFile(new URL('./tmp/poster.jpg', import.meta.url), result);
}); });