From 505cd84813a80e0a69b2cc95aa93c68cf2856cb2 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 30 Sep 2026 10:10:10 -0700 Subject: Videos are shrunk on the phone before upload; nostr.build is the first media host. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No public Blossom host transcodes (nostr.build's and primal's BUD-05 /media return the same bitrate), so a 15-20 Mbps phone clip stalled on playback. src/video-compress.js re-encodes on the device with WebCodecs via Mediabunny (a lazily loaded chunk, left out of the SW precache and the standalone file): H.264, short edge 720, ~2.5 Mbps, moov first, audio passed through, rotation baked in. Anything short of a clear win uploads the original. A toast counts the shrinking down. Media hosts: blossom.nostr.build first, nostr.download second (it was unreachable for hours); a HEAD probe skips a host that doesn't answer within 5 s instead of waiting out the whole upload timeout. video-compress-test: 1080p 20 Mbps 5 s (landscape and rotated portrait) → 1.6 MB H.264 720p 2.49 Mbps with audio, moov first, in 2-3 s. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp --- build.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'build.js') diff --git a/build.js b/build.js index c90e527..2bc5361 100644 --- a/build.js +++ b/build.js @@ -332,7 +332,13 @@ async function bundleApp({ minify, features, staging, splitting = false }) { // The split build emits real ESM chunks (deferred features arrive as their // own files); the single-file build inlines the dynamic imports instead. ...(splitting ? { splitting: true, format: 'esm', naming: { chunk: 'chunk-[hash].[ext]' } } : {}), - plugins: [featurePlugin(enabledFeatures(features)), flagsPlugin(staging)], + plugins: [featurePlugin(enabledFeatures(features)), flagsPlugin(staging), + // the single file inlines every dynamic import: the video compressor + // (Mediabunny, ~700 KB) would ride along in a file meant to be small + // and offline — there it uploads the original, as before + ...(splitting ? [] : [{ name: 'no-video-compress', setup(build) { + build.onLoad({ filter: /src\/video-compress\.js$/ }, () => ({ loader: 'js', contents: 'export async function compressVideo(f) { return f; }' })); + } }])], }); if (!result.success) { for (const log of result.logs) console.error(log); @@ -470,7 +476,10 @@ if (import.meta.main) { const nwcSw = feats.includes('nwc') && feats.includes('ark'); const swBody = SW .replaceAll('{{VERSION}}', version) - .replaceAll('{{EXTRA_SHELL}}', chunks.map((c) => `, '${c.name}'`).join('')) + // precached for offline use — except what only works online anyway: + // the video compressor (Mediabunny, ~700 KB) loads when a video is + // picked for upload, not on every install + .replaceAll('{{EXTRA_SHELL}}', chunks.filter((c) => !c.text.includes('Mediabunny Codec Registry')).map((c) => `, '${c.name}'`).join('')) .replaceAll('{{NWC_WAKE_FALLBACK}}', nwcSw ? '' : SW_WAKE_ONLY); await Bun.write('dist/sw.js', nwcSw ? swBody + '\n' + await buildSwNwc() : swBody); for (const f of STATIC) await Bun.write('dist/' + f, Bun.file('static/' + f)); -- cgit v1.2.3