Files
spotify-cli/spotify_cli/player.py
T
x3 c3f7d87e67 fix: Playlists Pagination + Player Fehlermeldungen
- Playlists: Alle werden jetzt geladen (Pagination)
- Player: Fehlermeldung wenn kein Geraet aktiv
- next/previous/repeat/shuffle zeigen Fehler statt stillschweigend zuversagen
2026-09-06 08:50:30 +00:00

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", ""),
}