219 lines
8.4 KiB
Python
219 lines
8.4 KiB
Python
"""
|
|
@header({
|
|
searchable: 1,
|
|
filterable: 1,
|
|
quickSearch: 1,
|
|
title: 'Beeg',
|
|
lang: 'hipy',
|
|
})
|
|
"""
|
|
|
|
# 七哥 - 2026 性能优化版 (解决加载慢与播放问题)
|
|
import sys
|
|
import time
|
|
import json
|
|
import re
|
|
import urllib.parse
|
|
from base64 import b64decode, b64encode
|
|
sys.path.append('..')
|
|
from base.spider import Spider
|
|
|
|
class Spider(Spider):
|
|
def init(self, extend=""):
|
|
self.host = "https://beeg.com"
|
|
self.api_host = "https://store.externulls.com"
|
|
self.video_host = "https://video.beeg.com"
|
|
self.headers = {
|
|
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
|
|
'Referer': self.host + '/',
|
|
'Connection': 'keep-alive'
|
|
}
|
|
self.timeout = 10 # 缩短超时时间以提高响应效率
|
|
self.retries = 2
|
|
|
|
def getName(self): return "Beeg"
|
|
def isVideoFormat(self, url): return True
|
|
def manualVideoCheck(self): return False
|
|
def destroy(self): pass
|
|
|
|
def homeContent(self, filter):
|
|
result = {}
|
|
result['class'] = [
|
|
{"type_id": "latest", "type_name": "最新更新"},
|
|
{"type_id": "channels", "type_name": "频道列表"},
|
|
{"type_id": "pornstars", "type_name": "影星列表"},
|
|
{"type_id": "categories", "type_name": "分类标签"}
|
|
]
|
|
return result
|
|
|
|
def homeVideoContent(self):
|
|
return self.categoryContent("latest", 1, None, {})
|
|
|
|
def categoryContent(self, tid, pg, filter, extend):
|
|
result = {}
|
|
videos = []
|
|
limit = 48
|
|
try: curr_pg = int(pg)
|
|
except: curr_pg = 1
|
|
offset = (curr_pg - 1) * limit
|
|
|
|
if tid in ["channels", "pornstars", "categories"]:
|
|
t_map = {"channels": "brand", "pornstars": "person", "categories": "other"}
|
|
url = f"{self.api_host}/tag/recommends?type={t_map[tid]}&slug=japanese"
|
|
videos = self._fetch_section_list(url, tid)
|
|
else:
|
|
if tid == "latest":
|
|
url = f"{self.api_host}/facts/tag?id=27173&limit={limit}&offset={offset}"
|
|
elif str(tid).isdigit():
|
|
url = f"{self.api_host}/facts/tag?id={tid}&limit={limit}&offset={offset}"
|
|
else:
|
|
url = f"{self.api_host}/facts/tag?slug={tid}&limit={limit}&offset={offset}"
|
|
|
|
url = re.sub(r"offset=\d+", f"offset={offset}", url)
|
|
videos = self._fetch_video_list(url)
|
|
|
|
result['list'] = videos
|
|
result['page'] = curr_pg
|
|
result['pagecount'] = 9999
|
|
result['limit'] = limit
|
|
result['total'] = 999999
|
|
return result
|
|
|
|
def searchContent(self, key, quick, pg="1"):
|
|
return {'list': []}
|
|
|
|
def detailContent(self, ids):
|
|
result = {}
|
|
url = ids[0]
|
|
video_id = url.rstrip('/').split('/')[-1]
|
|
api_url = f"{self.api_host}/facts/file/{video_id}"
|
|
try:
|
|
r = self.fetch(api_url)
|
|
data = json.loads(r.text)
|
|
file_info = data.get('file', {})
|
|
title = f"Video {video_id}"
|
|
file_data = file_info.get('data', [])
|
|
if file_data: title = file_data[0].get('cd_value', title)
|
|
|
|
vod = {
|
|
"vod_id": url, "vod_name": title.replace('{','').replace('}',''),
|
|
"vod_pic": f"https://thumbs.externulls.com/videos/{video_id}/0.webp?size=480x270",
|
|
"vod_play_from": "Beeg",
|
|
"vod_play_url": f"播放${self.e64(video_id)}"
|
|
}
|
|
result['list'] = [vod]
|
|
except: pass
|
|
return result
|
|
|
|
def playerContent(self, flag, id, vipFlags):
|
|
result = {"parse": 0}
|
|
try:
|
|
video_id = self.d64(id)
|
|
r = self.fetch(f"{self.api_host}/facts/file/{video_id}")
|
|
if not r: return {"parse": 0, "url": ""}
|
|
|
|
data = json.loads(r.text).get('file', {})
|
|
hls_res = data.get('hls_resources', {})
|
|
final_url = ""
|
|
|
|
# --- 优化:速度优先的路径搜索策略 ---
|
|
# 优先顺序:720p -> 1080p -> 480p -> 其他带 Token 的路径
|
|
priority_keys = ['720', '1080', '480', '360']
|
|
for pk in priority_keys:
|
|
for k, v in hls_res.items():
|
|
if pk in k and 'multi' not in k:
|
|
final_url = v
|
|
break
|
|
if final_url: break
|
|
|
|
# 如果没找到,尝试获取任何有效的 m3u8 或带 key 的路径
|
|
if not final_url:
|
|
for v in hls_res.values():
|
|
if isinstance(v, str) and ('.m3u8' in v or 'key=' in v):
|
|
final_url = v
|
|
break
|
|
|
|
if final_url:
|
|
# 智能域名与 Token 处理
|
|
if not final_url.startswith('http'):
|
|
domain = self.video_host if 'key=' in final_url else "https://video.externulls.com"
|
|
final_url = f"{domain}/{final_url.lstrip('/')}"
|
|
|
|
result["url"] = final_url.replace('.com//', '.com/')
|
|
result["header"] = {
|
|
'User-Agent': self.headers['User-Agent'],
|
|
'Referer': f'https://beeg.com/{video_id}',
|
|
'Origin': 'https://beeg.com',
|
|
'Connection': 'keep-alive',
|
|
'Accept': '*/*'
|
|
}
|
|
else:
|
|
result["parse"] = 1
|
|
result["url"] = f"https://beeg.com/{video_id}"
|
|
except:
|
|
result["url"] = ""
|
|
return result
|
|
|
|
def localProxy(self, param): return ""
|
|
|
|
def _fetch_video_list(self, url):
|
|
videos = []
|
|
try:
|
|
r = self.fetch(url)
|
|
data = json.loads(r.text)
|
|
items = data.get('relations', []) if isinstance(data, dict) else data
|
|
if not items and isinstance(data, dict): items = data.get('data', [])
|
|
for elem in items:
|
|
try:
|
|
f = elem.get("file") if elem.get("file") else elem
|
|
v_id = f.get("id")
|
|
if v_id:
|
|
title_info = f.get("data", [])
|
|
name = title_info[0].get("cd_value", str(v_id)) if title_info else str(v_id)
|
|
videos.append({
|
|
"vod_id": f"{self.host}/{v_id}",
|
|
"vod_name": name.replace('{','').replace('}',''),
|
|
"vod_pic": f"https://thumbs.externulls.com/videos/{v_id}/0.webp?size=480x270",
|
|
"vod_remarks": str(f.get("fl_duration", ""))
|
|
})
|
|
except: continue
|
|
except: pass
|
|
return videos
|
|
|
|
def _fetch_section_list(self, url, section_type):
|
|
videos = []
|
|
try:
|
|
r = self.fetch(url)
|
|
data = json.loads(r.text)
|
|
items = data if isinstance(data, list) else data.get('data', [])
|
|
for elem in items:
|
|
try:
|
|
v_id, slug = elem.get("id"), elem.get("tg_slug")
|
|
final_id = str(v_id) if v_id else slug
|
|
if final_id:
|
|
name = elem.get("tg_name", final_id).replace('{','').replace('}','')
|
|
thumb = ""
|
|
if elem.get("thumbs"):
|
|
t_id = elem["thumbs"][-1].get("id")
|
|
if t_id: thumb = f"https://thumbs.externulls.com/photos/{t_id}/to.webp"
|
|
videos.append({"vod_id": final_id, "vod_name": name, "vod_pic": thumb, "vod_tag": "folder", "vod_remarks": "分类"})
|
|
except: continue
|
|
except: pass
|
|
return videos
|
|
|
|
def e64(self, text): return b64encode(text.encode()).decode()
|
|
def d64(self, encoded_text): return b64decode(encoded_text.encode()).decode()
|
|
|
|
def fetch(self, url, params=None, headers=None, timeout=None):
|
|
import requests, ssl
|
|
try: ssl._create_default_https_context = ssl._create_unverified_context
|
|
except: pass
|
|
if headers is None: headers = self.headers
|
|
for _ in range(self.retries + 1):
|
|
try:
|
|
resp = requests.get(url, params=params, headers=headers, timeout=timeout or self.timeout, verify=False)
|
|
resp.encoding = 'utf-8'
|
|
return resp
|
|
except: time.sleep(0.5)
|
|
return None
|