Implement XDG compliance, logging, and improved 401 error handling
- Install to ~/.local/bin/winamp-mpris - Use ~/.local/state/winamp-mpris/bridge.log for logging - Use $XDG_RUNTIME_DIR/winamp-mpris.pid for PID management - Add detailed user notification for 401 Unauthorized errors - Add install.sh for automated, standard-compliant setup - Include Winamp Web Interface source code and installer in repository
This commit is contained in:
@@ -3,18 +3,49 @@ import requests
|
||||
import time
|
||||
import re
|
||||
import subprocess
|
||||
import os
|
||||
import logging
|
||||
from threading import Thread
|
||||
from pydbus import SessionBus
|
||||
from pydbus.generic import signal
|
||||
from gi.repository import GLib
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
# --- CONFIGURATION & XDG PATHS ---
|
||||
BASE_URL = "http://localhost:5666"
|
||||
AUTH = ('winamp', 'llama')
|
||||
APP_ID = "org.mpris.MediaPlayer2.winamp"
|
||||
DEFAULT_ART = "https://webamp.org/favicon.ico"
|
||||
|
||||
# XDG Standard Paths
|
||||
XDG_STATE_HOME = os.environ.get("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
|
||||
XDG_RUNTIME_DIR = os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
|
||||
|
||||
LOG_DIR = os.path.join(XDG_STATE_HOME, "winamp-mpris")
|
||||
LOG_FILE = os.path.join(LOG_DIR, "bridge.log")
|
||||
PID_FILE = os.path.join(XDG_RUNTIME_DIR, "winamp-mpris.pid")
|
||||
|
||||
# Ensure log directory exists
|
||||
os.makedirs(LOG_DIR, exist_ok=True)
|
||||
|
||||
# Configure Logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s [%(levelname)s] %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(LOG_FILE),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger("winamp-mpris")
|
||||
|
||||
def write_pid_file():
|
||||
try:
|
||||
with open(PID_FILE, "w") as f:
|
||||
f.write(str(os.getpid()))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to write PID file: {e}")
|
||||
|
||||
class WinampMPRIS:
|
||||
"""
|
||||
MPRIS2 specification implementation for Winamp Web Interface.
|
||||
@@ -94,16 +125,20 @@ class WinampMPRIS:
|
||||
self._volume = 1.0
|
||||
|
||||
def _request(self, endpoint):
|
||||
print(f"COMMAND RECEIVED: {endpoint}")
|
||||
logger.info(f"COMMAND RECEIVED: {endpoint}")
|
||||
try:
|
||||
r = requests.get(f"{BASE_URL}/{endpoint}", auth=AUTH, timeout=2)
|
||||
if r.status_code != 200:
|
||||
if r.status_code == 401:
|
||||
msg = "401 Unauthorized: Check gen_httpsrv.dll plugin config. Ensure user 'winamp' has correct password and 'Play' permissions in Users tab."
|
||||
logger.warning(f"ERROR: {msg}")
|
||||
subprocess.run(["notify-send", "-u", "critical", "-t", "10000", "Winamp Bridge Auth Error", msg])
|
||||
elif r.status_code != 200:
|
||||
msg = f"Failed to send '{endpoint}' to Winamp (Status {r.status_code})."
|
||||
print(f"ERROR: {msg}")
|
||||
logger.error(f"ERROR: {msg}")
|
||||
subprocess.run(["notify-send", "-u", "critical", "-t", "3000", "Winamp Bridge Error", msg])
|
||||
except Exception as e:
|
||||
msg = f"Connection error while sending '{endpoint}': {e}"
|
||||
print(f"ERROR: {msg}")
|
||||
logger.warning(f"ERROR: {msg}")
|
||||
subprocess.run(["notify-send", "-u", "critical", "-t", "3000", "Winamp Bridge Offline", msg])
|
||||
|
||||
# MPRIS Methods
|
||||
@@ -264,6 +299,7 @@ def update_loop(player):
|
||||
last_known_shuffle = False
|
||||
last_known_loop = "None"
|
||||
offline_logged = False
|
||||
auth_error_logged = False
|
||||
|
||||
while True:
|
||||
try:
|
||||
@@ -301,6 +337,7 @@ def update_loop(player):
|
||||
r = requests.get(f"{BASE_URL}/main", auth=AUTH, timeout=1)
|
||||
if r.status_code == 200:
|
||||
offline_logged = False
|
||||
auth_error_logged = False
|
||||
soup = BeautifulSoup(r.text, 'html.parser')
|
||||
p_tags = soup.find_all('p')
|
||||
status_raw = p_tags[0].text if p_tags else ""
|
||||
@@ -357,10 +394,17 @@ def update_loop(player):
|
||||
player._title = status_raw.split('-')[0].replace("Playing track ", "").strip()
|
||||
player._artist = "Unknown"
|
||||
player._album = ""
|
||||
elif r.status_code == 401:
|
||||
if not auth_error_logged:
|
||||
msg = "401 Unauthorized: Check gen_httpsrv.dll plugin config. Ensure user 'winamp' has correct password and 'Play' permissions in Users tab."
|
||||
logger.warning(f"ERROR: {msg}")
|
||||
subprocess.run(["notify-send", "-u", "critical", "-t", "10000", "Winamp Bridge Auth Error", msg])
|
||||
auth_error_logged = True
|
||||
player._status = "Stopped"
|
||||
except requests.exceptions.RequestException:
|
||||
if not window_title:
|
||||
if not offline_logged:
|
||||
print("Winamp Web Interface offline and no window found.")
|
||||
logger.info("Winamp Web Interface offline and no window found.")
|
||||
offline_logged = True
|
||||
player._status = "Stopped"
|
||||
|
||||
@@ -383,7 +427,7 @@ def update_loop(player):
|
||||
last_known_loop = player._loop_status
|
||||
|
||||
album_str = f" [{player._album}]" if player._album else ""
|
||||
print(f"UPDATE: [{player._status}] Shuffle: {player._shuffle}, Loop: {player._loop_status}, {player._artist}{album_str} - {player._title}")
|
||||
logger.info(f"UPDATE: [{player._status}] Shuffle: {player._shuffle}, Loop: {player._loop_status}, {player._artist}{album_str} - {player._title}")
|
||||
|
||||
player.PropertiesChanged(
|
||||
"org.mpris.MediaPlayer2.Player",
|
||||
@@ -398,12 +442,13 @@ def update_loop(player):
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Update error: {e}")
|
||||
logger.error(f"Update error: {e}")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
write_pid_file()
|
||||
bus = SessionBus()
|
||||
player_logic = WinampMPRIS()
|
||||
bus.publish(APP_ID, ("/org/mpris/MediaPlayer2", player_logic))
|
||||
@@ -420,7 +465,7 @@ if __name__ == "__main__":
|
||||
thread = Thread(target=update_loop, args=(player_logic,), daemon=True)
|
||||
thread.start()
|
||||
|
||||
print(f"--- Winamp Bridge Started (Window Title + Web UI + Album Art) ---")
|
||||
logger.info(f"--- Winamp Bridge Started (Window Title + Web UI + Album Art) ---")
|
||||
loop = GLib.MainLoop()
|
||||
try:
|
||||
loop.run()
|
||||
|
||||
Reference in New Issue
Block a user