@torrent-tv/proxy 2.89.5 → 2.89.6

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.
@@ -1,105 +1,111 @@
1
- /**
2
- * @file An output already made serves a viewer when the rules decided with the
3
- * user 2026-09-16 allow it, through the paths a real request takes.
4
- */
5
-
6
- import test from "node:test";
7
- import assert from "node:assert/strict";
8
- import { writeFileSync } from "node:fs";
9
- import path from "node:path";
10
- import { managerWithOwnStore } from "./helpers/manager.js";
11
- import { outputSpec } from "./helpers/output-spec.js";
12
- import { SourceFile } from "../services/media/SourceFile.js";
13
- import { Timeline } from "../services/encode/output/Timeline.js";
14
- import { fmp4Format } from "../services/encode/segment-formats/fmp4.js";
15
- import { Output } from "../services/encode/output/Output.js";
16
-
17
- const TORRENT = "torrent:11f0929918e2b5aa2e5b71ecdbe5c0f1a4bbf7d1";
18
-
19
- function hostWithA1080pOutput(t) {
20
- const { manager, store, cleanup } = managerWithOwnStore({
21
- startupWaitMs: 0,
22
- videoEncoder: { kind: "vaapi", name: "h264_vaapi", inputArgs: [] }
23
- });
24
- t.after(async () => {
25
- await manager.lifecycle.disposeAll();
26
- cleanup();
27
- });
28
- manager.encodeRuns.planEncodersNow = () => {};
29
- manager.encodeRuns.planEncodersSoon = () => {};
30
- manager.getCachedMediaInfo = () => ({ durationSeconds: 400, width: 1920, height: 1080, fps: 24 });
31
- // The soundtrack the plan listed, at the rate the file states: a copy of it
32
- // has a figure, so it stays a copy.
33
- manager.getCachedAudioTracks = () => [{ index: 0, fileIndex: 0, sourceTrackIndex: 0, bitrateKbps: 128 }];
34
- const spec = outputSpec({
35
- sourceKey: TORRENT,
36
- transcodeVideo: true,
37
- transcodeAudio: false,
38
- encoder: "h264_vaapi",
39
- width: 1920,
40
- height: 1080,
41
- fps: 24,
42
- cutGrid: "uniform"
43
- });
44
- const key = spec.toKey();
45
- store.useFormat(key, fmp4Format);
46
- // The piece at the start of the film is made.
47
- writeFileSync(path.join(store.directoryFor(key), fmp4Format.segmentFileName(0)), Buffer.alloc(100));
48
- const file = new SourceFile({ sourceKey: TORRENT, fileIndex: 0, name: "film.mkv" }).learn({ width: 1920, height: 1080, durationSeconds: 400 });
49
- const made = {
50
- id: spec.toName(),
51
- spec,
52
- get outputKey() { return this.spec.toKey(); },
53
- file,
54
- timeline: new Timeline({ boundaries: Array.from({ length: 101 }, (_, index) => index * 4), cutGrid: "uniform" }),
55
- output: new Output({ encodeWidth: 1920, encodeHeight: 1080, outputFps: 24 }),
56
- segmentFormat: fmp4Format,
57
- useSyntheticPlaylist: true
58
- };
59
- manager.outputs.set(made.id, made);
60
- return { manager, made };
61
- }
62
-
63
- function request(over) {
64
- return {
65
- sourceKey: TORRENT,
66
- fileIndex: 0,
67
- fileName: "film.mkv",
68
- transcodeVideo: true,
69
- transcodeAudio: false,
70
- segmentFormatId: "fmp4",
71
- ...over
72
- };
73
- }
74
-
75
- test("the automatic choice is served by a better output whose piece at the viewer's position is made", async (t) => {
76
- const { manager, made } = hostWithA1080pOutput(t);
77
- const answered = await manager.viewerRequests.createOrGetSession(
78
- request({ consumerId: "auto-viewer", targetWidth: 1280, targetHeight: 720 })
79
- );
80
- assert.equal(answered, made);
81
- });
82
-
83
- test("a size picked by hand is not served by another size", async (t) => {
84
- const { manager, made } = hostWithA1080pOutput(t);
85
- let answered = null;
86
- try {
87
- answered = await manager.viewerRequests.createOrGetSession(
88
- request({ consumerId: "picker", targetWidth: 1280, targetHeight: 720, exactSize: true })
89
- );
90
- } catch {
91
- answered = null;
92
- }
93
- assert.notEqual(answered, made);
94
- });
95
-
96
- test("a step asked for by an automatic viewer and served by the picture leaves the picture a picture", async (t) => {
97
- const { manager, made } = hostWithA1080pOutput(t);
98
- manager.viewers.of(made, "auto-viewer").qualityMode = "auto";
99
-
100
- const variant = await manager.renditions.resolveVariantSession(made.id, 720, 0, "auto-viewer");
101
-
102
- assert.equal(variant, made, "the picture already made at 1080p serves the 720p request");
103
- assert.notEqual(made.isStep, true, "and is not turned into a step of itself");
104
- assert.ok(manager.viewers.forOutput(made).has("auto-viewer"));
105
- });
1
+ /**
2
+ * @file An output already made serves a viewer when the rules decided with the
3
+ * user 2026-09-16 allow it, through the paths a real request takes.
4
+ */
5
+
6
+ import test from "node:test";
7
+ import assert from "node:assert/strict";
8
+ import { writeFileSync } from "node:fs";
9
+ import path from "node:path";
10
+ import { managerWithOwnStore } from "./helpers/manager.js";
11
+ import { outputSpec } from "./helpers/output-spec.js";
12
+ import { SourceFile } from "../services/media/SourceFile.js";
13
+ import { Timeline } from "../services/encode/output/Timeline.js";
14
+ import { fmp4Format } from "../services/encode/segment-formats/fmp4.js";
15
+ import { Output } from "../services/encode/output/Output.js";
16
+ import { qualityStateOf } from "../services/encode/quality/OutputQualityState.js";
17
+
18
+ const TORRENT = "torrent:11f0929918e2b5aa2e5b71ecdbe5c0f1a4bbf7d1";
19
+
20
+ function hostWithA1080pOutput(t) {
21
+ const { manager, store, cleanup } = managerWithOwnStore({
22
+ startupWaitMs: 0,
23
+ videoEncoder: { kind: "vaapi", name: "h264_vaapi", inputArgs: [] }
24
+ });
25
+ t.after(async () => {
26
+ await manager.lifecycle.disposeAll();
27
+ cleanup();
28
+ });
29
+ manager.encodeRuns.planEncodersNow = () => {};
30
+ manager.encodeRuns.planEncodersSoon = () => {};
31
+ manager.getCachedMediaInfo = () => ({ durationSeconds: 400, width: 1920, height: 1080, fps: 24 });
32
+ // The soundtrack the plan listed, at the rate the file states: a copy of it
33
+ // has a figure, so it stays a copy.
34
+ manager.getCachedAudioTracks = () => [{ index: 0, fileIndex: 0, sourceTrackIndex: 0, bitrateKbps: 128 }];
35
+ const spec = outputSpec({
36
+ sourceKey: TORRENT,
37
+ transcodeVideo: true,
38
+ transcodeAudio: false,
39
+ encoder: "h264_vaapi",
40
+ width: 1920,
41
+ height: 1080,
42
+ fps: 24,
43
+ cutGrid: "uniform"
44
+ });
45
+ const key = spec.toKey();
46
+ store.useFormat(key, fmp4Format);
47
+ // The piece at the start of the film is made.
48
+ writeFileSync(path.join(store.directoryFor(key), fmp4Format.segmentFileName(0)), Buffer.alloc(100));
49
+ const file = new SourceFile({ sourceKey: TORRENT, fileIndex: 0, name: "film.mkv" }).learn({ width: 1920, height: 1080, durationSeconds: 400 });
50
+ const made = {
51
+ id: spec.toName(),
52
+ spec,
53
+ get outputKey() { return this.spec.toKey(); },
54
+ file,
55
+ timeline: new Timeline({ boundaries: Array.from({ length: 101 }, (_, index) => index * 4), cutGrid: "uniform" }),
56
+ output: new Output({ encodeWidth: 1920, encodeHeight: 1080, outputFps: 24 }),
57
+ segmentFormat: fmp4Format,
58
+ useSyntheticPlaylist: true
59
+ };
60
+ manager.outputs.set(made.id, made);
61
+ // WHAT ITS ENCODER WAS SEEN DOING. Its first piece is made, so an encoder ran
62
+ // on it here; a viewer put on it now needs one again, and a place for one is
63
+ // asked by what one costs — with no measured cost the proxy refuses (roadmap
64
+ // item 97, step 14).
65
+ qualityStateOf(made).lastAloneSpeed = 2;
66
+ return { manager, made };
67
+ }
68
+
69
+ function request(over) {
70
+ return {
71
+ sourceKey: TORRENT,
72
+ fileIndex: 0,
73
+ fileName: "film.mkv",
74
+ transcodeVideo: true,
75
+ transcodeAudio: false,
76
+ segmentFormatId: "fmp4",
77
+ ...over
78
+ };
79
+ }
80
+
81
+ test("the automatic choice is served by a better output whose piece at the viewer's position is made", async (t) => {
82
+ const { manager, made } = hostWithA1080pOutput(t);
83
+ const answered = await manager.viewerRequests.createOrGetSession(
84
+ request({ consumerId: "auto-viewer", targetWidth: 1280, targetHeight: 720 })
85
+ );
86
+ assert.equal(answered, made);
87
+ });
88
+
89
+ test("a size picked by hand is not served by another size", async (t) => {
90
+ const { manager, made } = hostWithA1080pOutput(t);
91
+ let answered = null;
92
+ try {
93
+ answered = await manager.viewerRequests.createOrGetSession(
94
+ request({ consumerId: "picker", targetWidth: 1280, targetHeight: 720, exactSize: true })
95
+ );
96
+ } catch {
97
+ answered = null;
98
+ }
99
+ assert.notEqual(answered, made);
100
+ });
101
+
102
+ test("a step asked for by an automatic viewer and served by the picture leaves the picture a picture", async (t) => {
103
+ const { manager, made } = hostWithA1080pOutput(t);
104
+ manager.viewers.of(made, "auto-viewer").qualityMode = "auto";
105
+
106
+ const variant = await manager.renditions.resolveVariantSession(made.id, 720, 0, "auto-viewer");
107
+
108
+ assert.equal(variant, made, "the picture already made at 1080p serves the 720p request");
109
+ assert.notEqual(made.isStep, true, "and is not turned into a step of itself");
110
+ assert.ok(manager.viewers.forOutput(made).has("auto-viewer"));
111
+ });