加入了抖音视频处理逻辑

This commit is contained in:
liuwei
2025-11-17 15:34:21 +08:00
parent 57c6a0d234
commit 5f9fadb9e6

View File

@@ -4,6 +4,7 @@ import time
import traceback import traceback
import requests import requests
from typing import Dict, Any, List, Optional, Tuple from typing import Dict, Any, List, Optional, Tuple
from urllib.parse import urlparse
from loguru import logger from loguru import logger
from pathlib import Path from pathlib import Path
@@ -194,39 +195,109 @@ class DouyinParserPlugin(MessagePluginInterface):
return data return data
def _parse_douyin(self, url: str) -> Dict[str, Any]: def _parse_douyin(self, url: str) -> Dict[str, Any]:
"""解析抖音链接"""
try: try:
api_url = "https://api.pearktrue.cn/api/video/douyin/?url="
pay_api_url = "https://api.pearktrue.cn/api/video/api.php"
clean_url = self._clean_url(url) clean_url = self._clean_url(url)
# api_url = api_url + clean_url primary = self._parse_from_internal_api(clean_url)
# response = requests.get(api_url, timeout=30) if primary and primary.get('url'):
# 切换付费模式 return self._clean_response_data(primary)
# 发送请求 secondary = self._parse_from_external_api(clean_url)
params = { if secondary and secondary.get('url'):
"url": clean_url, return self._clean_response_data(secondary)
"key": "f56c1fed0c6e64e7" raise DouyinParserError("两种渠道均未获取到视频地址")
}
response = requests.post(pay_api_url, params=params, timeout=30)
if response.status_code != 200:
raise DouyinParserError(f"API请求失败状态码: {response.status_code}")
data = response.json()
self.LOG.info(f"[抖音] API响应数据: {data}")
if data.get("code") == 200:
result = data.get("data", {})
self.LOG.info(f"[抖音] API响应数据result: {result}")
if result.get('url'):
return result
else:
raise DouyinParserError(data.get("message", "未知错误"))
except Exception as e: except Exception as e:
self.LOG.error(f"[抖音] 解析过程发生未知错误: {str(e)}\n{traceback.format_exc()}") self.LOG.error(f"[抖音] 解析过程发生未知错误: {str(e)}\n{traceback.format_exc()}")
raise DouyinParserError(f"未知错误: {str(e)}") raise DouyinParserError(f"未知错误: {str(e)}")
def _build_proxies(self) -> Optional[Dict[str, str]]:
if self.http_proxy:
return {"http": self.http_proxy, "https": self.http_proxy}
return None
def _parse_from_internal_api(self, clean_url: str) -> Optional[Dict[str, Any]]:
try:
endpoint = "http://192.168.2.32:8999/api/hybrid/video_data"
headers = {"accept": "application/json"}
params = {"url": clean_url, "minimal": "false"}
response = requests.get(endpoint, headers=headers, params=params, timeout=10, proxies=self._build_proxies())
if response.status_code != 200:
return None
body = response.json() or {}
if body.get("code") != 200:
return None
data = body.get("data") or {}
video = data.get("video") or {}
bit_rates = video.get("bit_rate") or []
chosen_url = ""
mp4_sorted = sorted([br for br in bit_rates if br.get("format") == "mp4"], key=lambda x: x.get("bit_rate") or 0, reverse=True)
for br in mp4_sorted:
play_addr = br.get("play_addr") or {}
urls = play_addr.get("url_list") or []
selected = self._prefer_v3_v10(urls)
if selected:
chosen_url = selected
break
if not chosen_url:
play_addr = video.get("play_addr") or {}
urls = play_addr.get("url_list") or []
selected = self._prefer_v3_v10(urls)
if selected:
chosen_url = selected
cover = (video.get("cover") or {}).get("url_list") or []
cover_url = cover[0] if cover else ""
caption = data.get("caption") or "无标题"
author = (data.get("author") or {})
nickname = author.get("nickname") or author.get("unique_id") or "未知作者"
result = {"url": chosen_url or "", "title": caption, "author": nickname, "cover": cover_url}
if result.get("url"):
return result
return None
except Exception:
return None
def _parse_from_external_api(self, clean_url: str) -> Optional[Dict[str, Any]]:
try:
pay_api_url = "https://api.pearktrue.cn/api/video/api.php"
params = {"url": clean_url, "key": "f56c1fed0c6e64e7"}
response = requests.post(pay_api_url, params=params, timeout=10, proxies=self._build_proxies())
if response.status_code != 200:
return None
data = response.json() or {}
if data.get("code") == 200:
result = data.get("data", {})
if result.get("url"):
return result
return None
except Exception:
return None
def _prefer_v3_v10(self, urls: List[str]) -> Optional[str]:
try:
if not urls:
return None
cleaned = [(u or "").strip().strip("`") for u in urls if u]
def is_vx(n: str) -> bool:
return bool(re.match(r"^v(3|4|5|6|7|8|9|10)(?:[\-.]|$)", n, re.I))
def is_douyinvod(n: str) -> bool:
return "douyinvod.com" in n.lower()
first = None
for s in cleaned:
netloc = urlparse(s).netloc
if is_vx(netloc) and is_douyinvod(netloc):
return s
if first is None:
first = s
for s in cleaned:
netloc = urlparse(s).netloc
if is_vx(netloc):
return s
for s in cleaned:
netloc = urlparse(s).netloc
if is_douyinvod(netloc):
return s
return first
except Exception:
return urls[0] if urls else None
def _download_stream(self, url, save_path): def _download_stream(self, url, save_path):
""" """
从指定URL读取视频流并保存到本地 从指定URL读取视频流并保存到本地