@torrent-tv/proxy 2.9.58 → 2.9.59
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/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/services/hls-session-manager.js +22 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 2.9.59
|
|
2
|
+
|
|
3
|
+
- **Chore**: Diagnostics for seek handling. The session-start line now carries the proxy version (`transcode <id> start (proxy 2.9.59) "<file>"`), so a field report answers "is the host running the build I published?" by itself. And the seek restart guard added in 2.9.58 now states its decision: either `seek #N HELD — current run has produced Xs of the Ys first segment` or, on the restart line, why it was allowed (`run is dead` / `run produced Xs (first segment done)` / `grace expired`). Previously a permitted restart was indistinguishable in the log from the runaway ping-pong the guard exists to stop, which made diagnosing "seek still did not work" guesswork.
|
|
4
|
+
|
|
1
5
|
## 2.9.58
|
|
2
6
|
|
|
3
7
|
- **Fix**: A single user seek could leave playback permanently stuck with a flickering loading pill and no video. The encoder was allowed to restart at a new position even when the current run had not yet produced a **single segment**, so each restart destroyed the previous one's work and began the wait again — self-perpetuating, because the first segment after a seek is the slowest thing the pipeline does (field log 2026-08-02: restarts at #617 → #717 → #732 → #732 every 5-7 s, none producing anything). The extra targets were not further user seeks: unable to get its segment, the player SCANS the playlist (our synthetic VOD playlist lists every segment, so from its side they all exist), and each far-enough probe looked like a fresh seek. A seek restart now waits for the current run to produce its first segment (bounded by a 30 s grace, and skipped entirely if the run has died), which makes the scan harmless and lets one genuine seek complete. Independent of segment format.
|
package/package.json
CHANGED
|
@@ -14,7 +14,11 @@ import os from "node:os";
|
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
import { spawn } from "node:child_process";
|
|
17
|
+
import { createRequire } from "node:module";
|
|
17
18
|
import { logger } from "../utils/logger.js";
|
|
19
|
+
|
|
20
|
+
/** Own package version, stamped onto session-start log lines. */
|
|
21
|
+
const PROXY_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
18
22
|
import {
|
|
19
23
|
softwareDescriptor,
|
|
20
24
|
chooseSoftwareEncodeSettings,
|
|
@@ -1077,7 +1081,10 @@ export class HlsSessionManager {
|
|
|
1077
1081
|
this.sessionIdBySource.set(sourceMapKey, sessionId);
|
|
1078
1082
|
|
|
1079
1083
|
logger.info(
|
|
1080
|
-
|
|
1084
|
+
// Proxy version on the session-start line: a field report always includes
|
|
1085
|
+
// one of these, so "is the host actually running the build I published?"
|
|
1086
|
+
// is answered by the log itself instead of a round trip to the machine.
|
|
1087
|
+
`transcode ${sessionId} start (proxy ${PROXY_VERSION}) "${logName}" ` +
|
|
1081
1088
|
`video=${transcodeVideo ? `${this.videoEncoder.name}${softwarePreset ? `/${softwarePreset}` : ""}` : "copy"} ` +
|
|
1082
1089
|
`audio=${transcodeAudio ? "aac" : "copy"} ` +
|
|
1083
1090
|
// Branch tag for log correlation: A = video re-encode (fixed GOP, grid
|
|
@@ -2040,13 +2047,26 @@ export class HlsSessionManager {
|
|
|
2040
2047
|
producedThisRun < this.segmentDurationSec &&
|
|
2041
2048
|
sinceLastRestart < RUN_FIRST_SEGMENT_GRACE_MS
|
|
2042
2049
|
) {
|
|
2050
|
+
logger.info(
|
|
2051
|
+
`transcode ${session.id} seek #${target} HELD — current run has produced ` +
|
|
2052
|
+
`${producedThisRun.toFixed(1)}s of the ${this.segmentDurationSec}s first segment ` +
|
|
2053
|
+
`(${(sinceLastRestart / 1000).toFixed(1)}s into a ${RUN_FIRST_SEGMENT_GRACE_MS / 1000}s grace)`
|
|
2054
|
+
);
|
|
2043
2055
|
session.seekSettleTimer = setTimeout(() => this.#fireSettledSeek(session), SEEK_SETTLE_MS);
|
|
2044
2056
|
session.seekSettleTimer.unref?.();
|
|
2045
2057
|
return;
|
|
2046
2058
|
}
|
|
2059
|
+
// Why the restart was allowed — the counterpart of the HELD line above.
|
|
2060
|
+
// Without it a restart is indistinguishable from the runaway ping-pong this
|
|
2061
|
+
// guard exists to stop, and diagnosing a field report becomes guesswork.
|
|
2062
|
+
const allowedBecause = !runIsAlive
|
|
2063
|
+
? "run is dead"
|
|
2064
|
+
: producedThisRun >= this.segmentDurationSec
|
|
2065
|
+
? `run produced ${producedThisRun.toFixed(1)}s (first segment done)`
|
|
2066
|
+
: `grace of ${RUN_FIRST_SEGMENT_GRACE_MS / 1000}s expired`;
|
|
2047
2067
|
session.seekTarget = null;
|
|
2048
2068
|
session.seekFirstFarAt = 0;
|
|
2049
|
-
logger.info(`transcode ${session.id} seek settle → restart at segment #${target}`);
|
|
2069
|
+
logger.info(`transcode ${session.id} seek settle → restart at segment #${target} (${allowedBecause})`);
|
|
2050
2070
|
void this.#startEncodeRun(session, target);
|
|
2051
2071
|
}
|
|
2052
2072
|
|