Dual-Engine Media Streaming - Choose between a simple PHP version or a powerful Flask server for your audio/video library.
- Stream MP3, OGG, MP4, WebM from local folders
- Automatic cover art detection (
track.jpgfortrack.mp3) - OpenGraph support for social media previews
- CLI mode (direct playback via terminal)
- Structured/flat folder view
- Shuffle function for random playback
git clone https://github.com/ArnoldSchiller/playcard.git
cd playcard- configure
$MEDIA_DIRSinplaycard.php:
$MEDIA_DIRS = ["/path/to/music", "/other/folder"];2nd call in the browser:
http://deinserver/playcard.php
pip install flask flask-limiter
export AUDIO_PATH="/pfad/zu/musik"
python3 playcard_server.pyOr via apt (recommended on Debian/Ubuntu):
sudo apt install python3-flask python3-flask-limiterexport AUDIO_PATH="/absolute/path/to/your/audio/files"python3 playcard_server.py
or use create_playcard_service.sh
./create_playcard_service.sh
By default the app runs on: http://127.0.0.1:8010
→ Runs on http://localhost:8010 by default
| Feature | PHP version | Python/Flask version |
|---|---|---|
| Installation | PHP only required | Python + Pip |
| Performance | Good for small libraries | Optimized for large libraries |
| CLI- Player | ffplay integration |
Customizable |
| Rate limiting | No | ✅ (100 requests/min) |
| Proxy friendly | Yes | Yes (with HTTPS support) |
| Systemd service | Set up manually | See template here in README |
<meta property="og:audio" content="https://server/stream.mp3">
<meta property="og:image" content="https://server/cover.jpg">Perfect for Facebook/Discord/Telegram!
- Restricted to
$ALLOWED_EXTENSIONS - No directory traversal possible
- Additionally:
- Rate limiting via
flask-limiter - Input sanitization with
os.path.basename - Recommended: Run behind HTTPS reverse proxy
- Rate limiting via
[Unit]
Description=Playcard Flask Service
After=network.target
[Service]
User=www-data
ExecStart=/usr/bin/python3 /path/to/playcard_server.py
Restart=always
Environment="AUDIO_PATH=/music/path"sudo systemctl enable playcard.serviceProxyPass "/music" "http://localhost:8010/music"
ProxyPassReverse "/music" "http://localhost:8010/music"location /musik {
proxy_pass http://127.0.0.1:8010;
}This project is licensed under the MIT License 1.0.
Copyright (c) 2025 Arnold Schiller schiller@babsi.de
For the Media-kit library, the copyright is: Copyright (c) 2021 & onwards Hitesh Kumar Saini saini123hitesh@gmail.com
Make sure that you have activated mod_proxy and mod_proxy_http. You can activate these modules, if they are not yet activated, with :
sudo a2enmod proxy
sudo a2enmod proxy_httpAdd the following configuration to your Apache configuration file (usually in /etc/apache2/conf-available/playcard-proxy.conf or any other file you use) you can also use conf-available for a playcard-proxy.conf
# Example for Apache configuration
# Make sure that CGI is activated
<Directory “/usr/lib/cgi-bin”>
AllowOverride None
Options +ExecCGI
AddHandler cgi-script .cgi .py
Require all granted
</Directory>
# Example for proxy configuration
<IfModule mod_proxy.c>
# Forward requests for /music/folder to the Flask server
ProxyPass “/playcard” “http://127.0.0.1:8010/musik/playcard”
ProxyPassReverse “/playcard” “http://127.0.0.1:8010/musik/playcard”
</IfModule>sudo a2enmod playcard_proxy
sudo apache2 restartExample configuration for Nginx:
If you are using Nginx, you can use the following configuration in your Nginx configuration file (/etc/nginx/sites-available/default or another file):
# Example for Nginx configuration
server {
listen 80;
server_name yourdomain.com; # Replace with your actual domain name or IP address
location /music/playcard {
proxy_pass http://127.0.0.1:8010/musik/playcard; # Forward to the Flask server
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# **Your Audio Path Configuration**
# Replace the path with the path to your music folder, e.g. /var/www/html/music/ogg
# Make sure that the Python server has access to this folder
}The path to the music file is set by AUDIO_PATH = os.environ.get("AUDIO_PATH", None). If this value is not set, the error "Server not configured. Please set AUDIO_PATH as an environment variable." is returned, informing the user that they must set the path.
Only .mp3, .mp4 and .ogg are accepted as permitted audio formats. This ensures that no unwanted or dangerous file types are served.
The script checks whether the requested file exists in the specified AUDIO_PATH and whether it has the correct file extensions. If no file is specified, a list of available tracks is displayed.
When the page is shared on social media, OG metadata is included to create an engaging preview, including a cover image and audio file.
The Flask Limiter extension ensures that the number of requests per IP address is limited to 100 per minute.
Checks whether the requested file actually exists in the specified directory and whether access to the file is allowed (avoiding security issues such as directory traversal).
If you are using the script in a production environment, remember to take the appropriate security measures, e.g. enforce HTTPS, and make sure that sensitive data is not exposed in logs or error messages.
Secure it against cross-site scripting in Apache with mod_security.
