From 892fe1cb0af970c8d5191209f1614e450206b38a Mon Sep 17 00:00:00 2001 From: nose Date: Wed, 24 Dec 2025 05:10:07 -0800 Subject: [PATCH] lksjalk --- Provider/youtube.py | 116 ++++++++++++++++++------------------------- scripts/bootstrap.sh | 2 + scripts/setup.py | 7 ++- 3 files changed, 55 insertions(+), 70 deletions(-) diff --git a/Provider/youtube.py b/Provider/youtube.py index 3d5c139..7d9552a 100644 --- a/Provider/youtube.py +++ b/Provider/youtube.py @@ -1,8 +1,5 @@ from __future__ import annotations -import json -import shutil -import subprocess import sys from typing import Any, Dict, List, Optional @@ -11,7 +8,7 @@ from SYS.logger import log class YouTube(Provider): - """Search provider for YouTube using yt-dlp.""" + """Search provider for YouTube using the yt_dlp Python package.""" def search( self, @@ -20,75 +17,56 @@ class YouTube(Provider): filters: Optional[Dict[str, Any]] = None, **kwargs: Any, ) -> List[SearchResult]: - ytdlp_path = shutil.which("yt-dlp") - if not ytdlp_path: - log("[youtube] yt-dlp not found in PATH", file=sys.stderr) - return [] - - search_query = f"ytsearch{limit}:{query}" - cmd = [ytdlp_path, "--dump-json", "--flat-playlist", "--no-warnings", search_query] - + # Use the yt_dlp Python module (installed via requirements.txt). try: - process = subprocess.run( - cmd, - capture_output=True, - text=True, - encoding="utf-8", - errors="replace", - ) + import yt_dlp # type: ignore + ydl_opts: Dict[str, Any] = {"quiet": True, "skip_download": True, "extract_flat": True} + with yt_dlp.YoutubeDL(ydl_opts) as ydl: # type: ignore[arg-type] + search_query = f"ytsearch{limit}:{query}" + info = ydl.extract_info(search_query, download=False) + entries = info.get("entries") or [] + results: List[SearchResult] = [] + for video_data in entries[:limit]: + title = video_data.get("title", "Unknown") + video_id = video_data.get("id", "") + url = video_data.get("url") or f"https://youtube.com/watch?v={video_id}" + uploader = video_data.get("uploader", "Unknown") + duration = video_data.get("duration", 0) + view_count = video_data.get("view_count", 0) - if process.returncode != 0: - log(f"[youtube] yt-dlp failed: {process.stderr}", file=sys.stderr) - return [] + duration_str = f"{int(duration // 60)}:{int(duration % 60):02d}" if duration else "" + views_str = f"{view_count:,}" if view_count else "" - results: List[SearchResult] = [] - for line in process.stdout.splitlines(): - if not line.strip(): - continue - - try: - video_data = json.loads(line) - except json.JSONDecodeError: - continue - - title = video_data.get("title", "Unknown") - video_id = video_data.get("id", "") - url = video_data.get("url") or f"https://youtube.com/watch?v={video_id}" - uploader = video_data.get("uploader", "Unknown") - duration = video_data.get("duration", 0) - view_count = video_data.get("view_count", 0) - - duration_str = f"{int(duration // 60)}:{int(duration % 60):02d}" if duration else "" - views_str = f"{view_count:,}" if view_count else "" - - results.append( - SearchResult( - table="youtube", - title=title, - path=url, - detail=f"By: {uploader}", - annotations=[duration_str, f"{views_str} views"], - media_kind="video", - columns=[ - ("Title", title), - ("Uploader", uploader), - ("Duration", duration_str), - ("Views", views_str), - ], - full_metadata={ - "video_id": video_id, - "uploader": uploader, - "duration": duration, - "view_count": view_count, - }, + results.append( + SearchResult( + table="youtube", + title=title, + path=url, + detail=f"By: {uploader}", + annotations=[duration_str, f"{views_str} views"], + media_kind="video", + columns=[ + ("Title", title), + ("Uploader", uploader), + ("Duration", duration_str), + ("Views", views_str), + ], + full_metadata={ + "video_id": video_id, + "uploader": uploader, + "duration": duration, + "view_count": view_count, + }, + ) ) - ) - - return results - - except Exception as exc: - log(f"[youtube] Error: {exc}", file=sys.stderr) + return results + except Exception: + log("[youtube] yt_dlp import failed", file=sys.stderr) return [] def validate(self) -> bool: - return shutil.which("yt-dlp") is not None + try: + import yt_dlp # type: ignore + return True + except Exception: + return False diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index 792707f..756ef2b 100644 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -501,6 +501,8 @@ fi # At this point REPO may still be wrong if mm was invoked outside any project; keep the embedded path as a last resort. VENV="$REPO/.venv" +# Ensure tools installed into the venv are discoverable to subprocess-based providers +export PATH="$VENV/bin:$PATH" # Debug mode: set MM_DEBUG=1 to print repository, venv, and import diagnostics if [ -n "${MM_DEBUG:-}" ]; then diff --git a/scripts/setup.py b/scripts/setup.py index a698eac..69fd7b4 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -303,6 +303,8 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO="$SCRIPT_DIR" VENV="$REPO/.venv" +# Make tools installed into the local venv available in PATH for provider discovery +export PATH="$VENV/bin:$PATH" PY="$VENV/bin/python" if [ -x "$PY" ]; then exec "$PY" -m medeia_macina.cli_entry "$@" @@ -320,6 +322,9 @@ fi $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $repo = $scriptDir $venv = Join-Path $repo '.venv' +# Ensure venv Scripts dir is on PATH for provider discovery +$venvScripts = Join-Path $venv 'Scripts' +if (Test-Path $venvScripts) { $env:PATH = $venvScripts + ';' + $env:PATH } $py = Join-Path $venv 'Scripts\python.exe' $cli = Join-Path $repo 'CLI.py' if (Test-Path $py) { & $py -m medeia_macina.cli_entry @args; exit $LASTEXITCODE } @@ -335,10 +340,10 @@ python -m medeia_macina.cli_entry @args bat_text = ( "@echo off\r\n" "set SCRIPT_DIR=%~dp0\r\n" + "set PATH=%SCRIPT_DIR%\.venv\Scripts;%PATH%\r\n" "if exist \"%SCRIPT_DIR%\\.venv\\Scripts\\python.exe\" \"%SCRIPT_DIR%\\.venv\\Scripts\\python.exe\" -m medeia_macina.cli_entry %*\r\n" "if exist \"%SCRIPT_DIR%\\CLI.py\" python \"%SCRIPT_DIR%\\CLI.py\" %*\r\n" "python -m medeia_macina.cli_entry %*\r\n" - "python -m medeia_macina.cli_entry %*\r\n" ) try: bat.write_text(bat_text, encoding="utf-8")