Files
boys_streaming/app/cast_logic.py
T
2026-05-03 16:48:18 +01:00

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()