mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 13:53:12 -06:00
On Android TV with weak/unstable WiFi, YouTube embeds auto-select 1080p+
and buffer/stall. Add a per-item "Unstable connection" flag that biases the
YouTube embed toward a 720p ceiling.
- DB: content.unstable_connection INTEGER NOT NULL DEFAULT 0 (non-destructive,
existing rows unchanged).
- buildSnapshotItems: denormalize the flag into published_snapshot so it
reaches the player (that query enumerates columns, so it had to be added
explicitly — covered by a new test). preview-payload reuses the same query.
- content.js PUT: accept + coerce unstable_connection to 0/1.
- Player: playerVars.vq='hd720' + best-effort setPlaybackQuality('hd720') in
onReady when the flag is set. Both are hints YouTube may still override, but
together they bias the initial selection down to 720p.
- Edit modal: YouTube-only checkbox + hint; en/es i18n.
Scoped to the core ask; the issue's optional "Shorts get inverse hd1080"
refinement is intentionally left out (dubious for signage, and forcing higher
quality on a weak link is the opposite of the goal).
Closes #217
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.6 KiB
JavaScript
54 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
// #217 unstable-connection quality cap. The player only receives a content field if
|
|
// buildSnapshotItems (via publishPlaylist) denormalizes it into published_snapshot —
|
|
// that query enumerates columns explicitly, so this guards against the flag silently
|
|
// not reaching the player.
|
|
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const crypto = require('node:crypto');
|
|
process.env.DATA_DIR = path.join(os.tmpdir(), 'st-unstable-' + crypto.randomBytes(4).toString('hex'));
|
|
process.env.SELF_HOSTED = 'true';
|
|
process.env.NODE_ENV = 'test';
|
|
|
|
const { test, before } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { db } = require('../db/database');
|
|
const { publishPlaylist } = require('../routes/playlists');
|
|
|
|
const uid = (p) => p + '-' + crypto.randomBytes(4).toString('hex');
|
|
|
|
let USER;
|
|
before(() => {
|
|
USER = uid('u');
|
|
db.prepare("INSERT INTO users (id, email, password_hash) VALUES (?, ?, 'x')").run(USER, USER + '@t.local');
|
|
});
|
|
|
|
test('published snapshot carries unstable_connection so the player can cap quality', () => {
|
|
const capped = uid('capped'), normal = uid('normal');
|
|
db.prepare("INSERT INTO content (id, filename, mime_type, remote_url, unstable_connection) VALUES (?, ?, 'video/youtube', 'https://youtu.be/aaaaaaaaaaa', 1)")
|
|
.run(capped, capped);
|
|
db.prepare("INSERT INTO content (id, filename, mime_type, remote_url, unstable_connection) VALUES (?, ?, 'video/youtube', 'https://youtu.be/bbbbbbbbbbb', 0)")
|
|
.run(normal, normal);
|
|
|
|
const pl = uid('pl');
|
|
db.prepare("INSERT INTO playlists (id, user_id, name, status) VALUES (?, ?, 'P', 'draft')").run(pl, USER);
|
|
db.prepare('INSERT INTO playlist_items (playlist_id, content_id, sort_order) VALUES (?, ?, 0)').run(pl, capped);
|
|
db.prepare('INSERT INTO playlist_items (playlist_id, content_id, sort_order) VALUES (?, ?, 1)').run(pl, normal);
|
|
|
|
publishPlaylist(pl);
|
|
|
|
const snapshot = JSON.parse(db.prepare('SELECT published_snapshot FROM playlists WHERE id = ?').get(pl).published_snapshot);
|
|
const byId = Object.fromEntries(snapshot.map(i => [i.content_id, i]));
|
|
assert.equal(byId[capped].unstable_connection, 1, 'flagged item keeps the cap in the snapshot');
|
|
assert.equal(byId[normal].unstable_connection, 0, 'unflagged item stays uncapped');
|
|
});
|
|
|
|
test('unstable_connection defaults to 0 for content that never set it', () => {
|
|
const c = uid('default');
|
|
db.prepare("INSERT INTO content (id, filename, mime_type) VALUES (?, ?, 'video/youtube')").run(c, c);
|
|
const row = db.prepare('SELECT unstable_connection FROM content WHERE id = ?').get(c);
|
|
assert.equal(row.unstable_connection, 0);
|
|
});
|