readme update to include setup instructions for offloading the music stream from the webapp

main
simp 2025-03-13 17:29:46 -04:00
parent d66a25d533
commit 31942d6fa6
1 changed files with 204 additions and 36 deletions

240
README.md
View File

@ -4,7 +4,7 @@ This is a js-free music player. It uses iframes and css for interactivity and pa
Live instance at <a href="http://music.simp.i2p" target="_blank">music.simp.i2p</a>
## Features
### Features
- Minimal audio player using only html/css
- Can load balance streams to a number of b32s, see number of listeners
@ -26,13 +26,16 @@ The `updateplaylists.py` script will compress all found tracks to opus and chang
This script will also pack a playlist for torrenting, by hashing the contents of the directory and creating a symlink to a torrent directory. It then copies the .torrent file there.
## To be added
### To be added
- Track metadata
## Install Guide
# Install Guide
### Step 1: Make a user for the install
This will install the webapp stack and all dependencies.
wsgi.py runs gunicorn, which runs the flask app. This passes either a .sock file or a port to the webserver. It's recommended to run nginx with gunicorn, generally, though other webservers can be used. For streaming, we want nginx to be doing the heavy lifting.
## Step 1: Make a user for the install
sudo useradd music && sudo mkdir /home/music && sudo chown music:music /home/music && sudo usermod -aG sudo music && sudo passwd -d music && su music
@ -44,7 +47,7 @@ If not, run (check how to install for your OS):
sudo apt update && sudo apt install ffmpeg
### Step 2: Download and install dependencies
## Step 2: Download and install dependencies
Next, cd to the home directory of the "music" user:
@ -60,7 +63,7 @@ Run this to test that it's running:
It should be running on ports 5000 and 5095.
#### ONLY if that doesn't work, otherwise skip this part - Manual install
### ONLY if that doesn't work, otherwise skip this part - Manual install
Clone the repo using your i2p http proxy, cd into it, then make a python venv.
@ -76,11 +79,30 @@ After requirements are installed, make sure your venv is activated:
Running `python3 wsgi.py` should work, which will run gunicorn. Running `python3 app.py` will just run the flask app withou gunicorn. Useful for debugging. Do not run the flask app alone without wsgi!
### Step 3 - Different ways to run the app
## Step 3 - Edit config
in the /i2music directory, do
nano config.txt
Make sure to change:
secret_key = <change_it>
Worth a look:
- site_title
- options under [footer]
- bitrate
- if you want to get peers, change get_peercounts to True
The settings section has descriptions on the other options. To take effect the app needs to be restarted.
## Step 4 - Run the app
Running as a service will keep it running under the "music" user we created.
#### As a service (systemd) with .sock file
### As a service (systemd) with .sock file
This config will work for systemd linux. Run:
@ -108,7 +130,7 @@ Now we enable and start the service:
sudo systemctl daemon-reload && sudo systemctl enable i2music && sudo systemctl start i2music
#### Run the wsgi.py directly
### Run the wsgi.py directly
You can run just the wsgi.py directly, passing the .sock file or port to the webserver.
@ -124,7 +146,7 @@ OR, to get just the port, run:
python3 wsgi.py
### Step 4 - Setup Nginx
## Step 5 - Setup Nginx
This will pass the .sock file through nginx, listening on port 5050.
@ -150,6 +172,18 @@ Then paste the config, change server_name somedomain.i2p and make sure the path
if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
return 444;
}
keepalive_timeout 320;
proxy_buffering on;
proxy_buffer_size 256k;
proxy_busy_buffers_size 264k;
proxy_buffers 128 8k;
gzip on;
gzip_comp_level 6;
gzip_buffers 32 4k;
gzip_types text/plain text/css application/json image/gif image/jpeg image/png image/webp application/javascript text/xml application/xml application/xml+rss application/atom+xml audio/ogg audio/mp3 font/truetype font/opentype image/svg+xml
reset_timedout_connection on;
client_body_timeout 20;
send_timeout 10;
location / {
proxy_hide_header Server;
proxy_pass http://unix:/home/music/i2music/eepsite.sock;
@ -165,8 +199,28 @@ Now make symlink to sites-enabled.
Reload Nginx so it sees the new config:
sudo systemctl reload nginx
### Setup i2p tunnels
## Step 7 - Nginx optimizations for streaming (optional)
These can be added to the nginx.conf file
sudo nano /etc/nginx.conf
Nginx options to add under http: section that should help with streaming performance:
keepalive_timeout 320;
proxy_buffering on;
proxy_buffer_size 256k;
proxy_busy_buffers_size 264k;
proxy_buffers 128 8k;
gzip on;
gzip_comp_level 6;
gzip_buffers 32 4k;
gzip_types text/plain text/css application/json image/gif image/jpeg image/png image/webp application/javascript text/xml application/xml application/xml+rss application/atom+xml audio/ogg audio/mp3 font/truetype font/opentype image/svg+xml
reset_timedout_connection on;
client_body_timeout 20;
send_timeout 10;
## Step 6 - Setup i2p tunnels
From here you only need to pass the port 5050 from nginx to i2p. Don't use port 5095 (gunicorn) and never run the flask port anywhere other than locally, as its unsafe (5000)
@ -174,7 +228,139 @@ In java, make a new http server tunnel. Make sure to save the private key.
start with 4-6 tunnels, you may opt to reduce tunnels to conserve resources.
## Playlist update script: updateplaylists.py
# Increase performance with separate stream tunnels
This requires more setup, but will give a more performative stream and ability to handle more users. It will also let us optimize a set of tunnels for streaming, and save the ones for the app for http requests.
The main app will use its own set of tunnels this way, offloading music streaming to other tunnels. You can reduce the tunnel count on your main app, and use "reduce tunnels to conserve resources" option in java to allow the stream tunnels to die down when not in use.
We will run "wsgi_fileserve.py" alongside the normal "wsgi.py", which will start another gunicorn instances on another port, "gunicorn_stream_port = 5006" under the settings block. All this server instance can do is serve files with GET requests, all others will be dropped.
## Service config
This mirrors the setup from step 4.
Run:
sudo nano /etc/systemd/system/i2musicstream.service
Then paste the following:
[Unit]
Description=Gunicorn instance to serve music streams
After=network.target
[Service]
User=music
Group=www-data
WorkingDirectory=/home/music/i2music
Environment="PATH=/home/music/i2music/bin"
ExecStart=/home/music/i2music/bin/gunicorn --workers 1 --bind unix:stream.sock -m 007 wsgi_fileserve:app
[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl daemon-reload && sudo systemctl enable musicstream && sudo systemctl start musicstream
## Nginx config
We will have one optimized for streaming, and one for the app
### Nginx config for streaming
Go the the nginx directory (path may be different):
cd /etc/nginx
Make a new site in sites-enabled
sudo nano /etc/nginx/sites-available/musicstream
Then paste the config, change server_name somedomain.i2p and make sure the path in proxy_pass is right:
server {
listen 5051;
server_name somedomain.i2p;
server_tokens off;
if ($request_method !~ ^(GET)$ ) {
return 444;
}
if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
return 444;
}
keepalive_timeout 320;
proxy_buffering on;
proxy_buffer_size 256k;
proxy_busy_buffers_size 264k;
proxy_buffers 128 8k;
gzip on;
gzip_comp_level 6;
gzip_buffers 32 4k;
gzip_types text/plain text/css application/json image/gif image/jpeg image/png image/webp application/javascript text/xml application/xml application/xml+rss application/atom+xml audio/ogg audio/mp3 font/truetype font/opentype image/svg+xml
reset_timedout_connection on;
client_body_timeout 20;
send_timeout 10;
location / {
proxy_hide_header Server;
proxy_pass http://unix:/home/music/i2music/stream.sock;
}
}
### Change the nginx config for the webapp
Edit the i2music config made earlier
sudo nano /etc/nginx/sites-available/i2music
Remove the parts between "keepalive_timeout 320;" and "location / {" so it looks like this:
server {
listen 5050;
server_name somedomain.i2p;
server_tokens off;
if ($request_method !~ ^(GET|POST)$ ) {
return 444;
}
if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
return 444;
}
keepalive_timeout 320;
location / {
proxy_hide_header Server;
proxy_pass http://unix:/home/music/i2music/eepsite.sock;
}
}
ctrl+s / ctrl+x to save and exit.
Reload Nginx so it sees the new config:
sudo systemctl reload nginx
### Make i2p http server tunnels for streaming only
Make a new http server tunnel, listening for port 5051 (based on nginx config). In java, try starting with 2-3 tunnels for up/down. Enable "reduce tunnels to conserve resources" after 5 mins to 1 tunnel. Save the key file, start automatically when router starts.
Under custom options, paste this and adjust to your liking:
i2cp.fastReceive=true i2cp.gzip=true i2p.streaming.bufferSize=256k i2p.streaming.connectTimeout=20*1000 i2p.streaming.inactivityTimeout=600*1000 i2p.streaming.limitAction=http
Once you have one tunnel made up, copy it so you have 2 or more sets of streaming tunnels, all going to port 5051. You can make as many of these tunnels as needed with different b32's, using identical settings.
Then copy each of the server b32's, add 1 per line, tabbed. Found in the config.txt file under the "b32streams" block, like this:
[b32streams]
b32s =
hvyaj6xegbsluf65ob2rdw2yf25me7kavmy2o3crwtijf4yb4zwa.b32.i2p
gkbtx3a2s3pryryiagso7e5rehcbccphyvdm4y2nxops77os56nq.b32.i2p
After restarting the main webapp, it will keep track of the number of listeners using each b32 and try to use the least crowded one. A user will continue using the same stream server for the session.
# Playlist update script: updateplaylists.py
Note: this script need ffmpeg to work. Make sure that's installed.
@ -200,7 +386,7 @@ Much higher than 56k may result in some stuttering in i2p from my testing.
The script won't touch images and music files that have already been converted. BUT IT WILL REPLACE those files, so make sure you have the originals somewhere else.
## Torrent packing with updateplaylists.py
# Torrent packing with updateplaylists.py
The options for this are under the "torrent" section.
@ -221,7 +407,7 @@ To enable hashing of torrents, `make_playlist_torrents` should be True. This wil
With all features enabled, the updateplaylists.py script will compress the contents of enabled playlist directories, hash them as torrents, send them to your torrent clients watch folder, and save the playlist data as `data.json`
## How it works
# How it works
When the webapp is ran it loads the data.json file that was created from running `python3 updateplaylists.py`
@ -231,7 +417,7 @@ These details are kept in memory to determine track controls (next/previous song
There are 4 iframes, left is for playlist view, center is the audio player, right top is for settings, right bottom is for info. Using target="framename" we can get some limited interactivity this way, relying on backend.
## Track ordering
# Track ordering
Tracks are ordered using natsort, which sorts a-z/numerically.
@ -243,29 +429,11 @@ To manually order tracks, name them with a "xxx." in front of the title:
The number in front of the track name won't be shown in the player.
## Browser support
# Browser support
This relies entirely on browser suport for playing audio and autoplay especially. Major browsers should all support wav/mp3/ogg(opus), but some may block autoplay. The user may need to enable it to work right.
## Load balancing, using a separate stream server
This is entirely optional, but you can offload just the streams to other server tunnels to load balance. The main app will use its own set of tunnels this way, and should be more performative. You can reduce the tunnel count on your main app, and use "reduce tunnels to conserve resources" option in java to allow the stream tunnels to die down when not in use.
In this case you want to also run "wsgi_fileserve.py" which will start another gunicorn instances on another port, "gunicorn_stream_port = 5006" under the settings block. All this server instance can do is serve files with GET requests, all others will be dropped.
Then make other http server tunnels, all going to the port set on "gunicorn_stream_port"
With each of the additional server b32's, add 1 per line and tabbed under the b32streams block:
[b32streams]
b32s =
hvyaj6xegbsluf65ob2rdw2yf25me7kavmy2o3crwtijf4yb4zwa.b32.i2p
gkbtx3a2s3pryryiagso7e5rehcbccphyvdm4y2nxops77os56nq.b32.i2p
After restarting the main webapp, it will keep track of the number of listeners using each b32 and try to use the least crowded one. A user will continue using the same stream server for the session.
## Settings
# Settings
Most settings are in config.txt file
[settings]