- Playlists: Alle werden jetzt geladen (Pagination) - Player: Fehlermeldung wenn kein Geraet aktiv - next/previous/repeat/shuffle zeigen Fehler statt stillschweigend zuversagen
171 lines
5.4 KiB
Python
171 lines
5.4 KiB
Python
import threading
|
|
import time
|
|
from .client import SpotifyClient
|
|
|
|
|
|
class Player:
|
|
def __init__(self, client: SpotifyClient):
|
|
self.client = client
|
|
self._poll_thread = None
|
|
self._running = False
|
|
self._callbacks = []
|
|
self.current_playback = None
|
|
self.current_track = None
|
|
self.devices = []
|
|
|
|
def register_callback(self, cb):
|
|
self._callbacks.append(cb)
|
|
|
|
def _notify(self):
|
|
for cb in self._callbacks:
|
|
try:
|
|
cb(self.current_playback, self.current_track)
|
|
except Exception:
|
|
pass
|
|
|
|
def start_polling(self, interval=2):
|
|
self._running = True
|
|
self._poll_thread = threading.Thread(target=self._poll_loop, args=(interval,), daemon=True)
|
|
self._poll_thread.start()
|
|
|
|
def stop_polling(self):
|
|
self._running = False
|
|
if self._poll_thread:
|
|
self._poll_thread.join(timeout=3)
|
|
|
|
def _poll_loop(self, interval):
|
|
while self._running:
|
|
try:
|
|
self.refresh()
|
|
except Exception:
|
|
pass
|
|
time.sleep(interval)
|
|
|
|
def refresh(self):
|
|
try:
|
|
self.current_playback = self.client.get_playback()
|
|
if self.current_playback:
|
|
self.devices = self.current_playback.get("devices", [])
|
|
except Exception:
|
|
self.current_playback = None
|
|
|
|
try:
|
|
self.current_track = self.client.get_current_track()
|
|
except Exception:
|
|
self.current_track = None
|
|
|
|
self._notify()
|
|
|
|
def toggle_play(self):
|
|
if not self.current_playback:
|
|
raise Exception("Kein Wiedergabegeraet aktiv")
|
|
if self.current_playback.get("is_playing"):
|
|
self.client.pause()
|
|
else:
|
|
self.client.play()
|
|
time.sleep(0.3)
|
|
self.refresh()
|
|
|
|
def next(self):
|
|
if not self.current_playback:
|
|
raise Exception("Kein Wiedergabegeraet aktiv")
|
|
self.client.next_track()
|
|
time.sleep(0.5)
|
|
self.refresh()
|
|
|
|
def previous(self):
|
|
if not self.current_playback:
|
|
raise Exception("Kein Wiedergabegeraet aktiv")
|
|
self.client.previous_track()
|
|
time.sleep(0.5)
|
|
self.refresh()
|
|
|
|
def seek(self, position_ms):
|
|
self.client.seek(position_ms)
|
|
time.sleep(0.3)
|
|
self.refresh()
|
|
|
|
def set_volume(self, volume):
|
|
volume = max(0, min(100, volume))
|
|
self.client.set_volume(volume)
|
|
self.refresh()
|
|
|
|
def volume_up(self, step=5):
|
|
vol = self.get_volume()
|
|
self.set_volume(vol + step)
|
|
|
|
def volume_down(self, step=5):
|
|
vol = self.get_volume()
|
|
self.set_volume(vol - step)
|
|
|
|
def get_volume(self):
|
|
if self.current_playback and self.current_playback.get("device"):
|
|
return self.current_playback["device"].get("volume_percent", 50)
|
|
return 50
|
|
|
|
def toggle_repeat(self):
|
|
if not self.current_playback:
|
|
raise Exception("Kein Wiedergabegeraet aktiv")
|
|
state = self.current_playback.get("repeat_state", "off")
|
|
new_state = "track" if state == "off" else "off"
|
|
self.client.set_repeat(new_state)
|
|
self.refresh()
|
|
|
|
def toggle_shuffle(self):
|
|
if not self.current_playback:
|
|
raise Exception("Kein Wiedergabegeraet aktiv")
|
|
state = not self.current_playback.get("shuffle_state", False)
|
|
self.client.set_shuffle(state)
|
|
self.refresh()
|
|
|
|
def play_context(self, context_uri, offset=None):
|
|
self.client.play(context_uri=context_uri, offset=offset)
|
|
time.sleep(0.5)
|
|
self.refresh()
|
|
|
|
def play_tracks(self, uris, offset=0):
|
|
self.client.play(uris=uris, offset=offset)
|
|
time.sleep(0.5)
|
|
self.refresh()
|
|
|
|
def play_track(self, uri):
|
|
self.client.play(uris=[uri])
|
|
time.sleep(0.5)
|
|
self.refresh()
|
|
|
|
def transfer_to(self, device_id):
|
|
self.client.transfer_playback(device_id)
|
|
time.sleep(0.5)
|
|
self.refresh()
|
|
|
|
def get_track_info(self):
|
|
if not self.current_track or not self.current_track.get("item"):
|
|
return None
|
|
track = self.current_track["item"]
|
|
artists = ", ".join(a["name"] for a in track.get("artists", []))
|
|
album = track.get("album", {}).get("name", "")
|
|
progress = self.current_track.get("progress_ms", 0)
|
|
duration = track.get("duration_ms", 0)
|
|
return {
|
|
"name": track["name"],
|
|
"artists": artists,
|
|
"album": album,
|
|
"duration_ms": duration,
|
|
"progress_ms": progress,
|
|
"uri": track["uri"],
|
|
"is_playing": self.current_track.get("is_playing", False),
|
|
"image": track.get("album", {}).get("images", [{}])[0].get("url") if track.get("album", {}).get("images") else None,
|
|
}
|
|
|
|
def get_playback_state(self):
|
|
if not self.current_playback:
|
|
return None
|
|
return {
|
|
"is_playing": self.current_playback.get("is_playing", False),
|
|
"repeat_state": self.current_playback.get("repeat_state", "off"),
|
|
"shuffle_state": self.current_playback.get("shuffle_state", False),
|
|
"volume": self.current_playback.get("device", {}).get("volume_percent", 50),
|
|
"device": self.current_playback.get("device", {}).get("name", "Unknown"),
|
|
"device_type": self.current_playback.get("device", {}).get("type", ""),
|
|
}
|