feat: add TVBox JSON monitor
This commit is contained in:
commit
37f4ebf1fd
6 files changed
+1040
No files matched your search
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract, decode, and normalize a TVBox JSON configuration payload."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
source_url = os.environ["TVBOX_URL"]
|
||||
user_agent = os.environ.get("TVBOX_UA", "okhttp/3.15")
|
||||
output_json = Path(os.environ.get("OUT_JSON", "data/tvbox.json"))
|
||||
output_meta = Path(os.environ.get("OUT_META", "data/tvbox.meta.json"))
|
||||
|
||||
request = urllib.request.Request(
|
||||
source_url,
|
||||
headers={
|
||||
"User-Agent": user_agent,
|
||||
"Accept": "*/*",
|
||||
},
|
||||
)
|
||||
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
raw_payload = response.read()
|
||||
final_url = response.geturl()
|
||||
response_headers = dict(response.headers.items())
|
||||
|
||||
# The endpoint returns a JPEG-like payload to TVBox clients. The actual
|
||||
# configuration is appended after the JPEG EOI marker.
|
||||
jpeg_eoi_offset = raw_payload.find(b"\xff\xd9")
|
||||
if jpeg_eoi_offset < 0:
|
||||
raise SystemExit("JPEG EOI marker FF D9 was not found.")
|
||||
|
||||
appended_payload = raw_payload[jpeg_eoi_offset + 2 :]
|
||||
|
||||
# The appended section uses a short prefix followed by the "**" separator.
|
||||
separator_offset = appended_payload.find(b"**")
|
||||
if separator_offset < 0:
|
||||
raise SystemExit('Payload separator "**" was not found after JPEG EOI.')
|
||||
|
||||
base64_payload = appended_payload[separator_offset + 2 :]
|
||||
decoded_bytes = base64.b64decode(base64_payload, validate=True)
|
||||
decoded_text = decoded_bytes.decode("utf-8-sig")
|
||||
|
||||
# The upstream configuration may contain JSONC-style full-line comments.
|
||||
# This repository stores strict JSON for compatibility with standard tools.
|
||||
strict_json_text = "\n".join(
|
||||
line
|
||||
for line in decoded_text.splitlines()
|
||||
if not line.lstrip().startswith("//")
|
||||
)
|
||||
|
||||
parsed_json = json.loads(strict_json_text)
|
||||
|
||||
output_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_meta.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
output_json.write_text(
|
||||
json.dumps(parsed_json, ensure_ascii=False, indent=2, sort_keys=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
metadata = {
|
||||
"source_url": source_url,
|
||||
"final_url": final_url,
|
||||
"user_agent": user_agent,
|
||||
"raw_size": len(raw_payload),
|
||||
"raw_sha256": hashlib.sha256(raw_payload).hexdigest(),
|
||||
"decoded_size": len(decoded_bytes),
|
||||
"decoded_sha256": hashlib.sha256(decoded_bytes).hexdigest(),
|
||||
"json_sha256": hashlib.sha256(output_json.read_bytes()).hexdigest(),
|
||||
"jpeg_eoi_offset": jpeg_eoi_offset,
|
||||
"appended_size": len(appended_payload),
|
||||
"base64_size": len(base64_payload),
|
||||
"etag": response_headers.get("ETag"),
|
||||
"last_modified": response_headers.get("Last-Modified"),
|
||||
"cache_control": response_headers.get("Cache-Control"),
|
||||
"content_type": response_headers.get("Content-Type"),
|
||||
}
|
||||
|
||||
output_meta.write_text(
|
||||
json.dumps(metadata, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
print(json.dumps(metadata, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user