@zfadhli/tokrec 0.11.0 → 0.11.1

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 CHANGED
@@ -27,14 +27,14 @@
27
27
  - **Stream reconnection** — TikTok stream segments are short-lived (30-60s). When one ends, `tokrec` transparently fetches a fresh URL and continues writing to the same file
28
28
  - **Duration-limited recording** — record for a fixed time (`-d`) and auto-exit, or record indefinitely (default)
29
29
  - **Time-aligned MP4 segments** — post-recording, optionally split the stream into configurable-length MP4 segments, each independently playable
30
- - **Audio normalization** — EBU R128 two-pass loudness normalization via [peaknorm](https://github.com/sinedied/peaknorm)
30
+ - **Audio normalization** — EBU R128 two-pass loudness normalization via [peaknorm](https://github.com/zfadhli/peaknorm)
31
31
  - **Graceful shutdown** — responds to `SIGINT`/`SIGTERM`/`SIGHUP`. Aborts the download, flushes buffered data, converts to MP4, and exits cleanly
32
32
  - **Firefox cookie auto-detection** — reads `sessionid_ss` from Firefox's SQLite cookie store automatically (no config file needed)
33
33
  - **Live progress display** — animated spinner with real-time byte count, elapsed time, and download speed
34
34
  - **Typed event system** — 15 events with typed payloads for lifecycle tracking
35
35
  - **Proxy support** — route traffic through an HTTP proxy for regional access
36
36
  - **FLV + HLS support** — automatically detects and handles both stream formats
37
- - **65+ passing tests** — unit tests with real TikTok HTML/API fixtures
37
+ - **68+ passing tests** — unit tests with real TikTok HTML/API fixtures
38
38
 
39
39
  ---
40
40
 
@@ -75,11 +75,11 @@ npm install @zfadhli/tokrec
75
75
  ```
76
76
 
77
77
  ```ts
78
- import { createRecorder } from '@zfadhli/tokrec'
78
+ import { createRecorder } from "@zfadhli/tokrec";
79
79
 
80
- const recorder = createRecorder({ user: 'username' })
81
- recorder.on('recording:end', (info) => console.log('Done:', info.file))
82
- await recorder.start()
80
+ const recorder = createRecorder({ user: "username" });
81
+ recorder.on("recording:end", (info) => console.log("Done:", info.file));
82
+ await recorder.start();
83
83
  ```
84
84
 
85
85
  > [!TIP]
@@ -113,22 +113,22 @@ tokrec -u username --normalize
113
113
 
114
114
  ## CLI options
115
115
 
116
- | Option | Shorthand | Default | Description |
117
- |--------|-----------|---------|-------------|
118
- | `--user <name>` | `-u` | _(required)_ | TikTok username (with or without `@`) |
119
- | `--output <dir>` | `-o` | `./recordings` | Output directory |
120
- | `--interval <minutes>` | `-i` | `3` | Polling interval (min 1) |
121
- | `--duration <minutes>` | `-d` | unlimited | Max recording duration. Implies one-shot (exit after recording) |
122
- | `--segment-minutes <minutes>` | `-s` | disabled | Split recording into MP4 segments of this length |
123
- | `--proxy <url>` | `-p` | none | HTTP proxy URL |
124
- | `--cookies <path>` | `-c` | `./cookies.json` | Path to cookies JSON file |
125
- | `--log-level <level>` | `-l` | `info` | One of: `debug`, `info`, `warn`, `error` |
126
- | `--normalize` | _(none)_ | off | Enable EBU R128 audio normalization |
127
- | `--normalize-loudness <num>` | _(none)_ | `-14` | Target loudness in LUFS |
128
- | `--normalize-codec <name>` | _(none)_ | `aac` | Audio codec for normalized output |
129
- | `--normalize-bitrate <str>` | _(none)_ | `128k` | Audio bitrate for normalized output |
130
- | `--rate <n>` | _(none)_ | `5` | Max API requests/sec (0 = unlimited). Prevents WAF triggering |
131
- | `--debug` | _(none)_ | off | Show API debug logging on stderr |
116
+ | Option | Shorthand | Default | Description |
117
+ | ----------------------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------ |
118
+ | `--user <name>` | `-u` | _(required)_ | TikTok username (with or without `@`) |
119
+ | `--output <dir>` | `-o` | `./recordings` | Output directory |
120
+ | `--interval <minutes>` | `-i` | `3` | Polling interval (min 1) |
121
+ | `--duration <minutes>` | `-d` | unlimited | Max recording duration. Implies one-shot (exit after recording) |
122
+ | `--segment-minutes <minutes>` | `-s` | disabled | Split recording into MP4 segments of this length |
123
+ | `--proxy <url>` | `-p` | none | HTTP proxy URL |
124
+ | `--cookies <path>` | `-c` | `./cookies.json` | Path to cookies JSON file |
125
+ | `--log-level <level>` | `-l` | `info` | One of: `debug`, `info`, `warn`, `error` |
126
+ | `--normalize` | _(none)_ | on | Enable EBU R128 audio normalization (enabled by default, pass `--no-normalize` to disable) |
127
+ | `--normalize-loudness <num>` | _(none)_ | `-14` | Target loudness in LUFS |
128
+ | `--normalize-codec <name>` | _(none)_ | `libopus` | Audio codec for normalized output (peaknorm default) |
129
+ | `--normalize-bitrate <str>` | _(none)_ | `96k` | Audio bitrate for normalized output (peaknorm default) |
130
+ | `--rate <n>` | _(none)_ | `5` | Max API requests/sec (0 = unlimited). Prevents WAF triggering |
131
+ | `--debug` | _(none)_ | off | Show API debug logging on stderr |
132
132
 
133
133
  ---
134
134
 
@@ -144,7 +144,12 @@ If you're logged into TikTok in Firefox, `tokrec` automatically reads the cookie
144
144
  tokrec -u username
145
145
  ```
146
146
 
147
- You will see: `ℹ Firefox cookies loaded (30 cookies)`
147
+ You will see:
148
+
149
+ ```
150
+ tokrec v0.11.0
151
+ ℹ Firefox cookies loaded (30 cookies)
152
+ ```
148
153
 
149
154
  ### Option 2: cookies.json
150
155
 
@@ -231,23 +236,23 @@ File naming: `{username}={YYYYMMDD}_{HHMMSS}[_partN].mp4`
231
236
 
232
237
  The recorder emits typed events that you can subscribe to via `recorder.on()`:
233
238
 
234
- | Event | Payload | When |
235
- |-------|---------|------|
236
- | `checking` | `{ user }` | Before each poll tick |
237
- | `tick` | `{ user, isLive, roomId? }` | After checking live status |
238
- | `recording:start` | `{ user, file }` | When recording begins |
239
- | `download:progress` | `{ bytes, elapsed, speed }` | During download (~1s intervals) |
240
- | `download:end` | `{ file, duration, size }` | When download completes |
241
- | `recording:end` | `{ file, duration, size }` | Per segment/file after conversion |
242
- | `segmenting:start` | `{ input, outputPattern }` | Before FFmpeg segmenting |
243
- | `segmenting:end` | `{ segments }` | After segmenting |
244
- | `converting:start` | `{ input }` | Before simple conversion |
245
- | `converted` | `{ input, output }` | After each converted file |
246
- | `normalize:start` | `{ file }` | Before audio normalization |
247
- | `normalize:progress` | `{ file, percent, phase }` | During normalization |
248
- | `normalize:end` | `{ input, output }` | After normalization |
249
- | `normalize:error` | `{ input, error }` | On normalization error |
250
- | `error` | `err: TikTokError` | On non-fatal errors |
239
+ | Event | Payload | When |
240
+ | -------------------- | --------------------------- | --------------------------------- |
241
+ | `checking` | `{ user }` | Before each poll tick |
242
+ | `tick` | `{ user, isLive, roomId? }` | After checking live status |
243
+ | `recording:start` | `{ user, file }` | When recording begins |
244
+ | `download:progress` | `{ bytes, elapsed, speed }` | During download (~1s intervals) |
245
+ | `download:end` | `{ file, duration, size }` | When download completes |
246
+ | `recording:end` | `{ file, duration, size }` | Per segment/file after conversion |
247
+ | `segmenting:start` | `{ input, outputPattern }` | Before FFmpeg segmenting |
248
+ | `segmenting:end` | `{ segments }` | After segmenting |
249
+ | `converting:start` | `{ input }` | Before simple conversion |
250
+ | `converted` | `{ input, output }` | After each converted file |
251
+ | `normalize:start` | `{ file }` | Before audio normalization |
252
+ | `normalize:progress` | `{ file, percent, phase }` | During normalization |
253
+ | `normalize:end` | `{ input, output }` | After normalization |
254
+ | `normalize:error` | `{ input, error }` | On normalization error |
255
+ | `error` | `err: TikTokError` | On non-fatal errors |
251
256
 
252
257
  ---
253
258
 
@@ -256,31 +261,31 @@ The recorder emits typed events that you can subscribe to via `recorder.on()`:
256
261
  ### `createRecorder(config)`
257
262
 
258
263
  ```ts
259
- import { createRecorder } from '@zfadhli/tokrec'
260
- import type { RecorderConfig, RecorderController, RecorderEvent } from '@zfadhli/tokrec'
264
+ import { createRecorder } from "@zfadhli/tokrec";
265
+ import type { RecorderConfig, RecorderController, RecorderEvent } from "@zfadhli/tokrec";
261
266
 
262
267
  const recorder: RecorderController = createRecorder({
263
- user: 'username', // required
264
- outputDir: './videos', // default: './recordings'
265
- interval: 3, // polling interval in minutes
266
- duration: 600, // max recording duration in seconds
267
- segmentMinutes: 20, // segment length in minutes
268
- proxy: 'http://127.0.0.1:8080',
269
- cookies: { sessionid_ss: '...' },
270
- logLevel: 'info',
268
+ user: "username", // required
269
+ outputDir: "./videos", // default: './recordings'
270
+ interval: 3, // polling interval in minutes
271
+ duration: 600, // max recording duration in seconds
272
+ segmentMinutes: 20, // segment length in minutes
273
+ proxy: "http://127.0.0.1:8080",
274
+ cookies: { sessionid_ss: "..." },
275
+ logLevel: "info",
271
276
  logConsole: true,
272
- })
277
+ });
273
278
  ```
274
279
 
275
280
  ### `RecorderController`
276
281
 
277
282
  ```ts
278
283
  interface RecorderController {
279
- start(): Promise<void>
280
- stop(): Promise<void>
281
- getStatus(): RecorderStatus
282
- on<E extends RecorderEvent>(event: E, handler: RecorderEventHandler[E]): void
283
- off<E extends RecorderEvent>(event: E, handler: RecorderEventHandler[E]): void
284
+ start(): Promise<void>;
285
+ stop(): Promise<void>;
286
+ getStatus(): RecorderStatus;
287
+ on<E extends RecorderEvent>(event: E, handler: RecorderEventHandler[E]): void;
288
+ off<E extends RecorderEvent>(event: E, handler: RecorderEventHandler[E]): void;
284
289
  }
285
290
  ```
286
291
 
@@ -288,35 +293,35 @@ interface RecorderController {
288
293
 
289
294
  ```ts
290
295
  interface RecorderStatus {
291
- state: 'idle' | 'polling' | 'recording' | 'converting' | 'stopped'
292
- user: string
293
- currentFile?: string
294
- sessionDuration?: number
295
- lastPollTime?: string
296
- lastError?: string
296
+ state: "idle" | "polling" | "recording" | "converting" | "stopped";
297
+ user: string;
298
+ currentFile?: string;
299
+ sessionDuration?: number;
300
+ lastPollTime?: string;
301
+ lastError?: string;
297
302
  }
298
303
  ```
299
304
 
300
305
  ### Example: build a notification bot
301
306
 
302
307
  ```ts
303
- import { createRecorder } from '@zfadhli/tokrec'
308
+ import { createRecorder } from "@zfadhli/tokrec";
304
309
 
305
- const recorder = createRecorder({ user: 'username' })
310
+ const recorder = createRecorder({ user: "username" });
306
311
 
307
- recorder.on('checking', ({ user }) => console.log(`Checking @${user}...`))
308
- recorder.on('tick', ({ isLive, roomId, user }) => {
312
+ recorder.on("checking", ({ user }) => console.log(`Checking @${user}...`));
313
+ recorder.on("tick", ({ isLive, roomId, user }) => {
309
314
  if (isLive) {
310
- console.log(`@${user} is LIVE! (room: ${roomId})`)
315
+ console.log(`@${user} is LIVE! (room: ${roomId})`);
311
316
  // Send a Discord/Telegram notification here
312
317
  }
313
- })
314
- recorder.on('recording:end', ({ file, duration }) => {
315
- console.log(`Recorded ${file} (${Math.round(duration)}s)`)
316
- })
317
- recorder.on('error', (err) => console.error(`[${err.kind}] ${err.message}`))
318
+ });
319
+ recorder.on("recording:end", ({ file, duration }) => {
320
+ console.log(`Recorded ${file} (${Math.round(duration)}s)`);
321
+ });
322
+ recorder.on("error", (err) => console.error(`[${err.kind}] ${err.message}`));
318
323
 
319
- await recorder.start()
324
+ await recorder.start();
320
325
  ```
321
326
 
322
327
  ---
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.11.0";
2
+ var version = "0.11.1";
3
3
  //#endregion
4
4
  export { version };
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zfadhli/tokrec",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Minimal TikTok live stream recorder — Bun + TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/lib.mjs",