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.
- 🎨 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
- Python 3.9 or higher
- uv package manager
# 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# 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.mainPerfect 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.mainAccess 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:
- Add "Browser Source" to your scene
- Set URL to:
http://localhost:8000/now-playing.svg(adjust port if needed) - Set dimensions to 400x120 (or customize)
- Enable "Refresh browser when scene becomes active"
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.mainGitHub README Integration:
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": "default"
}
}
# Start client
uv run python server/public_client.pyThe service uses a unified configuration system with the following priority:
- Environment Variables (highest priority)
- config.json (main configuration file)
- Built-in defaults (lowest priority)
# Copy example configuration
cp config.example.json config.json
# Edit configuration as needed
# The config.json file contains both server and client settings{
"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": "default" // Default template
}
}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=modern-cardAvailable templates:
default: Full-featured with album art and progressminimalist: Clean and simple designrounded: Modern rounded design with clean layoutmusic-card: Spotify-like card with animated sound bars- etc..
# Use specific template
http://localhost:8000/now-playing.svg?template=music-cardInject 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;}| 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 |
# 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# coming soonuv sync --dev- Create SVG template in
client/renderer/templates/ - Use Jinja2 templating with
media_infocontext - Follow existing template patterns for consistency
- 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](https://github.com/ungive/mediaremote-adapter)** by [@ungive](https://github.com/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](LICENSE) file for details.