Umbenennung: Audiofy heißt jetzt Playwish
Name, Seitentitel, Dienste (playwish-web, playwish-controller), Dienst-Benutzer (playwish), Pfade (/opt/playwish, /usr/local/src/playwish), Vorlagen und Repository-Adresse. „Audiofy“ war bereits von mehreren Produkten belegt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+5
-5
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audiofy – einmaliger Spotify-Login (Authorization Code Flow).
|
||||
"""Playwish – einmaliger Spotify-Login (Authorization Code Flow).
|
||||
|
||||
Holt einen Refresh-Token für das eigene Spotify-Konto und schreibt ihn in die
|
||||
.env-Datei. Funktioniert ohne Browser auf dem Gerät selbst:
|
||||
@@ -11,8 +11,8 @@ Holt einen Refresh-Token für das eigene Spotify-Konto und schreibt ihn in die
|
||||
und hier einfügen.
|
||||
|
||||
Aufruf:
|
||||
python3 auth.py --env /opt/audiofy/config/.env # Player (Wiedergabe steuern)
|
||||
python3 auth.py --profil server --env /opt/audiofy/.env # Server (nur Suche)
|
||||
python3 auth.py --env /opt/playwish/config/.env # Player (Wiedergabe steuern)
|
||||
python3 auth.py --profil server --env /opt/playwish/.env # Server (nur Suche)
|
||||
|
||||
Benötigte Einträge in der .env: SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET,
|
||||
optional SPOTIFY_REDIRECT_URI. Nur Python-Standardbibliothek, kein venv nötig.
|
||||
@@ -75,7 +75,7 @@ def env_setzen(pfad: Path, key: str, wert: str) -> None:
|
||||
|
||||
inhalt = "\n".join(neu) + "\n"
|
||||
if not os.access(pfad.parent, os.W_OK):
|
||||
# z. B. Server: /opt/audiofy gehört root, nur die .env selbst dem Dienst-Benutzer
|
||||
# z. B. Server: /opt/playwish gehört root, nur die .env selbst dem Dienst-Benutzer
|
||||
fd = os.open(pfad, os.O_WRONLY | os.O_TRUNC)
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(inhalt)
|
||||
@@ -112,7 +112,7 @@ def me_abfragen(access_token: str) -> dict:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Audiofy – Spotify-Login, schreibt den Refresh-Token in die .env")
|
||||
parser = argparse.ArgumentParser(description="Playwish – Spotify-Login, schreibt den Refresh-Token in die .env")
|
||||
parser.add_argument("--profil", choices=SCOPES, default="player", help="Scope-Satz (Standard: player)")
|
||||
parser.add_argument("--env", type=Path, default=Path(__file__).resolve().parent / ".env",
|
||||
help="Pfad zur .env (Standard: neben dem Skript)")
|
||||
|
||||
+11
-11
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audiofy – Controller (Player).
|
||||
"""Playwish – Controller (Player).
|
||||
|
||||
Spielt zufällige Titel aus der Grund-Playlist auf dem Spotify-Connect-Gerät
|
||||
"Audiofy" und schiebt nach jeweils N-1 Playlist-Titeln einen Wunsch ein.
|
||||
"Playwish" und schiebt nach jeweils N-1 Playlist-Titeln einen Wunsch ein.
|
||||
Spotify bekommt immer nur den jeweils nächsten Titel (POST /me/player/queue,
|
||||
ca. 15 s vor Titelende); die Reihenfolge verwaltet ausschließlich der Controller.
|
||||
|
||||
@@ -14,7 +14,7 @@ Ist der Server nicht erreichbar, läuft einfach die Grund-Playlist weiter.
|
||||
Bedienung über die Spotify-App bleibt möglich: Fremde Titel werden erkannt und
|
||||
geduldet, eine Pause durch den Besitzer (App oder Admin-Bereich) wird respektiert.
|
||||
|
||||
Aufruf: .venv/bin/python controller.py --env /opt/audiofy/config/.env
|
||||
Aufruf: .venv/bin/python controller.py --env /opt/playwish/config/.env
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -38,7 +38,7 @@ from auth import env_lesen, env_setzen
|
||||
from spotify_client import SpotifyClient, SpotifyFehler
|
||||
from server_client import ServerVerbindung, Wunsch
|
||||
|
||||
log = logging.getLogger("audiofy.controller")
|
||||
log = logging.getLogger("playwish.controller")
|
||||
|
||||
ORDNER = Path(__file__).resolve().parent
|
||||
POLL_S = 3 # Abfrageintervall /me/player und Server
|
||||
@@ -289,7 +289,7 @@ class Controller:
|
||||
return None
|
||||
|
||||
def starten(self) -> None:
|
||||
"""Wiedergabe auf dem Gerät "Audiofy" (neu) starten."""
|
||||
"""Wiedergabe auf dem Gerät "Playwish" (neu) starten."""
|
||||
gid = self.geraet_id()
|
||||
if not gid:
|
||||
# Warnung beim ersten Mal, danach höchstens einmal pro Minute
|
||||
@@ -547,7 +547,7 @@ class Controller:
|
||||
log.info("Als Nächstes eingereiht: %s [%s]", eintrag.uri, eintrag.quelle)
|
||||
return
|
||||
|
||||
# --- es läuft nichts (auf Audiofy) ---
|
||||
# --- es läuft nichts (auf Playwish) ---
|
||||
if self.bt_pause:
|
||||
# librespot pausiert bei Audiofehlern selbst – nach Neuverbindung fortsetzen
|
||||
if self.lautsprecher_bereit():
|
||||
@@ -558,13 +558,13 @@ class Controller:
|
||||
log.info("Lautsprecher wieder da – Wiedergabe fortgesetzt")
|
||||
return
|
||||
if vorher is None:
|
||||
# Controller-Start: pausierter Titel auf Audiofy = Besitzer-Pause, sonst starten
|
||||
# Controller-Start: pausierter Titel auf Playwish = Besitzer-Pause, sonst starten
|
||||
if st and unser and item and fortschritt >= PAUSE_MIN_MS:
|
||||
if not self._bt_pruefen_jetzt():
|
||||
log.warning("Audiofy pausiert und Lautsprecher getrennt – geht nach Neuverbindung weiter")
|
||||
log.warning("Playwish pausiert und Lautsprecher getrennt – geht nach Neuverbindung weiter")
|
||||
self.bt_pause = True
|
||||
else:
|
||||
log.info("Audiofy ist pausiert – warte, bis der Besitzer fortsetzt")
|
||||
log.info("Playwish ist pausiert – warte, bis der Besitzer fortsetzt")
|
||||
self.besitzer_pause = True
|
||||
else:
|
||||
self.neustart = True
|
||||
@@ -708,7 +708,7 @@ class Controller:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Audiofy-Controller")
|
||||
parser = argparse.ArgumentParser(description="Playwish-Controller")
|
||||
parser.add_argument("--env", type=Path, default=ORDNER / ".env")
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -741,7 +741,7 @@ def main() -> None:
|
||||
Playlist(sp, env.get("SPOTIFY_PLAYLIST_ID", ""), int(env.get("PLAYLIST_REPEAT_PERCENT") or 50)),
|
||||
server,
|
||||
n=int(env.get("WISH_EVERY_N") or 3),
|
||||
geraetename=env.get("DEVICE_NAME") or "Audiofy",
|
||||
geraetename=env.get("DEVICE_NAME") or "Playwish",
|
||||
bt_mac=env.get("BT_SPEAKER_MAC") or None,
|
||||
zustand_pfad=args.env.parent / "zustand.json",
|
||||
env_pfad=args.env,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Audiofy – Bluetooth-Lautsprecher suchen, koppeln und verbinden (bluetoothctl, ohne sudo).
|
||||
"""Playwish – Bluetooth-Lautsprecher suchen, koppeln und verbinden (bluetoothctl, ohne sudo).
|
||||
|
||||
Gekoppelt wird immer MIT Agent in derselben bluetoothctl-Sitzung – sonst speichert
|
||||
BlueZ den Schlüssel nicht dauerhaft ("Bonded: no"), und der Lautsprecher lässt sich
|
||||
@@ -11,7 +11,7 @@ import shutil
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
log = logging.getLogger("audiofy.lautsprecher")
|
||||
log = logging.getLogger("playwish.lautsprecher")
|
||||
|
||||
MAC_RE = re.compile(r"^([0-9A-F]{2}:){5}[0-9A-F]{2}$")
|
||||
SUCHDAUER_S = 15
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Audiofy – Verbindung Player → Server (Player-API mit Bearer-Token, nur ausgehend).
|
||||
"""Playwish – Verbindung Player → Server (Player-API mit Bearer-Token, nur ausgehend).
|
||||
|
||||
Alle Aufrufe fangen Fehler selbst ab: Ist der Server nicht erreichbar, liefern sie
|
||||
None/False, und der Controller spielt einfach die Grund-Playlist weiter. Im
|
||||
@@ -11,7 +11,7 @@ from dataclasses import dataclass
|
||||
|
||||
import httpx
|
||||
|
||||
log = logging.getLogger("audiofy.server")
|
||||
log = logging.getLogger("playwish.server")
|
||||
|
||||
OFFLINE_RETRY_S = 15
|
||||
TIMEOUT_S = 5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Audiofy – schlanker Spotify-Web-API-Client für den Controller (httpx).
|
||||
"""Playwish – schlanker Spotify-Web-API-Client für den Controller (httpx).
|
||||
|
||||
Erneuert den Access-Token selbst per Refresh-Token. Fehler werden als
|
||||
SpotifyFehler geworfen (bei 429 mit retry_after); Netzwerkfehler kommen als
|
||||
@@ -12,7 +12,7 @@ from collections.abc import Callable
|
||||
|
||||
import httpx
|
||||
|
||||
log = logging.getLogger("audiofy.spotify")
|
||||
log = logging.getLogger("playwish.spotify")
|
||||
|
||||
API = "https://api.spotify.com/v1"
|
||||
TOKEN_URL = "https://accounts.spotify.com/api/token"
|
||||
|
||||
@@ -93,7 +93,7 @@ def test_ein_titel_bleibt_immer_frei():
|
||||
def test_zustand_speichert_zuletzt(tmp_path):
|
||||
datei = tmp_path / "zustand.json"
|
||||
neu = lambda: Controller(sp=None, playlist=Playlist(FakeSpotify(TITEL), "liste", 50), server=None, # noqa: E731
|
||||
n=3, geraetename="Audiofy", bt_mac=None, zustand_pfad=datei)
|
||||
n=3, geraetename="Playwish", bt_mac=None, zustand_pfad=datei)
|
||||
c = neu()
|
||||
c.playlist.gespielt(TITEL[3])
|
||||
c.playlist.gespielt(TITEL[4])
|
||||
|
||||
@@ -42,7 +42,7 @@ class FakeServer:
|
||||
|
||||
|
||||
def controller(sp, server):
|
||||
c = Controller(sp=sp, playlist=Playlist(sp, "liste"), server=server, n=1, geraetename="Audiofy", bt_mac=None)
|
||||
c = Controller(sp=sp, playlist=Playlist(sp, "liste"), server=server, n=1, geraetename="Playwish", bt_mac=None)
|
||||
return c
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user