98 lines
3.4 KiB
Python
98 lines
3.4 KiB
Python
import pychromecast
|
|
from pychromecast.controllers.media import MediaController
|
|
import time
|
|
import threading
|
|
from typing import List, Optional
|
|
from .utils import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
class CastManager:
|
|
def __init__(self):
|
|
self.chromecast = None
|
|
self.mc: Optional[MediaController] = None
|
|
self.queue: List[str] = []
|
|
self.current_index = -1
|
|
self.is_playing = False
|
|
self._lock = threading.Lock()
|
|
self._discover_thread = None
|
|
self.browser = None
|
|
|
|
def discover(self, timeout=5):
|
|
logger.info("Discovering Chromecasts...")
|
|
chromecasts, browser = pychromecast.get_chromecasts(timeout=timeout)
|
|
self.browser = browser
|
|
if chromecasts:
|
|
# For simplicity, pick the first one found or allow selection later
|
|
# In a real scenario, we might want to target a specific name
|
|
self.chromecast = chromecasts[0]
|
|
logger.info(f"Found Chromecast: {self.chromecast.name}")
|
|
self.chromecast.wait()
|
|
self.mc = self.chromecast.media_controller
|
|
self.mc.register_status_listener(self)
|
|
else:
|
|
logger.error("No Chromecasts found.")
|
|
|
|
def play_queue(self, urls: List[str]):
|
|
with self._lock:
|
|
self.queue = urls
|
|
self.current_index = 0
|
|
self._play_current()
|
|
|
|
def _play_current(self):
|
|
if 0 <= self.current_index < len(self.queue):
|
|
url = self.queue[self.current_index]
|
|
logger.info(f"Playing {self.current_index + 1}/{len(self.queue)}: {url}")
|
|
if not self.chromecast:
|
|
self.discover()
|
|
|
|
if self.chromecast:
|
|
self.chromecast.wait()
|
|
# Use generic video type, or try to guess from URL
|
|
self.mc.play_media(url, 'video/mp4')
|
|
self.is_playing = True
|
|
else:
|
|
logger.error("Cannot play: No Chromecast connected.")
|
|
else:
|
|
logger.info("Queue finished.")
|
|
self.is_playing = False
|
|
self.current_index = -1
|
|
|
|
def stop(self):
|
|
with self._lock:
|
|
if self.mc:
|
|
self.mc.stop()
|
|
self.queue = []
|
|
self.current_index = -1
|
|
self.is_playing = False
|
|
|
|
def new_media_status(self, status):
|
|
"""Callback from pychromecast when media status changes."""
|
|
logger.debug(f"Media status update: {status.player_state}")
|
|
|
|
# Check if the current video finished
|
|
if status.player_state == "IDLE" and status.idle_reason == "FINISHED":
|
|
logger.info("Current track finished. Moving to next...")
|
|
with self._lock:
|
|
if self.current_index != -1:
|
|
self.current_index += 1
|
|
# Small delay to ensure state transitions
|
|
threading.Timer(1.0, self._play_current).start()
|
|
|
|
def get_status(self):
|
|
status = {
|
|
"is_playing": self.is_playing,
|
|
"queue_len": len(self.queue),
|
|
"current_index": self.current_index,
|
|
}
|
|
if self.mc and self.mc.status:
|
|
status.update({
|
|
"player_state": self.mc.status.player_state,
|
|
"current_time": self.mc.status.current_time,
|
|
"duration": self.mc.status.duration,
|
|
})
|
|
return status
|
|
|
|
# Singleton instance
|
|
cast_manager = CastManager()
|