@torrent-tv/proxy 2.89.6 → 2.89.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +32 -12
- package/CHANGELOG.md +2340 -2330
- package/assets/calibration/audio/NOTICE.md +19 -19
- package/assets/calibration/audio/manifest.json +98 -98
- package/biome.json +412 -412
- package/docs/browser-proxy-contract.md +155 -155
- package/docs/container-architecture.md +273 -273
- package/docs/encode-run-state.md +1 -1
- package/docs/playback-start-forecast.md +113 -98
- package/package.json +51 -50
- package/routes/api/sources/files/get.js +106 -106
- package/scripts/build-audio-samples.mjs +31 -31
- package/services/encode/EncodeRun.js +67 -15
- package/services/encode/EncodeRuns.js +19 -7
- package/services/encode/OutputOpening.js +784 -784
- package/services/encode/audio-calibration.js +67 -67
- package/services/encode/audio-work-rate.js +15 -15
- package/services/encode/encode-exit.js +2 -0
- package/services/encode/hwaccel.js +1894 -1894
- package/services/encode/output/index.js +24 -24
- package/services/encode/piece-completeness.js +60 -2
- package/services/encode/run-command.js +757 -755
- package/services/encode/segment-formats/index.js +1 -0
- package/services/encode/segment-formats/mp4-boxes.js +8 -1
- package/services/media/ContainerOrchestrator.js +202 -202
- package/services/media/KeyframeTables.js +356 -356
- package/services/media/SubtitleCues.js +592 -592
- package/services/media/audio-inventory.js +299 -299
- package/services/media/container/AviContainer.js +268 -268
- package/services/media/container/Container.js +632 -632
- package/services/media/container/ContainerFactory.js +38 -38
- package/services/media/container/MatroskaContainer.js +1031 -1031
- package/services/media/container/Mp4Container.js +1354 -1354
- package/services/media/container/SubtitleFileContainer.js +339 -339
- package/services/media/container/ebml-stream.js +174 -174
- package/services/media/container/matroska-clusters.js +1165 -1165
- package/services/media/container/unavailable.js +77 -77
- package/services/media/tracks/ContainerTrack.js +84 -84
- package/services/media/tracks/TextSubtitleTrack.js +295 -295
- package/services/storage/piece-store/shared-piece-store.js +1937 -1937
- package/services/storage/segment-store/SegmentStore.js +1294 -1294
- package/services/torrent/Contents.js +382 -382
- package/services/torrent/episode-naming.js +182 -182
- package/services/torrent/torrent-pool.js +2688 -2688
- package/services/torrent/worker/client.js +836 -836
- package/services/torrent/worker/held-bytes.js +231 -231
- package/services/torrent/worker/worker.js +1137 -1137
- package/services/transport/data-channel-handler.js +1373 -1373
- package/services/viewer/PriorityMap.js +421 -421
- package/test/audio-calibration.test.js +23 -23
- package/test/auto-quality-step.test.js +762 -762
- package/test/coalesce.test.js +39 -39
- package/test/coverage-follows-the-disk.test.js +5 -0
- package/test/decode-cost.test.js +475 -475
- package/test/encode-run.test.js +58 -0
- package/test/episode-naming.test.js +131 -131
- package/test/failed-encoder-still-serves-what-exists.test.js +1 -1
- package/test/fixtures/audio-frame-boundary.json +1 -1
- package/test/fixtures/refused-audio-20261002.json +10 -0
- package/test/fmp4-presentation-clock.test.js +4 -2
- package/test/format-keyed-outputs.test.js +78 -78
- package/test/health-metrics.test.js +11 -13
- package/test/held-read-ownership.test.js +77 -77
- package/test/helpers/encode-run.js +14 -6
- package/test/helpers/held-file.js +77 -77
- package/test/helpers/manager.js +62 -62
- package/test/helpers/matroska-file.js +296 -296
- package/test/keyframe-table-budget.test.js +123 -123
- package/test/matroska-cluster-discovery.test.js +323 -323
- package/test/one-output-one-session.test.js +273 -273
- package/test/output-speed.test.js +6 -1
- package/test/piece-completeness.test.js +37 -1
- package/test/quality-variants.test.js +1274 -1270
- package/test/rate-control-in-the-output.test.js +4 -3
- package/test/reads-not-yet-arrived.test.js +218 -218
- package/test/seek-frees-the-viewers-requests.test.js +1 -1
- package/test/segment-closed-on-both-branches.test.js +114 -114
- package/test/segment-judged-by-its-own-header.test.js +222 -222
- package/test/segment-serve-wiring.test.js +481 -481
- package/test/serving-existing-output.test.js +111 -111
- package/test/source-files-route.test.js +113 -113
- package/test/subtitle-cue-walk.test.js +385 -385
- package/test/subtitle-metadata.test.js +15 -15
- package/test/subtitle-plan-waits.test.js +125 -125
- package/test/subtitle-track-numbering.test.js +391 -391
- package/test/two-viewers-one-picture.test.js +508 -508
- package/test/viewer-requests-admission.test.js +92 -92
- package/test/viewer-starts-on-map.test.js +27 -27
- package/test/viewer-states.test.js +5 -2
- package/test/viewing-generations.test.js +390 -390
- package/test/whole-file-held-read.test.js +30 -30
- package/utils/coalesce.js +41 -41
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file A file kept whole is read from the disk by the subtitle walk.
|
|
3
|
-
*
|
|
4
|
-
* Once a file is kept whole its pieces leave the torrent's store while the
|
|
5
|
-
* bitfield still says they are held, and a read through the torrent waited on
|
|
6
|
-
* a store that no longer had them (field 2026-10-01). The reading itself is
|
|
7
|
-
* checked here over a temporary file; the client that uses it starts the
|
|
8
|
-
* torrent thread and is not built.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import test from "node:test";
|
|
12
|
-
import assert from "node:assert/strict";
|
|
13
|
-
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
14
|
-
import os from "node:os";
|
|
15
|
-
import path from "node:path";
|
|
16
|
-
|
|
17
|
-
import { readWholeFile } from "../services/torrent/worker/client.js";
|
|
18
|
-
|
|
19
|
-
test("a range of a whole file is read from the disk, and a range past it is not here", async (t) => {
|
|
20
|
-
const dir = await mkdtemp(path.join(os.tmpdir(), "ttv-whole-"));
|
|
21
|
-
t.after(() => rm(dir, { recursive: true, force: true }));
|
|
22
|
-
const file = path.join(dir, "film.mkv");
|
|
23
|
-
await writeFile(file, Buffer.from("0123456789"));
|
|
24
|
-
const whole = { path: file, length: 10, name: "film.mkv" };
|
|
25
|
-
|
|
26
|
-
assert.equal((await readWholeFile(whole, 2, 5)).toString(), "2345");
|
|
27
|
-
assert.equal((await readWholeFile(whole, 8, 20)).toString(), "89", "clamped to the file");
|
|
28
|
-
assert.equal(await readWholeFile(whole, 12, 20), null);
|
|
29
|
-
await assert.rejects(readWholeFile({ ...whole, path: path.join(dir, "gone.mkv") }, 0, 1), { code: "ENOENT" });
|
|
30
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file A file kept whole is read from the disk by the subtitle walk.
|
|
3
|
+
*
|
|
4
|
+
* Once a file is kept whole its pieces leave the torrent's store while the
|
|
5
|
+
* bitfield still says they are held, and a read through the torrent waited on
|
|
6
|
+
* a store that no longer had them (field 2026-10-01). The reading itself is
|
|
7
|
+
* checked here over a temporary file; the client that uses it starts the
|
|
8
|
+
* torrent thread and is not built.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
14
|
+
import os from "node:os";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
import { readWholeFile } from "../services/torrent/worker/client.js";
|
|
18
|
+
|
|
19
|
+
test("a range of a whole file is read from the disk, and a range past it is not here", async (t) => {
|
|
20
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "ttv-whole-"));
|
|
21
|
+
t.after(() => rm(dir, { recursive: true, force: true }));
|
|
22
|
+
const file = path.join(dir, "film.mkv");
|
|
23
|
+
await writeFile(file, Buffer.from("0123456789"));
|
|
24
|
+
const whole = { path: file, length: 10, name: "film.mkv" };
|
|
25
|
+
|
|
26
|
+
assert.equal((await readWholeFile(whole, 2, 5)).toString(), "2345");
|
|
27
|
+
assert.equal((await readWholeFile(whole, 8, 20)).toString(), "89", "clamped to the file");
|
|
28
|
+
assert.equal(await readWholeFile(whole, 12, 20), null);
|
|
29
|
+
await assert.rejects(readWholeFile({ ...whole, path: path.join(dir, "gone.mkv") }, 0, 1), { code: "ENOENT" });
|
|
30
|
+
});
|
package/utils/coalesce.js
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file One run of a piece of work per key at a time, and one more after it
|
|
3
|
-
* whenever it was asked for meanwhile.
|
|
4
|
-
*
|
|
5
|
-
* For work that is asked for by events — pieces arriving — and that reads
|
|
6
|
-
* whatever is there when it runs. A request that arrives while a run is going
|
|
7
|
-
* is neither queued nor dropped: it is remembered as "run again", once,
|
|
8
|
-
* because one more run reads everything that arrived during the first. Dropping
|
|
9
|
-
* it lost exactly the run that would have found the last pieces of a file
|
|
10
|
-
* (`research/subtitles-never-appear-2026-10-01.md`).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @template {unknown[]} A
|
|
15
|
-
* @param {(...args: A) => Promise<void>} work
|
|
16
|
-
* @returns {(key: string, ...args: A) => Promise<void>}
|
|
17
|
-
*/
|
|
18
|
-
export function coalescing(work) {
|
|
19
|
-
/** @type {Set<string>} */
|
|
20
|
-
const running = new Set();
|
|
21
|
-
/** The latest arguments a run was asked for with meanwhile. @type {Map<string, A>} */
|
|
22
|
-
const askedAgain = new Map();
|
|
23
|
-
const run = async (key, ...args) => {
|
|
24
|
-
if (running.has(key)) {
|
|
25
|
-
askedAgain.set(key, args);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
running.add(key);
|
|
29
|
-
try {
|
|
30
|
-
await work(...args);
|
|
31
|
-
} finally {
|
|
32
|
-
running.delete(key);
|
|
33
|
-
const again = askedAgain.get(key);
|
|
34
|
-
if (again) {
|
|
35
|
-
askedAgain.delete(key);
|
|
36
|
-
void run(key, ...again);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
return run;
|
|
41
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file One run of a piece of work per key at a time, and one more after it
|
|
3
|
+
* whenever it was asked for meanwhile.
|
|
4
|
+
*
|
|
5
|
+
* For work that is asked for by events — pieces arriving — and that reads
|
|
6
|
+
* whatever is there when it runs. A request that arrives while a run is going
|
|
7
|
+
* is neither queued nor dropped: it is remembered as "run again", once,
|
|
8
|
+
* because one more run reads everything that arrived during the first. Dropping
|
|
9
|
+
* it lost exactly the run that would have found the last pieces of a file
|
|
10
|
+
* (`research/subtitles-never-appear-2026-10-01.md`).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @template {unknown[]} A
|
|
15
|
+
* @param {(...args: A) => Promise<void>} work
|
|
16
|
+
* @returns {(key: string, ...args: A) => Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
export function coalescing(work) {
|
|
19
|
+
/** @type {Set<string>} */
|
|
20
|
+
const running = new Set();
|
|
21
|
+
/** The latest arguments a run was asked for with meanwhile. @type {Map<string, A>} */
|
|
22
|
+
const askedAgain = new Map();
|
|
23
|
+
const run = async (key, ...args) => {
|
|
24
|
+
if (running.has(key)) {
|
|
25
|
+
askedAgain.set(key, args);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
running.add(key);
|
|
29
|
+
try {
|
|
30
|
+
await work(...args);
|
|
31
|
+
} finally {
|
|
32
|
+
running.delete(key);
|
|
33
|
+
const again = askedAgain.get(key);
|
|
34
|
+
if (again) {
|
|
35
|
+
askedAgain.delete(key);
|
|
36
|
+
void run(key, ...again);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return run;
|
|
41
|
+
}
|