先從 README 選擇 Demo 並載入配置。範例統一放在 Demo 程式碼,本文只說明接口、參數與限制;實際爬蟲只保留需要的功能。
目前 TV 專案使用以下語言基準,App 與對應 runtime 模組需配套更新:
| 語言 | 環境與寫法 |
|---|---|
| Java | Java 17 語法、Android 7.0 起;繼承 Spider,使用注入的 net,以 spiderJar 打包並執行 R8 |
| Python | CPython 3.10、Chaquopy 17.1.0;可用 match/case、str | None 等 3.10 語法,爬蟲入口維持同步 def,網路使用 self.net |
| QuickJS | QuickJS-NG 0.17.0 基底,含上游更新;使用 ES module 的 export default,支援 async/await、?.、??,網路使用注入的 net |
Java SDK 以 compileOnly 引用,取得與更新方式見 README。Function JAR 使用 com.fongmi.quickjs 的 QuickJSContext、JSArray 等 API,配套更新後重新編譯,不打包 SDK 類別。
主機驗證使用 Python 3.10;App 不等待 Python coroutine。
QuickJS 的宿主功能由 App 注入,需使用 v11 配套的 wrapper、runtime 與 TV 介接:
| 功能 | 支援範圍 |
|---|---|
| 計時與輸出 | timeout/interval、額外參數與共用取消 ID;console 的 assert、dir、count、time、trace、group、table,clear 只重設縮排 |
| 網址與事件 | URL/URLSearchParams、Event/EventTarget/CustomEvent、AbortController/AbortSignal(timeout、any、reason) |
| 資料與串流 | structuredClone、Blob/File/FormData、Web Streams 與 BYOB;clone 不支援 function/SharedArrayBuffer |
| 編碼 | UTF-8 TextEncoder、多編碼 TextDecoder(含 GBK/GB18030/Big5)、TextEncoderStream/TextDecoderStream |
| HTTP | Headers/Request/Response;網路請求使用注入的 net.http/net.json |
| 加密 | getRandomValues/randomUUID、SHA、HMAC、AES-GCM/CBC、raw/octet JWK、wrap/unwrap;不支援 RSA、橢圓曲線或 KDF;GCM tag 為 96、104、112、120、128 bits,附加於密文末端 |
| 模組 | JSON module import attributes,type 為 json |
大型資料用 net.openStream,proxy 轉送用 net.stream。不是 Node.js 環境。Intl 與 Atomics.waitAsync 未提供。
共用 aesX/Crypto bridge 的 GCM 密文末端含 16-byte tag,二進位明文用 Base64;Web Crypto 的 tagLength 依上表。
| 用途 | Java | Python | QuickJS |
|---|---|---|---|
| 初始化 | init(context, extend) |
init(extend) |
init(ext);default(site) 建立實例 |
| 分類與篩選 | homeContent(filter) |
homeContent(filter) |
home(filter) |
| 推薦 | homeVideoContent() |
homeVideoContent() |
homeVod() |
| 分類列表 | categoryContent(tid, pg, filter, extend) |
categoryContent(tid, pg, filter, extend) |
category(tid, pg, filter, extend) |
| 搜尋 | searchContent(key, quick, pg) |
searchContent(key, quick, pg) |
search(key, quick, pg) |
| 詳情 | detailContent(ids) |
detailContent(ids) |
detail(id) |
| 播放 | playerContent(flag, id, vipFlags) |
playerContent(flag, id, vipFlags) |
play(flag, id, vipFlags) |
| 即時彈幕 | danmaku(id, Consumer<JSONObject>) |
danmaku(id, emit) |
danmaku(id, emit) |
| 操作 | action(value) |
action(value) |
action(value) |
| 直播 | liveContent(url) |
liveContent(url) |
live(url) |
| 本機代理 | proxy(params) |
localProxy(params) |
proxy(params) |
| 嗅探判斷 | manualVideoCheck()/isVideoFormat(url) |
manualVideoCheck()/isVideoFormat(url) |
sniffer()/isVideo(url) |
| 釋放 | destroy() |
destroy() |
destroy() |
Java Demo 用 Gson 組裝標準 JSON 字串;Python 資料方法回傳 dict/list;QuickJS 回傳物件/陣列,Promise 由 App 等待。proxy 與布林接口維持各自格式。Java/Python 的 detail 收 ID 集合,QuickJS 收單一 ID。
destroy() 用來關閉自行建立的資源。QuickJS 的 destroy hook(含回傳的 Promise)有 5 秒期限,逾時仍會釋放 runtime;呼叫前已取消網路工作,不在此安排新請求。
Java 使用本專案 com.github.catvod.bean 模型,以 create() 建立並鏈式填值,方法不加 set 前綴。com.github.catvod.utils.Response 用 Gson 組裝 JSON 與分頁,兩者隨爬蟲 JAR 打包;App 的 Result 只負責接收解析。
Response.vod() 填詳情,classes()/filters() 填分類與篩選,put(name, value) 填其他欄位,string() 輸出 JSON。明確的 0/false 保留,null 模型欄位省略;也可直接用 Gson 組裝標準 JSON。
Response.url() 接受播放網址字串或 Url 多畫質模型,傳入 null 時移除 URL 欄位。
| 回傳資料 | Java 模型與方法 |
|---|---|
| 分類、影片 | Class.create().id().name().flag()、Vod.create().id().name().pic().remarks() |
| 篩選、選項 | Filter.create().key().name().value().init()、Value.create().n().v() |
| 樣式 | Style.create().type().ratio()、Cate.create().land().circle().ratio() |
| 字幕 | Sub.create().url().name().lang().format(ext).flag(),交給 Response.subs() |
| 彈幕 | Danmaku.create().name().url(),交給 Response.danmaku() |
| DRM | Drm.create().key().type().header().forceKey(),交給 Response.drm() |
| 跳過片段 | SkipSegment.create().type().start().end(),交給 Response.skips() |
| 多畫質 | Url.create().values().position(),values 使用 Value,交給 Response.url() |
| 網頁 | Web.create().url().mode().cookie(),cookie 使用 Web.Cookie.create().url().keys(),交給 Response.web() |
播放附加欄位的實作見 Java Demo 的 playerContent。
Response 也提供 playUrl、artwork、jxFrom、flag、desc、format、click、key、position、code、msg 鏈式方法。上述播放與網頁模型未填的可選欄位省略;ending 片段可不呼叫 .end(),Web Cookie 未填 .url() 時使用網頁網址。多畫質的 position 是從 0 開始的索引,與回應頂層以毫秒計的起播 position 不同。
Java 的 Sub.format(ext) 傳入字幕副檔名,例如 vtt、srt、ass,由模型呼叫 VodUtil.getSubMimeType(ext) 轉成 MIME;回傳 JSON 的 subs[].format 仍使用 MIME。
分類與搜尋回傳 list、page、pagecount、limit,有精確總筆數才加 total。完整單頁清單可只填 list、page=1、pagecount=1。網站已分頁時直接回傳當頁,不再次切片或推算 total;App 以 page < pagecount 決定載入下一頁。filters 的鍵對應分類 type_id。實作見 Demo 的分類與搜尋入口。
線路用 $$$ 分隔,每線集數用 #,每集為 名稱$播放值。清理名稱中的分隔符;播放值可用自訂 ID,在播放時取得實際網址。
VOD 的 style.type 支援 rect/oval/list,同頁保持一致;分類使用 land/circle/ratio。資料夾、action 卡片與演員超連結見 Demo。Site.lang 由 App 處理搜尋語系,爬蟲不轉字。
三語言使用相同的 12 筆資料、分類 ID、篩選、搜尋與播放 ID;共通操作的 action 名稱及順序一致。從「互動功能」選擇以下操作:
| 功能 | 操作/入口 |
|---|---|
| 列表與樣式 | 首頁、分類、資料夾、演員/導演連結;搜尋「示範」檢查分頁 |
| 播放 | 主要/備用/代理線、多畫質、字幕、DRM、片段跳過、起播位置、直播回看、播放詳情更新 |
| HTTP | json、request_sync、request、binary、cookie、cookie_clear、session |
| 並行與下載 | parallel、parallel_map、download、sleep |
| 串流 | stream_read、proxy_stream;本機 proxy 轉送與分塊讀取 |
| WebSocket | websocket 單次交換;socket-start/socket-state/socket-stop 持續收訊 |
| 快取與儲存 | ttl_cache/ttl_peek/ttl_background/ttl_persist/ttl_clear/ttl_clear_all;cache_write/cache_read/cache_delete 操作 Local |
| 腳本與互動 | script、runtime、proxy、toast、site_key、web、web_view;live_danmaku 選項啟用即時彈幕 |
三語言的 dialog、qrcode、dialog_steps 分別示範表單與欄位更新、QR Code 與文字複製、圖片更新與連續對話框。語言差異保留在對應 Demo:
| 語言 | 專屬操作與差異 |
|---|---|
| Java | text_get/text_post 取得正文;native_toast 呼叫 Android;dialog 同步取得表單結果;binary 使用 buffer=1;串流使用 InputStream |
| Python | ttl_text 讀取 JSON 快取文字;native_toast 呼叫 Android;dialog 同步取得表單結果;proxy_body 使用 generator;binary 回傳 bytes |
| QuickJS | dialog 以 Promise 取得表單結果;html_query 解析 HTML 欄位;proxy_body 使用 async generator;binary 回傳 ArrayBuffer;下載保留在 App cache |
在 playerContent(flag, id, vipFlags)/play(flag, id, vipFlags) 的回傳結果中,以 list[0] 帶回 VOD 詳情。vod_id 可省略,提供時會連動歷史與收藏識別。
以下 vod 為詳情資料,url 為播放網址。
return Response.create().url(url).vod(vod).string();return {"parse": 0, "url": url, "list": [vod]}return {parse: 0, url, list: [vod]};Node.js 的 /play 與 type=4 HTTP API 使用相同 JSON 格式。App 採用結果時更新 VOD;預載結果在切集時套用,取消或過期的結果會丟棄。
episode 依相同 vod_play_from 合併,相同名稱與播放值去重,其餘追加,保留目前選集位置。
選項放在 Site.ext 或直播 ext,使用到的功能才需要填。web_url 預設 https://example.com/,request_url 省略時沿用 web_url;外部請求與播放需換成自己的有效來源。
| ext | 用途 |
|---|---|
image_base |
封面網址前綴,留空使用 App 預設圖 |
web_url、game_url、cookie_name |
一般網頁、獨立內容及單一登入 Cookie;game_url 留空時使用 web_url |
web_cookie |
登入 Cookie 分組陣列,取代 cookie_name |
request_url、request_method、request_headers、request_options |
request/request_sync/binary 的網址、方法、標頭與 HTTP options;binary 固定使用自己的 buffer 型別 |
json_url |
JSON API |
ws_url、ws_data、ws_headers |
WebSocket |
ws_idle |
持續連線的接收逾時毫秒數,預設 0 關閉;僅用於確定有持續訊息或心跳回覆的來源 |
live_danmaku |
true 時,播放回應附上爬蟲即時彈幕來源,使用 ws_url 的文字訊息 |
media_url、backup_media_url |
主要與備用播放網址 |
media_urls |
多畫質 {values: [{n, v}], position},取代主要線 media_url |
media_headers、backup_media_headers、media_format |
播放標頭及格式 |
timeshift |
直播回看 {url, start, window},三語言 Demo 原樣附在播放回應 |
subtitles、danmaku、drm |
字幕物件陣列、彈幕物件陣列及 DRM 物件 |
skips、start_position_ms |
跳過片段與起播位置,時間為毫秒 |
sniff_url |
未填主要播放網址時,交給 App 嗅探的網頁 |
沒有播放網址時顯示提示;有 sniff_url 時回傳 parse=1。直播未填播放網址時回傳空清單。HLS 代理線需使用以 .m3u8 結尾的 media_url。完整附加欄位的組裝方式見對應 Demo 的播放方法。
字幕的 flag=1 為預設選取,flag=2 為強制字幕。skips 的 start/end 是影片開頭起算的毫秒;播放到 start 時跳至 end,ending 可省略 end。
Java 使用繼承的 net;Python 使用 self.net;QuickJS 使用注入的 net。下文以 net 簡寫,時間單位為毫秒。Java 的 options 除特別標示外使用 JSON 字串。
只取 JSON 用 net.json(url, options);連線失敗、HTTP 非 2xx 或無效 JSON 會拋出例外。需要狀態碼、錯誤正文或 bytes 時,用 Java/Python net.req、QuickJS await net.http,分開檢查 error 與 code;Java 先解析 JSON 字串。實作見 Demo 的 json/request 操作,驗證方式見接口驗證。
Java 只要正文可用 net.get(url)、net.get(url, headers)、net.post(url, json, headers)。網路失敗拋出例外,HTTP 非 2xx 仍保留正文;需要判斷狀態碼時用 net.req。
| HTTP options | 用途 |
|---|---|
headers |
標頭物件 |
params |
自動編碼 query,陣列值重複同一個 key |
method、data、postType |
POST 資料;postType 為 json/form/form-data |
body |
原始文字或位元組陣列,搭配 Content-Type,與 data 擇一;陣列每項為 0~255 整數 |
buffer |
0 文字、1 byte 陣列、2 Base64;3 在 Python 回傳 bytes、QuickJS 回傳 ArrayBuffer |
redirect |
預設 1 跟隨重新導向,0 不跟隨 |
timeout、callTimeout |
連線讀寫/整次請求逾時,單位毫秒 |
cookie |
true 使用 WebView Cookie |
QuickJS 的 http/json/batch/map/ws/cached/download/sleep/openStream/stream 回傳 Promise,取結果時用 await;req 預設同步,refresh 直接返回。獨立請求可並行等待 I/O,JS 與 callback 仍在同一執行緒依序執行;不要同步等待同一 JS 執行緒的 callback。
Python/QuickJS Demo 的 binary 操作使用 buffer: 3。Python 回傳 bytes,可用 memoryview(content)[start:end] 建立切片檢視;QuickJS 回傳 ArrayBuffer,長度用 content.byteLength,讀取內容用 new Uint8Array(content)。建立檢視不複製原有資料,跨語言傳遞不保證 zero copy。
三語言 Demo 的 request 可在 ext.request_options 設定原始二進位 POST:
{
"method": "POST",
"headers": {"Content-Type": "application/octet-stream"},
"body": [0, 127, 128, 255]
}Python bytes 先用 list 轉換,QuickJS Uint8Array 先用 Array.from 轉換;buffer 只控制回應格式。
Python/QuickJS 用 net.getUserAgent() 取得 App 的 User-Agent,再按站點需要放進 headers;不會自動套用到每個請求。
Cookie 使用 net.getCookie(url) 讀取、net.setCookie(url, cookie) 寫入。cookie 是單筆 Set-Cookie 格式,寫入回傳布林值;清除可寫入 Max-Age=0 的過期 Cookie。站點 net 讀寫 WebView Cookie,請求加 cookie:true 才會自動帶入與更新。
隔離 Cookie 用 net.session(),在回傳的物件上請求、讀寫 Cookie 與快取;不共用 WebView Cookie 或站點 TTL 快取,也不支援 persist。session 自動帶入與更新自己的 Cookie。Java 用 try-with-resources,Python 用 with,QuickJS 用 try/finally 呼叫 close;關閉會取消其請求、等待、串流與連線,釋放自己的 Cookie 和快取,之後不可繼續使用。
批次用 net.batch(requests, limit),每筆為 {url, options},預設最多 4 筆並行、按輸入順序回傳。Java 傳 requests JSON、回傳 Net.Result[];Python/QuickJS 回傳結果陣列。每筆結果都要檢查狀態。
每項還需快取、簽名或解析時,用 net.map(items, mapper, limit);Java 傳 List,Python 加 self,QuickJS 加 await。mapper 收 (item, index),每次預設最多 4 項並行,按輸入順序回傳;拋錯時停止派送後續項目,已啟動的工作繼續完成。Java/Python 回呼在背景執行緒執行,QuickJS 可用 async,JS 本身仍依序執行;不要修改共用狀態。關閉 net/Session 會取消其網路操作;Java/Python 也會中斷 map 的等待,任意運算或第三方阻塞不保證可強制中止。見 Demo 的 parallel_map。
net.sleep(ms) 可取消,取消後不繼續請求。
下載用 Java net.download(url, optionsJson, file)、Python/QuickJS net.download(url, path, options),直接寫檔。相對路徑以 App cache 為基準,父目錄需存在;完整傳輸後才取代目標檔案。下載不回傳 HTTP 狀態,也不會僅因非 2xx 拒絕,檔案內容由呼叫者確認。
Java 需要進度與暫停控制時,使用共用 SDK 的 Download.create(url, file).start(callback),以 pause() 暫停、收到 paused() 後再次 start(callback) 繼續,cancel() 取消。預設拒絕非 2xx 與空檔,回呼在主執行緒執行;progress(total, downloaded, speed) 的單位是 bytes 與 bytes/s,未知 total 為負數。檔案內容仍須驗證。對話框、暫存檔與資源釋放見 Market。
下載可傳 HTTP options,另支援以下選項:
| options | 用途 |
|---|---|
maxBytes |
寫入上限,正整數 bytes;省略時不設上限 |
size |
預期完整大小,正整數 bytes;與實際大小不符時失敗 |
threads |
0(預設)自動選擇,1 單路,2..4 指定分段並行上限 |
共用下載器管理暫存檔,依 GET、Range 與 ETag/Last-Modified 等條件決定續傳與分段;帶 cookie:true 或使用 session 時不續傳。Demo 的 download 操作使用預設選項,Python 讀取完成後移除暫存目錄,QuickJS 保留在 App cache。
讀取介面提供 code、headers、contentType,HTTP 狀態自行判斷;同一 reader 不可並行讀取。三語言 Demo 的 stream_read 計算回應大小,proxy_stream 經本機代理讀取,均限 15 秒,適用有限長度回應。
| 語言 | 開啟與讀取 | EOF/釋放 |
|---|---|---|
| Java | net.requestStreamAsync(url, NetOptions).get() 取得繼承 InputStream 的 Net.StreamResult | read 回傳 -1;try-with-resources 關閉 |
| Python | self.net.openStream(url, options);reader.read(size) 回傳 bytes | None;with 或 finally 關閉 |
| QuickJS | await net.openStream(url, options);await reader.read(size) 回傳 ArrayBuffer | null;finally 關閉 |
Java 等待 Future 被中斷時,取消 Future 並保留中斷狀態,見 Demo 的 openStream。Python/QuickJS 的 size 預設 65,536,範圍 1..1,048,576 bytes。
Java 將 StreamResult 放入 proxy 第三格。Python/QuickJS 原樣轉送用 net.stream(url, options),Python 加上 self;回應的 code、contentType、stream、headers 依序放入 proxy 回傳值。成功交付後由 App 關閉 stream,不交付時自行 close;QuickJS handle 只供當次 proxy 使用一次,不存入 Local/快取。
| 語言 | 自訂正文 | 每塊型別 |
|---|---|---|
| Java | proxy 第三格回傳自訂 InputStream | 依 InputStream 介面讀取 |
| Python | self.net.stream(generator()) |
bytes/bytearray/連續 memoryview |
| QuickJS | await net.stream(asyncGenerator()) |
ArrayBuffer/TypedArray |
產生器在播放器讀取時才執行,可依序開啟多個上游請求,不接受 HTTP options。reader 在產生器內開啟,以 with/finally 關閉;App 關閉 body 時取消其等待中的串流請求與讀取,再關閉產生器。Python 無法強制中止任意計算或第三方阻塞操作。見 Python/QuickJS Demo 的 proxy_body。
QuickJS 每次拉取沿用站點呼叫逾時,整段輸出不受最初 proxy 方法期限限制。上游直播可用 callTimeout:0 關閉整次 HTTP 期限,保留 timeout 防止讀取卡住。串流功能需更新配套 App 與對應模組。
WebSocket 一次交換用 net.ws(url, options),成功回應 code=101,首個完整訊息放在 content 後關閉。
持續收訊用 net.connect(url, options, callback),立即回傳連線;callback 接收 (socket, event)。使用回呼傳入的 socket,避免 open 早於外部變數賦值。切台或 destroy 時呼叫 close();需立即中斷時用 cancel()。主動關閉或站點/session 釋放後,不再重連。
實作見三語言 Demo 的 socket-start、socket-state 與 socket-stop 操作。
事件為 open/message/retry/error/close;open 的 code=101,message 包含 binary 與 content,retry 包含 code 及選填 reason/error,表示等待重連;error/close 表示永久結束。二進位 content 在 Java 是 byte[]、Python 是 bytes、QuickJS 是 Uint8Array;文字為字串。send 回傳 false 表示未接受發送,不能當成已送達伺服器。
connect 沿用 headers/params/cookie/session;data 或 body 可指定開啟後的首個文字封包。timeout 為建立連線的期限,成功後不限制連線總時長。ping 是協定 Ping 的間隔,單位毫秒,預設 0 關閉;平台要求的訂閱與應用層心跳仍由爬蟲處理。
idle 是未收到文字或二進位訊息的逾時毫秒數,預設 0 關閉。送出訊息或協定 Ping/Pong 不會重設期限;逾時進入錯誤/重連流程。只用於保證有持續訊息或應用心跳回覆的來源。
reconnect:true 啟用暫時網路/伺服器錯誤的自動重連,預設 false。等待由 1 秒倍增至 30 秒,連線穩定 30 秒後重設;認證、憑證、協定或 callback 錯誤不重連。每次 open 重新訂閱,retry 時停止心跳並清除殘包,不另設重連計時器。
Java/Python 回呼不保證在主執行緒,操作 UI 時須切回主執行緒。QuickJS 回呼回到其 JS 執行緒,並在爬蟲方法返回後繼續有效。回呼保持簡短;QuickJS 待處理事件超過 256 筆會回報錯誤並取消連線。
net.cached(key, options, loader) 儲存 loader 回傳的 JSON 資料,同 key 合併載入。Java 的回傳值使用 JSON 字串。
Python self.net.cachedText(key, options, loader) 回傳同一項目的 JSON 文字,loader 仍回傳 JSON 資料。大型目錄可按文字重用私有索引,但每次須經原生快取判斷有效期,destroy 時釋放快照。實作見 Python Demo 的 ttl_text;一般資料用 cached,每次取得獨立可修改物件。
| options | 用途 |
|---|---|
ttl |
必填,毫秒;0 不快取 |
stale |
預設 true,過期先回舊值並背景更新;false 等待新資料 |
persist |
預設 false;true 持久化公開資料,隔離 session 不支援 |
net.peek(key) 只讀已有值,包括過期值;net.refresh(key, options, loader) 立即返回並依 TTL 背景更新。net.clearCache(key) 清一筆,省略 key 清全部並取消載入。快取按站點隔離,可被系統清除;播放網址使用短 TTL、stale:false,不持久化。
Local 保存小型設定:Java 使用 local、Python 使用 self.local、QuickJS 使用 local,皆提供 get(key)、set(key, value)、delete(key),App 按 Site.key 隔離。Java 使用 JSON 字串,Python/QuickJS 直接存 JSON 值;不存在的 key 回傳 null(Python 為 None)。清快取不會刪除 Local。
短段腳本用 js.eval(source, options);Java 的 options 與回傳值使用 JSON 字串,Python 加 self,QuickJS 加 await。每次建立隔離環境,回傳最後一個運算式的 JSON 值;undefined、函式、非有限數值及循環物件會失敗,不接受 Promise。內建 md5X(text),不提供網路、DOM 或模組匯入。
options.globals 傳入 JSON 變數,不拼接資料到程式碼,也不能覆蓋 md5X;options.timeout 為正整數毫秒,預設 5000。完成、逾時或取消時關閉環境;站點釋放會取消尚未完成的執行。三種語言都需要更新後的 App 與 QuickJS 模組,Python 另需 Python 模組。見 Demo 的 script。
完整清單分頁使用 Java Response.page(items, page, limit).string()、Python self.page(items, pg, limit)、QuickJS page(items, pg, limit)。網站已分頁時使用 Response.create().vods(items).page(page, pagecount, limit, total).string(),未知 total 時只填已知欄位。完整單頁清單可用 .page(),只輸出 page=1 與 pagecount=1。超連結用 Java Json.link、Python self.link、QuickJS link。
Python/QuickJS 的 page 也可傳第四個參數 total。省略時切分完整清單;傳入精確總筆數時,items 使用網站回傳的當頁資料,不再切片。total 未知時直接回傳已知的 page/pagecount/limit,不估算總筆數。
在播放回應附上 timeshift,App 便會顯示可回拉的直播時間軸。來源須確實提供對應時間的內容;未填時沿用原本播放行為。
直播列表的 Channel.timeshift 使用相同格式。節目表回看仍用 catchup,時間軸以該節目為範圍。
{
"parse": 0,
"url": "https://example.com/live.flv",
"timeshift": {
"url": "http://127.0.0.1:9978/proxy?do=js&siteKey=demo&time={time}",
"start": 1792700000000,
"window": 7200000
}
}start 是本場直播開始的 Unix 毫秒,window 是最多可回看的毫秒數。url 使用 getProxyUrl 產生,保留 {time}(亦接受 URL 編碼);App 以目標 Unix 毫秒取代後 GET,0 表示回到直播。端點回傳直接播放的 url、header、format JSON,按來源保留範圍調整邊界,不在本機累積直播。
拖曳至最右側或回到預設位置會重新取得直播來源;連續拖曳與切台會取消舊請求。取源失敗顯示提示並保留目前播放。回看期間暫停爬蟲即時彈幕,回到直播後恢復;一般彈幕檔不受影響。此欄位需搭配支援直播回看的 App。
播放回應的 danmaku[].url 用 getDanmakuUrl(id) 產生,Python 使用 self.getDanmakuUrl(id);App 呼叫該站點的 danmaku(id, emit)。HTTP(S) 來源仍按彈幕檔案載入。
emit 傳入彈幕物件,例如 {"text":"即時彈幕","color":6737151}。Java 使用 JSONObject 並呼叫 emit.accept(item)、Python 使用 dict、QuickJS 使用物件。
Java 回傳 AutoCloseable,Python/QuickJS 回傳停止函式。切台或關閉時,App 呼叫停止;爬蟲取消自己的連線與心跳,不關閉站點 net。
| 欄位 | 說明 | 預設 |
|---|---|---|
| text | 必填文字,不得全空白,最多 4096 字元 | — |
| color | RGB 整數,0~16777215 | 白色 |
| size | 字級 0~96 sp;0 跟隨使用者設定 | 0 |
| type | 1 滾動、4 底部、5 頂部、6 反向 | 1 |
| colors | 2~8 個 RGB 整數組成漸層;省略時使用 color | — |
狀態也使用 emit,格式為 {"state":"retrying","msg":"連線中斷,等待恢復"},不可同時包含 text。msg 選填,最多 512 字元,不放 Cookie、token 或簽名網址。
| state | 說明 |
|---|---|
| ready | 確認訂閱可用;不要僅因 WebSocket open 就回報 |
| retrying | 連線中斷,正在恢復 |
| error | 終止錯誤,不再恢復;App 會停止訂閱並忽略後續回呼 |
connecting 由 App 設定,相同 state/msg 會合併,初始化失敗轉成 error。主動停止時清除狀態,不需回報 closed;狀態只記錄日誌。
爬蟲負責解碼並把平台樣式轉成 RGB/sp;App 按使用者設定顯示,超量或過期訊息可能丟棄。需配套更新 App、runtime 與 catvod-api.jar。
三語言 Demo 的 live_danmaku 以收到首個文字訊息回報 ready;實際站點須依平台確認訂閱並解碼封包。
htmlQuery(html, plan) 一次解析 HTML 並回傳普通物件,不發請求、不保留 DOM。實作見 QuickJS Demo 的 htmlQueryAction/html_query;欄位、回傳值與限制見 HTML 查詢參考。必填值驗證、trim、相對 URL 與去重由爬蟲處理。
action 回傳 web 開網頁、msg 顯示提示。實作見 Demo 的 web/web_view/toast 操作。
url 限 HTTP(S)。一般網頁省略 mode,獨立內容設 mode=view,不啟動外部 App、不自動全螢幕。返回先退出全螢幕,再依歷史回上一頁,沒有歷史才關閉。Java 提示可用 Response.notify()。
Cookie 同組全部需要、不同組任一組即可;需有新值且頁面同源。各組可指定 url,未指定時用開啟網址。這是登入完成條件,與 HTTP 的 cookie:true 分開。
直播回傳 [{name, channel: [{name, urls}]}],多線路網址加 $線路名稱;TXT/M3U 也可直接回傳文字。
本機代理網址用 getProxyUrl(params),Java 傳 Map,Python 需加 self。App 自動加入 do/siteKey 並編碼 query。proxy 回傳 [status, mime, body, headers];Java body 用 InputStream,Python 可用 bytes,QuickJS 可用文字、byte 陣列、ArrayBuffer 或 TypedArray。EPG 在產生的網址後接 &date={date},由 App 代入日期。
分塊轉送與產生器正文見串流讀取與代理。
Java/Python 的 getActivity() 即時取得目前 Activity,可能為 null/None。自訂 Dialog 在主執行緒執行時才取得,不保存 Activity 或自行註冊生命週期 callback。Java 工具類可用 Runtime.activity(),Application Context 用 Runtime.context()。
共用 SDK 的 com.github.catvod.Runtime 提供 Context、Activity 與 Toast;Python 透過 java.jclass 呼叫,Toast 由 App 切到主執行緒。原生 Toast 用法見 Java/Python Demo;常用對話框使用下方的共用 Dialog。QuickJS 的 Toast 用 action 回傳 msg。JAR 的 com.github.catvod.spider.Init.init(Context) 是獨立的可選初始化入口。
三語言共用 Dialog 參數與宿主介面,顯示文字、QR Code、圖片或表單,回傳 {button, values}。button 為按鈕 id,values 以欄位 name 對應字串,多選使用字串陣列;允許的返回或點外側操作、預設取消按鈕及頁面銷毀時回傳 null/None。
| 語言 | 呼叫與回呼 |
|---|---|
| Java | dialog.show(options);options/結果為 JSONObject,取消回傳 null |
| Python | self.dialog(options);options/結果為 dict,取消回傳 None |
| QuickJS | await dialog(options);options/結果為物件,取消回傳 null |
onAction 為選填:Java/Python 以第二參數傳入,QuickJS 放在 options 內。Java 的回呼使用 Dialog.Callback,接收 JSONObject、回傳 JSONObject/null,可拋出例外;Python 接收 dict、回傳 dict/None;QuickJS 可回傳 Promise。
Java/Python 在爬蟲工作執行緒同步呼叫,回呼也在原工作執行緒執行,宿主負責 UI 切換;不要在主執行緒阻塞等待。
Java 需使用配套 App 與新版編譯 SDK,Python/QuickJS 需使用配套 App 與模組。Java 的 Dialog 由宿主爬蟲載入器管理,Python 由 runtime 管理;釋放會關閉自己的待處理視窗。
沒有可用 Activity、參數或回呼錯誤、宿主不支援時,Java/Python 拋出例外,QuickJS Promise reject;呼叫取消或爬蟲釋放會關窗並停止等待。Java/Python 回呼須配合工作執行緒中斷;QuickJS 回呼另受 JS 執行期限控制。
| 參數 | 型別與行為 |
|---|---|
| title/message | 選填字串,標題與說明 |
| image | 選填 base64 圖片 data URI,如 data:image/png;base64,...;由爬蟲準備內容,使用 Android 支援的點陣圖片;空字串移除圖片 |
| qrcode | 選填非空白字串;在本機產生 QR Code,內容不必是網址;無法編碼時回報錯誤 |
| copyable | 選填布林值,預設 false;可選取 message,並提供複製全文按鈕;複製不關窗,也不呼叫 onAction |
| cancelable | 選填布林值,預設 true;允許返回取消,false 時也阻止點外側取消;頁面銷毀及呼叫取消仍會清理視窗 |
| inputs | 選填陣列;每項 name 必填且不可重複,label 預設 name;value 為字串,預設空字串;多選為字串陣列,預設空陣列 |
| inputs[].helperText | 選填字串,欄位下方的說明文字;省略或空字串時隱藏,錯誤清除後恢復顯示 |
| inputs[].type | text(預設)、phone、number、password 為單行,textarea 為多行,select 為單選,multiselect 為多選清單;格式與業務驗證由爬蟲負責 |
| inputs[].options | select/multiselect 必填非空陣列;每項 value 為非空白且唯一的字串,label 預設 value 且不可空白;顯示 label。單選回傳字串,多選依 options 順序回傳字串陣列;非空初始值須存在於選項,多選不可重複 |
| inputs[].enabled | 選填布林值,預設 true;允許操作欄位;停用時略過內建驗證,values 仍包含其值 |
| inputs[].required | 選填布林值,預設 false;不可為空或全空白,多選至少一項 |
| inputs[].minLength/maxLength | 選填非負整數,僅適用字串欄位,以 Unicode 字元計算;minLength 預設 0,maxLength 預設不限制且不可小於 minLength;留空時略過 minLength |
| inputs[].minItems/maxItems | 多選欄位的選填非負整數限制,以已選數量計算;minItems 預設 0,maxItems 預設不限制且不可小於 minItems;留空時略過 minItems |
| buttons | 選填陣列,1~3 項,依序對應 positive、negative、neutral;每項 id/label 必填且不可為空,id 不可重複;省略時提供確定(ok)與取消 |
| buttons[].close | 選填布林值,預設 true;false 時保留視窗,必須提供 onAction |
| buttons[].enabled | 選填布林值,預設 true;允許操作按鈕 |
| buttons[].validate | 選填欄位 name 陣列,預設驗證全部欄位;空陣列略過驗證,可只驗證目前操作需要的欄位 |
| onAction | 選填回呼,接收 {button, values},驗證通過後才呼叫;Java/Python 以第二參數傳入同步回呼,QuickJS 放在 options 內並可回傳 Promise |
onAction 可回傳 {message, qrcode, image, values, errors, inputs, buttons, close}:message/qrcode/image 更新顯示內容,values 更新欄位值,errors 以欄位 name 對應錯誤文字,inputs/buttons 更新 enabled,close 覆蓋按鈕的關窗設定。有非空錯誤時保留視窗與輸入值;回傳 null/None 沿用按鈕設定,QuickJS 也可回傳 undefined。
回呼期間停用欄位與按鈕並顯示處理中,結束後恢復各自的 enabled;返回及點外側依 cancelable 處理。可修正的欄位錯誤用 errors 顯示;回呼拋出例外時關窗並回報錯誤。
初始 value 與回呼更新使用相同型別;布林參數不接受字串或數字。
依序呼叫 Dialog 可串接連續對話框,QuickJS 使用 await;前一個視窗完成關閉後才回傳。每一步檢查 null/None,可在取消時停止後續步驟。步驟、資料傳遞與請求仍由爬蟲控制,範例見三語言 Demo 的 dialog_steps。
image 由爬蟲取得圖片內容後提供 data URI;onAction 可更新,省略時保留原圖,空字串移除。解碼失敗時,尚未結束的 dialog 會關窗並回報錯誤;關窗會取消並釋放自己的圖片請求。
回傳 values 只更新指定欄位;空字串可清空文字或取消單選,空陣列可清空多選。整批先核對欄位、型別與選項再套用,未知欄位、型別錯誤、不存在或重複的選項會關窗並回報錯誤。更新不觸發 onAction,必填與長度檢查仍在按鈕操作時執行;帶入或重設按鈕可設 validate:[]。更新欄位會清除其舊錯誤,同次回傳的 errors 隨後套用;關窗結果包含更新後的 values。
textarea 保留換行;select 的長度驗證依回傳 value 計算。兩者沿用 required、按鈕指定驗證欄位及 errors 提示,未選取的非必填 select 回傳空字串。
回傳 inputs 陣列以 name、buttons 陣列以 id 指定既有控制項,只更新 enabled,如 {inputs:[{name:"text",enabled:true}],buttons:[{id:"confirm",enabled:true}]}。省略時保留原設定,未知控制項、重複更新或無效布林值會關窗並回報錯誤。
multiselect 開啟原生勾選清單,確認後才套用;取消保留原值。
上下鍵移動焦點,多行輸入在首尾行才移出欄位;確認鍵開啟單選/多選清單,返回後回到原欄位。
回傳 qrcode 可更新或新增 QR Code,須為可編碼的非空白字串,否則關窗並回報錯誤;省略則保留原 QR Code。更新 QR Code 保留輸入值,按鈕設定 close:false 或回傳 close:false 可繼續操作。
message、QR Code、圖片與輸入欄位共用捲動區域。copyable 為 true 且 message 非空時顯示複製按鈕;onAction 更新 message 後複製的是最新全文,保留換行與前導零。要複製 QR Code 原始內容,可將相同字串填入 message;省略 qrcode 即為純文字或表單 Dialog。
等待使用者期間暫停目前呼叫及其 App 工作的執行期限,onAction 執行及 QuickJS 等待 Promise 時依剩餘時間繼續計時;網路期限照常生效。站點請求與業務驗證留在爬蟲。
三語言 Demo 的 dialog 示範帶入資料、欄位錯誤與確認,qrcode 示範 QR Code 更新與文字複製,dialog_steps 示範圖片更新與連續對話框;範例不記錄或保存輸入內容。
手機驗證可把 phone 與 code 放在同一表單,發碼按鈕設定 close:false、validate:["phone"]。登入請求成功後回傳 close:true,並在 dialog 確認回傳後才保存憑據;取消時不套用登入結果。驗證碼保持字串,保留前導零。
Java 分享檔案使用 AndroidX FileProvider,authority 為 context.getPackageName() + ".provider",Intent 加 FLAG_GRANT_READ_URI_PERMISSION;可分享目錄由 TV 的 file_paths.xml 決定。新增 Android 呼叫需在 TV 的混淆版驗證。