perf(video): cache attachment auth so Range playback stops stuttering; strict on-demand
Root cause of a downloaded/streaming video buffering repeatedly: every /files Range request (a playing video fires dozens) re-ran the full attachment authorization, which scans the messages table by attachment_id (un-indexed) — a per-chunk table scan = stutter. Now the auth decision is cached per user+attachment for 60s (module-level, bounded), so range requests after the first are ~free. Also: preload='none' (nothing about a video downloads until the user taps play — only the small poster loads), per 'no auto-download'. And the buffering spinner no longer hides on canplay/loadeddata (they fire mid-buffer), so it reliably spins whenever it's buffering. build batch159. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+28
-14
@@ -9,6 +9,34 @@ const { PUBLIC_DIR, REC_DIR, TRANS_DIR, UPLOADS_DIR, DOWNLOADS_DIR } = require('
|
||||
|
||||
const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.json': 'application/json', '.webmanifest': 'application/manifest+json' };
|
||||
|
||||
// Authorize an attachment id: the uploader, a member of the group using it as an avatar, or a participant of
|
||||
// ANY message carrying it (the "any" covers forwarded attachments, which reuse the same id). Returns the row.
|
||||
function authAttachmentRaw(id, u) {
|
||||
const a = R.attachments.byId(id);
|
||||
if (!a || a.team_id !== u.team_id) return null;
|
||||
const avatarGroup = R.conversations.byAvatar(id);
|
||||
const carriers = R.messages.allByAttachment(id);
|
||||
const ok = a.uploader_id === u.id
|
||||
|| (avatarGroup && R.conversations.isMember(avatarGroup.id, u.id))
|
||||
|| carriers.some((msg) => msg.conversation_id
|
||||
? R.conversations.isMember(msg.conversation_id, u.id)
|
||||
: (msg.sender_id === u.id || msg.recipient_id === u.id));
|
||||
return ok ? a : null;
|
||||
}
|
||||
// Video playback fires MANY /files Range requests, and authAttachmentRaw scans messages by attachment_id
|
||||
// (un-indexed) each time — that per-chunk scan is what made playback stutter ("buffers and plays…"). Cache the
|
||||
// decision per user+attachment for 60s so range requests after the first are ~free. Bounded to keep memory flat.
|
||||
const _attAuth = new Map();
|
||||
function authAttachment(id, u) {
|
||||
const key = u.id + ':' + id, now = Date.now();
|
||||
const hit = _attAuth.get(key);
|
||||
if (hit && hit.exp > now) return hit.a;
|
||||
const a = authAttachmentRaw(id, u);
|
||||
if (_attAuth.size > 4000) _attAuth.clear();
|
||||
_attAuth.set(key, { a, exp: now + 60000 });
|
||||
return a;
|
||||
}
|
||||
|
||||
function serveStatic(req, res) {
|
||||
let p = req.url.split('?')[0];
|
||||
if (p === '/') p = '/index.html';
|
||||
@@ -150,20 +178,6 @@ function handleGet(req, res) {
|
||||
rs.pipe(res);
|
||||
});
|
||||
}
|
||||
// Authorize an attachment id: the uploader, a member of the group using it as an avatar, or a participant of
|
||||
// ANY message carrying it (the "any" covers forwarded attachments, which reuse the same id). Returns the row.
|
||||
const authAttachment = (id, u) => {
|
||||
const a = R.attachments.byId(id);
|
||||
if (!a || a.team_id !== u.team_id) return null;
|
||||
const avatarGroup = R.conversations.byAvatar(id);
|
||||
const carriers = R.messages.allByAttachment(id);
|
||||
const ok = a.uploader_id === u.id
|
||||
|| (avatarGroup && R.conversations.isMember(avatarGroup.id, u.id))
|
||||
|| carriers.some((msg) => msg.conversation_id
|
||||
? R.conversations.isMember(msg.conversation_id, u.id)
|
||||
: (msg.sender_id === u.id || msg.recipient_id === u.id));
|
||||
return ok ? a : null;
|
||||
};
|
||||
// Video POSTER thumbnail — first frame extracted with ffmpeg, cached next to the file. Cosmetic: if ffmpeg
|
||||
// is missing or fails we 404 and the <video> just falls back to its own (black) poster.
|
||||
if (pathOnly.startsWith('/thumbs/')) {
|
||||
|
||||
Reference in New Issue
Block a user