@torrent-tv/proxy 2.87.8 → 2.87.9
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/.claude/commands/opsx/apply.md +155 -155
- package/.claude/commands/opsx/archive.md +160 -160
- package/.claude/commands/opsx/explore.md +174 -174
- package/.claude/commands/opsx/propose.md +109 -109
- package/.claude/commands/opsx/sync.md +143 -143
- package/.claude/skills/openspec-apply-change/SKILL.md +159 -159
- package/.claude/skills/openspec-archive-change/SKILL.md +117 -117
- package/.claude/skills/openspec-explore/SKILL.md +289 -289
- package/.claude/skills/openspec-propose/SKILL.md +113 -113
- package/.claude/skills/openspec-sync-specs/SKILL.md +147 -147
- package/.dockerignore +5 -5
- package/AGENTS.md +322 -322
- package/CHANGELOG.md +4 -0
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +411 -411
- package/docs/container-architecture.md +218 -218
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +729 -729
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +267 -267
- package/knip.json +14 -14
- package/openspec/changes/adaptive-bitrate/design.md +92 -92
- package/openspec/changes/adaptive-bitrate/proposal.md +56 -56
- package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +51 -51
- package/openspec/changes/adaptive-bitrate/tasks.md +40 -40
- package/openspec/changes/binary-distribution/.openspec.yaml +2 -2
- package/openspec/changes/binary-distribution/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/design.md +81 -81
- package/openspec/changes/chunked-request-bodies/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +30 -30
- package/openspec/changes/chunked-request-bodies/tasks.md +40 -40
- package/openspec/changes/cold-start/design.md +98 -98
- package/openspec/changes/cold-start/proposal.md +62 -62
- package/openspec/changes/cold-start/specs/cold-start/spec.md +47 -47
- package/openspec/changes/cold-start/tasks.md +48 -48
- package/openspec/changes/disk-cap/.openspec.yaml +2 -2
- package/openspec/changes/disk-cap/proposal.md +37 -37
- package/openspec/changes/disk-cap/specs/disk-cap/spec.md +25 -25
- package/openspec/changes/disk-cap/tasks.md +19 -19
- package/openspec/changes/proxy-observability/.openspec.yaml +2 -2
- package/openspec/changes/proxy-observability/design.md +38 -38
- package/openspec/changes/proxy-observability/proposal.md +49 -49
- package/openspec/changes/proxy-observability/specs/observability/spec.md +33 -33
- package/openspec/changes/proxy-observability/tasks.md +21 -21
- package/openspec/changes/seek-debounce/design.md +118 -118
- package/openspec/changes/seek-debounce/proposal.md +59 -59
- package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +41 -41
- package/openspec/changes/seek-debounce/tasks.md +33 -33
- package/openspec/changes/subtitle-language/.openspec.yaml +2 -2
- package/openspec/changes/subtitle-language/proposal.md +50 -50
- package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +34 -34
- package/openspec/changes/subtitle-language/tasks.md +26 -26
- package/openspec/changes/track-selection/.openspec.yaml +2 -2
- package/openspec/changes/track-selection/design.md +47 -47
- package/openspec/changes/track-selection/proposal.md +45 -45
- package/openspec/changes/track-selection/specs/track-selection/spec.md +41 -41
- package/openspec/changes/track-selection/tasks.md +20 -20
- package/openspec/changes/transcode-quality/.openspec.yaml +2 -2
- package/openspec/changes/transcode-quality/proposal.md +51 -51
- package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +139 -139
- package/openspec/changes/transcode-quality/tasks.md +63 -63
- package/openspec/config.yaml +38 -38
- package/package.json +1 -1
- package/public/.well-known/appspecific/com.chrome.devtools.json +1 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/research/handover-2026-09-13.md +209 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +166 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +175 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +55 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +225 -225
- package/routes/api/client-logs/post.js +86 -86
- package/routes/api/delivery-sink/get.js +72 -72
- package/routes/api/link-probe/get.js +51 -51
- package/routes/api/playback-plan/audio-tracks/post.js +29 -29
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +100 -100
- package/routes/api/sources/post.js +36 -36
- package/routes/api/sources/stats/get.js +132 -132
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +60 -60
- package/routes/api/transcode-sessions/net-report/post.js +129 -129
- package/routes/api/transcode-sessions/post.js +160 -160
- package/routes/api/transcode-sessions/progress/get.js +27 -27
- package/routes/api/transcode-sessions/release/post.js +41 -41
- package/routes/api/transcode-sessions/seek/post.js +63 -63
- package/routes/health/get.js +13 -13
- package/routes/healthz/get.js +13 -13
- package/routes/stream/get.js +361 -361
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +84 -84
- package/routes/transcode/session-file/get.js +52 -52
- package/routes/transcode/variant-file/get.js +95 -95
- package/routes/transcode/variant-warm/get.js +84 -84
- package/scripts/render-run-graph.js +173 -173
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -345
- package/services/encode/EncodeAdmission.js +332 -332
- package/services/encode/EncodeOrchestrator.js +1217 -1217
- package/services/encode/EncodePlan.js +1366 -1366
- package/services/encode/EncodeRun.js +776 -776
- package/services/encode/EncodeRuns.js +1229 -1229
- package/services/encode/Encoder.js +142 -142
- package/services/encode/EncoderSelection.js +38 -38
- package/services/encode/LocalObservations.js +359 -359
- package/services/encode/NvencEncoder.js +50 -50
- package/services/encode/OutputOpening.js +755 -755
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2162 -2162
- package/services/encode/RunProgress.js +84 -84
- package/services/encode/SegmentDemand.js +95 -95
- package/services/encode/SoftwareEncoder.js +121 -121
- package/services/encode/V4l2m2mEncoder.js +53 -53
- package/services/encode/VaapiEncoder.js +66 -66
- package/services/encode/args.js +416 -416
- package/services/encode/available-share.js +89 -89
- package/services/encode/calibration.js +361 -361
- package/services/encode/contention.js +128 -128
- package/services/encode/decode-cost-fit.js +363 -363
- package/services/encode/encode-exit.js +114 -114
- package/services/encode/encode-run-state.js +551 -551
- package/services/encode/encoder-readings.js +45 -45
- package/services/encode/fingerprint.js +322 -322
- package/services/encode/host-load.js +174 -174
- package/services/encode/hwaccel.js +1894 -1894
- package/services/encode/index.js +9 -9
- package/services/encode/learned-median.js +90 -90
- package/services/encode/output/EncodedOutput.js +57 -57
- package/services/encode/output/Output.js +131 -131
- package/services/encode/output/OutputCatalog.js +414 -414
- package/services/encode/output/OutputSpec.js +390 -390
- package/services/encode/output/Timeline.js +308 -308
- package/services/encode/output/cut-grid.js +107 -107
- package/services/encode/output/index.js +20 -20
- package/services/encode/output/ladder.js +32 -32
- package/services/encode/output/playlists.js +284 -284
- package/services/encode/output/rates.js +125 -125
- package/services/encode/output-key-format.js +21 -21
- package/services/encode/quality/EncodeCost.js +1083 -1083
- package/services/encode/quality/HostLoad.js +525 -525
- package/services/encode/quality/HostTimings.js +249 -249
- package/services/encode/quality/OutputQualityState.js +28 -28
- package/services/encode/quality/QualityController.js +935 -935
- package/services/encode/quality/QualityOffer.js +416 -416
- package/services/encode/quality/drain-threat.js +47 -47
- package/services/encode/quality/link-budget.js +254 -254
- package/services/encode/quality/output-format.js +266 -266
- package/services/encode/quality/serving-output.js +216 -216
- package/services/encode/quality/visible-rung.js +57 -57
- package/services/encode/run-budget.js +97 -97
- package/services/encode/run-command.js +747 -747
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +42 -42
- package/services/encode/segment-formats/fmp4.js +437 -437
- package/services/encode/segment-formats/index.js +77 -77
- package/services/encode/segment-formats/init-compat.js +324 -324
- package/services/encode/segment-formats/mp4-boxes.js +311 -311
- package/services/encode/segment-formats/mpegts.js +134 -134
- package/services/encode/start-stop-cost.js +178 -178
- package/services/encode/throughput.js +97 -97
- package/services/encode/torrent-cost.js +88 -88
- package/services/identity/installation-id.js +38 -38
- package/services/media/ContainerOrchestrator.js +149 -149
- package/services/media/KeyframeTables.js +316 -316
- package/services/media/SourceFile.js +298 -298
- package/services/media/SubtitleCues.js +520 -520
- package/services/media/SubtitleOrchestrator.js +167 -167
- package/services/media/container/AviContainer.js +266 -266
- package/services/media/container/Container.js +603 -603
- package/services/media/container/ContainerFactory.js +56 -56
- package/services/media/container/KeyframeTable.js +272 -272
- package/services/media/container/MatroskaContainer.js +1494 -1494
- package/services/media/container/Mp4Container.js +1244 -1244
- package/services/media/container/SubtitleFileContainer.js +322 -322
- package/services/media/container/ebml-reader.js +159 -159
- package/services/media/ffmpeg-banner.js +260 -260
- package/services/media/keyframe-probe.js +157 -157
- package/services/media/media-info-probe.js +93 -93
- package/services/media/playback-planner.js +1 -1
- package/services/media/tracks/AudioTrack.js +131 -131
- package/services/media/tracks/ContainerTrack.js +75 -75
- package/services/media/tracks/ImageSubtitleTrack.js +19 -19
- package/services/media/tracks/SubtitleTrack.js +38 -38
- package/services/media/tracks/TextSubtitleTrack.js +287 -287
- package/services/media/tracks/VideoTrack.js +33 -33
- package/services/media/tracks/index.js +15 -15
- package/services/media/tracks/language-detect.js +174 -174
- package/services/server/OutputLifecycle.js +418 -418
- package/services/server/SegmentServing.js +1146 -1146
- package/services/server/ViewerRequests.js +522 -522
- package/services/server/WaitLedger.js +142 -142
- package/services/server/controllers/SubtitleController.js +109 -109
- package/services/server/http-responses.js +60 -60
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +983 -983
- package/services/storage/Diagnostics.js +202 -202
- package/services/storage/MachineBudget.js +235 -235
- package/services/storage/allowance.js +107 -107
- package/services/storage/core-dumps.js +106 -106
- package/services/storage/files/CompletedFiles.js +391 -391
- package/services/storage/files/piece-from-whole-file.js +118 -118
- package/services/storage/free.js +71 -71
- package/services/storage/keep.js +48 -48
- package/services/storage/machine-memory.js +56 -56
- package/services/storage/memory-report.js +575 -575
- package/services/storage/piece-store/piece-disk-store.js +516 -516
- package/services/storage/piece-store/piece-lru.js +495 -495
- package/services/storage/piece-store/shared-piece-store.js +1880 -1880
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1196 -1196
- package/services/storage/wire.js +175 -175
- package/services/torrent/Contents.js +324 -324
- package/services/torrent/demand/DemandRegister.js +202 -202
- package/services/torrent/demand/Urgency.js +137 -137
- package/services/torrent/demand/Window.js +118 -118
- package/services/torrent/demand/index.js +8 -8
- package/services/torrent/demand/pieces.js +140 -140
- package/services/torrent/download/SwarmSelection.js +386 -386
- package/services/torrent/download/registry.js +102 -102
- package/services/torrent/download/withdraw-claim.js +80 -80
- package/services/torrent/files.js +455 -455
- package/services/torrent/naming.js +619 -619
- package/services/torrent/piece-store-of.js +29 -29
- package/services/torrent/supply-margin.js +262 -262
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/fastest-wires.js +342 -342
- package/services/torrent/worker/handle-state.js +23 -23
- package/services/torrent/worker/held-bytes.js +155 -155
- package/services/torrent/worker/install-webrtc-shim.js +32 -32
- package/services/torrent/worker/piece-reader.js +1078 -1078
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- package/services/transport/delivery-probe.js +842 -842
- package/services/transport/health-collector.js +53 -53
- package/services/transport/nat-classifier.js +231 -231
- package/services/transport/packet-witness.js +808 -808
- package/services/transport/port-mapper.js +235 -235
- package/services/transport/registry-api.js +81 -81
- package/services/transport/tunnel-client.js +447 -447
- package/services/transport/usrsctp-state.js +175 -175
- package/services/transport/webrtc-manager.js +484 -484
- package/services/viewer/Assignments.js +340 -340
- package/services/viewer/PriorityMap.js +390 -390
- package/services/viewer/PriorityOrchestrator.js +325 -325
- package/services/viewer/Viewer.js +657 -657
- package/services/viewer/Viewers.js +368 -368
- package/services/viewer/active-output.js +37 -37
- package/services/viewer/audio-start.js +46 -46
- package/services/viewer/buffer-trend.js +81 -81
- package/services/viewer/choices.js +647 -647
- package/services/viewer/positions.js +126 -126
- package/services/viewer/report-intake.js +46 -46
- package/store/source-registry.js +71 -71
- package/test/asset-paths.test.js +28 -28
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +707 -707
- package/test/available-share.test.js +76 -76
- package/test/background-fill.test.js +190 -190
- package/test/client-log-file.test.js +110 -110
- package/test/completed-files.test.js +115 -115
- package/test/concurrent-cost.test.js +148 -148
- package/test/container-media-info.test.js +228 -228
- package/test/contention.test.js +76 -76
- package/test/core-dumps.test.js +57 -57
- package/test/coverage-follows-the-disk.test.js +228 -228
- package/test/coverage-map.test.js +195 -195
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -58
- package/test/cut-times-timeline.test.js +65 -65
- package/test/cuts-follow-published-grid.test.js +109 -109
- package/test/data-channel-frame.test.js +63 -63
- package/test/dead-torrent-handle.test.js +52 -52
- package/test/declared-rates.test.js +116 -116
- package/test/declared-tracks.test.js +35 -35
- package/test/decode-cost-fit.test.js +202 -202
- package/test/decode-cost.test.js +462 -462
- package/test/delivery-probe.test.js +393 -393
- package/test/demand-register.test.js +206 -206
- package/test/dht-bootstrap.test.js +72 -72
- package/test/drain-threat.test.js +65 -65
- package/test/duplicate-add.test.js +48 -48
- package/test/encode-admission.test.js +146 -146
- package/test/encode-cost.test.js +283 -283
- package/test/encode-exit.test.js +111 -111
- package/test/encode-orchestrator.test.js +419 -419
- package/test/encode-plan-viewers.test.js +718 -718
- package/test/encode-plan.test.js +545 -545
- package/test/encode-run-state.test.js +314 -314
- package/test/encode-run.test.js +161 -161
- package/test/encode-slope.test.js +46 -46
- package/test/encoded-output.test.js +39 -39
- package/test/encoder-kinds.test.js +122 -122
- package/test/encoder-readings.test.js +46 -46
- package/test/every-claimant-is-told-its-share.test.js +152 -152
- package/test/failed-encoder-still-serves-what-exists.test.js +354 -354
- package/test/failed-start-breaker.test.js +202 -202
- package/test/fastest-wires.test.js +143 -143
- package/test/ffmpeg-stream-counts.test.js +78 -78
- package/test/file-edges.test.js +191 -191
- package/test/fmp4-self-contained.test.js +80 -80
- package/test/fmp4-timestamps.test.js +140 -140
- package/test/fmp4-track-completeness.test.js +41 -41
- package/test/format-keyed-outputs.test.js +78 -78
- package/test/health-metrics.test.js +38 -38
- package/test/held-request-width.test.js +157 -157
- package/test/helpers/encode-run.js +136 -136
- package/test/helpers/held-file.js +29 -29
- package/test/helpers/manager.js +57 -57
- package/test/helpers/output-spec.js +63 -63
- package/test/host-load.test.js +68 -68
- package/test/index-accuracy-is-about-the-file.test.js +128 -128
- package/test/init-compat.test.js +219 -219
- package/test/input-lost-quiets-the-plan.test.js +261 -261
- package/test/input-unavailable.test.js +48 -48
- package/test/installation-id.test.js +39 -39
- package/test/keeping-period.test.js +83 -83
- package/test/keyframe-index-accuracy.test.js +188 -188
- package/test/keyframe-table-budget.test.js +102 -102
- package/test/keyframes-belong-to-the-file.test.js +249 -249
- package/test/learned-median.test.js +37 -37
- package/test/letting-go-asks-who-is-watching.test.js +191 -191
- package/test/limit-row-by-area.test.js +60 -60
- package/test/link-budget.test.js +111 -111
- package/test/link-probe.test.js +56 -56
- package/test/local-encoding-data.test.js +273 -273
- package/test/logger-repeats.test.js +134 -134
- package/test/look-ahead-coverage.test.js +56 -56
- package/test/manager-surface.test.js +292 -292
- package/test/matroska-cues-track.test.js +192 -192
- package/test/memory-budget.test.js +390 -390
- package/test/move-cost.test.js +216 -216
- package/test/mp4-subtitles.test.js +173 -173
- package/test/one-authority.test.js +285 -285
- package/test/one-budget.test.js +149 -149
- package/test/one-output-one-session.test.js +204 -204
- package/test/one-resource-divided.test.js +185 -185
- package/test/one-serving-output-per-height.test.js +458 -458
- package/test/orchestrator-wired.test.js +206 -206
- package/test/output-catalog.test.js +39 -39
- package/test/output-shape.test.js +21 -21
- package/test/output-spec.test.js +251 -251
- package/test/output-speed.test.js +88 -88
- package/test/outputs-being-prepared.test.js +40 -40
- package/test/packet-witness-ring.test.js +236 -236
- package/test/packet-witness.test.js +148 -148
- package/test/piece-disk-store.test.js +381 -381
- package/test/piece-from-whole-file.test.js +129 -129
- package/test/piece-lru.test.js +319 -319
- package/test/piece-reader.test.js +162 -162
- package/test/piece-selection-offset.test.js +85 -85
- package/test/piece-store-eviction.test.js +560 -560
- package/test/piece-store-never-refuses.test.js +153 -153
- package/test/piece-store-reservations.test.js +318 -318
- package/test/piece-store-slow-disk.test.js +159 -159
- package/test/piece-tail.test.js +97 -97
- package/test/plan-host-timings.test.js +68 -68
- package/test/plan-is-steady.test.js +225 -225
- package/test/priority-map-download.test.js +174 -174
- package/test/priority-map-emptied.test.js +204 -204
- package/test/priority-map-per-output.test.js +215 -215
- package/test/priority-map.test.js +163 -163
- package/test/probe-wedge-certainty.test.js +60 -60
- package/test/produced-copy-choice.test.js +213 -213
- package/test/published-timeline.test.js +83 -83
- package/test/quality-offer.test.js +162 -162
- package/test/quality-variants.test.js +1229 -1229
- package/test/rate-control-in-the-output.test.js +180 -180
- package/test/read-survives-withdrawal.test.js +164 -164
- package/test/read-window.test.js +365 -365
- package/test/resume-warm.test.js +39 -39
- package/test/run-graph-drift.test.js +37 -37
- package/test/run-inputs.test.js +80 -80
- package/test/run-intervals.test.js +100 -100
- package/test/run-position-follows-published-grid.test.js +89 -89
- package/test/run-progress.test.js +65 -65
- package/test/same-height-switch.test.js +664 -664
- package/test/seek-frees-the-viewers-requests.test.js +217 -217
- package/test/seek-landing.test.js +109 -109
- package/test/seek-target-not-superseded.test.js +195 -195
- package/test/segment-closed-on-both-branches.test.js +96 -96
- package/test/segment-cut-times.test.js +63 -63
- package/test/segment-demand.test.js +80 -80
- package/test/segment-hold-supersede.test.js +91 -91
- package/test/segment-judged-by-its-own-header.test.js +221 -221
- package/test/segment-serve-wiring.test.js +475 -475
- package/test/segment-store-eviction.test.js +247 -247
- package/test/segment-store.test.js +323 -323
- package/test/segment-true-start.test.js +86 -86
- package/test/segments-are-shared.test.js +176 -176
- package/test/send-chunk-bytes.test.js +86 -86
- package/test/serving-existing-output.test.js +102 -102
- package/test/serving-output.test.js +102 -102
- package/test/shared-piece-store.test.js +310 -310
- package/test/sidecar-naming.test.js +142 -142
- package/test/source-file.test.js +133 -133
- package/test/source-files-route.test.js +103 -103
- package/test/source-registry.test.js +18 -18
- package/test/split-a-stretch.test.js +139 -139
- package/test/stale-request-after-seek.test.js +158 -158
- package/test/startup-readings.test.js +124 -124
- package/test/stream-route.test.js +182 -182
- package/test/subtitle-cue-framing.test.js +200 -200
- package/test/subtitle-cue-source.test.js +102 -102
- package/test/subtitle-cue-walk.test.js +370 -370
- package/test/subtitle-cursor.test.js +63 -63
- package/test/subtitle-defaults.test.js +118 -118
- package/test/subtitle-language.test.js +252 -252
- package/test/subtitle-track-numbering.test.js +386 -386
- package/test/supply-margin.test.js +177 -177
- package/test/swarm-follows-readers.test.js +174 -174
- package/test/swarm-reach.test.js +130 -130
- package/test/swarm-selection.test.js +220 -220
- package/test/tail-duplication.test.js +167 -167
- package/test/the-channel-has-both-ends.test.js +91 -91
- package/test/the-torrent-thread-serves-bytes.test.js +107 -107
- package/test/thread-pool.test.js +53 -53
- package/test/timeline.test.js +84 -84
- package/test/torrent-contents.test.js +235 -235
- package/test/torrent-cost.test.js +71 -71
- package/test/torrent-files.test.js +221 -221
- package/test/torrent-naming.test.js +255 -255
- package/test/tracks-begin-together.test.js +209 -209
- package/test/tunnel-renewal.test.js +167 -167
- package/test/two-viewers-one-picture.test.js +407 -407
- package/test/upload-hurry.test.js +186 -186
- package/test/usrsctp-state.test.js +131 -131
- package/test/video-facts.test.js +102 -102
- package/test/viewer-assignments.test.js +143 -143
- package/test/viewer-outputs.test.js +285 -285
- package/test/viewer-presence.test.js +116 -116
- package/test/viewer-requests-admission.test.js +89 -89
- package/test/viewer-states.test.js +189 -189
- package/test/viewer-subtitle-subscription.test.js +96 -96
- package/test/viewer.test.js +139 -139
- package/test/viewing-generations.test.js +390 -390
- package/test/visible-rung.test.js +37 -37
- package/test/wait-ledger.test.js +100 -100
- package/test/webrtc-shim.test.js +149 -149
- package/test/wedge-certainty.test.js +131 -131
- package/test/withdraw-piece-claim.test.js +212 -212
- package/test/worker-channel.test.js +158 -158
- package/utils/client-log-file.js +151 -151
- package/utils/logger.js +312 -312
- package/utils/parse-range.js +25 -25
- package/utils/perf.js +59 -59
- package/utils/torrent-source-key.js +38 -38
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Many viewers of one output are handed one session.
|
|
3
|
-
*
|
|
4
|
-
* This is the product behaviour the whole output key exists for: two people
|
|
5
|
-
* watching the same film, in the same form, at whatever moments, are served by
|
|
6
|
-
* one encode and one set of pieces. It had no check of any kind — every test
|
|
7
|
-
* that needed a session either built one by hand or replaced
|
|
8
|
-
* `createOrGetSession` with a stub — so the one line that decides it could have
|
|
9
|
-
* been broken by any edit without a word.
|
|
10
|
-
*
|
|
11
|
-
* What is checked here is the decision, not the creation: a session under the
|
|
12
|
-
* name this request produces already exists, and the request must be answered
|
|
13
|
-
* with it rather than with a new one. The name is the format produced, so the
|
|
14
|
-
* request is first taken as far as the format — the file's facts and its
|
|
15
|
-
* keyframe table are given as already known — and making the FIRST session,
|
|
16
|
-
* which reaches the disk, is a different subject.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import test from "node:test";
|
|
20
|
-
import assert from "node:assert/strict";
|
|
21
|
-
import { wireOutputs } from "../services/server/wire-outputs.js";
|
|
22
|
-
import { AudioOutput, CutGrid, OutputSpec, VideoOutput } from "../services/encode/output/OutputSpec.js";
|
|
23
|
-
import { Timeline } from "../services/encode/output/Timeline.js";
|
|
24
|
-
import { SourceFile } from "../services/media/SourceFile.js";
|
|
25
|
-
|
|
26
|
-
const TORRENT = "torrent:11f0929918e2b5aa2e5b71ecdbe5c0f1a4bbf7d1";
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* What a browser asks for: this file, copied, sound inside the stream.
|
|
30
|
-
*
|
|
31
|
-
* @param {object} [over]
|
|
32
|
-
* @returns {object}
|
|
33
|
-
*/
|
|
34
|
-
function request(over = {}) {
|
|
35
|
-
return {
|
|
36
|
-
sourceKey: TORRENT,
|
|
37
|
-
fileIndex: 0,
|
|
38
|
-
fileName: "film.mkv",
|
|
39
|
-
transcodeVideo: false,
|
|
40
|
-
transcodeAudio: false,
|
|
41
|
-
audioTrackIndex: 0,
|
|
42
|
-
segmentFormatId: "fmp4",
|
|
43
|
-
...over
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* The name the manager will look that request up under, built the way it builds
|
|
49
|
-
* it: a copied picture carrying its own sound, cut at the file's keyframes.
|
|
50
|
-
*
|
|
51
|
-
* @returns {string}
|
|
52
|
-
*/
|
|
53
|
-
function specOfThatOutput() {
|
|
54
|
-
return new OutputSpec({
|
|
55
|
-
sourceKey: TORRENT,
|
|
56
|
-
segmentFormatId: "fmp4",
|
|
57
|
-
grid: new CutGrid({ kind: "keyframe", fileIndex: 0 }),
|
|
58
|
-
video: new VideoOutput({ fileIndex: 0, encode: null }),
|
|
59
|
-
audio: new AudioOutput({ fileIndex: 0, trackIndex: 0, transcode: false })
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** @returns {string} */
|
|
64
|
-
function nameOfThatOutput() {
|
|
65
|
-
return specOfThatOutput().toName();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @param {object} manager
|
|
70
|
-
* @returns {object} The session it is seeded with.
|
|
71
|
-
*/
|
|
72
|
-
function seedOneSession(manager) {
|
|
73
|
-
const session = {
|
|
74
|
-
id: nameOfThatOutput(),
|
|
75
|
-
spec: specOfThatOutput(),
|
|
76
|
-
outputKey: "seeded",
|
|
77
|
-
state: "ready",
|
|
78
|
-
file: new SourceFile({ sourceKey: TORRENT, fileIndex: 0, name: "film.mkv" }),
|
|
79
|
-
timeline: new Timeline({ boundaries: [0, 4, 8], cutGrid: "keyframe" }),
|
|
80
|
-
runs: new Set(),
|
|
81
|
-
progress: { processedSeconds: 0, startPositionSeconds: 0, updatedAt: Date.now() },
|
|
82
|
-
lastAccessedAt: Date.now(),
|
|
83
|
-
// READY, because a joining viewer waits for the session to be able to
|
|
84
|
-
// answer them, and this file is about which session answers rather than
|
|
85
|
-
// about that wait. A session publishing its own playlist from the probed
|
|
86
|
-
// duration has nothing to wait for; left out, every check here would sit
|
|
87
|
-
// out the warm-up deadline and would be measuring that timer.
|
|
88
|
-
useSyntheticPlaylist: true
|
|
89
|
-
};
|
|
90
|
-
manager.outputs.set(session.id, session);
|
|
91
|
-
return session;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* What the proxy already knows about the file when a second viewer arrives:
|
|
96
|
-
* what a probe said, and where its keyframes are.
|
|
97
|
-
*
|
|
98
|
-
* @param {object} manager
|
|
99
|
-
*/
|
|
100
|
-
function knownFile(manager) {
|
|
101
|
-
manager.getCachedMediaInfo = () => ({ durationSeconds: 8, width: 1920, height: 1080, fps: 24 });
|
|
102
|
-
manager.keyframeTables.learn({ sourceKey: TORRENT, fileIndex: 0 }, { times: [0, 4], format: "test" });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
test("a second viewer of one output is served by the session that exists", async (t) => {
|
|
106
|
-
const manager = wireOutputs({
|
|
107
|
-
enabled: true,
|
|
108
|
-
ffmpegBin: "ffmpeg",
|
|
109
|
-
localBindHost: "127.0.0.1",
|
|
110
|
-
localPort: 9090
|
|
111
|
-
});
|
|
112
|
-
t.after(() => manager.lifecycle.disposeAll());
|
|
113
|
-
// No plan runs here: this file is about which session answers a request, and
|
|
114
|
-
// placing encoders would spawn real processes.
|
|
115
|
-
manager.encodeRuns.planEncodersNow = () => {};
|
|
116
|
-
manager.encodeRuns.planEncodersSoon = () => {};
|
|
117
|
-
knownFile(manager);
|
|
118
|
-
const seeded = seedOneSession(manager);
|
|
119
|
-
|
|
120
|
-
const answered = await manager.viewerRequests.createOrGetSession(request({ consumerId: "viewer-two" }));
|
|
121
|
-
|
|
122
|
-
assert.equal(answered, seeded, "one output, one session");
|
|
123
|
-
assert.ok(manager.viewers.forOutput(seeded).has("viewer-two"), "and the second viewer is on it");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("a request whose parameters differ is not that session", async (t) => {
|
|
127
|
-
// The other half, and it is what makes the first mean anything: the name is
|
|
128
|
-
// the output's, so a request producing something else must not find it. Here
|
|
129
|
-
// the sound is to be re-encoded, which is a different output and a different
|
|
130
|
-
// set of bytes.
|
|
131
|
-
const manager = wireOutputs({
|
|
132
|
-
enabled: true,
|
|
133
|
-
ffmpegBin: "ffmpeg",
|
|
134
|
-
localBindHost: "127.0.0.1",
|
|
135
|
-
localPort: 9090,
|
|
136
|
-
// A session made for real here waits for a playlist that nothing is going
|
|
137
|
-
// to write. Zero, so this check spends no time on a deadline it is not
|
|
138
|
-
// about — the deadline has its own subject elsewhere.
|
|
139
|
-
startupWaitMs: 0
|
|
140
|
-
});
|
|
141
|
-
t.after(() => manager.lifecycle.disposeAll());
|
|
142
|
-
manager.encodeRuns.planEncodersNow = () => {};
|
|
143
|
-
manager.encodeRuns.planEncodersSoon = () => {};
|
|
144
|
-
const seeded = seedOneSession(manager);
|
|
145
|
-
|
|
146
|
-
let answered = null;
|
|
147
|
-
try {
|
|
148
|
-
answered = await manager.viewerRequests.createOrGetSession(request({ consumerId: "viewer-two", transcodeAudio: true }));
|
|
149
|
-
} catch {
|
|
150
|
-
// Making a session for real needs a probe and a disk, which this file does
|
|
151
|
-
// not give it. Failing there is the proof: it did not take the seeded one.
|
|
152
|
-
answered = null;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
assert.notEqual(answered, seeded);
|
|
156
|
-
assert.ok(!manager.viewers.forOutput(seeded).has("viewer-two"), "and nobody was added to somebody else's output");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("two screens that come to the same format share one output, and a different format does not", async (t) => {
|
|
160
|
-
const manager = wireOutputs({
|
|
161
|
-
enabled: true,
|
|
162
|
-
ffmpegBin: "ffmpeg",
|
|
163
|
-
localBindHost: "127.0.0.1",
|
|
164
|
-
localPort: 9090,
|
|
165
|
-
startupWaitMs: 0,
|
|
166
|
-
// A host with no ladder to choose from: the box asked for, fitted to the
|
|
167
|
-
// source, is what is produced.
|
|
168
|
-
videoEncoder: { kind: "vaapi", name: "h264_vaapi", inputArgs: [] }
|
|
169
|
-
});
|
|
170
|
-
t.after(() => manager.lifecycle.disposeAll());
|
|
171
|
-
manager.encodeRuns.planEncodersNow = () => {};
|
|
172
|
-
manager.encodeRuns.planEncodersSoon = () => {};
|
|
173
|
-
knownFile(manager);
|
|
174
|
-
const spec = new OutputSpec({
|
|
175
|
-
sourceKey: TORRENT,
|
|
176
|
-
segmentFormatId: "fmp4",
|
|
177
|
-
grid: new CutGrid({ kind: "uniform", fileIndex: 0 }),
|
|
178
|
-
video: new VideoOutput({
|
|
179
|
-
fileIndex: 0,
|
|
180
|
-
encode: { encoder: "h264_vaapi", width: 1920, height: 1080, fps: 24, preset: null, tonemap: false }
|
|
181
|
-
}),
|
|
182
|
-
audio: new AudioOutput({ fileIndex: 0, trackIndex: 0, transcode: false })
|
|
183
|
-
});
|
|
184
|
-
const seeded = { ...seedOneSession(manager), id: spec.toName(), spec, timeline: new Timeline({ boundaries: [0, 4, 8], cutGrid: "uniform" }) };
|
|
185
|
-
manager.outputs.delete(nameOfThatOutput());
|
|
186
|
-
manager.outputs.set(seeded.id, seeded);
|
|
187
|
-
|
|
188
|
-
// A taller window than the film: fitted to the source it is the same 1920x1080.
|
|
189
|
-
const joined = await manager.viewerRequests.createOrGetSession(
|
|
190
|
-
request({ consumerId: "tall-window", transcodeVideo: true, targetWidth: 1920, targetHeight: 1200 })
|
|
191
|
-
);
|
|
192
|
-
assert.equal(joined, seeded, "the key names the format produced, not the window it was asked for");
|
|
193
|
-
|
|
194
|
-
let other = null;
|
|
195
|
-
try {
|
|
196
|
-
other = await manager.viewerRequests.createOrGetSession(
|
|
197
|
-
request({ consumerId: "small-window", transcodeVideo: true, targetWidth: 1280, targetHeight: 720 })
|
|
198
|
-
);
|
|
199
|
-
} catch {
|
|
200
|
-
other = null;
|
|
201
|
-
}
|
|
202
|
-
assert.notEqual(other, seeded, "a smaller picture is another output");
|
|
203
|
-
assert.ok(!manager.viewers.forOutput(seeded).has("small-window"));
|
|
204
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file Many viewers of one output are handed one session.
|
|
3
|
+
*
|
|
4
|
+
* This is the product behaviour the whole output key exists for: two people
|
|
5
|
+
* watching the same film, in the same form, at whatever moments, are served by
|
|
6
|
+
* one encode and one set of pieces. It had no check of any kind — every test
|
|
7
|
+
* that needed a session either built one by hand or replaced
|
|
8
|
+
* `createOrGetSession` with a stub — so the one line that decides it could have
|
|
9
|
+
* been broken by any edit without a word.
|
|
10
|
+
*
|
|
11
|
+
* What is checked here is the decision, not the creation: a session under the
|
|
12
|
+
* name this request produces already exists, and the request must be answered
|
|
13
|
+
* with it rather than with a new one. The name is the format produced, so the
|
|
14
|
+
* request is first taken as far as the format — the file's facts and its
|
|
15
|
+
* keyframe table are given as already known — and making the FIRST session,
|
|
16
|
+
* which reaches the disk, is a different subject.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import test from "node:test";
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
import { wireOutputs } from "../services/server/wire-outputs.js";
|
|
22
|
+
import { AudioOutput, CutGrid, OutputSpec, VideoOutput } from "../services/encode/output/OutputSpec.js";
|
|
23
|
+
import { Timeline } from "../services/encode/output/Timeline.js";
|
|
24
|
+
import { SourceFile } from "../services/media/SourceFile.js";
|
|
25
|
+
|
|
26
|
+
const TORRENT = "torrent:11f0929918e2b5aa2e5b71ecdbe5c0f1a4bbf7d1";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* What a browser asks for: this file, copied, sound inside the stream.
|
|
30
|
+
*
|
|
31
|
+
* @param {object} [over]
|
|
32
|
+
* @returns {object}
|
|
33
|
+
*/
|
|
34
|
+
function request(over = {}) {
|
|
35
|
+
return {
|
|
36
|
+
sourceKey: TORRENT,
|
|
37
|
+
fileIndex: 0,
|
|
38
|
+
fileName: "film.mkv",
|
|
39
|
+
transcodeVideo: false,
|
|
40
|
+
transcodeAudio: false,
|
|
41
|
+
audioTrackIndex: 0,
|
|
42
|
+
segmentFormatId: "fmp4",
|
|
43
|
+
...over
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The name the manager will look that request up under, built the way it builds
|
|
49
|
+
* it: a copied picture carrying its own sound, cut at the file's keyframes.
|
|
50
|
+
*
|
|
51
|
+
* @returns {string}
|
|
52
|
+
*/
|
|
53
|
+
function specOfThatOutput() {
|
|
54
|
+
return new OutputSpec({
|
|
55
|
+
sourceKey: TORRENT,
|
|
56
|
+
segmentFormatId: "fmp4",
|
|
57
|
+
grid: new CutGrid({ kind: "keyframe", fileIndex: 0 }),
|
|
58
|
+
video: new VideoOutput({ fileIndex: 0, encode: null }),
|
|
59
|
+
audio: new AudioOutput({ fileIndex: 0, trackIndex: 0, transcode: false })
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @returns {string} */
|
|
64
|
+
function nameOfThatOutput() {
|
|
65
|
+
return specOfThatOutput().toName();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {object} manager
|
|
70
|
+
* @returns {object} The session it is seeded with.
|
|
71
|
+
*/
|
|
72
|
+
function seedOneSession(manager) {
|
|
73
|
+
const session = {
|
|
74
|
+
id: nameOfThatOutput(),
|
|
75
|
+
spec: specOfThatOutput(),
|
|
76
|
+
outputKey: "seeded",
|
|
77
|
+
state: "ready",
|
|
78
|
+
file: new SourceFile({ sourceKey: TORRENT, fileIndex: 0, name: "film.mkv" }),
|
|
79
|
+
timeline: new Timeline({ boundaries: [0, 4, 8], cutGrid: "keyframe" }),
|
|
80
|
+
runs: new Set(),
|
|
81
|
+
progress: { processedSeconds: 0, startPositionSeconds: 0, updatedAt: Date.now() },
|
|
82
|
+
lastAccessedAt: Date.now(),
|
|
83
|
+
// READY, because a joining viewer waits for the session to be able to
|
|
84
|
+
// answer them, and this file is about which session answers rather than
|
|
85
|
+
// about that wait. A session publishing its own playlist from the probed
|
|
86
|
+
// duration has nothing to wait for; left out, every check here would sit
|
|
87
|
+
// out the warm-up deadline and would be measuring that timer.
|
|
88
|
+
useSyntheticPlaylist: true
|
|
89
|
+
};
|
|
90
|
+
manager.outputs.set(session.id, session);
|
|
91
|
+
return session;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* What the proxy already knows about the file when a second viewer arrives:
|
|
96
|
+
* what a probe said, and where its keyframes are.
|
|
97
|
+
*
|
|
98
|
+
* @param {object} manager
|
|
99
|
+
*/
|
|
100
|
+
function knownFile(manager) {
|
|
101
|
+
manager.getCachedMediaInfo = () => ({ durationSeconds: 8, width: 1920, height: 1080, fps: 24 });
|
|
102
|
+
manager.keyframeTables.learn({ sourceKey: TORRENT, fileIndex: 0 }, { times: [0, 4], format: "test" });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
test("a second viewer of one output is served by the session that exists", async (t) => {
|
|
106
|
+
const manager = wireOutputs({
|
|
107
|
+
enabled: true,
|
|
108
|
+
ffmpegBin: "ffmpeg",
|
|
109
|
+
localBindHost: "127.0.0.1",
|
|
110
|
+
localPort: 9090
|
|
111
|
+
});
|
|
112
|
+
t.after(() => manager.lifecycle.disposeAll());
|
|
113
|
+
// No plan runs here: this file is about which session answers a request, and
|
|
114
|
+
// placing encoders would spawn real processes.
|
|
115
|
+
manager.encodeRuns.planEncodersNow = () => {};
|
|
116
|
+
manager.encodeRuns.planEncodersSoon = () => {};
|
|
117
|
+
knownFile(manager);
|
|
118
|
+
const seeded = seedOneSession(manager);
|
|
119
|
+
|
|
120
|
+
const answered = await manager.viewerRequests.createOrGetSession(request({ consumerId: "viewer-two" }));
|
|
121
|
+
|
|
122
|
+
assert.equal(answered, seeded, "one output, one session");
|
|
123
|
+
assert.ok(manager.viewers.forOutput(seeded).has("viewer-two"), "and the second viewer is on it");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("a request whose parameters differ is not that session", async (t) => {
|
|
127
|
+
// The other half, and it is what makes the first mean anything: the name is
|
|
128
|
+
// the output's, so a request producing something else must not find it. Here
|
|
129
|
+
// the sound is to be re-encoded, which is a different output and a different
|
|
130
|
+
// set of bytes.
|
|
131
|
+
const manager = wireOutputs({
|
|
132
|
+
enabled: true,
|
|
133
|
+
ffmpegBin: "ffmpeg",
|
|
134
|
+
localBindHost: "127.0.0.1",
|
|
135
|
+
localPort: 9090,
|
|
136
|
+
// A session made for real here waits for a playlist that nothing is going
|
|
137
|
+
// to write. Zero, so this check spends no time on a deadline it is not
|
|
138
|
+
// about — the deadline has its own subject elsewhere.
|
|
139
|
+
startupWaitMs: 0
|
|
140
|
+
});
|
|
141
|
+
t.after(() => manager.lifecycle.disposeAll());
|
|
142
|
+
manager.encodeRuns.planEncodersNow = () => {};
|
|
143
|
+
manager.encodeRuns.planEncodersSoon = () => {};
|
|
144
|
+
const seeded = seedOneSession(manager);
|
|
145
|
+
|
|
146
|
+
let answered = null;
|
|
147
|
+
try {
|
|
148
|
+
answered = await manager.viewerRequests.createOrGetSession(request({ consumerId: "viewer-two", transcodeAudio: true }));
|
|
149
|
+
} catch {
|
|
150
|
+
// Making a session for real needs a probe and a disk, which this file does
|
|
151
|
+
// not give it. Failing there is the proof: it did not take the seeded one.
|
|
152
|
+
answered = null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
assert.notEqual(answered, seeded);
|
|
156
|
+
assert.ok(!manager.viewers.forOutput(seeded).has("viewer-two"), "and nobody was added to somebody else's output");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("two screens that come to the same format share one output, and a different format does not", async (t) => {
|
|
160
|
+
const manager = wireOutputs({
|
|
161
|
+
enabled: true,
|
|
162
|
+
ffmpegBin: "ffmpeg",
|
|
163
|
+
localBindHost: "127.0.0.1",
|
|
164
|
+
localPort: 9090,
|
|
165
|
+
startupWaitMs: 0,
|
|
166
|
+
// A host with no ladder to choose from: the box asked for, fitted to the
|
|
167
|
+
// source, is what is produced.
|
|
168
|
+
videoEncoder: { kind: "vaapi", name: "h264_vaapi", inputArgs: [] }
|
|
169
|
+
});
|
|
170
|
+
t.after(() => manager.lifecycle.disposeAll());
|
|
171
|
+
manager.encodeRuns.planEncodersNow = () => {};
|
|
172
|
+
manager.encodeRuns.planEncodersSoon = () => {};
|
|
173
|
+
knownFile(manager);
|
|
174
|
+
const spec = new OutputSpec({
|
|
175
|
+
sourceKey: TORRENT,
|
|
176
|
+
segmentFormatId: "fmp4",
|
|
177
|
+
grid: new CutGrid({ kind: "uniform", fileIndex: 0 }),
|
|
178
|
+
video: new VideoOutput({
|
|
179
|
+
fileIndex: 0,
|
|
180
|
+
encode: { encoder: "h264_vaapi", width: 1920, height: 1080, fps: 24, preset: null, tonemap: false }
|
|
181
|
+
}),
|
|
182
|
+
audio: new AudioOutput({ fileIndex: 0, trackIndex: 0, transcode: false })
|
|
183
|
+
});
|
|
184
|
+
const seeded = { ...seedOneSession(manager), id: spec.toName(), spec, timeline: new Timeline({ boundaries: [0, 4, 8], cutGrid: "uniform" }) };
|
|
185
|
+
manager.outputs.delete(nameOfThatOutput());
|
|
186
|
+
manager.outputs.set(seeded.id, seeded);
|
|
187
|
+
|
|
188
|
+
// A taller window than the film: fitted to the source it is the same 1920x1080.
|
|
189
|
+
const joined = await manager.viewerRequests.createOrGetSession(
|
|
190
|
+
request({ consumerId: "tall-window", transcodeVideo: true, targetWidth: 1920, targetHeight: 1200 })
|
|
191
|
+
);
|
|
192
|
+
assert.equal(joined, seeded, "the key names the format produced, not the window it was asked for");
|
|
193
|
+
|
|
194
|
+
let other = null;
|
|
195
|
+
try {
|
|
196
|
+
other = await manager.viewerRequests.createOrGetSession(
|
|
197
|
+
request({ consumerId: "small-window", transcodeVideo: true, targetWidth: 1280, targetHeight: 720 })
|
|
198
|
+
);
|
|
199
|
+
} catch {
|
|
200
|
+
other = null;
|
|
201
|
+
}
|
|
202
|
+
assert.notEqual(other, seeded, "a smaller picture is another output");
|
|
203
|
+
assert.ok(!manager.viewers.forOutput(seeded).has("small-window"));
|
|
204
|
+
});
|