diff options
| author | Adam Soltys <[email protected]> | 2026-09-30 10:10:10 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-30 10:10:10 -0700 |
| commit | 505cd84813a80e0a69b2cc95aa93c68cf2856cb2 (patch) | |
| tree | 3453513920b23476e01efe508e73ba8651f438da /src/media-upload.js | |
| parent | 93c07bec934e8e8d847349b7792f4cc9089adbb7 (diff) | |
| download | coinosv3-505cd84813a80e0a69b2cc95aa93c68cf2856cb2.tar.gz coinosv3-505cd84813a80e0a69b2cc95aa93c68cf2856cb2.zip | |
Videos are shrunk on the phone before upload; nostr.build is the first media host.
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
Diffstat (limited to 'src/media-upload.js')
| -rw-r--r-- | src/media-upload.js | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/src/media-upload.js b/src/media-upload.js index 686fec5..8e5e8ed 100644 --- a/src/media-upload.js +++ b/src/media-upload.js @@ -2,10 +2,11 @@ import { sha256 } from '@noble/hashes/sha256'; import { bytesToHex } from '@noble/hashes/utils'; // Independent public hosts: a phone upload should not fail just because one -// provider is down or rejects a particular container. nostr.download's NIP-96 -// document currently advertises large image/video/audio uploads; nostr.build -// is the second, widely-used Blossom home. -export const PUBLIC_MEDIA_SERVERS = ['https://nostr.download', 'https://blossom.nostr.build']; +// provider is down or rejects a particular container. nostr.build is the +// widely-used Blossom home and goes first; nostr.download (which advertises +// large image/video/audio uploads) is the fallback — it was unreachable for +// hours on 2026-09-30, and every upload waited on it. +export const PUBLIC_MEDIA_SERVERS = ['https://blossom.nostr.build', 'https://nostr.download']; export class MediaUploadError extends Error { constructor(code, details = '') { @@ -59,7 +60,13 @@ export async function uploadPublicMedia(file, signEvent, { if (!auth) throw new MediaUploadError('failed'); const authorization = 'Nostr ' + btoa(JSON.stringify(auth)); const failures = []; - for (const server of servers) { + for (const [i, server] of servers.entries()) { + // A host that doesn't answer at all costs a few seconds, not the whole + // upload timeout. Any HTTP answer, even an error, means it's there. + if (i < servers.length - 1) { + try { await fetcher(server.replace(/\/$/, '') + '/upload', { method: 'HEAD', signal: AbortSignal.timeout(5000) }); } + catch (error) { failures.push({ status: 0, reason: 'unreachable' }); continue; } + } try { const response = await fetcher(server.replace(/\/$/, '') + '/upload', { method: 'PUT', body: file, signal: AbortSignal.timeout(timeoutMs), |
