@torrent-tv/proxy 2.89.14 → 2.89.16
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 +8 -0
- package/README.md +8 -0
- package/docs/logs.md +53 -12
- package/package.json +1 -1
- package/server.js +7 -4
- package/services/torrent/worker/files-in-use.js +35 -0
- package/services/torrent/worker/worker.js +17 -12
- package/test/client-log-file.test.js +17 -1
- package/test/files-in-use.test.js +34 -0
- package/utils/client-log-file.js +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 2.89.16
|
|
2
|
+
|
|
3
|
+
- **Fix**: A proxy started without `--log-file` keeps the browser's log. The page sends its log to the proxy for most of a viewing, and the proxy wrote it only to per-session files beside its own log file; with no log file named, which is every run from npm or Docker other than the Home Assistant addon, the browser's half of every viewing was dropped. It now goes to the console, beside the proxy's lines, each prefixed `client <sessionId>`. `docs/logs.md` states where the log goes for each way of starting the proxy (torrent-tv/meta#96).
|
|
4
|
+
|
|
5
|
+
## 2.89.15
|
|
6
|
+
|
|
7
|
+
- **Fix**: A torrent downloaded whole is no longer removed while something is still reading it. The sweep that writes whole files out and removes the torrent asked only who had stated a need, and a read whose pieces are all here waits for nothing and states nothing. On the stand on 2026-10-04 the torrent was removed under ffmpeg's own read of the film: the read ended with "Piece store is closed" 277 MB in, ffmpeg reported `Stream ends prematurely`, the copied picture lost 2.6 s, and the page refused to start with "Prepared media contains a timestamp gap" (torrent-tv/meta#105). Open reads now count as use.
|
|
8
|
+
|
|
1
9
|
## 2.89.14
|
|
2
10
|
|
|
3
11
|
- **Fix**: A failed run counts only the source's own streams. The planner probes with an output, so ffmpeg's banner goes on to list the streams it maps and writes, and those were counted too: a file with one picture, one soundtrack, one subtitle and one font read as `2 video, 2 audio, 1 subtitle, 3 other` in the field — enough to make a second soundtrack look present when it is not.
|
package/README.md
CHANGED
|
@@ -303,6 +303,7 @@ npm start -- --server-url http://localhost:3000
|
|
|
303
303
|
| `--name` | hostname | Display name in registry |
|
|
304
304
|
| `--token` | — | Auth token for register/heartbeat |
|
|
305
305
|
| `--ffmpeg-bin` | bundled | Path to custom ffmpeg binary |
|
|
306
|
+
| `--log-file` | — | Also write the log to this file, and the browser's lines to `client-*.log` beside it; without it everything goes to the console only. See `docs/logs.md` |
|
|
306
307
|
| `--no-transcode-audio` | — | Disable HLS audio transcoding |
|
|
307
308
|
| `--help` | — | Print all options and exit |
|
|
308
309
|
|
|
@@ -313,6 +314,13 @@ docker build -t torrent-tv-proxy .
|
|
|
313
314
|
docker run torrent-tv-proxy --server-url http://my-server:8080
|
|
314
315
|
```
|
|
315
316
|
|
|
317
|
+
Without `--log-file` the log is only in `docker logs`, which recreating the
|
|
318
|
+
container deletes. To keep it, put it on a volume:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
docker run -v ttv-proxy-logs:/logs torrent-tv-proxy --server-url http://my-server:8080 --log-file /logs/proxy.log
|
|
322
|
+
```
|
|
323
|
+
|
|
316
324
|
## Full End-to-End Flow
|
|
317
325
|
|
|
318
326
|
```mermaid
|
package/docs/logs.md
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
# Logs — where to look
|
|
2
2
|
|
|
3
3
|
No browser console copy-paste is needed: the page forwards its own log. But it
|
|
4
|
-
lands in TWO places depending on the phase of the session
|
|
5
|
-
|
|
4
|
+
lands in TWO places depending on the phase of the session — the registry server
|
|
5
|
+
and the proxy — see "Browser logs are in TWO places" below before concluding
|
|
6
6
|
anything from a half-session.
|
|
7
7
|
|
|
8
|
+
## Where the proxy writes its log, by how it is started
|
|
9
|
+
|
|
10
|
+
The proxy is deployment-agnostic: the Home Assistant addon is one way to run it,
|
|
11
|
+
npm and Docker on Linux, macOS or Windows are others. Where its log goes is
|
|
12
|
+
decided by one start parameter, `--log-file <path>`, and nothing else:
|
|
13
|
+
|
|
14
|
+
- **With `--log-file`**: every line goes to the console AND to that file
|
|
15
|
+
(appended, rotated to `<path>.1` past 1 GiB), and the browser's lines go to
|
|
16
|
+
one file per viewing session in the same directory (see "The files on the
|
|
17
|
+
proxy" below). The directory of the file must exist.
|
|
18
|
+
- **Without it**: everything goes to the console only, the browser's lines
|
|
19
|
+
included, each prefixed `client <sessionId:8>`. Before torrent-tv/meta#96 the
|
|
20
|
+
browser's lines were dropped in this case. The console is kept by whatever
|
|
21
|
+
started the process, and only as long as that keeps it.
|
|
22
|
+
|
|
23
|
+
| How it is started | Log | Browser lines | Survives |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| Home Assistant addon | `/data/proxy.log` (`run.sh` passes `--log-file /data/proxy.log`) | `/data/client-*.log` | restarts and addon updates |
|
|
26
|
+
| npm / `npx`, any OS, no `--log-file` | the terminal | the terminal | nothing |
|
|
27
|
+
| npm under systemd, no `--log-file` | the unit's journal: `journalctl -u <unit>` | the same journal | restarts, within journald's limits |
|
|
28
|
+
| npm with `--log-file`, e.g. `/var/log/torrent-tv-proxy/proxy.log`, `~/Library/Logs/torrent-tv/proxy.log`, `%LOCALAPPDATA%\torrent-tv\proxy.log` | that file | `client-*.log` beside it | restarts and updates |
|
|
29
|
+
| Docker, no `--log-file` | `docker logs <container>` | the same | only the container's life: recreating it deletes the log |
|
|
30
|
+
| Docker with a volume and `--log-file`, e.g. `-v ttv-proxy-logs:/logs … --log-file /logs/proxy.log` | the file in the volume | `client-*.log` in the volume | recreating the container |
|
|
31
|
+
|
|
32
|
+
So on any host where the log matters, pass `--log-file` and point it at a
|
|
33
|
+
directory that outlives the process — which is what the addon does.
|
|
34
|
+
|
|
8
35
|
## HA proxy (aarch64, addon `b34a1737_torrent_tv_proxy`)
|
|
9
36
|
|
|
10
37
|
Image `b34a1737/aarch64-addon-torrent_tv_proxy:<version>` = `@torrent-tv/proxy` `<version>` (see `ha-addon/torrent_tv_proxy/config.yaml`).
|
|
@@ -30,15 +57,27 @@ SSH to HA requires `MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh
|
|
|
30
57
|
|
|
31
58
|
## DO server (webauth.courses, `infra-server-1`)
|
|
32
59
|
|
|
33
|
-
- Server is `infra-server-1` (`ghcr.io/torrent-tv/server
|
|
34
|
-
-
|
|
35
|
-
-
|
|
60
|
+
- Server is `infra-server-1` (`ghcr.io/torrent-tv/server`) on `do` (`206.189.97.152`).
|
|
61
|
+
- Every container there logs to the host journal, and rsyslog writes one file per
|
|
62
|
+
source to `/var/log/torrent-tv/`; the files and the journal survive the
|
|
63
|
+
deployments that recreate the containers (`infra` README, "Logs";
|
|
64
|
+
torrent-tv/meta#96):
|
|
65
|
+
- `server.log` — the server's own lines;
|
|
66
|
+
- `client.log` — browser lines the server received, prefixed
|
|
67
|
+
`[client <tag> <id> sig=<webrtcSessionId>]`
|
|
68
|
+
(`server/routes/api/client-logs/post.js`);
|
|
69
|
+
- `nginx.log`, `doco-cd.log`.
|
|
70
|
+
- Frontend logs reach the droplet only in the phases the proxy cannot be reached
|
|
71
|
+
in — see "Browser logs are in TWO places" below.
|
|
72
|
+
- Reading:
|
|
36
73
|
```bash
|
|
37
|
-
ssh do "
|
|
74
|
+
ssh do "tail -n 300 /var/log/torrent-tv/client.log"
|
|
38
75
|
# filter a single viewing:
|
|
39
|
-
ssh do "
|
|
40
|
-
ssh do "
|
|
76
|
+
ssh do "grep 003ed2fd /var/log/torrent-tv/client.log /var/log/torrent-tv/server.log"
|
|
77
|
+
ssh do "journalctl -t torrent-tv-server --since '-1h' -o short-iso-precise"
|
|
41
78
|
```
|
|
79
|
+
- `docker logs infra-server-1` shows only the CURRENT container's lines — a
|
|
80
|
+
deployment gives it a new container. Use the files or `journalctl -t`.
|
|
42
81
|
- No need to open eruda or copy the browser console on a phone — it is forwarded.
|
|
43
82
|
|
|
44
83
|
## Browser logs are in TWO places, split by phase — read both
|
|
@@ -52,7 +91,7 @@ The split is by PHASE, and neither half is the whole session:
|
|
|
52
91
|
|
|
53
92
|
1. **Before a transport exists** — the page opening, the proxy being chosen, a
|
|
54
93
|
connection failing — there is nowhere else to send it, so it goes to the
|
|
55
|
-
SERVER (
|
|
94
|
+
SERVER (`/var/log/torrent-tv/client.log` on the droplet);
|
|
56
95
|
2. **From the moment the data channel is up**, the page's logger is given a
|
|
57
96
|
proxy sink (`loading.js`, `setProxySink`) and every batch goes to the PROXY
|
|
58
97
|
over that channel. This is the bulk of a viewing;
|
|
@@ -66,8 +105,10 @@ failure needs both.
|
|
|
66
105
|
|
|
67
106
|
### The files on the proxy
|
|
68
107
|
|
|
69
|
-
|
|
70
|
-
addon host), named so the two
|
|
108
|
+
When the proxy was started with `--log-file`: one file per viewing session, in
|
|
109
|
+
the proxy log's own directory (`/data` on the addon host), named so the two
|
|
110
|
+
halves join without guessing. Without `--log-file` the same lines are in the
|
|
111
|
+
proxy's console, prefixed `client <sessionId:8>`.
|
|
71
112
|
|
|
72
113
|
```
|
|
73
114
|
client-<YYYYMMDD-HHMMSS UTC of the session start>-<sessionId:8>-<torrent name:60>-<infoHash:8>.log
|
|
@@ -86,7 +127,7 @@ Implementation: `utils/client-log-file.js`, route `routes/api/client-logs/post.j
|
|
|
86
127
|
|
|
87
128
|
## Quick triage
|
|
88
129
|
|
|
89
|
-
- Rewind/seek bug (hold 0ms → 500): HA `hold ... failed after 0ms → 500` + `encode-run` lines for the same `<sessionId>`, and DO `[client ...] fragLoadError / levelLoadError 500` for same `sn`.
|
|
130
|
+
- Rewind/seek bug (hold 0ms → 500): HA `hold ... failed after 0ms → 500` + `encode-run` lines for the same `<sessionId>`, and DO `client.log` `[client ...] fragLoadError / levelLoadError 500` for same `sn`.
|
|
90
131
|
- Cushion / link budget: HA `memory: rss=... anon=...` and `cushion` lines; DO `[eta]` / `[cushion]` from client.
|
|
91
132
|
- Update check: `ssh ha "sudo docker exec hassio_cli ha apps info b34a1737_torrent_tv_proxy | grep version"` and `ssh ha "sudo docker ps --filter name=app_b34a1737_torrent_tv_proxy"`.
|
|
92
133
|
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -25,7 +25,7 @@ import { handleApiSourceWarmPost } from "./routes/api/sources/warm/post.js";
|
|
|
25
25
|
import { handleApiPlaybackPlanPost } from "./routes/api/playback-plan/post.js";
|
|
26
26
|
import { handleApiPlaybackPlanAudioTracksPost } from "./routes/api/playback-plan/audio-tracks/post.js";
|
|
27
27
|
import { handleApiClientLogsPost } from "./routes/api/client-logs/post.js";
|
|
28
|
-
import { createClientLogFiles } from "./utils/client-log-file.js";
|
|
28
|
+
import { createClientLogConsole, createClientLogFiles } from "./utils/client-log-file.js";
|
|
29
29
|
import { handleApiSubtitlesGet } from "./routes/api/subtitles/get.js";
|
|
30
30
|
import { handleApiTranscodeSessionsPost } from "./routes/api/transcode-sessions/post.js";
|
|
31
31
|
import { handleApiTranscodeSessionsProgressGet } from "./routes/api/transcode-sessions/progress/get.js";
|
|
@@ -629,9 +629,12 @@ export async function startProxyServer({
|
|
|
629
629
|
}
|
|
630
630
|
})
|
|
631
631
|
);
|
|
632
|
-
// The browser's own log, kept beside the proxy's
|
|
633
|
-
//
|
|
634
|
-
|
|
632
|
+
// The browser's own log, kept beside the proxy's: a file per session when
|
|
633
|
+
// the proxy writes its log to a file, its console otherwise. See the route's
|
|
634
|
+
// own file for why it is here and not only on the registry server.
|
|
635
|
+
const clientLogs = logFile
|
|
636
|
+
? createClientLogFiles(logFile)
|
|
637
|
+
: createClientLogConsole((message) => logger.info(message));
|
|
635
638
|
app.post("/api/client-logs", async (req, reply) =>
|
|
636
639
|
handleApiClientLogsPost(req, reply, { clientLogs })
|
|
637
640
|
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Which files of a torrent somebody is using, for the sweep that writes
|
|
3
|
+
* whole files out and removes a torrent that has nothing left to fetch.
|
|
4
|
+
*
|
|
5
|
+
* Two facts, and both are needed. A file somebody has stated a need for is in
|
|
6
|
+
* use. So is a file being read: a read whose pieces are all here waits for
|
|
7
|
+
* nothing, and so states nothing in the demand register. Field 2026-10-04: a
|
|
8
|
+
* torrent was removed under such a read, ffmpeg's input ended with "Piece store
|
|
9
|
+
* is closed" 277 MB in, and the copied picture lost 2.6 s that the readiness
|
|
10
|
+
* forecast then refused as a timestamp gap (torrent-tv/meta#105).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Claimants that keep pieces coming without anybody reading. */
|
|
14
|
+
const NOT_A_READER = ["file-edges:", "torrent-fill:", "background-fill:"];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} options
|
|
18
|
+
* @param {object} options.torrent
|
|
19
|
+
* @param {Array<{ claimant: unknown, fileIndex: number }>} options.windows - The demand register's windows.
|
|
20
|
+
* @param {Iterable<{ torrent: object, fileIndex: number }>} options.openReads - Reads in flight.
|
|
21
|
+
* @returns {Set<number>} File indices in use.
|
|
22
|
+
*/
|
|
23
|
+
export function filesInUse({ torrent, windows, openReads }) {
|
|
24
|
+
const used = new Set(
|
|
25
|
+
windows
|
|
26
|
+
.filter((window) => !NOT_A_READER.some((prefix) => String(window.claimant).startsWith(prefix)))
|
|
27
|
+
.map((window) => window.fileIndex)
|
|
28
|
+
);
|
|
29
|
+
for (const read of openReads) {
|
|
30
|
+
if (read.torrent === torrent) {
|
|
31
|
+
used.add(read.fileIndex);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return used;
|
|
35
|
+
}
|
|
@@ -34,6 +34,7 @@ import { heldRangesOf, ownsItsMemory, readHeldBytes } from "./held-bytes.js";
|
|
|
34
34
|
import { CompletedFiles, completedFilesRoot } from "../../storage/files/CompletedFiles.js";
|
|
35
35
|
import { pieceFromWholeFiles, pieceIsInWholeFiles } from "../../storage/files/piece-from-whole-file.js";
|
|
36
36
|
import { Command, Event } from "./protocol.js";
|
|
37
|
+
import { filesInUse } from "./files-in-use.js";
|
|
37
38
|
import { startMemoryReport, WORKER_MEMORY_SAMPLE_MS } from "../../storage/memory-report.js";
|
|
38
39
|
import { forwardLogsTo, logger } from "../../../utils/logger.js";
|
|
39
40
|
|
|
@@ -94,6 +95,14 @@ const torrentsByKey = new Map();
|
|
|
94
95
|
const sourceRecipes = new Map();
|
|
95
96
|
/** In-flight reads, so a cancel can stop one mid-body. */
|
|
96
97
|
const readsById = new Map();
|
|
98
|
+
/**
|
|
99
|
+
* What each in-flight read is reading. A read whose pieces are all here waits
|
|
100
|
+
* for nothing and so states nothing in the demand register; this is how the
|
|
101
|
+
* thread knows it is still open.
|
|
102
|
+
*
|
|
103
|
+
* @type {Map<number, { torrent: object, fileIndex: number }>}
|
|
104
|
+
*/
|
|
105
|
+
const openReads = new Map();
|
|
97
106
|
|
|
98
107
|
/**
|
|
99
108
|
* Shorthand for this file. The same path as `logger.info` anywhere else in the
|
|
@@ -257,6 +266,7 @@ async function streamRange({ id, sourceKey, fileIndex, start, end, windowBytes }
|
|
|
257
266
|
|
|
258
267
|
const sender = createSendStream({ port: parentPort, requestId: id });
|
|
259
268
|
readsById.set(id, sender);
|
|
269
|
+
openReads.set(id, { torrent, fileIndex });
|
|
260
270
|
|
|
261
271
|
const rangeStart = start ?? 0;
|
|
262
272
|
const rangeEnd = end ?? file.length - 1;
|
|
@@ -295,6 +305,7 @@ async function streamRange({ id, sourceKey, fileIndex, start, end, windowBytes }
|
|
|
295
305
|
throw error;
|
|
296
306
|
} finally {
|
|
297
307
|
readsById.delete(id);
|
|
308
|
+
openReads.delete(id);
|
|
298
309
|
// Any fragment still awaiting confirmation will never get one now; settling
|
|
299
310
|
// it here releases its pin rather than leaking a held slot.
|
|
300
311
|
settleFragment(id);
|
|
@@ -947,18 +958,12 @@ async function keepWholeFiles() {
|
|
|
947
958
|
if (!infoHash || !Array.isArray(torrent.files)) {
|
|
948
959
|
continue;
|
|
949
960
|
}
|
|
950
|
-
// What anybody wants of this torrent
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
!String(window.claimant).startsWith("file-edges:") &&
|
|
957
|
-
!String(window.claimant).startsWith("torrent-fill:") &&
|
|
958
|
-
!String(window.claimant).startsWith("background-fill:")
|
|
959
|
-
)
|
|
960
|
-
.map((window) => window.fileIndex)
|
|
961
|
-
);
|
|
961
|
+
// What anybody wants of this torrent or is reading from it now.
|
|
962
|
+
const wanted = filesInUse({
|
|
963
|
+
torrent,
|
|
964
|
+
windows: demandFor(torrent).register.windows(),
|
|
965
|
+
openReads: openReads.values()
|
|
966
|
+
});
|
|
962
967
|
for (const [fileIndex, file] of torrent.files.entries()) {
|
|
963
968
|
const key = `${infoHash}/${fileIndex}`;
|
|
964
969
|
if (file?.done !== true || completedFiles.find(infoHash, fileIndex) || beingKept.has(key)) {
|
|
@@ -12,7 +12,7 @@ import { mkdtempSync, readdirSync, readFileSync } from "node:fs";
|
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
|
|
15
|
-
import { createClientLogFiles } from "../utils/client-log-file.js";
|
|
15
|
+
import { createClientLogConsole, createClientLogFiles } from "../utils/client-log-file.js";
|
|
16
16
|
|
|
17
17
|
/** A directory with a proxy log in it, which is what the sink is given. */
|
|
18
18
|
function aDirectory() {
|
|
@@ -108,3 +108,19 @@ test("no proxy log path means nothing is written and nothing throws", async () =
|
|
|
108
108
|
logs.write(aSession(), ["dropped"]);
|
|
109
109
|
await logs.close();
|
|
110
110
|
});
|
|
111
|
+
|
|
112
|
+
test("without a log file the lines go to the console, each naming its session", async () => {
|
|
113
|
+
// A proxy started from npm or Docker names no file; the browser's half of
|
|
114
|
+
// the viewing must still reach the output the host keeps.
|
|
115
|
+
const printed = [];
|
|
116
|
+
const logs = createClientLogConsole((message) => printed.push(message));
|
|
117
|
+
logs.write(aSession(), ["first line", "second line"]);
|
|
118
|
+
logs.write(aSession({ sessionId: "" }), ["no id"]);
|
|
119
|
+
await logs.close();
|
|
120
|
+
|
|
121
|
+
assert.deepEqual(printed, [
|
|
122
|
+
"client abcd1234 first line",
|
|
123
|
+
"client abcd1234 second line",
|
|
124
|
+
"client unknown no id"
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Which files of a torrent are in use for the whole-file sweep. Pure:
|
|
3
|
+
* no torrent is started (see services/torrent/worker/files-in-use.js).
|
|
4
|
+
*/
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { filesInUse } from "../services/torrent/worker/files-in-use.js";
|
|
8
|
+
|
|
9
|
+
test("a file being read is in use though nobody states a need for it", () => {
|
|
10
|
+
// Field 2026-10-04: every piece was here, so the read waited for nothing and
|
|
11
|
+
// stated nothing, and the torrent was removed under it.
|
|
12
|
+
const torrent = {};
|
|
13
|
+
const used = filesInUse({ torrent, windows: [], openReads: [{ torrent, fileIndex: 0 }] });
|
|
14
|
+
assert.deepEqual([...used], [0]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("a read of another torrent does not count", () => {
|
|
18
|
+
const used = filesInUse({ torrent: {}, windows: [], openReads: [{ torrent: {}, fileIndex: 0 }] });
|
|
19
|
+
assert.equal(used.size, 0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("stated needs count, and the fills that read nothing do not", () => {
|
|
23
|
+
const used = filesInUse({
|
|
24
|
+
torrent: {},
|
|
25
|
+
windows: [
|
|
26
|
+
{ claimant: "reader:7", fileIndex: 2 },
|
|
27
|
+
{ claimant: "file-edges:1", fileIndex: 1 },
|
|
28
|
+
{ claimant: "torrent-fill:x", fileIndex: 3 },
|
|
29
|
+
{ claimant: "background-fill:x", fileIndex: 4 }
|
|
30
|
+
],
|
|
31
|
+
openReads: []
|
|
32
|
+
});
|
|
33
|
+
assert.deepEqual([...used], [2]);
|
|
34
|
+
});
|
package/utils/client-log-file.js
CHANGED
|
@@ -16,6 +16,32 @@
|
|
|
16
16
|
import { createWriteStream, mkdirSync, renameSync, statSync } from "node:fs";
|
|
17
17
|
import { dirname, join } from "node:path";
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Where a browser's lines go when the proxy was started without `--log-file`.
|
|
21
|
+
*
|
|
22
|
+
* The proxy runs as the Home Assistant addon, from npm, in Docker, on Linux,
|
|
23
|
+
* macOS or Windows; only the addon names a file. Without one these lines were
|
|
24
|
+
* dropped, so on every other host the browser's half of a viewing existed
|
|
25
|
+
* nowhere at all. They go to the proxy's own console instead, beside the
|
|
26
|
+
* proxy's lines, and reach whatever keeps that output on the host: `docker
|
|
27
|
+
* logs`, the journal of a systemd unit, a terminal. Each line carries the
|
|
28
|
+
* session id, which is what the file name carries when there is a file.
|
|
29
|
+
*
|
|
30
|
+
* @param {(message: string) => void} log - The proxy's logger.
|
|
31
|
+
* @returns {{ write: (session: object, lines: string[]) => void, close: () => Promise<void> }}
|
|
32
|
+
*/
|
|
33
|
+
export function createClientLogConsole(log) {
|
|
34
|
+
return {
|
|
35
|
+
write(session, lines) {
|
|
36
|
+
const id = safeName(session?.sessionId, 8) || "unknown";
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
log(`client ${id} ${line}`);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async close() {}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
19
45
|
/** One file may reach this before it is rotated. */
|
|
20
46
|
const MAX_BYTES = 16 * 1024 * 1024;
|
|
21
47
|
|