@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.
Files changed (2) hide show
  1. package/README.md +126 -0
  2. 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
+ [![npm version](https://img.shields.io/npm/v/@zfadhli/tokrec?style=flat-square)](https://www.npmjs.com/package/@zfadhli/tokrec)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/zfadhli/tokrec/ci.yml?style=flat-square&label=CI)](https://github.com/zfadhli/tokrec/actions/workflows/ci.yml)
7
+ [![License](https://img.shields.io/npm/l/@zfadhli/tokrec?style=flat-square)](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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zfadhli/tokrec",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "Minimal TikTok live stream recorder — Bun + TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",