Files
tvbox_collect/tvbox/py/ssjh.py
T
2026-06-29 03:41:14 +00:00

145 lines
4.6 KiB
Python

# coding=utf-8
import os
import logging
import sys
import asyncio
import aiohttp
BASE_URL = "http://api.hclyz.com:81/mf"
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
TARGET_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "lib"))
M3U_FILE = os.path.join(TARGET_DIR, "sbjh.m3u")
HEADERS = {"User-Agent": "Mozilla/5.0"}
MAX_WORKERS = 5
def setup_logging():
logger = logging.getLogger("ScraperLogger")
logger.setLevel(logging.INFO)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
consoleHandler = logging.StreamHandler(sys.stdout)
consoleHandler.setFormatter(formatter)
logger.addHandler(consoleHandler)
return logger
log = setup_logging()
async def safeGetJson(url, session, maxRetries=3, retryDelay=2):
for attemptCount in range(maxRetries):
try:
# Set 10 seconds timeout to prevent freezing
async with session.get(url, headers=HEADERS, timeout=10) as responseObj:
if responseObj.status == 200:
return await responseObj.json(content_type=None)
# Log non-200 status codes like 412
log.warning(f"HTTP {responseObj.status} for {url}. Attempt {attemptCount + 1} of {maxRetries}")
except Exception as e:
log.error(f"Request Exception: {url} -> {e}. Attempt {attemptCount + 1} of {maxRetries}")
# Wait before the next retry
if attemptCount < maxRetries - 1:
await asyncio.sleep(retryDelay)
return None
async def processPlatform(item, session, sem):
async with sem:
roomTitle = item.get("title", "").strip()
number = item.get("Number", "")
address = item.get("address", "")
xinImg = item.get("xinimg", "")
platformLogo = xinImg.replace("clun.top", "cdn.gcufbd.top")
log.info(f"📺 Fetching Platform: {roomTitle} (Resource count: {number})")
detail = await safeGetJson(f"{BASE_URL}/{address}", session)
if not detail:
return roomTitle, [], 1
zhubo = detail.get("zhubo", [])
if not zhubo:
return roomTitle, [], 1
groupName = f"{roomTitle}"
results = []
errors = 0
for vod in zhubo:
name = vod.get("title", "").strip()
url = vod.get("address", "").strip()
if not url:
errors += 1
continue
results.append((groupName, name, url, platformLogo))
return roomTitle, results, errors
async def mainAsync():
totalError = 0
totalSuccess = 0
log.info("🚀 Enhanced task initiated.")
# Increase limit_per_host to prevent DNS resolution issues
connector = aiohttp.TCPConnector(limit=MAX_WORKERS, limit_per_host=10)
async with aiohttp.ClientSession(connector=connector) as session:
home = await safeGetJson(f"{BASE_URL}/json.txt", session)
if not home:
log.error("❌ Retrieval failed, collection terminated.")
sys.exit(1)
# Retrieve platform list and remove the first element
data = home.get("pingtai", [])[1:]
m3uLines = ["#EXTM3U x-tvg-url=\"\""]
seenUrls = set()
log.info(f"⚡ Found {len(data)} platforms in total.")
sem = asyncio.Semaphore(MAX_WORKERS)
tasks = [processPlatform(item, session, sem) for item in data]
results = await asyncio.gather(*tasks)
for roomTitle, res, errors in results:
totalError += errors
for groupName, name, url, logo in res:
if url in seenUrls:
continue
seenUrls.add(url)
# Generate M3U tag with Logo
m3uLines.append(f'#EXTINF:-1 tvg-logo="{logo}" group-title="{groupName}",{name}')
m3uLines.append(url)
totalSuccess += 1
try:
os.makedirs(os.path.dirname(M3U_FILE), exist_ok=True)
with open(M3U_FILE, "w", encoding="utf-8") as f:
f.write("\n".join(m3uLines))
log.info(f"📄 Generation successful. Total Streams: {totalSuccess}")
except Exception as e:
log.error(f"❌ Failed to write to file: {e}")
sys.exit(1)
summaryMsg = f"Collection completed, valid: {totalSuccess}, Abnormal: {totalError}"
print(f"::notice title=📁 Save path: {M3U_FILE}::{summaryMsg}")
def main():
asyncio.run(mainAsync())
if __name__ == "__main__":
main()