4.8 KiB
Analysis — the chain, overlaps, stacks
The chain
camera owners (Dishub/bank/operator cameras)
→ operator/government stream servers (HLS: Ant Media, MediaMTX, datarhei, custom)
→ government portals publish catalogs (HTML/JSON; some embed player iframes)
→ transit.oyenza.com harvests + verifies + curates → static catalog in its bundle
→ visitor's browser plays the owner's HLS URL directly (hotlink, no proxy)
Evidence per source (cross-match against oyenza's catalog)
Method: extract oyenza's view URLs from asset-cctv.json; extract the portal's camera identifiers from its page/JSON; compare sets exactly (case-insensitively where the portal is inconsistent).
- DKI — 46/46. Portal
cardsiframes carry<camera-id>/embed.html; oyenza lists<camera-id>/index.fmp4.m3u8+ the sameembed.htmlas "owner page". Every oyenza DKI path appears in the portal set (52 cameras at capture). Example id:501844_JKP_DISHUB_JL.-MH.-THAMRIN-C01_CCTV-01. - Toll — 190/190. BPJT's inline JSON carries
streamhlsvalues; after unescaping (\/) and cleaning a few literal\r\nartifacts, all 190 oyenza toll view URLs appear verbatim. BPJT page at capture: 1,530 records → 1,415 unique URLs across ~39 hosts. - Bekasi — 23/23. oyenza's 23 stream filenames all match
list.jsonnamevalues exactly. - Bogor — 51/51. All 51 unique detail-link IDs (
/cctv/<id>/detail) used by oyenza appear on the BSW page (96 IDs total). Strongest link: the Alun-Alun detail page (id 82) embeds exactlyhttps://restreamer2.kotabogor.go.id/memfs/c07c1926-288c-46e4-a19c-9f51022edc5d.m3u8— the URL oyenza lists for that camera.
Interpretation: oyenza is a curated mirror, not a re-publisher with its own pipeline to the cameras. The catalog is rebuilt and re-verified (footer "Daftar diperiksa 13 Oktober 2026"; checkedAt 2026-10-12T22:07:39Z).
Selection logic (the 154 toll cams)
oyenza's toll subset = 190 views / 154 cams from 9 operators: Hutama Karya 58, Trans Bumi Serbaraja 29, Cimanggis-Cibitung 19, Cibitung-Tanjung Priok 19, JTD 8, Marga Mandalasakti 7, KKDM 6, Jakarta Lingkar Baratsatu 5, CMNP 3.
Not simple geography: 589 BPJT cams fall inside a Jabodetabek bounding box, but only ~190 views were taken. Conspicuous exclusions, with live checks:
- Jasa Marga (~605 cams) — absent.
jmlive.jasamarga.comreturned HTTP 200 with an empty body (manifest not publicly served to us). Exclusion is consistent with "dead unless probed from a permitted vantage". - BSD Toll — absent.
stream.bsdtol.com→ nginx 502 at capture. - Waskita (
wbw, 55) — absent even though its streams work (playlist verified) → curation beyond pure liveness (coverage-area choice).
Confidence: Stream-liveness exclusions (JM, BSD) = established via probes. "Verify-then-pick + Jabodetabek coverage" selection rule = strong inference; the exact rule isn't observable from outside.
Stream-stack fingerprints (observed live)
/LiveApp/streams/<id>.m3u8→ Ant Media Server — HK (Cloudflare-fronted), Serbaraja, CCT. 2 s TS segments.server: mediamtx→ MediaMTX — CTP. Master playlist →stream.m3u8./memfs/<uuid>.m3u8(+?session=on sub-playlists) → datarhei Restreamer — all Bogor servers (also bsdtol, PPTR).index.fmp4.m3u8+tracks-v1/+X-Sid/X-Run-Timeheaders,server: Streamer 26.04.1→ Bali Tower custom — DKI.- nginx +
X-Powered-By: Expresspackagers → JTD, KKDM. Plain nginx/Apache HLS → MMS, JORRW1, Citramarga, Bekasi.
Playback path (from the site's own code)
- Catalog fetched at runtime:
fetch("/assets/asset-cctv-<hash>.json"). - Views mapped to
{label, hls, page}; on tap:import("/assets/lib-E4YH4VVH.js")(hls.js) →hls.loadSource(view.hls); hls.attachMedia(video); Safari falls back to nativecanPlayType("application/vnd.apple.mpegurl")→video.src. - "Buka Layar penuh" opens
view.page— the owner's page (portal orembed.html). - No proxy: streams load from the owner host in the visitor's browser. Probed hosts sent
Access-Control-Allow-Origin: *, and returned 200 even withoutOrigin/Referer— no hotlink protection at capture. - The site's own claim ("Video diputar langsung dari server pemilik kamera dan tidak kami simpan") matches the code path.
Anti-overclaim notes
- The static catalog proves what the client understands (URLs, labels, owner pages). It does not prove the operator's exact checker tooling — no public repo was found;
checkedAt+ redeploy hashes = inference of a periodic probe + rebuild. - Uptime varies: one host timed out once and answered on retry. Treat single probes as readings, not facts — timestamp them (done: Oct 13, 2026).
- Access policy can change at any time; CORS
*today ≠ CORS*forever.