aboutsummaryrefslogtreecommitdiffstats
path: root/src/media-upload.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-30 10:10:10 -0700
committerAdam Soltys <[email protected]>2026-09-30 10:10:10 -0700
commit505cd84813a80e0a69b2cc95aa93c68cf2856cb2 (patch)
tree3453513920b23476e01efe508e73ba8651f438da /src/media-upload.js
parent93c07bec934e8e8d847349b7792f4cc9089adbb7 (diff)
downloadcoinosv3-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.js17
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),