152 lines
4.6 KiB
Python
152 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, "18/sbjh.m3u")
|
||
|
||
BLACK_LIST = ["支付宝风控解除", "依依实力带飞"]
|
||
|
||
HEADERS = {"User-Agent": "Mozilla/5.0"}
|
||
VALID_PREFIX = ("http://", "https://", "rtmp://")
|
||
|
||
MAX_WORKERS = 15
|
||
|
||
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')
|
||
|
||
console_handler = logging.StreamHandler(sys.stdout)
|
||
console_handler.setFormatter(formatter)
|
||
logger.addHandler(console_handler)
|
||
|
||
return logger
|
||
|
||
log = setup_logging()
|
||
|
||
async def safe_get_json(url, session):
|
||
try:
|
||
async with session.get(url, headers=HEADERS, timeout=10) as r:
|
||
if r.status != 200:
|
||
return None
|
||
return await r.json(content_type=None)
|
||
except Exception as e:
|
||
log.error(f"Request Exception: {url} -> {e}")
|
||
return None
|
||
|
||
def is_valid_stream(url):
|
||
url = url.lower()
|
||
return url.startswith(VALID_PREFIX) and (".m3u8" in url or ".flv" in url or ".mp4" in url or url.startswith("rtmp://"))
|
||
|
||
async def process_platform(item, session, sem):
|
||
async with sem:
|
||
room_title = item.get("title", "").strip()
|
||
number = item.get("Number", "")
|
||
address = item.get("address", "")
|
||
|
||
log.info(f"📺 Concurrent requests:{room_title}({number})")
|
||
|
||
detail = await safe_get_json(f"{BASE_URL}/{address}", session)
|
||
if not detail:
|
||
return room_title, [], 1, 0
|
||
|
||
zhubo = detail.get("zhubo", [])
|
||
if not zhubo:
|
||
return room_title, [], 1, 0
|
||
|
||
group_name = f"{room_title}"
|
||
results = []
|
||
errors = 0
|
||
filtered = 0
|
||
|
||
for vod in zhubo:
|
||
name = vod.get("title", "").strip()
|
||
url = vod.get("address", "").strip()
|
||
|
||
if any(keyword in name for keyword in BLACK_LIST):
|
||
log.info(f"🚫 Blocked words: {name}")
|
||
filtered += 1
|
||
continue
|
||
|
||
if not url or not is_valid_stream(url):
|
||
errors += 1
|
||
continue
|
||
|
||
results.append((group_name, name, url))
|
||
|
||
return room_title, results, errors, filtered
|
||
|
||
async def main_async():
|
||
total_error = 0
|
||
total_success = 0
|
||
total_filtered = 0
|
||
|
||
log.info("🚀 Task initiated.")
|
||
log.info(f"📂 Output the absolute path:{M3U_FILE}")
|
||
|
||
# aiohttp 连接池配置
|
||
connector = aiohttp.TCPConnector(limit=MAX_WORKERS)
|
||
async with aiohttp.ClientSession(connector=connector) as session:
|
||
|
||
home = await safe_get_json(f"{BASE_URL}/json.txt", session)
|
||
if not home:
|
||
log.error("❌ Retrieval failed, collection terminated.")
|
||
sys.exit(1)
|
||
|
||
data = home.get("pingtai", [])[1:]
|
||
data = sorted(data, key=lambda x: int(x.get("Number", 0) or 0), reverse=True)
|
||
|
||
m3u_lines = ["#EXTM3U"]
|
||
seen_urls = set()
|
||
|
||
log.info(f"⚡ Multi-threading (Async): {MAX_WORKERS}")
|
||
|
||
sem = asyncio.Semaphore(MAX_WORKERS)
|
||
|
||
tasks = [process_platform(item, session, sem) for item in data]
|
||
|
||
results = await asyncio.gather(*tasks)
|
||
|
||
for room_title, res, errors, filtered in results:
|
||
total_error += errors
|
||
total_filtered += filtered
|
||
|
||
for group_name, name, url in res:
|
||
if url in seen_urls:
|
||
continue
|
||
|
||
seen_urls.add(url)
|
||
m3u_lines.append(f'#EXTINF:-1 group-title="{group_name}",{name}')
|
||
m3u_lines.append(url)
|
||
total_success += 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(m3u_lines))
|
||
log.info(f"📄 It has been generated and saved.")
|
||
log.info(f"✅ Absolute path: {M3U_FILE}")
|
||
except Exception as e:
|
||
log.error(f"❌ Failed to write to file: {e}")
|
||
sys.exit(1)
|
||
|
||
summary_msg = f"Collection completed, valid:{total_success},Shield:{total_filtered},abnormal:{total_error}"
|
||
log.info(summary_msg)
|
||
|
||
print(f"::notice title=📁 Save path: {M3U_FILE}::{summary_msg}")
|
||
|
||
def main():
|
||
asyncio.run(main_async())
|
||
|
||
if __name__ == "__main__":
|
||
main()
|