Skip to content

Repository files navigation

Now Playing

A cross-platform music information service that displays currently playing media as beautiful SVG cards. Perfect for OBS streaming overlays, GitHub profiles, and web integrations.

Python 3.9+ License: MIT uv

Features

  • Beautiful SVG Cards: Multiple customizable templates with high-quality album art
  • Cross-Platform: Native support for Windows and macOS media APIs
  • Real-time Updates: Live media information with automatic refresh
  • OBS Integration: Perfect for streaming overlays and screen recordings
  • GitHub Integration: Display your music taste on your profile
  • Modern Stack: Built with FastAPI, uv, and platform-specific APIs
  • Responsive Design: Adapts to different screen sizes and contexts

Template Gallery

Pick a style by adding ?template=<name> to the card URL. The six designs below are the current lineup — expand the section underneath for the legacy set.

turntable template
turntable · spinning vinyl, parking tonearm
terminal template
terminal · phosphor CRT with blinking cursor
poster template
poster · swiss brutalist typography
neon template
neon · glowing sign, CLOSED when idle
ticket template
ticket · concert stub with barcode
polaroid template
polaroid · taped instant photo
Show the 2 classic templates »
music-card template
music-card · full-bleed portrait artwork
minimalist template
minimalist · transparent overlay

Quick Start

Prerequisites

  • Python 3.9 or higher
  • uv package manager

Installation

Option 1: Shell Script (Recommended)

# Clone the repository
git clone https://github.com/ACAne0320/now-playing.git
cd now-playing

# Copy configuration template
cp config.example.json config.json

# Edit config.json as needed (optional)

# Run the interactive launcher
chmod +x start.sh
./start.sh

Option 2: Manual Installation

# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh

# Clone and navigate to the project
git clone https://github.com/ACAne0320/now-playing.git
cd now-playing

# Setup configuration
cp config.example.json config.json

# Sync dependencies
uv sync

# Platform-specific dependencies
uv sync --extra windows  # Windows
uv sync --extra macos    # macOS

# Start the service (will use config.json settings)
uv run python -m client.main

🎯 Usage Modes

1. Local Mode (OBS Integration)

Perfect for streaming and local applications:

# Configure config.json for local mode
{
  "server": {
    "public_mode": false,
    "port": 8000
  }
}

# Start local service (will use config.json settings)
uv run python -m client.main

Access points:

  • Health check: http://localhost:8000/ (or your configured port)
  • Web interface: http://localhost:8000/web
  • SVG card: http://localhost:8000/now-playing.svg
  • Status API: http://localhost:8000/api/v1/status

OBS Studio Setup:

  1. Add "Browser Source" to your scene
  2. Set URL to: http://localhost:8000/now-playing.svg (adjust port if needed)
  3. Set dimensions to 400x120 (or customize)
  4. Enable "Refresh browser when scene becomes active"

2. Public Mode (GitHub Integration)

Deploy to cloud for GitHub README integration:

# Configure config.json for public mode
{
  "server": {
    "public_mode": true,
    "api_key": "your-secure-api-key",
    "port": 8000
  }
}

# Or use environment variables
export PUBLIC_MODE=true
export NOW_PLAYING_API_KEY=your-secure-api-key

# Start public server
uv run python -m client.main

On a serverless host (e.g. Vercel) also configure a shared KV store so all template cards stay in sync — see Vercel deployment. A single long-lived server needs no KV.

GitHub README Integration:

![Now Playing](https://yours-now-playing-server/now-playing.svg)

3. Client Mode (Remote Data Push)

Send local media data to remote server:

# Configure config.json
{
  "server": {
    "public_mode": false,
    "api_key": "dev-secret-key-12345",
    "poll_interval": 5,
    "log_level": "INFO",
    "port": 8000,
    "template_dir": "",
    "enable_album_art": true
  },
  "client": {
    "server_url": "https://yours-now-playing-serverp",
    "api_key": "your-secure-api-key",
    "poll_interval": 10,
    "template": "turntable"
  }
}

# Start client
uv run python server/public_client.py

Configuration

Configuration Files

The service uses a unified configuration system with the following priority:

  1. Environment Variables (highest priority)
  2. config.json (main configuration file)
  3. Built-in defaults (lowest priority)

Setup Configuration

# Copy example configuration
cp config.example.json config.json

# Edit configuration as needed
# The config.json file contains both server and client settings

Configuration Structure

{
  "server": {
    "public_mode": false,           // Enable public/private mode
    "api_key": "your-secret-key",   // API authentication key
    "poll_interval": 5,             // Media polling interval (seconds)
    "log_level": "INFO",            // Logging level
    "port": 8000,                   // Server port
    "template_dir": "",             // Custom template directory
    "enable_album_art": true        // Enable album art display
  },
  "client": {
    "server_url": "http://localhost:8000",  // Remote server URL
    "api_key": "your-secret-key",           // Client API key
    "poll_interval": 10,                    // Client polling interval
    "template": "turntable"                 // Default template
  }
}

Environment Variable Overrides

You can override any configuration setting using environment variables:

# Server configuration
export PUBLIC_MODE=true
export NOW_PLAYING_API_KEY=your-secret-key
export NOW_PLAYING_POLL_INTERVAL=5
export LOG_LEVEL=DEBUG
export PORT=8080
export TEMPLATE_DIR=./custom_templates
export ENABLE_ALBUM_ART=false

# Client configuration
export NOW_PLAYING_SERVER_URL=https://your-app.vercel.app
export NOW_PLAYING_CLIENT_API_KEY=your-client-key
export NOW_PLAYING_CLIENT_POLL_INTERVAL=15
export NOW_PLAYING_TEMPLATE=turntable

Customization

Templates

Available templates:

  • turntable: Spinning vinyl deck with a tonearm that parks when paused
  • terminal: Phosphor-green CRT terminal with scanlines and a blinking cursor
  • poster: Swiss brutalist typographic poster
  • neon: Neon sign that switches to CLOSED when nothing is playing
  • ticket: Concert ticket stub with perforation, barcode and PAUSED stamp
  • polaroid: Taped instant photo with handwritten caption
  • music-card: Full-bleed portrait artwork card
  • minimalist: Transparent text-only overlay for OBS/streaming
# Use specific template
http://localhost:8000/now-playing.svg?template=music-card

Custom CSS

Inject custom styles:

# Custom colors
http://localhost:8000/now-playing.svg?custom_css=.title{fill:red;}.artist{fill:blue;}

# Custom fonts
http://localhost:8000/now-playing.svg?custom_css=.title{font-family:Arial,sans-serif;}

Architecture

Platform Support

Platform API Status Features
Windows 10/11 Windows Media Control API ✅ Full Media info, album art, playback status
macOS 10.15+ MediaRemote.framework + mediaremote-adapter ✅ Full Media info, album art, playback status

Deployment

Docker

# Build image
docker build -t now-playing .

# Run container
docker run -d -p 8000:8000 \
  -e PUBLIC_MODE=true \
  -e NOW_PLAYING_API_KEY=your-key \
  now-playing

Cloud Platforms

Vercel (Recommended)

  1. Import the repo into Vercel (the included vercel.json and api/index.py are detected automatically).

  2. Add a shared KV store so every serverless instance reports the same track (see below), then connect it to the project.

  3. Set the project Environment Variables:

    Variable Value
    PUBLIC_MODE true
    NOW_PLAYING_API_KEY a secret shared with your local client
    KV_REST_API_URL from your KV store (auto-added if you use Vercel KV)
    KV_REST_API_TOKEN from your KV store (auto-added if you use Vercel KV)
  4. Deploy, then run the local client in Client Mode pointed at your Vercel URL.

Why the KV store? In public mode the "now playing" state is shared via Upstash / Vercel KV. Without it the state is kept only in each serverless function's memory, so an update that lands on one instance is invisible to the instance serving another template — causing some README cards to show your track while others show "No music playing". With KV configured, all templates stay in sync. Standalone Upstash works too (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN). If no KV variables are set, the server falls back to in-memory storage (fine for a single long-lived server, not for serverless).

Development

Setup Development Environment

uv sync --dev

Adding New Templates

  1. Create SVG template in client/renderer/templates/
  2. Use Jinja2 templating with media_info context
  3. Follow existing template patterns for consistency
  4. Test with various media states
<!-- Example template structure -->
<svg width="400" height="120" xmlns="http://www.w3.org/2000/svg">
  {% if media_info %}
    <text class="title">{{ media_info.title }}</text>
    <text class="artist">{{ media_info.artist }}</text>
    {% if media_info.album_art_b64 %}
      <image href="data:image/png;base64,{{ media_info.album_art_b64 }}" />
    {% endif %}
  {% else %}
    <text>No media playing</text>
  {% endif %}
</svg>

Security

API Security

  • Authentication: API key-based authentication for public mode
  • Rate Limiting: Built-in request throttling
  • Input Validation: Pydantic-based data validation
  • CORS: Configurable cross-origin resource sharing

Best Practices

  • Use strong API keys (32+ characters)
  • Enable HTTPS in production
  • Regularly rotate API keys
  • Monitor logs for unusual activity
  • Implement IP whitelisting if needed

Acknowledgments

Special thanks to the following open-source projects that make this service possible:

  • mediaremote-adapter by @ungive - Essential for accessing macOS MediaRemote framework on macOS 15.4+. This library provides the critical functionality needed to bypass Apple's MediaRemote restrictions and access complete media information including album artwork.
  • FastAPI - Modern, fast web framework for building APIs with Python 3.6+
  • uv - Ultra-fast Python package installer and resolver

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A cross-platform music information service that captures real-time media playback and generates beautiful SVG cards for OBS streaming overlays and GitHub user profiles.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages