goj · docs/architecture.md · chart 1

the goj pipeline

one page URL in, one playable file out. goj reads the page through the site's TLS-fingerprint gate, pulls the media URL out of it, parses the HLS playlist, fetches and decrypts the segments in parallel, emits them in playlist order, and tees them into a concurrent ffmpeg for a lossless MP4. Two sibling modes crawl a listing into a list of video URLs (--list), or download every URL in a list file (--download-list).

PNG 11114 × 2272 SVG SOURCE MENU

world map — the whole pipeline

internal/progress — live lines on stderr, only when a console

internal/output — the final file and its fallback

internal/media — concurrent fetch, decrypt, in-order emit

internal/hls — what the playlist says (no I/O)

internal/listing — pagination walk + list file format

internal/page — HTML acquisition (utls) + URL extraction

main.go — flags, wiring, exit codes (0 ok, 1 failed, 2 usage)

goj URL, or --list URL, or --download-list FILE

a page URL

--list URL

--download-list FILE

per URL, skipped when .mp4 or .ts is already on disk

the file feeds the third mode

HTTPS with a browser's ClientHello

media URL (m3u8)

GET playlist with Referer

GET key

GET segment

gates

ordered TS bytes

progress callback

on failure

User

flags: -o base, -c N, -n N, --no-progress, --list, --download-list,
the URL may sit before or after them

mode

one(): download one video

listMode(): crawl a listing page

each(): batch of URLs, one file each

Client.read: try browser TLS fingerprints in order,
ALPN pinned to http/1.1; never returns a half page

Extract: first matching per-host Rule regex, gives hlsUrl

ExtractLinks: ListingRules, gives video URLs + next-page URL

Crawl: follow the next link page by page, dedupe videos first-seen,
stop on a repeat or the last page, cap 100 pages

Read / Write: one URL per line

Parse: one #EXT-X-KEY (AES-128 + explicit IV),
ordered #EXTINF segments

Resolve: relative key / segment URIs against the playlist URL

fetchKey: exactly 16 bytes

semaphore: bounds in-flight segment requests (default 32)

one worker goroutine per segment:
GET, retry with backoff, 90s attempt timeout

DecryptSegment: AES-128-CBC + PKCS#7 strip

window: bounded reorder buffer, 4 x concurrency segments resident

emitter: the only writer of the output, strictly playlist order

Sink: append to .ts and tee the same bytes

ffmpeg -c copy, TS on pipe:0, writes .mp4.part

rename to .mp4 on success, then delete the .ts

ffmpeg absent: keep the joined .ts

Abort on failure: kill ffmpeg, remove partial .ts / .mp4.part

spinner: page / list phase, elapsed seconds

bar: percent, done/total, bytes, average rate (no ETA)

jable.tv — behind a Cloudflare gate that decides on the TLS handshake

media host — speaks HTTP/1.1 only, serves playlist, key and segments

stdout: one video URL per line, plus wrote FILE.txt

the chart from docs/architecture.md, drawn from the code. [Z] ACTUAL SIZE switches the file to the diagram's intrinsic 5557 px width with a horizontal scrollbar; zooming the browser stays sharp because the chart here is vector SVG, not a bitmap.

reading the chart

PartWhat it does
main.goParses flags, picks the mode (single URL, --list, --download-list), wires everything, and owns the exit codes 0 / 1 / 2.
internal/pageReads the page over HTTPS presenting a browser's TLS ClientHello (four fingerprints tried in order, ALPN pinned to http/1.1) and extracts the media URL — or, for a listing, the video URLs and the next-page link.
internal/listingWalks a listing's pagination, dedupes the videos it finds, and reads/writes the one-URL-per-line list file.
internal/hlsPure playlist parsing: one AES-128 key with its explicit IV, and the ordered segments; also resolves relative key and segment URIs.
internal/mediaThe heart: one worker goroutine per segment bounded by a semaphore, retries with backoff, AES-128-CBC decrypt with PKCS#7 strip, a bounded reorder window, and a single emitter that writes strictly in playlist order.
internal/outputThe sink: appends to the .ts and tees the same bytes into a concurrent ffmpeg -c copy, renaming .mp4.part to .mp4 only on success; keeps the .ts when ffmpeg is absent; aborts remove the partial files.
internal/progressThe two live lines on stderr — a spinner for the page/list phase and a bar for the segments — drawn only when stderr is a console.
Network boxesjable.tv sits behind a gate that decides on the TLS handshake; the media host speaks HTTP/1.1 only, which the media client pins deliberately.
map screen · chart 1 goj-docs.pages.dev © 2026