modified: app.py
This commit is contained in:
118
app.py
118
app.py
@@ -374,14 +374,14 @@ except Exception as e:
|
|||||||
print(f"⚠️ Cookie configuration error: {e}")
|
print(f"⚠️ Cookie configuration error: {e}")
|
||||||
|
|
||||||
YTDL_OPTS = {
|
YTDL_OPTS = {
|
||||||
'format': 'bestaudio/best',
|
'format': 'bestaudio[ext=m4a]/bestaudio/best',
|
||||||
'noplaylist': True,
|
'noplaylist': True,
|
||||||
'quiet': True,
|
'quiet': True,
|
||||||
'default_search': 'ytsearch',
|
'default_search': 'ytsearch',
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_ytdl_opts(client_hint: Optional[str] = None) -> Dict:
|
def get_ytdl_opts(client_hint: Optional[str] = None, use_cookies: bool = True) -> Dict:
|
||||||
"""Build yt-dlp options dynamically, injecting cookies and headers if configured.
|
"""Build yt-dlp options dynamically, injecting cookies and headers if configured.
|
||||||
client_hint: preferred YouTube client (e.g., 'android', 'web', 'ios', 'tv').
|
client_hint: preferred YouTube client (e.g., 'android', 'web', 'ios', 'tv').
|
||||||
"""
|
"""
|
||||||
@@ -400,10 +400,14 @@ def get_ytdl_opts(client_hint: Optional[str] = None) -> Dict:
|
|||||||
# player_client hint
|
# player_client hint
|
||||||
opts['extractor_args']['youtube']['player_client'] = [yt_client]
|
opts['extractor_args']['youtube']['player_client'] = [yt_client]
|
||||||
# Use cookies if available
|
# Use cookies if available
|
||||||
if YTDL_COOKIEFILE:
|
if use_cookies:
|
||||||
opts['cookiefile'] = YTDL_COOKIEFILE
|
if YTDL_COOKIEFILE:
|
||||||
elif YTDL_COOKIESFROMBROWSER:
|
opts['cookiefile'] = YTDL_COOKIEFILE
|
||||||
opts['cookiesfrombrowser'] = YTDL_COOKIESFROMBROWSER
|
elif YTDL_COOKIESFROMBROWSER:
|
||||||
|
opts['cookiesfrombrowser'] = YTDL_COOKIESFROMBROWSER
|
||||||
|
# Optional force IPv4 (set YTDL_FORCE_IPV4=1)
|
||||||
|
if os.getenv('YTDL_FORCE_IPV4'):
|
||||||
|
opts['force_ipv4'] = True
|
||||||
return opts
|
return opts
|
||||||
|
|
||||||
FFMPEG_BEFORE = "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5"
|
FFMPEG_BEFORE = "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5"
|
||||||
@@ -452,47 +456,77 @@ async def _ytdlp_extract(loop: asyncio.AbstractEventLoop, query: str) -> Optiona
|
|||||||
|
|
||||||
# Try multiple YouTube client profiles to bypass some restrictions
|
# Try multiple YouTube client profiles to bypass some restrictions
|
||||||
env_client = os.getenv('YTDL_YT_CLIENT', 'android')
|
env_client = os.getenv('YTDL_YT_CLIENT', 'android')
|
||||||
# Prefer android first in fallbacks to avoid SABR issues on web
|
cookies_present = bool(YTDL_COOKIEFILE or YTDL_COOKIESFROMBROWSER)
|
||||||
fallback_clients = ['android', 'web', 'ios', 'tv']
|
|
||||||
clients_to_try: List[str] = []
|
# Helper to try a sequence of clients with/without cookies
|
||||||
for c in [env_client] + fallback_clients:
|
async def try_clients(clients: List[str], use_cookies: bool) -> Optional[Dict]:
|
||||||
if c not in clients_to_try:
|
nonlocal query
|
||||||
clients_to_try.append(c)
|
last_err: Optional[Exception] = None
|
||||||
|
for client in clients:
|
||||||
|
def _extract_with_client():
|
||||||
|
with ytdlp.YoutubeDL(get_ytdl_opts(client, use_cookies=use_cookies)) as ytdl:
|
||||||
|
return ytdl.extract_info(query, download=False)
|
||||||
|
try:
|
||||||
|
info = await loop.run_in_executor(executor, _extract_with_client)
|
||||||
|
if info is None:
|
||||||
|
continue
|
||||||
|
if 'entries' in info:
|
||||||
|
info = info['entries'][0]
|
||||||
|
return info
|
||||||
|
except Exception as e:
|
||||||
|
last_err = e
|
||||||
|
msg = str(e)
|
||||||
|
print(f"⚠️ yt-dlp attempt failed (cookies={'on' if use_cookies else 'off'}) client='{client}': {msg[:200]}")
|
||||||
|
retriable_markers = (
|
||||||
|
'Sign in to confirm', 'Use --cookies', 'pass cookies', 'HTTP Error 403',
|
||||||
|
'Signature extraction failed', 'Requested format is not available',
|
||||||
|
'Only images are available', 'missing a url', 'SABR streaming'
|
||||||
|
)
|
||||||
|
if any(m in msg for m in retriable_markers):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if last_err:
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Build ordered lists
|
||||||
|
# When cookies are present, android/ios are skipped by yt-dlp; so try web/tv first with cookies.
|
||||||
|
# If that fails (e.g., SABR), try without cookies to enable android extraction.
|
||||||
|
cookie_clients: List[str] = []
|
||||||
|
for c in [env_client, 'web', 'tv']:
|
||||||
|
if c in ('web', 'tv') and c not in cookie_clients:
|
||||||
|
cookie_clients.append(c)
|
||||||
|
nocookie_clients: List[str] = []
|
||||||
|
for c in [env_client, 'android', 'ios', 'web', 'tv']:
|
||||||
|
if c not in nocookie_clients:
|
||||||
|
nocookie_clients.append(c)
|
||||||
|
|
||||||
last_error: Optional[Exception] = None
|
last_error: Optional[Exception] = None
|
||||||
for client in clients_to_try:
|
# Pass 1: with cookies (if provided)
|
||||||
def _extract_with_client():
|
if cookies_present:
|
||||||
with ytdlp.YoutubeDL(get_ytdl_opts(client)) as ytdl:
|
info = await try_clients(cookie_clients, use_cookies=True)
|
||||||
return ytdl.extract_info(query, download=False)
|
if info:
|
||||||
try:
|
return info
|
||||||
info = await loop.run_in_executor(executor, _extract_with_client)
|
# Pass 2: without cookies to allow android/ios
|
||||||
if info is None:
|
info = await try_clients(nocookie_clients, use_cookies=False)
|
||||||
continue
|
if info:
|
||||||
if 'entries' in info:
|
return info
|
||||||
info = info['entries'][0]
|
else:
|
||||||
|
# No cookies: try standard nocookie list
|
||||||
|
info = await try_clients(nocookie_clients, use_cookies=False)
|
||||||
|
if info:
|
||||||
return info
|
return info
|
||||||
except Exception as e:
|
|
||||||
last_error = e
|
|
||||||
msg = str(e)
|
|
||||||
print(f"⚠️ yt-dlp attempt failed with client='{client}': {msg[:200]}")
|
|
||||||
# If it's clearly a bot-check/cookies or client format/signature issue, try next client
|
|
||||||
retriable_markers = (
|
|
||||||
'Sign in to confirm', 'Use --cookies', 'pass cookies', 'HTTP Error 403',
|
|
||||||
'Signature extraction failed', 'Requested format is not available',
|
|
||||||
'Only images are available', 'missing a url', 'SABR streaming'
|
|
||||||
)
|
|
||||||
if any(m in msg for m in retriable_markers):
|
|
||||||
continue
|
|
||||||
# Otherwise, break early for other errors
|
|
||||||
break
|
|
||||||
|
|
||||||
# All attempts failed
|
# All attempts failed
|
||||||
if last_error:
|
# At this point, all attempts failed; print last traceback if any
|
||||||
msg = str(last_error)
|
try:
|
||||||
if 'Sign in to confirm' in msg or 'Use --cookies' in msg or 'pass cookies' in msg:
|
if cookies_present:
|
||||||
print("❌ yt-dlp error: YouTube requires cookies to proceed.")
|
print("❌ yt-dlp: All attempts failed with and without cookies. Consider trying another client or refreshing cookies.")
|
||||||
print(" Provide YTDL_COOKIES_FILE or YTDL_COOKIES_B64 (Netscape cookies.txt).")
|
else:
|
||||||
traceback.print_exc()
|
print("❌ yt-dlp: All attempts failed without cookies. Consider providing cookies or changing YTDL_YT_CLIENT.")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _create_audio_source(loop: asyncio.AbstractEventLoop, search: str, volume: float):
|
async def _create_audio_source(loop: asyncio.AbstractEventLoop, search: str, volume: float):
|
||||||
|
|||||||
Reference in New Issue
Block a user