抖音解析新增Cookie配置并接入本地后备提取链路
变更项: - 配置文件新增 cookie 与 cookie_file 两个参数,支持账号态提取 - 解析请求与媒资下载统一接入请求头构建,按配置自动注入 Cookie - yt-dlp Python/命令行两种后备提取方式均支持 Cookie 注入 - Cookie 注入策略为 cookie_file 优先、cookie 次之,提升受限场景下无水印链接提取成功率
This commit is contained in:
@@ -8,3 +8,16 @@ download_mode = "card"
|
|||||||
# 格式: http://用户名:密码@代理地址:代理端口
|
# 格式: http://用户名:密码@代理地址:代理端口
|
||||||
# 例如:http://127.0.0.1:7890
|
# 例如:http://127.0.0.1:7890
|
||||||
http_proxy = ""
|
http_proxy = ""
|
||||||
|
|
||||||
|
# 抖音Cookie(可选,建议配置,提升本地提取命中率与稳定性)
|
||||||
|
# 说明:
|
||||||
|
# 1. 可直接粘贴浏览器请求头中的完整 Cookie 字符串;
|
||||||
|
# 2. 当接口解析失败进入本地后备提取时,会携带该 Cookie。
|
||||||
|
cookie = ""
|
||||||
|
|
||||||
|
# 抖音Cookie文件路径(可选)
|
||||||
|
# 说明:
|
||||||
|
# 1. 仅用于本地后备提取(yt-dlp);
|
||||||
|
# 2. 若同时配置 cookie 与 cookie_file,优先使用 cookie_file。
|
||||||
|
# 例如:D:/learn/abot/resource/douyin_cookies.txt
|
||||||
|
cookie_file = ""
|
||||||
|
|||||||
@@ -91,6 +91,12 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
douyin_config = self._config.get("Douyin", {})
|
douyin_config = self._config.get("Douyin", {})
|
||||||
self.enable = douyin_config.get("enable", True)
|
self.enable = douyin_config.get("enable", True)
|
||||||
self.http_proxy = douyin_config.get("http_proxy", "")
|
self.http_proxy = douyin_config.get("http_proxy", "")
|
||||||
|
# Cookie 配置说明:
|
||||||
|
# 1) cookie: 直接粘贴请求头 Cookie 字符串;
|
||||||
|
# 2) cookie_file: Netscape 格式 cookies 文件路径;
|
||||||
|
# 3) 当二者同时存在时,后备提取优先 cookie_file(兼容性更好)。
|
||||||
|
self.cookie = douyin_config.get("cookie", "") or ""
|
||||||
|
self.cookie_file = douyin_config.get("cookie_file", "") or ""
|
||||||
self.download_mode = douyin_config.get("download_mode", "card") # card或file
|
self.download_mode = douyin_config.get("download_mode", "card") # card或file
|
||||||
|
|
||||||
self.LOG.debug(f"[{self.name}] 插件初始化完成,代理设置: {self.http_proxy}")
|
self.LOG.debug(f"[{self.name}] 插件初始化完成,代理设置: {self.http_proxy}")
|
||||||
@@ -261,10 +267,30 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
return {"http": self.http_proxy, "https": self.http_proxy}
|
return {"http": self.http_proxy, "https": self.http_proxy}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _build_request_headers(self) -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
构建通用请求头。
|
||||||
|
|
||||||
|
设计说明:
|
||||||
|
- User-Agent 保持常规浏览器标识,降低被目标站点直接拒绝的概率;
|
||||||
|
- Cookie 在有配置时注入到请求头,提升受限资源的提取成功率。
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"User-Agent": (
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||||
|
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||||
|
"Chrome/124.0.0.0 Safari/537.36"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if self.cookie:
|
||||||
|
headers["Cookie"] = self.cookie
|
||||||
|
return headers
|
||||||
|
|
||||||
def _parse_from_internal_api(self, clean_url: str) -> Optional[Dict[str, Any]]:
|
def _parse_from_internal_api(self, clean_url: str) -> Optional[Dict[str, Any]]:
|
||||||
try:
|
try:
|
||||||
endpoint = "http://192.168.2.32:8999/api/hybrid/video_data"
|
endpoint = "http://192.168.2.32:8999/api/hybrid/video_data"
|
||||||
headers = {"accept": "application/json"}
|
headers = self._build_request_headers()
|
||||||
|
headers["accept"] = "application/json"
|
||||||
params = {"url": clean_url, "minimal": "false"}
|
params = {"url": clean_url, "minimal": "false"}
|
||||||
response = requests.get(endpoint, headers=headers, params=params, timeout=10, proxies=self._build_proxies())
|
response = requests.get(endpoint, headers=headers, params=params, timeout=10, proxies=self._build_proxies())
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
@@ -325,7 +351,13 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
try:
|
try:
|
||||||
pay_api_url = "https://api.pearktrue.cn/api/video/api.php"
|
pay_api_url = "https://api.pearktrue.cn/api/video/api.php"
|
||||||
params = {"url": clean_url, "key": "f56c1fed0c6e64e7"}
|
params = {"url": clean_url, "key": "f56c1fed0c6e64e7"}
|
||||||
response = requests.post(pay_api_url, params=params, timeout=10, proxies=self._build_proxies())
|
response = requests.post(
|
||||||
|
pay_api_url,
|
||||||
|
params=params,
|
||||||
|
headers=self._build_request_headers(),
|
||||||
|
timeout=10,
|
||||||
|
proxies=self._build_proxies(),
|
||||||
|
)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return None
|
return None
|
||||||
data = response.json() or {}
|
data = response.json() or {}
|
||||||
@@ -391,7 +423,13 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 发送GET请求,启用流式传输
|
# 发送GET请求,启用流式传输
|
||||||
response = requests.get(url, stream=True)
|
response = requests.get(
|
||||||
|
url,
|
||||||
|
stream=True,
|
||||||
|
headers=self._build_request_headers(),
|
||||||
|
proxies=self._build_proxies(),
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
|
||||||
# 检查请求是否成功
|
# 检查请求是否成功
|
||||||
response.raise_for_status() # 如果状态码不是200,将抛出异常
|
response.raise_for_status() # 如果状态码不是200,将抛出异常
|
||||||
@@ -423,7 +461,12 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
|
|
||||||
def _download_image_bytes(self, url: str) -> Optional[bytes]:
|
def _download_image_bytes(self, url: str) -> Optional[bytes]:
|
||||||
try:
|
try:
|
||||||
resp = requests.get(url, timeout=15, proxies=self._build_proxies())
|
resp = requests.get(
|
||||||
|
url,
|
||||||
|
headers=self._build_request_headers(),
|
||||||
|
timeout=15,
|
||||||
|
proxies=self._build_proxies(),
|
||||||
|
)
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
return resp.content
|
return resp.content
|
||||||
return None
|
return None
|
||||||
@@ -540,6 +583,13 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
"proxy": self.http_proxy or None,
|
"proxy": self.http_proxy or None,
|
||||||
"nocheckcertificate": True,
|
"nocheckcertificate": True,
|
||||||
}
|
}
|
||||||
|
# Cookie 注入策略:
|
||||||
|
# - 优先使用 cookie_file(yt-dlp 官方支持的 cookies 文件,兼容性更高);
|
||||||
|
# - 否则回退到手工 Cookie 请求头。
|
||||||
|
if self.cookie_file and os.path.exists(self.cookie_file):
|
||||||
|
ydl_opts["cookiefile"] = self.cookie_file
|
||||||
|
elif self.cookie:
|
||||||
|
ydl_opts["http_headers"] = {"Cookie": self.cookie}
|
||||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||||
info = ydl.extract_info(clean_url, download=False)
|
info = ydl.extract_info(clean_url, download=False)
|
||||||
if isinstance(info, dict):
|
if isinstance(info, dict):
|
||||||
@@ -559,6 +609,11 @@ class DouyinParserPlugin(MessagePluginInterface):
|
|||||||
cmd = [yt_dlp_bin, "-J", "--no-warnings", "--skip-download", clean_url]
|
cmd = [yt_dlp_bin, "-J", "--no-warnings", "--skip-download", clean_url]
|
||||||
if self.http_proxy:
|
if self.http_proxy:
|
||||||
cmd.extend(["--proxy", self.http_proxy])
|
cmd.extend(["--proxy", self.http_proxy])
|
||||||
|
# 命令行模式下同样注入 Cookie,确保与 Python 模式行为一致。
|
||||||
|
if self.cookie_file and os.path.exists(self.cookie_file):
|
||||||
|
cmd.extend(["--cookies", self.cookie_file])
|
||||||
|
elif self.cookie:
|
||||||
|
cmd.extend(["--add-header", f"Cookie: {self.cookie}"])
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=25)
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=25)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user