added naomusic.py and moved all music stuff to it

This commit is contained in:
Lucca Pirovano
2026-07-16 14:06:05 -04:00
parent 99f2d6a9cd
commit cdb2e1f90f
2 changed files with 151 additions and 441 deletions
+147
View File
@@ -0,0 +1,147 @@
# naomusic.py
# -*- encoding: UTF-8 -*-
import subprocess
import os
import json
import re
import time
import threading
class NaoMusicPlayer:
def __init__(self, session, nao_ip):
self.session = session
self.player = session.service("ALAudioPlayer")
self.nao_ip = nao_ip
self.music_dir = "/home/nao/music"
self.local_path = "/tmp/song.mp3"
self.remote_path = f"{self.music_dir}/song.mp3"
try:
subprocess.run(["sshpass", "-p", "nao", "ssh", f"nao@{nao_ip}", f"mkdir -p {self.music_dir}"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except: pass
self.current_file = None
self.current_task_id = None
self.is_playing = False
self.current_title = ""
self.is_loading = False
self.load_error = None
self.status = ""
self.progress = 0.0
def search(self, query):
try:
cmd = ["yt-dlp", f"ytsearch5:{query}", "--flat-playlist", "--dump-json",
"--no-warnings", "--quiet"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
entries = []
for line in result.stdout.strip().splitlines():
if not line.strip():
continue
try:
data = json.loads(line)
except json.JSONDecodeError:
continue
vid = data.get("id")
url = data.get("url")
if not url or not url.startswith("http"):
url = f"https://www.youtube.com/watch?v={vid}" if vid else None
if url:
entries.append({"url": url, "title": data.get("title", "Unknown")})
return entries
except Exception as e:
print(f"Search error: {e}")
return []
_DL_PROGRESS_RE = re.compile(r'\[download\]\s+([\d.]+)%')
def play(self, url, title):
self.stop()
self.current_title = title
self.is_loading = True
self.load_error = None
self.status = "Starting download..."
self.progress = 0.0
try:
if os.path.exists(self.local_path):
os.remove(self.local_path)
except: pass
try:
cmd = ["yt-dlp", url,
"-x", "--audio-format", "mp3",
"-o", self.local_path,
"--no-playlist", "--no-warnings", "--newline"]
print(f"\n▶ yt-dlp {url}")
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, bufsize=1)
for line in proc.stdout:
line = line.rstrip()
if not line:
continue
print(f" [yt-dlp] {line}")
m = self._DL_PROGRESS_RE.search(line)
if m:
self.progress = float(m.group(1))
self.status = f"Downloading... {self.progress:.0f}%"
elif "ExtractAudio" in line or "Extracting audio" in line:
self.status = "Converting to mp3..."
elif line.startswith("[download] Destination"):
self.status = "Downloading..."
proc.wait(timeout=120)
if proc.returncode != 0 or not os.path.exists(self.local_path):
print(f"Download failed (exit {proc.returncode})")
self.load_error = "Download failed"
return False
size_mb = os.path.getsize(self.local_path) / (1024 * 1024)
print(f"▶ scp {self.local_path} -> nao@{self.nao_ip}:{self.remote_path} ({size_mb:.1f} MB)")
self.progress = 0.0
upload_start = time.time()
stop_ticker = threading.Event()
def ticker():
while not stop_ticker.is_set():
elapsed = time.time() - upload_start
self.status = f"Uploading to NAO ({size_mb:.1f} MB)... {elapsed:.0f}s"
stop_ticker.wait(1.0)
t = threading.Thread(target=ticker, daemon=True)
t.start()
scp_result = subprocess.run(
["sshpass", "-p", "nao", "scp", self.local_path, f"nao@{self.nao_ip}:{self.remote_path}"],
capture_output=True, text=True, timeout=60
)
stop_ticker.set()
t.join(timeout=1.0)
print(f" [scp] exit {scp_result.returncode}" +
(f": {scp_result.stderr.strip()}" if scp_result.stderr.strip() else ""))
if scp_result.returncode != 0:
print(f"SCP failed: {scp_result.stderr.strip()}")
self.load_error = "SCP failed"
return False
self.current_file = self.remote_path
self.status = "Playing"
self.progress = 100.0
self.current_task_id = self.player.playFile(self.remote_path)
self.is_playing = True
print(f"♪ Playing on NAO: {title} (task {self.current_task_id})")
return True
except Exception as e:
print(f"Playback error: {e}")
self.load_error = str(e)
return False
finally:
self.is_loading = False
def stop(self):
try:
self.player.stopAll()
except Exception as e:
print(f"Stop error: {e}")
self.is_playing = False
self.current_file = None
self.current_task_id = None
self.current_title = ""