"""search-provider cmdlet: Search external providers (bandcamp, libgen, soulseek, youtube).""" from __future__ import annotations from typing import Any, Dict, List, Sequence import sys from helper.logger import log, debug from helper.provider import get_search_provider, list_search_providers from ._shared import Cmdlet, CmdletArg, should_show_help import pipeline as ctx class Search_Provider(Cmdlet): """Search external content providers.""" def __init__(self): super().__init__( name="search-provider", summary="Search external providers (bandcamp, libgen, soulseek, youtube)", usage="search-provider [-limit N]", arg=[ CmdletArg("provider", type="string", required=True, description="Provider name: bandcamp, libgen, soulseek, youtube"), CmdletArg("query", type="string", required=True, description="Search query (supports provider-specific syntax)"), CmdletArg("limit", type="int", description="Maximum results to return (default: 50)"), ], detail=[ "Search external content providers:", "- bandcamp: Search for music albums/tracks", " Example: search-provider bandcamp \"artist:altrusian grace\"", "- libgen: Search Library Genesis for books", " Example: search-provider libgen \"python programming\"", "- soulseek: Search P2P network for music", " Example: search-provider soulseek \"pink floyd\"", "- youtube: Search YouTube for videos", " Example: search-provider youtube \"tutorial\"", "", "Query syntax:", "- bandcamp: Use 'artist:Name' to search by artist", "- libgen: Supports isbn:, author:, title: prefixes", "- soulseek: Plain text search", "- youtube: Plain text search", "", "Results can be piped to other cmdlets:", " search-provider bandcamp \"artist:grace\" | @1 | download-data", ], exec=self.run ) self.register() def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: """Execute search-provider cmdlet.""" if should_show_help(args): ctx.emit(self.__dict__) return 0 # Parse arguments if len(args) < 2: log("Error: search-provider requires and arguments", file=sys.stderr) log(f"Usage: {self.usage}", file=sys.stderr) log("Available providers:", file=sys.stderr) providers = list_search_providers(config) for name, available in sorted(providers.items()): status = "✓" if available else "✗" log(f" {status} {name}", file=sys.stderr) return 1 provider_name = args[0] query = args[1] # Parse optional limit limit = 50 if len(args) >= 4 and args[2] in ("-limit", "--limit"): try: limit = int(args[3]) except ValueError: log(f"Warning: Invalid limit value '{args[3]}', using default 50", file=sys.stderr) debug(f"[search-provider] provider={provider_name}, query={query}, limit={limit}") # Get provider provider = get_search_provider(provider_name, config) if not provider: log(f"Error: Provider '{provider_name}' is not available", file=sys.stderr) log("Available providers:", file=sys.stderr) providers = list_search_providers(config) for name, available in sorted(providers.items()): if available: log(f" - {name}", file=sys.stderr) return 1 # Execute search try: debug(f"[search-provider] Calling {provider_name}.search()") results = provider.search(query, limit=limit) debug(f"[search-provider] Got {len(results)} results") if not results: log(f"No results found for query: {query}", file=sys.stderr) return 0 # Emit results for pipeline for search_result in results: ctx.emit(search_result.to_dict()) log(f"Found {len(results)} result(s) from {provider_name}", file=sys.stderr) return 0 except Exception as e: log(f"Error searching {provider_name}: {e}", file=sys.stderr) import traceback debug(traceback.format_exc()) return 1 # Register cmdlet instance Search_Provider_Instance = Search_Provider()