@zfadhli/tokrec 0.15.0 → 0.15.2
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/README.md +126 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @zfadhli/tokrec
|
|
2
|
+
|
|
3
|
+
Library half of [tokrec](https://github.com/zfadhli/tokrec) — polls a TikTok user's live status, records the stream while it is live, and returns playable MP4 files.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@zfadhli/tokrec)
|
|
6
|
+
[](https://github.com/zfadhli/tokrec/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/zfadhli/tokrec/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
Want the ready-made command-line recorder instead? Use
|
|
10
|
+
[`@zfadhli/tokrec-cli`](https://www.npmjs.com/package/@zfadhli/tokrec-cli).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bun add @zfadhli/tokrec
|
|
16
|
+
# or
|
|
17
|
+
npm install @zfadhli/tokrec
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Built for Bun ≥ 1.2. Nothing in the library is Bun-only — `Bun.which()` is used
|
|
21
|
+
when it exists and falls back to a `PATH` search — so the published `dist/`
|
|
22
|
+
also runs under Node ≥ 20. **FFmpeg must be on `PATH`** for MP4 conversion,
|
|
23
|
+
segmenting, and normalization.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { createRecorder } from "@zfadhli/tokrec"
|
|
29
|
+
|
|
30
|
+
const recorder = createRecorder({
|
|
31
|
+
user: "somebody",
|
|
32
|
+
outputDir: "./recordings",
|
|
33
|
+
interval: 3, // minutes between live-status polls
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
recorder.on("verdict", (v) => console.log("status:", v.reason))
|
|
37
|
+
recorder.on("progress", (p) => {
|
|
38
|
+
if (p.kind === "download") console.log(`${p.percent.toFixed(1)}%`)
|
|
39
|
+
})
|
|
40
|
+
recorder.on("record", (e) => {
|
|
41
|
+
if (e.phase === "end") console.log("saved:", e.file)
|
|
42
|
+
})
|
|
43
|
+
recorder.on("failure", (err) => console.error(err.code, err.message))
|
|
44
|
+
|
|
45
|
+
await recorder.start() // polls until offline or stop()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`start()` resolves when the run ends; call `stop()` from a signal handler to shut
|
|
49
|
+
down gracefully — the download is aborted, buffered data is flushed, and the
|
|
50
|
+
partial recording is still remuxed to `.mp4` before the promise settles.
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
| Field | Type | Default | Meaning |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `user` | `string` | — | TikTok username (required) |
|
|
57
|
+
| `outputDir` | `string` | `./recordings` | Destination directory |
|
|
58
|
+
| `interval` | `number` | `3` | Minutes between live-status polls |
|
|
59
|
+
| `duration` | `number` | `0` (unlimited) | Max recording length in **seconds** |
|
|
60
|
+
| `proxy` | `string` | — | HTTP proxy, e.g. `http://127.0.0.1:8080` |
|
|
61
|
+
| `cookiesPath` | `string` | `./cookies.json` | Path to a cookies file |
|
|
62
|
+
| `cookies` | `CookieAuth` | — | Cookie auth passed as an object instead of a file |
|
|
63
|
+
| `logLevel` | `"debug" \| "info" \| "warn" \| "error"` | `info` | Console verbosity |
|
|
64
|
+
| `logConsole` | `boolean` | `true` | Set `false` when you render your own output |
|
|
65
|
+
| `segmentMinutes` | `number` | `0` (off) | Split the recording into N-minute segments |
|
|
66
|
+
| `normalizeAudio` | `boolean` | `false` | EBU R128 loudness normalization via [peaknorm](https://github.com/zfadhli/peaknorm) |
|
|
67
|
+
| `normalizeLoudness` | `number` | `-14` | Target loudness in LUFS |
|
|
68
|
+
| `normalizeCodec` | `string` | `libopus` | Audio codec for normalized output |
|
|
69
|
+
| `normalizeBitrate` | `string` | `96k` | Audio bitrate for normalized output |
|
|
70
|
+
| `ratePerSecond` | `number` | `5` | Request cap; `0` disables the limiter |
|
|
71
|
+
| `debug` | `boolean` | `false` | API debug logging on stderr |
|
|
72
|
+
|
|
73
|
+
> The CLI accepts `--duration` in minutes; the library takes seconds.
|
|
74
|
+
|
|
75
|
+
## Events
|
|
76
|
+
|
|
77
|
+
Four typed channels:
|
|
78
|
+
|
|
79
|
+
| Event | Payload | Fires when |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `verdict` | `Verdict` — `reason`, `stream`, `evidence` | Every status poll resolves |
|
|
82
|
+
| `record` | `RecordEvent` | Recording lifecycle: `start`, `download`, `segment`, `convert`, `normalize`, `end` |
|
|
83
|
+
| `progress` | `ProgressEvent` — `{ kind: "download" }` or `{ kind: "normalize" }` | Bytes/percent updates |
|
|
84
|
+
| `failure` | `TikTokError` | A recoverable or fatal error |
|
|
85
|
+
|
|
86
|
+
`Verdict["reason"]` is one of `live`, `offline`, `restricted`, `no-stream-url`,
|
|
87
|
+
`no-room-id`, `error`, and carries the evidence sources that produced it.
|
|
88
|
+
|
|
89
|
+
## Controller
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
interface RecorderController {
|
|
93
|
+
start(): Promise<void>
|
|
94
|
+
stop(): Promise<void> // graceful: joins the active recording first
|
|
95
|
+
getStatus(): RecorderStatus
|
|
96
|
+
on(event, handler): void
|
|
97
|
+
off(event, handler): void
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`RecorderStatus` exposes `state`, `user`, `currentFile`, `sessionDuration`,
|
|
102
|
+
`lastPollTime`, and `lastError`.
|
|
103
|
+
|
|
104
|
+
## Exports
|
|
105
|
+
|
|
106
|
+
| Export | Kind | Purpose |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| `createRecorder(config)` | value | Build a `RecorderController` |
|
|
109
|
+
| `TikTokError` | value | Typed error class |
|
|
110
|
+
| `isLiveReason(reason)` | value | Narrow a verdict reason |
|
|
111
|
+
| `bytesToHuman`, `formatDuration`, `relativeTime`, `sanitizeUser` | value | Formatting helpers used by the CLI |
|
|
112
|
+
| `RecorderConfig`, `RecorderController`, `RecorderStatus`, `RecorderEvent`, `RecorderEventHandler`, `RecordEvent`, `ProgressEvent`, `RecordedFile`, `LiveReason`, `Verdict`, `Evidence`, `EvidenceOutcome`, `RoomRef`, `StreamRef`, `SourceId`, `Phase`, `LogLevel`, `TikTokErrorCode` | type | Public types (18) |
|
|
113
|
+
|
|
114
|
+
Error codes: `config-error`, `ffmpeg-not-found`, `ffmpeg-error`, `aborted`,
|
|
115
|
+
`unknown`.
|
|
116
|
+
|
|
117
|
+
## Output
|
|
118
|
+
|
|
119
|
+
Recordings are named `<user>=YYYYMMDD_HHMMSS.mp4`; segmenting produces
|
|
120
|
+
`<name>_partN.mp4`.
|
|
121
|
+
|
|
122
|
+
## Links
|
|
123
|
+
|
|
124
|
+
- [CLI package](https://www.npmjs.com/package/@zfadhli/tokrec-cli)
|
|
125
|
+
- [Repository](https://github.com/zfadhli/tokrec) · [Changelog](https://github.com/zfadhli/tokrec/blob/main/CHANGELOG.md)
|
|
126
|
+
- [MIT License](https://github.com/zfadhli/tokrec/blob/main/LICENSE)
|