AI 生成: 一个基于 Python Flask 的影视采集站,自动从公开采集接口抓取影视资源,提供现代风格的分类浏览页面和在线播放功能。
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
|
||||
# 苹果CMS 采集接口地址(可替换为任意可用源)
|
||||
API_BASE = "https://caiji.dyttzyapi.com/api.php/provide/vod"
|
||||
|
||||
# 本地缓存文件
|
||||
CACHE_FILE = "cache.json"
|
||||
CACHE_TTL = 60 * 30 # 30 分钟
|
||||
|
||||
|
||||
def _fetch(params):
|
||||
"""调用采集接口"""
|
||||
try:
|
||||
resp = requests.get(API_BASE, params=params, timeout=15)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
except Exception as e:
|
||||
print(f"[collector] 请求失败: {e}")
|
||||
return {"list": []}
|
||||
|
||||
|
||||
def fetch_categories():
|
||||
"""获取分类列表"""
|
||||
data = _fetch({"ac": "list"})
|
||||
cats = data.get("class", []) or []
|
||||
result = []
|
||||
for c in cats:
|
||||
result.append({"id": c.get("type_id"), "name": c.get("type_name")})
|
||||
return result
|
||||
|
||||
|
||||
def fetch_list(category_id=None, page=1, keyword=None):
|
||||
"""获取影视列表"""
|
||||
params = {"ac": "detail", "pg": page}
|
||||
if category_id:
|
||||
params["t"] = category_id
|
||||
if keyword:
|
||||
params["wd"] = keyword
|
||||
data = _fetch(params)
|
||||
return _normalize_list(data.get("list", []) or [])
|
||||
|
||||
|
||||
def fetch_detail(vod_id):
|
||||
"""获取影视详情"""
|
||||
data = _fetch({"ac": "detail", "ids": vod_id})
|
||||
items = data.get("list", []) or []
|
||||
if not items:
|
||||
return None
|
||||
return _normalize_detail(items[0])
|
||||
|
||||
|
||||
def _normalize_list(items):
|
||||
out = []
|
||||
for it in items:
|
||||
out.append({
|
||||
"vod_id": it.get("vod_id"),
|
||||
"name": it.get("vod_name"),
|
||||
"pic": it.get("vod_pic"),
|
||||
"remarks": it.get("vod_remarks"),
|
||||
"year": it.get("vod_year"),
|
||||
"area": it.get("vod_area"),
|
||||
"type_name": it.get("type_name"),
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
def _normalize_detail(it):
|
||||
play_from = (it.get("vod_play_from") or "").split("$$$")
|
||||
play_url = (it.get("vod_play_url") or "").split("$$$")
|
||||
episodes = []
|
||||
for i, urls in enumerate(play_url):
|
||||
source = play_from[i] if i < len(play_from) else f"线路{i+1}"
|
||||
ep_list = []
|
||||
for seg in urls.split("#"):
|
||||
if "$" in seg:
|
||||
title, url = seg.split("$", 1)
|
||||
ep_list.append({"title": title, "url": url})
|
||||
episodes.append({"source": source, "episodes": ep_list})
|
||||
|
||||
return {
|
||||
"vod_id": it.get("vod_id"),
|
||||
"name": it.get("vod_name"),
|
||||
"pic": it.get("vod_pic"),
|
||||
"content": it.get("vod_content"),
|
||||
"year": it.get("vod_year"),
|
||||
"area": it.get("vod_area"),
|
||||
"type_name": it.get("type_name"),
|
||||
"remarks": it.get("vod_remarks"),
|
||||
"actors": it.get("vod_actor"),
|
||||
"director": it.get("vod_director"),
|
||||
"play_sources": episodes,
|
||||
}
|
||||
|
||||
|
||||
# ------- 本地缓存 -------
|
||||
|
||||
def _load_cache():
|
||||
if not os.path.exists(CACHE_FILE):
|
||||
return {}
|
||||
try:
|
||||
with open(CACHE_FILE, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def _save_cache(data):
|
||||
try:
|
||||
with open(CACHE_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
print(f"[collector] 缓存写入失败: {e}")
|
||||
|
||||
|
||||
def get_categories_cached():
|
||||
cache = _load_cache()
|
||||
now = time.time()
|
||||
if "categories" in cache and now - cache["categories"].get("ts", 0) < CACHE_TTL:
|
||||
return cache["categories"]["data"]
|
||||
data = fetch_categories()
|
||||
cache["categories"] = {"ts": now, "data": data}
|
||||
_save_cache(cache)
|
||||
return data
|
||||
Reference in New Issue
Block a user