15 Commits

12 changed files with 76 additions and 21 deletions
+2
View File
@@ -4,3 +4,5 @@ navidrome-upload.service
.idea/ .idea/
.env .env
/README.md /README.md
__pycache__/
*.deb
+27
View File
@@ -0,0 +1,27 @@
# Navidome-Uploader Dockerfile for building .deb packages
# Arian Nasr
# May 9, 2026
FROM debian:13-slim
# Prevent interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
build-essential \
debhelper \
devscripts \
fakeroot \
python3 \
python3-venv \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build/src
RUN mkdir -p /dist
COPY . .
RUN chmod +x release/build-deb.sh
CMD ["sh", "-c", "./release/build-deb.sh && mv ../*.deb /dist/"]
+1 -1
View File
@@ -9,7 +9,7 @@ Group=navidrome-uploader
WorkingDirectory=/opt/navidrome-uploader WorkingDirectory=/opt/navidrome-uploader
Environment="PATH=/opt/navidrome-uploader/venv/bin" Environment="PATH=/opt/navidrome-uploader/venv/bin"
EnvironmentFile=/etc/default/navidrome-uploader/.env EnvironmentFile=/etc/default/navidrome-uploader/.env
ExecStart=/opt/navidrome-uploader/venv/bin/gunicorn -c gunicorn.conf.py main:app ExecStart=/opt/navidrome-uploader/venv/bin/gunicorn --no-control-socket -c gunicorn.conf.py main:app
Restart=on-failure Restart=on-failure
RestartSec=30 RestartSec=30
+12
View File
@@ -1,3 +1,15 @@
navidrome-uploader (0.1.0-2) unstable; urgency=high
* Run pip install stage as navidrome-uploader user instead of root
-- Arian Nasr <arian@2ari.ca> Tue, 14 Apr 2026 12:11:00 -0400
navidrome-uploader (0.1.0-1) unstable; urgency=medium
* Disable gunicorn control socket in systemd service unit
-- Arian Nasr <arian@2ari.ca> Thu, 09 Apr 2026 01:58:00 -0400
navidrome-uploader (0.1.0) unstable; urgency=medium navidrome-uploader (0.1.0) unstable; urgency=medium
* Add Debian packaging with systemd service integration and venv setup. * Add Debian packaging with systemd service integration and venv setup.
+1 -1
View File
@@ -5,5 +5,5 @@ requirements.txt opt/navidrome-uploader/
templates/* opt/navidrome-uploader/templates/ templates/* opt/navidrome-uploader/templates/
static/css/* opt/navidrome-uploader/static/css/ static/css/* opt/navidrome-uploader/static/css/
static/js/* opt/navidrome-uploader/static/js/ static/js/* opt/navidrome-uploader/static/js/
contrib/navidrome-uploader.service lib/systemd/system/ contrib/navidrome-uploader.service lib/systemd/system/navidrome-uploader.service
+7 -3
View File
@@ -3,12 +3,15 @@ set -e
APP_DIR="/opt/navidrome-uploader" APP_DIR="/opt/navidrome-uploader"
VENV_DIR="${APP_DIR}/venv" VENV_DIR="${APP_DIR}/venv"
APP_USER="navidrome-uploader"
case "$1" in case "$1" in
configure) configure)
python3 -m venv "${VENV_DIR}" chown -R "$APP_USER:$APP_USER" "$APP_DIR"
"${VENV_DIR}/bin/pip" install --no-cache-dir --upgrade pip
"${VENV_DIR}/bin/pip" install --no-cache-dir -r "${APP_DIR}/requirements.txt" runuser -u "$APP_USER" -- python3 -m venv "$VENV_DIR"
runuser -u "$APP_USER" -- "$VENV_DIR/bin/pip" install --no-cache-dir --upgrade pip
runuser -u "$APP_USER" -- "$VENV_DIR/bin/pip" install --no-cache-dir -r "$APP_DIR/requirements.txt"
if command -v systemctl >/dev/null 2>&1; then if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload || true systemctl daemon-reload || true
@@ -20,3 +23,4 @@ esac
exit 0 exit 0
+1
View File
@@ -8,6 +8,7 @@ fi
if [ "$1" = "purge" ]; then if [ "$1" = "purge" ]; then
rm -rf /etc/default/navidrome-uploader rm -rf /etc/default/navidrome-uploader
rm -rf /opt/navidrome-uploader/venv rm -rf /opt/navidrome-uploader/venv
rm -rf /opt/navidrome-uploader/__pycache__
fi fi
exit 0 exit 0
+2
View File
@@ -8,9 +8,11 @@ from werkzeug.utils import secure_filename
UPLOAD_FOLDER = os.environ.get('NAVIDROME_MUSIC_FOLDER', '/opt/navidrome/music') UPLOAD_FOLDER = os.environ.get('NAVIDROME_MUSIC_FOLDER', '/opt/navidrome/music')
ALLOWED_EXTENSIONS = {'flac', 'mp3', 'wav'} ALLOWED_EXTENSIONS = {'flac', 'mp3', 'wav'}
MAX_CONTENT_LENGTH = 500 * 1024 * 1024
app = Flask(__name__) app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
def allowed_file(filename): def allowed_file(filename):
return '.' in filename and \ return '.' in filename and \
View File
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
PROJECT_ROOT="$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd)"
cd "${PROJECT_ROOT}"
docker build --platform linux/amd64 -t uploader-builder -f Dockerfile.build .
docker run --rm --platform linux/amd64 -v "$(pwd)/output:/dist" uploader-builder
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
if ! getent passwd navidrome-uploader > /dev/null 2>&1; then
printf "Creating navidrome-uploader user\n"
useradd --system --shell /usr/sbin/nologin --user-group navidrome-uploader
fi
+4 -2
View File
@@ -1,8 +1,10 @@
blinker==1.9.0 blinker==1.9.0
click==8.3.2 click==8.3.3
Flask==3.1.3 Flask==3.1.3
itsdangerous==2.2.0 itsdangerous==2.2.0
Jinja2==3.1.6 Jinja2==3.1.6
MarkupSafe==3.0.3 MarkupSafe==3.0.3
Werkzeug==3.1.8 Werkzeug==3.1.8
gunicorn==25.3.0 gunicorn==26.0.0
pip==26.1.1
packaging==26.2