Now Playing Logger Plugin (Python)

A Python example plugin that demonstrates the Scheduler and SubsonicAPI host services by periodically logging what is currently playing in Navidrome.

Features

  • Uses scheduler_schedulerecurring host function to set up a recurring task
  • Uses subsonicapi_call host function to query the getNowPlaying API
  • Configurable cron expression and user via plugin config
  • Demonstrates Python host function imports using @extism.import_fn

Prerequisites

  • extism-py - Python PDK compiler
    curl -Ls https://raw.githubusercontent.com/extism/python-pdk/main/install.sh | bash
    

Note: extism-py requires Binaryen (wasm-merge, wasm-opt) to be installed.

Building

From the plugins/examples directory:

make nowplaying-py.wasm

Or directly:

extism-py plugin/__init__.py -o nowplaying-py.wasm

Installation

  1. Copy nowplaying-py.wasm to your Navidrome plugins folder

  2. Enable plugins in navidrome.toml:

    [Plugins]
    Enabled = true
    Folder = "/path/to/plugins"
    
  3. Configure the plugin (optional):

    [PluginConfig.nowplaying-py]
    cron = "*/1 * * * *"  # Check every minute (default)
    user = "admin"        # Navidrome user for API calls (default)
    

Configuration Options

Key Description Default
cron Cron expression for check frequency */1 * * * * (every minute)
user Navidrome user for SubsonicAPI admin

Testing

Test the manifest:

extism call nowplaying-py.wasm nd_manifest --wasi

Output

When running, the plugin logs messages like:

🎵 john is playing: Pink Floyd - Comfortably Numb (The Wall)
🎵 jane is playing: Radiohead - Paranoid Android (OK Computer)

Or when no one is playing:

🎵 No users currently playing music

How It Works

  1. Initialization (nd_on_init): Reads the cron expression from config and schedules a recurring task using the Scheduler host service.

  2. Callback (nd_scheduler_callback): When the scheduled task fires, calls the SubsonicAPI getNowPlaying endpoint and logs the results.

Host Function Usage (Python)

This plugin demonstrates how to call Navidrome host functions from Python:

import extism
import json

# Import the host function
@extism.import_fn("extism:host/user", "subsonicapi_call")
def _subsonicapi_call(offset: int) -> int:
    """Raw host function - returns memory offset."""
    ...

# Wrapper for JSON marshalling
def subsonicapi_call(uri: str) -> dict:
    request = {"uri": uri}
    request_bytes = json.dumps(request).encode('utf-8')
    request_mem = extism.memory.alloc(request_bytes)
    response_offset = _subsonicapi_call(request_mem.offset)
    response_mem = extism.memory.find(response_offset)
    response = json.loads(extism.memory.string(response_mem))
    
    if response.get("error"):
        raise Exception(response["error"])
    
    return json.loads(response.get("responseJSON", "{}"))