#!/usr/bin/env python3 """ Minimal CLI helper to interact with a running SpotSpot / SamCloud container via its Socket.IO interface. Features implemented (Phase-0 proof of concept): 1. Perform a Spotify search (same payload as the web UI) and print the results. 2. Queue a download for a given Spotify track URL. 3. Stream real-time status updates while the download is running. Usage examples: # Search for a track python spotspot_cli.py search "speedin track" # Download a specific Spotify track (copy URL from Spotify) python spotspot_cli.py download "https://open.spotify.com/track/0Y7HK80Z8EULyF93764YY3" The script assumes SpotSpot is accessible at ws://localhost:6544 (default). Change --host / --port arguments if you mapped the container elsewhere. """ import argparse import json import sys import time from pathlib import Path import socketio # python-socketio >= 5.0 # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def pretty(data): print(json.dumps(data, indent=2, ensure_ascii=False)) class SpotSpotClient: def __init__(self, host: str = "localhost", port: int = 6544): self.url = f"ws://{host}:{port}" # Socket.IO will append /socket.io/?EIO=4&... self.sio = socketio.Client(reconnection=False) self._register_handlers() # ---------------- Socket.IO event handlers ---------------------------- def _register_handlers(self): @self.sio.event def connect(): print("[✓] Connected to SpotSpot server") @self.sio.event def disconnect(): print("[i] Disconnected") @self.sio.on("search_results") def on_results(data): print("[search_results]") pretty(data) @self.sio.on("toast") def on_toast(data): print("[toast]", data) @self.sio.on("download_progress") def on_progress(data): print("[progress]", data) @self.sio.on("download_complete") def on_complete(data): print("[complete]", data) # Once download completes we can disconnect. self.sio.disconnect() @self.sio.on("update_status") def on_update(data): print("[status]") pretty(data) # ---------------- Public methods ------------------------------------- def connect(self): self.sio.connect(self.url, wait_timeout=5) def disconnect(self): if self.sio.connected: self.sio.disconnect() # -- high-level ops -- def search(self, query: str, search_type: str = "track"): print(f"[i] Searching for '{query}' ({search_type})…") self.sio.emit("search", {"query": query, "type": search_type}) def download(self, spotify_url: str, name: str = "", artist: str = ""): item = { "url": spotify_url, "name": name, "artist": artist, "type": "track", } print(f"[i] Queueing download for {spotify_url}") self.sio.emit("download", item) # Ask for status updates right away self.sio.emit("get_status") # --------------------------------------------------------------------------- # CLI parsing # --------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser(description="CLI helper for SpotSpot Socket.IO interface") parser.add_argument("command", choices=["search", "download"], help="Action to perform") parser.add_argument("value", help="Search query or Spotify track URL") parser.add_argument("--host", default="localhost", help="SpotSpot host (default: localhost)") parser.add_argument("--port", type=int, default=6544, help="SpotSpot port (default: 6544)") args = parser.parse_args() client = SpotSpotClient(host=args.host, port=args.port) try: client.connect() if args.command == "search": client.search(args.value) # Give the server a few seconds to return results time.sleep(4) elif args.command == "download": client.download(args.value) # Keep script alive until disconnected by on_complete while client.sio.connected: time.sleep(1) except KeyboardInterrupt: print("Interrupted by user") finally: client.disconnect() if __name__ == "__main__": main()