Compare commits

..

12 Commits

Author SHA1 Message Date
c668c24e6f ignore pid file 2026-04-11 11:37:28 -04:00
ebfb990aca auth fallback as wawi plugin sometimes forgets passwords 2026-04-11 11:34:39 -04:00
f8300c9a1f add image for wacup title format 2026-04-08 19:21:39 -04:00
f04cdc5890 add screenshot for title format config 2026-04-08 19:20:45 -04:00
feaf149186 update readme re wawi 2026-04-08 19:14:55 -04:00
e6796aa2e2 loop video 2026-04-08 19:10:52 -04:00
a0c67b4403 fix video 2026-04-08 19:10:03 -04:00
8c6b30fe14 fix video 2026-04-08 19:09:18 -04:00
9f7b980b5c fix video 2026-04-08 19:08:27 -04:00
299d7a4030 Add screenshot/video 2026-04-08 19:06:05 -04:00
79da23eaac Add WACUP download links and testing environment details 2026-04-08 18:47:31 -04:00
7b59cf4ed7 Credit Phil Himsworth and update Wawi download instructions 2026-04-08 18:39:12 -04:00
7 changed files with 47 additions and 9 deletions

2
.gitignore vendored
View File

@@ -174,3 +174,5 @@ cython_debug/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc
# PID/run file
*.pid

View File

@@ -85,6 +85,9 @@ busctl --user call org.mpris.MediaPlayer2.winamp /org/mpris/MediaPlayer2 org.mpr
- **`winamp_mpris_old.py`**: A previous iteration of the bridge. - **`winamp_mpris_old.py`**: A previous iteration of the bridge.
- **`sample_Winamp Web Interface.html`**: A sample of the HTML returned by the Winamp Web Interface, used for reference in parsing logic. - **`sample_Winamp Web Interface.html`**: A sample of the HTML returned by the Winamp Web Interface, used for reference in parsing logic.
> [!NOTE]
> The source code in `Wawi Source/` and `gen_httpSrv_systray/` is provided for reference only. The bridge currently interacts with the compiled binaries.
## Development Conventions ## Development Conventions
- **D-Bus Interface:** Defined via XML introspection string within the `WinampMPRIS` class. - **D-Bus Interface:** Defined via XML introspection string within the `WinampMPRIS` class.

View File

@@ -13,9 +13,14 @@ A Python-based bridge that provides an MPRIS2 interface for Winamp/WACUP on Linu
## Prerequisites ## Prerequisites
### 1. Winamp Web Interface Plugin ### 1. Winamp / WACUP
The bridge communicates with Winamp through the **Winamp Web Interface** plugin. The bridge is designed to work with both classic Winamp and [WACUP](https://getwacup.org.uk/) (Winamp Community Update Project).
- **Download**: [Winamp Web Interface v7.5.10](https://winampheritage.com/plugin/winamp-web-interface/92511) (Source included in repository). - **Download WACUP**: [https://getwacup.org.uk/preview/](https://getwacup.org.uk/preview/)
- **Testing Environment**: This bridge was primarily tested using **WACUP** installed via [Bottles](https://usebottles.com/) using the `soda-9.0-1` runner.
### 2. Winamp Web Interface Plugin
The bridge communicates with Winamp through the **Winamp Web Interface** plugin (Wawi), originally written by **Phil Himsworth** (2001-2002).
The Wawi v7.5.13 installer `Wawi_7-5-13.exe` is included in this repository. More info can be found at [Winamp Heritage (v7.5.10)](https://winampheritage.com/plugin/winamp-web-interface/92511).
- **Installation**: Install the plugin in Winamp/WACUP. - **Installation**: Install the plugin in Winamp/WACUP.
- **Configuration**: - **Configuration**:
1. Open Winamp Preferences -> Plug-ins -> General Purpose. 1. Open Winamp Preferences -> Plug-ins -> General Purpose.
@@ -32,6 +37,7 @@ To ensure the bridge can extract correct metadata, you must configure Winamp to
%artist%%album%%title% | %playback_time%/%length% - Winamp %artist%%album%%title% | %playback_time%/%length% - Winamp
``` ```
*(Note: The separator between Artist, Album, and Title should be an en-dash `` or a simple dash `-`)*. *(Note: The separator between Artist, Album, and Title should be an en-dash `` or a simple dash `-`)*.
![](WACUP-Title-Format.png)
### 3. Linux Dependencies ### 3. Linux Dependencies
Install the required Python libraries, `wmctrl`, and `libnotify` (for notifications): Install the required Python libraries, `wmctrl`, and `libnotify` (for notifications):
@@ -84,3 +90,6 @@ busctl --user call org.mpris.MediaPlayer2.winamp /org/mpris/MediaPlayer2 org.mpr
- **Controls not working**: Ensure the Winamp Web Interface is accessible at `http://localhost:5666`. - **Controls not working**: Ensure the Winamp Web Interface is accessible at `http://localhost:5666`.
- **No Metadata**: Ensure `wmctrl -l` can see the Winamp window and the title matches the expected format. - **No Metadata**: Ensure `wmctrl -l` can see the Winamp window and the title matches the expected format.
- **KDE Plasma 6**: If using Plasma 6 on Wayland, you may need to grant "Remote Control" permissions when prompted. - **KDE Plasma 6**: If using Plasma 6 on Wayland, you may need to grant "Remote Control" permissions when prompted.
## Demo
<video controls autoplay muted loop src="Screencast_20260408_184805.mp4"></video>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
WACUP-Title-Format.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -14,6 +14,7 @@ from bs4 import BeautifulSoup
# --- CONFIGURATION & XDG PATHS --- # --- CONFIGURATION & XDG PATHS ---
BASE_URL = "http://localhost:5666" BASE_URL = "http://localhost:5666"
AUTH = ('winamp', 'llama') AUTH = ('winamp', 'llama')
POSSIBLE_CREDS = [('winamp', 'llama'), ('winamp', ''), None]
APP_ID = "org.mpris.MediaPlayer2.winamp" APP_ID = "org.mpris.MediaPlayer2.winamp"
DEFAULT_ART = "https://webamp.org/favicon.ico" DEFAULT_ART = "https://webamp.org/favicon.ico"
@@ -39,6 +40,29 @@ logging.basicConfig(
) )
logger = logging.getLogger("winamp-mpris") logger = logging.getLogger("winamp-mpris")
def authenticated_get(url, timeout=2):
"""Helper to perform GET with authentication fallback."""
global AUTH
try:
r = requests.get(url, auth=AUTH, timeout=timeout)
if r.status_code != 401:
return r
except requests.RequestException as e:
raise e
for cred in POSSIBLE_CREDS:
if cred == AUTH:
continue
try:
r = requests.get(url, auth=cred, timeout=timeout)
if r.status_code != 401:
logger.info(f"Authentication fallback successful: switched to {cred}")
AUTH = cred
return r
except requests.RequestException:
continue
return r
def write_pid_file(): def write_pid_file():
try: try:
with open(PID_FILE, "w") as f: with open(PID_FILE, "w") as f:
@@ -127,9 +151,9 @@ class WinampMPRIS:
def _request(self, endpoint): def _request(self, endpoint):
logger.info(f"COMMAND RECEIVED: {endpoint}") logger.info(f"COMMAND RECEIVED: {endpoint}")
try: try:
r = requests.get(f"{BASE_URL}/{endpoint}", auth=AUTH, timeout=2) r = authenticated_get(f"{BASE_URL}/{endpoint}", timeout=2)
if r.status_code == 401: 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." msg = "401 Unauthorized: All authentication attempts failed (winamp:llama, winamp:, anon). Check plugin config."
logger.warning(f"ERROR: {msg}") logger.warning(f"ERROR: {msg}")
subprocess.run(["notify-send", "-u", "critical", "-t", "10000", "Winamp Bridge Auth Error", msg]) subprocess.run(["notify-send", "-u", "critical", "-t", "10000", "Winamp Bridge Auth Error", msg])
elif r.status_code != 200: elif r.status_code != 200:
@@ -334,7 +358,7 @@ def update_loop(player):
# 2. Poll Web UI (Status and Time source) # 2. Poll Web UI (Status and Time source)
try: try:
r = requests.get(f"{BASE_URL}/main", auth=AUTH, timeout=1) r = authenticated_get(f"{BASE_URL}/main", timeout=1)
if r.status_code == 200: if r.status_code == 200:
offline_logged = False offline_logged = False
auth_error_logged = False auth_error_logged = False
@@ -396,12 +420,12 @@ def update_loop(player):
player._album = "" player._album = ""
elif r.status_code == 401: elif r.status_code == 401:
if not auth_error_logged: 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." msg = "401 Unauthorized: All authentication attempts failed (winamp:llama, winamp:, anon). Check plugin config."
logger.warning(f"ERROR: {msg}") logger.warning(f"ERROR: {msg}")
subprocess.run(["notify-send", "-u", "critical", "-t", "10000", "Winamp Bridge Auth Error", msg]) subprocess.run(["notify-send", "-u", "critical", "-t", "10000", "Winamp Bridge Auth Error", msg])
auth_error_logged = True auth_error_logged = True
player._status = "Stopped" player._status = "Stopped"
except requests.exceptions.RequestException: except requests.RequestException:
if not window_title: if not window_title:
if not offline_logged: if not offline_logged:
logger.info("Winamp Web Interface offline and no window found.") logger.info("Winamp Web Interface offline and no window found.")