modified: Dockerfile
modified: app.py
This commit is contained in:
@@ -11,6 +11,8 @@ RUN apt-get update && apt-get install -y \
|
|||||||
|
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
# Ensure yt-dlp is up-to-date so extractor fixes are applied
|
||||||
|
RUN pip install --no-cache-dir -U yt-dlp
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|||||||
64
app.py
64
app.py
@@ -444,24 +444,56 @@ async def _ensure_connected(ctx: commands.Context) -> Optional[discord.VoiceClie
|
|||||||
async def _ytdlp_extract(loop: asyncio.AbstractEventLoop, query: str) -> Optional[Dict]:
|
async def _ytdlp_extract(loop: asyncio.AbstractEventLoop, query: str) -> Optional[Dict]:
|
||||||
if not ytdlp:
|
if not ytdlp:
|
||||||
return None
|
return None
|
||||||
def _extract():
|
# Try extraction with multiple player_client hints if extraction fails due to player/nsig issues.
|
||||||
with ytdlp.YoutubeDL(get_ytdl_opts()) as ytdl:
|
# Start with the configured client, then fall back to common alternatives.
|
||||||
return ytdl.extract_info(query, download=False)
|
preferred = os.getenv('YTDL_YT_CLIENT', 'android')
|
||||||
try:
|
candidates = [preferred]
|
||||||
info = await loop.run_in_executor(executor, _extract)
|
for c in ('web', 'tv', 'android_embedded', 'firetv'):
|
||||||
if info is None:
|
if c not in candidates:
|
||||||
|
candidates.append(c)
|
||||||
|
|
||||||
|
last_exc = None
|
||||||
|
for client_hint in candidates:
|
||||||
|
def _extract_with_client(client=client_hint):
|
||||||
|
opts = get_ytdl_opts()
|
||||||
|
# Override player_client for this attempt
|
||||||
|
try:
|
||||||
|
opts['extractor_args']['youtube']['player_client'] = [client]
|
||||||
|
except Exception:
|
||||||
|
opts.setdefault('extractor_args', {}).setdefault('youtube', {})['player_client'] = [client]
|
||||||
|
with ytdlp.YoutubeDL(opts) 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_exc = e
|
||||||
|
msg = str(e).lower()
|
||||||
|
# If it's a cookies/sign-in issue, surface a helpful message and stop trying
|
||||||
|
if 'sign in to confirm' in msg or 'use --cookies' in msg or 'pass cookies' in msg:
|
||||||
|
print("❌ yt-dlp error: YouTube requires cookies to proceed.")
|
||||||
|
print(" Provide YTDL_COOKIES_FILE or YTDL_COOKIES_B64 (Netscape cookies.txt).")
|
||||||
|
traceback.print_exc()
|
||||||
|
return None
|
||||||
|
# If nsig/sabr or unsupported client warnings occurred, try next client hint
|
||||||
|
if 'nsig extraction failed' in msg or 'sabr' in msg or 'unsupported client' in msg:
|
||||||
|
print(f"⚠️ yt-dlp warning with client={client_hint}: {e}")
|
||||||
|
# continue to try other clients
|
||||||
|
continue
|
||||||
|
# Otherwise, log and stop trying
|
||||||
|
traceback.print_exc()
|
||||||
return None
|
return None
|
||||||
if 'entries' in info:
|
|
||||||
info = info['entries'][0]
|
# If we tried everything and failed, log last exception
|
||||||
return info
|
if last_exc:
|
||||||
except Exception as e:
|
print(f"❌ All yt-dlp client attempts failed. Last error: {type(last_exc).__name__}: {last_exc}")
|
||||||
# Make YouTube cookie issues clearer in logs
|
|
||||||
msg = str(e)
|
|
||||||
if 'Sign in to confirm you’re not a bot' in msg or 'Use --cookies' in msg or 'pass cookies' in msg:
|
|
||||||
print("❌ yt-dlp error: YouTube requires cookies to proceed.")
|
|
||||||
print(" Provide YTDL_COOKIES_FILE or YTDL_COOKIES_B64 (Netscape cookies.txt).")
|
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
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):
|
||||||
# Accept either URL or search text; prepend ytsearch1: if not a URL
|
# Accept either URL or search text; prepend ytsearch1: if not a URL
|
||||||
|
|||||||
Reference in New Issue
Block a user