加入了抖音视频处理逻辑
This commit is contained in:
@@ -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读取视频流并保存到本地
|
||||||
|
|||||||
Reference in New Issue
Block a user