Fixes
This commit is contained in:
@@ -57,6 +57,12 @@ pipeline {
|
|||||||
pip install --upgrade yt-dlp
|
pip install --upgrade yt-dlp
|
||||||
pip show yt-dlp | grep -E "^(Name|Version)"
|
pip show yt-dlp | grep -E "^(Name|Version)"
|
||||||
|
|
||||||
|
# ── curl_cffi: TikTok impersonation (REQUIRED) ─────
|
||||||
|
# Without this yt-dlp cannot bypass TikTok bot detection
|
||||||
|
pip install --upgrade curl-cffi
|
||||||
|
pip show curl-cffi | grep -E "^(Name|Version)"
|
||||||
|
python3 -c "import curl_cffi; print('curl_cffi OK')"
|
||||||
|
|
||||||
# ── playwright-stealth version check ───────────────
|
# ── playwright-stealth version check ───────────────
|
||||||
pip show playwright-stealth | grep -E "^(Name|Version)"
|
pip show playwright-stealth | grep -E "^(Name|Version)"
|
||||||
python3 -c "
|
python3 -c "
|
||||||
@@ -69,12 +75,12 @@ except ImportError:
|
|||||||
"
|
"
|
||||||
|
|
||||||
# ── Sanity checks ──────────────────────────────────
|
# ── Sanity checks ──────────────────────────────────
|
||||||
python3 -c "import atproto; print('atproto OK')"
|
python3 -c "import atproto; print('atproto OK')"
|
||||||
python3 -c "import playwright; print('playwright OK')"
|
python3 -c "import playwright; print('playwright OK')"
|
||||||
python3 -c "import yt_dlp; print('yt_dlp OK')"
|
python3 -c "import yt_dlp; print('yt_dlp OK')"
|
||||||
python3 -c "import httpx; print('httpx OK')"
|
python3 -c "import httpx; print('httpx OK')"
|
||||||
python3 -c "import arrow; print('arrow OK')"
|
python3 -c "import arrow; print('arrow OK')"
|
||||||
python3 -c "import moviepy; print('moviepy OK')"
|
python3 -c "import moviepy; print('moviepy OK')"
|
||||||
|
|
||||||
# ── System tools ───────────────────────────────────
|
# ── System tools ───────────────────────────────────
|
||||||
ffmpeg -version | head -1
|
ffmpeg -version | head -1
|
||||||
|
|||||||
@@ -596,51 +596,20 @@ def compress_video(input_path: str, output_path: str,
|
|||||||
logging.error(f"❌ compress_video error: {e}")
|
logging.error(f"❌ compress_video error: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def download_video(url: str, output_path: str,
|
def download_video(url: str, output_path: str,
|
||||||
cookies: list = None) -> bool:
|
cookies: list = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Download a video from a URL (MP4 or M3U8) using httpx or yt-dlp.
|
Download a TikTok video using yt-dlp with impersonation.
|
||||||
Falls back to yt-dlp for HLS streams or when direct download fails.
|
Direct HTTP download is skipped — TikTok always returns HTML
|
||||||
|
for video page URLs, never a raw MP4.
|
||||||
"""
|
"""
|
||||||
# ── Try direct HTTP download first ────────────────────────────────
|
|
||||||
if not url.endswith(".m3u8"):
|
|
||||||
try:
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
"Referer": "https://www.tiktok.com/",
|
|
||||||
}
|
|
||||||
with httpx.stream("GET", url, headers=headers,
|
|
||||||
follow_redirects=True, timeout=60) as r:
|
|
||||||
r.raise_for_status()
|
|
||||||
with open(output_path, "wb") as f:
|
|
||||||
for chunk in r.iter_bytes(chunk_size=1024 * 64):
|
|
||||||
f.write(chunk)
|
|
||||||
size = os.path.getsize(output_path)
|
|
||||||
if size > 10_000:
|
|
||||||
logging.info(
|
|
||||||
f"✅ Direct download OK: {size / 1024 / 1024:.1f} MB"
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
logging.warning(
|
|
||||||
f"⚠️ Direct download too small ({size} bytes), trying yt-dlp..."
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logging.warning(f"⚠️ Direct download failed: {e}. Trying yt-dlp...")
|
|
||||||
|
|
||||||
# ── Fall back to yt-dlp ────────────────────────────────────────────
|
|
||||||
return download_video_ytdlp(url, output_path, cookies=cookies)
|
return download_video_ytdlp(url, output_path, cookies=cookies)
|
||||||
|
|
||||||
|
|
||||||
def download_video_ytdlp(url: str, output_path: str,
|
def download_video_ytdlp(url: str, output_path: str,
|
||||||
cookies: list = None) -> bool:
|
cookies: list = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Download a video using yt-dlp with TikTok impersonation support.
|
Download a video using yt-dlp with TikTok impersonation.
|
||||||
curl_cffi must be installed for impersonation to work.
|
Requires curl-cffi: pip install curl-cffi
|
||||||
"""
|
"""
|
||||||
cookie_file = None
|
cookie_file = None
|
||||||
try:
|
try:
|
||||||
@@ -652,11 +621,31 @@ def download_video_ytdlp(url: str, output_path: str,
|
|||||||
"quiet": True,
|
"quiet": True,
|
||||||
"no_warnings": False,
|
"no_warnings": False,
|
||||||
"merge_output_format": "mp4",
|
"merge_output_format": "mp4",
|
||||||
# ── TikTok impersonation ───────────────────────────────────
|
|
||||||
# Requires curl_cffi: pip install curl-cffi
|
|
||||||
"impersonate": "chrome",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Impersonation: try targets in order of preference ──────────
|
||||||
|
# curl_cffi must be installed: pip install curl-cffi
|
||||||
|
impersonate_targets = ["chrome126", "chrome124", "chrome", "safari"]
|
||||||
|
impersonate_set = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yt_dlp.networking.impersonate as _imp
|
||||||
|
available = {str(t) for t in _imp.ImpersonateTarget.supported_targets()}
|
||||||
|
for target in impersonate_targets:
|
||||||
|
if any(target in a for a in available):
|
||||||
|
ydl_opts["impersonate"] = target
|
||||||
|
logging.info(f"🎭 yt-dlp impersonation target: {target}")
|
||||||
|
impersonate_set = True
|
||||||
|
break
|
||||||
|
if not impersonate_set:
|
||||||
|
logging.warning(
|
||||||
|
f"⚠️ No impersonation target available. "
|
||||||
|
f"Available: {available}. "
|
||||||
|
f"Install curl-cffi: pip install curl-cffi"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"⚠️ Could not check impersonation targets: {e}")
|
||||||
|
|
||||||
if cookies:
|
if cookies:
|
||||||
cookie_file = _write_netscape_cookies(cookies)
|
cookie_file = _write_netscape_cookies(cookies)
|
||||||
if cookie_file:
|
if cookie_file:
|
||||||
@@ -689,6 +678,7 @@ def download_video_ytdlp(url: str, output_path: str,
|
|||||||
if cookie_file and os.path.exists(cookie_file):
|
if cookie_file and os.path.exists(cookie_file):
|
||||||
os.unlink(cookie_file)
|
os.unlink(cookie_file)
|
||||||
|
|
||||||
|
|
||||||
def _write_netscape_cookies(cookies: list) -> str | None:
|
def _write_netscape_cookies(cookies: list) -> str | None:
|
||||||
"""Write cookies list to a Netscape-format temp file for yt-dlp."""
|
"""Write cookies list to a Netscape-format temp file for yt-dlp."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user