Releases: zfadhli/tokrec
Releases · zfadhli/tokrec
v0.11.2
[0.11.2] — 2026-06-15
Fixed
--no-normalizenow actually disables normalization — the action handler
only checked truthiness (if (opts.normalize)), so passing--no-normalize
was silently ignored and normalization stayed on (the default). Now the boolean
value is propagated correctly.- Post-processing no longer trusts the pipe byte counter — the early-return
guard inprocessRecording()comparedresult.size === 0using the in-memory
pipe counter, which could diverge from the actual bytes written to disk under
backpressure or error-recovery paths. Now it reads the real file size from disk
viastatSync, so valid recordings are never skipped.
What's Changed
Full Changelog: v0.11.1...v0.11.2
v0.11.1
Changed
--normalizedefaults now accurate in docs — the README's CLI options table
now correctly showson(notoff) for--normalize, andlibopus/96kfor
the codec/bitrate defaults (matching the actual v0.10.0 behavior).
Added
- Version banner shown in README example — the authentication output example
now includes thetokrec v0.X.Ystartup line.
What's Changed
Full Changelog: v0.11.0...v0.11.1
v0.11.0
Changed
- CLI binary renamed from
tiktok-live-recordertotokrec— the installed
binary is nowtokrecinstead oftiktok-live-recorder. Update any scripts or
aliases accordingly.
Added
- Version banner at startup — the CLI now shows
tokrec v0.11.0as the first
line of output when running.
What's Changed
Full Changelog: v0.10.0...v0.11.0
v0.10.0
Changed
- Normalize audio now uses peaknorm's defaults (libopus/96k) — tokrec's
hardcodedaac/128kdefaults for--normalize-codecand
--normalize-bitratehave been removed. When these flags are not
explicitly provided, peaknorm's own defaults now apply:libopusaudio
codec at96kbitrate (previouslyaacat128k).
v0.9.1
Changed
- Consolidated download modules —
download-stream.tsanddownload-hls.ts
(which were 90% identical) merged into a singledownload.ts. The only difference
(log message prefix) is now a parameterizedlabelargument. - Utilities centralized in
utils.ts—findFfmpegPathandformatDuration
moved fromffmpeg-utils.tstoutils.ts, making the utility module the single
source of truth for pure helper functions. - Removed
getFfmpegPath()wrapper — the orchestrator (recorder/index.ts) now
callsfindFfmpegPath()directly instead of wrapping it in a one-liner that only
added an error message. ffmpeg-utils.tsscoped to pipe-only — after extracting general utilities, the
module now only exports the corepipeFfmpegSegmentprimitive and its associated
constants. Cleaner separation of concerns.- Removed no-op
updateProgressfrom Display — theupdateProgress()method
was already a no-op (the recording timer is driven by an independentsetInterval).
Removed from theDisplayinterface and the event subscription inindex.ts. The
download:progressevent is still available in the recorder's event system for
API consumers.
What's Changed
Full Changelog: v0.9.0...v0.9.1
v0.9.0
Added
- Recording elapsed timer — during recording, the spinner now shows a
live counter that ticks every second (Recording... [1m 5s]) instead of
a staticRecording stream...message. Elapsed time is now visible from
the very first second instead of waiting for the first progress event.
Changed
- Audio normalization enabled by default —
--normalizeis no longer
needed; EBU R128 loudness normalization now runs automatically after
every conversion. To disable, use--no-normalize.
Fixed
- Offline status delayed 3 minutes after stream end — when a live stream
ended, the recorder waited for the next poll tick (default 3 minutes) before
reporting@user is offline. Now it immediately re-checks via the
check_aliveendpoint right after the recording finishes. - Misleading
[last check: ...]label — the offline repeat message now
shows[last online: ...]instead of[last check: ...], matching what
the timestamp actually represents.
v0.8.0
Added
- Lightweight live check via
/webcast/room/check_alive/— new
fetchCheckAlive(roomId)endpoint returns a tiny boolean response (~100 bytes)
vs the multi-kilobyteroom/inforesponse.isRoomAlive()uses it as a
cache-miss fallback. Down from kilobytes to ~100 bytes per check. --debugCLI flag — gates[API_DEBUG]log output to stderr for
troubleshooting without spamming the console by default.- Token-bucket rate limiter — limits API requests to
--rateper second
(default 5). Prevents WAF triggering on rapid polling or URL refresh loops. - Formal state machine — validated
setState()transitions enforce the
lifecycle:idle → polling → recording → converting → polling | stopped.
Prevents invalid state transitions with clear error messages. pendingRemuxesbackground queue — thestop()method now awaits
in-flight conversion/segmenting promises with a 60-second timeout, ensuring
they complete before the process exits.- Crash-safe MPEG-TS intermediate format — switched download output from
raw FLV to MPEG-TS. TS is fully repair-able and append-friendly. The file
survives even if FFmpeg is killed mid-write. - Segmenting disabled by default —
segmentMinutesnow defaults to 0
(disabled). Simple TS→MP4 conversion is the default post-processing path. - Cookie source feedback — startup now logs which cookie source was used
(ℹ Firefox cookies loadedorℹ cookies.json loaded). - Auto-exit after
-drecording — when a duration limit completes, the
recorder now exits instead of going back to polling mode. - MP4 conversion result shown in terminal — the
convertedevent is now
wired to the display, showing✔ Converted: filename.mp4on success.
Fixed
- Slardar WAF bypass — the HTML page scrape on
www.tiktok.comwas blocked
by the WAF. The detection now falls through to the/api-live/user/room/
endpoint which is not behind the WAF, using a Firefox TLS fingerprint for
the HTTP session. -dduration limit ignored during long segments — a single TikTok stream
URL can stay alive for 90+ seconds. The duration check only ran between
segments, so-d 1could overshoot by 30+ seconds. Fixed by passing
-t <remaining>to FFmpeg for per-segment enforcement.- AbortSignal listener memory leak — each
sleep()call that used an
AbortSignaladded a newabortlistener but never removed it. Fixed by
cleaning up the listener in both the resolve and abort paths. - Race condition in FFmpeg abort —
proc.kill()could lose the race
againstproc.on("close"). Replaced withspawn()signaloption which
guarantees atomic cleanup. - Missing abort signal in some FFmpeg spawns — not all FFmpeg invocations
received the abort signal, leaving orphaned processes on Ctrl-C. Fixed by
threading the signal to every spawn call. - FFmpeg startup hang — a bad URL would cause FFmpeg to hang indefinitely.
Added a 30-second startup timeout that escalates from SIGTERM to SIGKILL. - Process signal handlers accumulation —
process.on("SIGINT", ...)was
called multiple times withoutprocess.off(), causing duplicate handlers.
Fixed by removing handlers before re-registering. - Ctrl-C during recording left
.tsunconverted —stop()aborted the
converter signal before awaiting the in-flight tick, so the conversion
skipped. Reorderedstop()to let conversion complete first. - Memory leak from HTTP client + stream references — the download loop
held references to HTTP responses that prevented GC. Fixed by draining and
destroying stale sockets.
Changed
- Download output format: FLV → MPEG-TS — FFMpeg now writes MPEG-TS
(-f mpegts) instead of raw FLV. The.tsfile is crash-safe: even if
FFmpeg is killed mid-write, all data before the last complete TS packet is
valid and playable. - Default segmenting disabled —
segmentMinutesdefaults to 0. Existing
users who rely on segmenting should pass-s 20(or their preferred value). - Architectural refactoring — the monolithic
recorder.tswas split into
9 focused files undersrc/recorder/. Shared utilities were extracted to
ffmpeg-utils.ts. No behavioral changes. - Dependency bumps — various dependencies updated to latest versions.
What's Changed
Full Changelog: v0.7.1...v0.8.0
v0.7.1
Fixed
- Ctrl-C graceful shutdown now converts partial downloads —
reader.cancel()
was unreliable at resolving the pending read, causing a 60-second hang on
Ctrl-C. Replaced withAbortController+Promise.racefor instant abort
(~5ms). The download catch block now flushes the in-memory buffer and closes
the write stream so the partial FLV is valid for FFmpeg conversion. - Duplicate signals no longer force-exit mid-conversion — Bun fires both
SIGINT and SIGTERM for a single Ctrl-C. The second signal is now silently
ignored instead of callingprocess.exit(1), letting the graceful shutdown
pipeline (abort → flush → convert → exit) finish. - Offline
[last check: ...]timestamp now increases over time — the
elapsed time was incorrectly measured from the previous poll tick instead of
from the first offline detection, so it always showed the polling interval
(e.g. always3m ago) rather than growing (3m ago→6m ago→ ...).
Changed
- CLI output alignment — removed the 2-space indent from all icon lines so
spinners and icons both start at column 0, fixing visual misalignment.
What's Changed
Full Changelog: v0.7.0...v0.7.1
v0.7.0
Added
- Audio normalization via peaknorm — new
--normalizeflag applies EBU R128
two-pass loudness normalization usingpeaknorm. Configurable target loudness
(--normalize-loudness), audio codec (--normalize-codec), and bitrate
(--normalize-bitrate). Runs after conversion/segmenting on each MP4 file
sequentially with a live progress spinner (Analyzing / Normalizing phases).
Fixed
- Offline timestamp not updating — the
[last check: ...]timestamp now
correctly measures elapsed time from the first offline detection instead of
from the previous poll tick, so it shows an increasing duration (e.g.3m ago
→6m ago→9m ago) rather than always displaying the polling interval.
Changed
- Dependency upgrades — TypeScript 5.9 → 6.0, Biome 1.9 → 2.4,
peaknorm 0.2.2 → 0.2.4.
What's Changed
Full Changelog: v0.6.0...v0.7.0