aboutsummaryrefslogtreecommitdiffstats
path: root/tools/video-compress-test.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-30 10:23:20 -0700
committerAdam Soltys <[email protected]>2026-09-30 10:23:20 -0700
commit0076c591df9432e2bb5b2281afc1fb4106500297 (patch)
tree7b9ef49079158af7e6e2545f1e847c4c2baeb428 /tools/video-compress-test.js
parent505cd84813a80e0a69b2cc95aa93c68cf2856cb2 (diff)
downloadcoinosv3-0076c591df9432e2bb5b2281afc1fb4106500297.tar.gz
coinosv3-0076c591df9432e2bb5b2281afc1fb4106500297.zip
A picture or video on its way shows a bar with its percentage — shrinking, then uploading; the pencil reads "New post".
ui.mediaProgress { stage, pct } (a render per whole percent) drawn as a labelled bar under the reply and post composers, instead of only the paperclip turning into a spinner. Upload progress comes from XMLHttpRequest upload events (fetch() has none) in uploadPublicMedia's new onProgress; injected fetchers (tests) keep the fetch path. upload-progress-test: 1.2 MB at 400 KB/s climbs 0→100 in steps. video-compress-test: the bar counts the shrink, then the upload. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
Diffstat (limited to 'tools/video-compress-test.js')
-rw-r--r--tools/video-compress-test.js43
1 files changed, 34 insertions, 9 deletions
diff --git a/tools/video-compress-test.js b/tools/video-compress-test.js
index dc17036..ce1a936 100644
--- a/tools/video-compress-test.js
+++ b/tools/video-compress-test.js
@@ -34,16 +34,34 @@ const server = Bun.serve({ port: 5287, fetch: async (req) => {
} });
const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] });
const page = await browser.newPage();
+// the upload goes out by XMLHttpRequest (for its progress events): keep a
+// copy of the body, and answer at the network layer — nothing leaves
await page.evaluateOnNewDocument(() => {
- const f = window.fetch;
- window.fetch = async (url, init) => {
- if (init && init.method === 'PUT' && /nostr\.build|nostr\.download/.test(String(url))) {
- window.__put = { size: init.body.size, type: init.body.type, buf: await init.body.arrayBuffer() };
- return new Response(JSON.stringify({ url: 'https://blossom.nostr.build/' + 'f'.repeat(64) + '.mp4' }), { status: 201 });
+ const open = XMLHttpRequest.prototype.open, send = XMLHttpRequest.prototype.send;
+ XMLHttpRequest.prototype.open = function (m, url) { this.__m = m; this.__url = String(url); return open.apply(this, arguments); };
+ XMLHttpRequest.prototype.send = function (body) {
+ if (this.__m === 'PUT' && /nostr\.build|nostr\.download/.test(this.__url) && body instanceof Blob) {
+ body.arrayBuffer().then((buf) => { window.__put = { size: body.size, type: body.type, buf }; });
}
- if (init && init.method === 'HEAD' && /nostr\.build|nostr\.download/.test(String(url))) return new Response('', { status: 200 });
- return f(url, init);
+ return send.apply(this, arguments);
};
+ // every state the progress bar shows
+ window.__prog = [];
+ new MutationObserver(() => {
+ const m = document.querySelector('.media-progress');
+ const s = m ? m.innerText.replace(/\s+/g, ' ').trim() : null;
+ if (s && window.__prog.at(-1) !== s) window.__prog.push(s);
+ }).observe(document, { childList: true, subtree: true, characterData: true, attributes: true });
+});
+await page.setRequestInterception(true);
+page.on('request', (req) => {
+ const u = req.url();
+ if (!/nostr\.build|nostr\.download/.test(u)) return req.continue();
+ const cors = { 'access-control-allow-origin': '*', 'access-control-allow-methods': 'PUT, HEAD, GET', 'access-control-allow-headers': '*' };
+ if (req.method() === 'OPTIONS') return req.respond({ status: 204, headers: cors });
+ if (req.method() === 'HEAD') return req.respond({ status: 200, headers: cors });
+ return req.respond({ status: 201, headers: cors, contentType: 'application/json',
+ body: JSON.stringify({ url: 'https://blossom.nostr.build/' + 'f'.repeat(64) + '.mp4' }) });
});
const click = (t) => page.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x)); if (e) { e.click(); return true; } return false; }, t);
const waitText = async (x, ms = 25000) => { for (let i = 0; i < ms / 250; i++) { if ((await page.evaluate(() => document.body.innerText)).toLowerCase().includes(x)) return true; await sleep(250); } return false; };
@@ -72,9 +90,16 @@ try {
dt.items.add(new File([b], 'phone.mp4', { type: 'video/mp4' }));
document.querySelector('.post-input').dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }));
});
- const sawShrink = await waitText('shrinking the video', 15000);
- check('the toast says it is shrinking the video', sawShrink);
const done = await waitFor(() => !!window.__put, 120000);
+ await waitFor(() => !document.querySelector('.media-progress'), 10000);
+ const prog = await page.evaluate(() => window.__prog);
+ const pcts = (stage) => prog.filter((x) => x.startsWith(stage)).map((x) => +x.match(/(\d+)%/)[1]);
+ const shrink = pcts('Shrinking video'), up = pcts('Uploading');
+ check('a bar shows the shrinking with its percentage', shrink.length >= 3 && shrink.some((p) => p > 0 && p < 100), JSON.stringify(shrink.slice(0, 12)));
+ // (the answer here is instant, so only its start shows — the percentages
+ // themselves are upload-progress-test's)
+ check('...then the upload with its own', up.length >= 1 && prog.findIndex((x) => x.startsWith('Uploading')) > prog.findIndex((x) => x.startsWith('Shrinking')), JSON.stringify(up));
+ check('...and goes away when it is done', await page.evaluate(() => !document.querySelector('.media-progress')));
const put = done && await page.evaluate(() => ({ size: window.__put.size, type: window.__put.type }));
const secs = ((Date.now() - t0) / 1000).toFixed(1);
check('the upload is the shrunk video', !!put && put.type === 'video/mp4' && put.size < srcSize * 0.35,