From 8feac2cc62e6ab2e8a1ec13ef58d72102e80295a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Oct 2025 20:09:10 +0200 Subject: [PATCH] Initial commit: Nicotine-Less Docker container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lightweight Docker container for Nicotine+ with noVNC web interface and Firefox ESR browser. Features: - noVNC web interface with dynamic resize support - Firefox ESR for testing ports and browsing - TigerVNC with AcceptSetDesktopSize enabled - Supervisor for process management - Configurable user/group permissions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitea/workflows/build.yml | 133 +++++++++++++++++++++++++++++++++++++ .gitignore | 6 ++ Dockerfile | 89 +++++++++++++++++++++++++ README.md | 122 ++++++++++++++++++++++++++++++++++ docker-compose.yml.example | 62 +++++++++++++++++ entrypoint.sh | 43 ++++++++++++ index.html | 12 ++++ openbox-autostart.sh | 5 ++ openbox-rc.xml | 50 ++++++++++++++ supervisord.conf | 43 ++++++++++++ 10 files changed, 565 insertions(+) create mode 100644 .gitea/workflows/build.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml.example create mode 100755 entrypoint.sh create mode 100644 index.html create mode 100755 openbox-autostart.sh create mode 100644 openbox-rc.xml create mode 100644 supervisord.conf diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..0149d4d --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,133 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - main + workflow_dispatch: + schedule: + - cron: '0 * * * *' # Every hour + +jobs: + check-version: + runs-on: linux-amd64 + outputs: + should_build: ${{ steps.compare.outputs.should_build }} + latest_version: ${{ steps.get_version.outputs.version }} + steps: + - name: Get latest Nicotine+ version from PyPI + id: get_version + run: | + VERSION=$(curl -s https://pypi.org/pypi/nicotine-plus/json | jq -r '.info.version') + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Latest Nicotine+ version: $VERSION" + + - name: Get current built version + id: current_version + run: | + CURRENT=$(curl -s https://git.nicola.sh/api/v1/repos/public/nicotine-less/releases/latest 2>/dev/null | jq -r '.tag_name // "none"') + echo "current=$CURRENT" >> $GITHUB_OUTPUT + echo "Current built version: $CURRENT" + + - name: Compare versions + id: compare + run: | + if [ "${{ steps.get_version.outputs.version }}" != "${{ steps.current_version.outputs.current }}" ]; then + echo "should_build=true" >> $GITHUB_OUTPUT + echo "New version detected, will build" + else + echo "should_build=false" >> $GITHUB_OUTPUT + echo "Already up to date" + fi + + build: + needs: check-version + if: needs.check-version.outputs.should_build == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + strategy: + matrix: + include: + - platform: linux/amd64 + runner: linux-amd64 + arch: amd64 + - platform: linux/arm64 + runner: linux-arm64 + arch: arm64 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: git.nicola.sh + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v5 + with: + context: . + platforms: ${{ matrix.platform }} + outputs: type=image,name=git.nicola.sh/public/nicotine-less,push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=git.nicola.sh/public/nicotine-less:buildcache-${{ matrix.platform }} + cache-to: type=registry,ref=git.nicola.sh/public/nicotine-less:buildcache-${{ matrix.platform }},mode=max + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.platform }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + needs: build + runs-on: linux-amd64 + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: git.nicola.sh + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + -t git.nicola.sh/public/nicotine-less:latest \ + -t git.nicola.sh/public/nicotine-less:${{ needs.check-version.outputs.latest_version }} \ + $(printf 'git.nicola.sh/public/nicotine-less@sha256:%s ' *) + + - name: Create release + if: needs.check-version.outputs.should_build == 'true' + run: | + curl -X POST "https://git.nicola.sh/api/v1/repos/public/nicotine-less/releases" \ + -H "Authorization: token ${{ secrets.TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"${{ needs.check-version.outputs.latest_version }}\", + \"name\": \"Nicotine+ ${{ needs.check-version.outputs.latest_version }}\", + \"body\": \"Automated build with Nicotine+ version ${{ needs.check-version.outputs.latest_version }}\" + }" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..015865e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Dati utente e configurazioni personali +data/ + +# File di configurazione personalizzati +nicotine-config +docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fda5732 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,89 @@ +FROM debian:bookworm-slim + +# Metadata +LABEL maintainer="Nicola" +LABEL description="Nicotine+ with noVNC web interface and SSH X forwarding support" +LABEL version="1.0" + +# Avoid interactive prompts +ENV DEBIAN_FRONTEND=noninteractive + +# Environment variables +ENV DISPLAY=:0 \ + VNC_PORT=5900 \ + NOVNC_PORT=6080 \ + VNC_RESOLUTION=1280x720 \ + VNC_DEPTH=24 \ + USER=nicotine \ + PUID=1000 \ + PGID=1000 + +# Install dependencies +RUN apt-get update && apt-get install -y \ + # Lightweight window manager + openbox \ + xterm \ + dbus-x11 \ + # VNC server + tigervnc-standalone-server \ + tigervnc-common \ + # noVNC for web interface + novnc \ + python3-websockify \ + # Nicotine+ dependencies + python3 \ + python3-pip \ + python3-gi \ + python3-gi-cairo \ + gir1.2-gtk-3.0 \ + # Lightweight browser + firefox-esr \ + # For SSH X forwarding + xauth \ + # Supervisord to manage processes + supervisor \ + # Utilities + wget \ + curl \ + procps \ + net-tools \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install Nicotine+ latest version +RUN pip3 install --no-cache-dir --break-system-packages nicotine-plus + +# Create non-root user +RUN groupadd -g ${PGID} ${USER} && \ + useradd -u ${PUID} -g ${PGID} -m -s /bin/bash ${USER} + +# Create directories +RUN mkdir -p \ + /config \ + /downloads \ + /incomplete \ + /home/${USER}/.config/openbox \ + /home/${USER}/.config/nicotine \ + /home/${USER}/.vnc \ + /var/log/supervisor + +# Copy configurations +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +COPY openbox-rc.xml /home/${USER}/.config/openbox/rc.xml +COPY index.html /usr/share/novnc/index.html + +# Set permissions +RUN chmod +x /usr/local/bin/entrypoint.sh \ + && chown -R ${USER}:${USER} /home/${USER} /config /downloads /incomplete + +# Expose noVNC and Soulseek ports +EXPOSE 6080 2234 2235/udp + +# Volumes +VOLUME ["/config", "/downloads", "/incomplete"] + +WORKDIR /home/${USER} + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..130b313 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +# Nicotine-Less + +Lightweight Docker container for Nicotine+ (Soulseek client) with noVNC web interface and integrated Firefox ESR browser. + +## Features + +- Web interface accessible from browser (noVNC) +- Integrated Firefox ESR browser +- Dynamic VNC window resizing +- SSH X forwarding support +- Persistent volumes for configuration and downloads + +## Requirements + +- Docker and Docker Compose installed +- Available ports: 6080 (web), 2234 (TCP), 2235 (UDP) + +## Installation + +### 1. Clone the repository + +```bash +git clone https://git.nicola.sh/public/nicotine-less.git +cd nicotine-less +``` + +### 2. Configure docker-compose.yml + +Copy the example file and edit it: + +```bash +cp docker-compose.yml.example docker-compose.yml +``` + +**Required changes in `docker-compose.yml`:** + +- Set your download and incomplete paths: + ```yaml + volumes: + - ./data/config:/config + - /path/to/your/downloads:/downloads # CHANGE THIS + - /path/to/your/incomplete:/incomplete # CHANGE THIS + ``` + +- (Optional) Add shared folders: + ```yaml + - /path/to/your/music:/shares/music:ro # ADD YOUR SHARES + ``` + +- (Optional) Adjust PUID/PGID to match your user (to avoid permission issues): + ```bash + id -u # Get your user ID + id -g # Get your group ID + ``` + Then update in `docker-compose.yml`: + ```yaml + environment: + - PUID=1000 # Change to your user ID + - PGID=1000 # Change to your group ID + ``` + +### 3. Create config directory + +```bash +mkdir -p data/config +``` + +### 4. Build and start + +```bash +docker compose build +docker compose up -d +``` + +## Usage + +### Web access + +Open browser: +``` +http://localhost:6080 +``` + +Or from another device: +``` +http://YOUR_HOST_IP:6080 +``` + +### SSH X Forwarding + +```bash +ssh -X user@host_ip +docker exec -it nicotine-less nicotine +``` + +## Updates + +**Automatic updates**: The container image is automatically rebuilt every hour when a new Nicotine+ version is released on PyPI. + +To update to the latest version, pull the new image: + +```bash +docker compose pull +docker compose up -d +``` + +Or rebuild locally: + +```bash +docker compose down +docker compose build --no-cache +docker compose up -d +``` + +## License + +This project is provided "as-is" without warranties. + +## Links + +- [Nicotine+ GitHub](https://github.com/nicotine-plus/nicotine-plus) +- [noVNC GitHub](https://github.com/novnc/noVNC) diff --git a/docker-compose.yml.example b/docker-compose.yml.example new file mode 100644 index 0000000..4f2a5c0 --- /dev/null +++ b/docker-compose.yml.example @@ -0,0 +1,62 @@ +services: + nicotine-less: + image: git.nicola.sh/public/nicotine-less:latest + # Or build locally: + # build: . + container_name: nicotine-less + hostname: nicotine-less + restart: unless-stopped + + # Ports + ports: + - "6080:6080" # noVNC web interface + - "2234:2234" # Soulseek TCP + - "2235:2235/udp" # Soulseek UDP + + # Environment variables + environment: + - PUID=1000 # User ID (change if needed) + - PGID=1000 # Group ID (change if needed) + - TZ=Europe/Rome # Timezone + - VNC_RESOLUTION=1920x1080 # Desktop resolution + - VNC_DEPTH=24 # Color depth + - VNC_PASSWORD=nicotine # VNC password (optional) + - DISPLAY=:0 + + # Persistent volumes + volumes: + # Nicotine+ configuration (database, settings, user lists, etc) + - ./data/config:/config + + # Completed downloads - CHANGE TO YOUR PATH + - /path/to/your/downloads:/downloads + + # Incomplete downloads - CHANGE TO YOUR PATH + - /path/to/your/incomplete:/incomplete + + # Optional: additional shared folders - CHANGE TO YOUR PATHS + # - /path/to/your/music:/shares/music:ro + # - /path/to/your/other:/shares/other:ro + + # Resource limits (optional, useful for Raspberry Pi) + deploy: + resources: + limits: + memory: 1G + cpus: '2' + reservations: + memory: 256M + + # Network + network_mode: bridge + + # Privileged mode not required + privileged: false + + # Healthcheck + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:6080"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..fdf921d --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +# Update PUID and PGID if needed +if [ ! -z "$PUID" ] && [ "$PUID" != "1000" ]; then + usermod -u $PUID nicotine +fi + +if [ ! -z "$PGID" ] && [ "$PGID" != "1000" ]; then + groupmod -g $PGID nicotine +fi + +# Fix permissions on volumes +chown -R nicotine:nicotine /config /downloads /incomplete 2>/dev/null || true + +# Create necessary directories +mkdir -p /home/nicotine/.local/share/nicotine +chown -R nicotine:nicotine /home/nicotine/.local + +# Create symlink from Nicotine+ config to persistent volume +if [ ! -L "/home/nicotine/.config/nicotine" ]; then + rm -rf /home/nicotine/.config/nicotine + ln -s /config /home/nicotine/.config/nicotine +fi + +# Configure VNC password if specified +if [ ! -z "$VNC_PASSWORD" ]; then + mkdir -p /home/nicotine/.vnc + echo "$VNC_PASSWORD" | vncpasswd -f > /home/nicotine/.vnc/passwd + chmod 600 /home/nicotine/.vnc/passwd + chown nicotine:nicotine /home/nicotine/.vnc/passwd +fi + +# Export environment variables for supervisord +export DISPLAY +export VNC_PORT +export NOVNC_PORT +export VNC_RESOLUTION +export VNC_DEPTH +export USER + +# Start supervisord as root (which will start processes as nicotine user) +exec "$@" diff --git a/index.html b/index.html new file mode 100644 index 0000000..82fe46a --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + Nicotine+ noVNC + + +

Redirecting to noVNC with remote resize enabled...

+

If not redirected automatically, click here.

+ + diff --git a/openbox-autostart.sh b/openbox-autostart.sh new file mode 100755 index 0000000..8bf89a9 --- /dev/null +++ b/openbox-autostart.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# Avvia Nicotine+ automaticamente +sleep 2 +nicotine & diff --git a/openbox-rc.xml b/openbox-rc.xml new file mode 100644 index 0000000..31641d8 --- /dev/null +++ b/openbox-rc.xml @@ -0,0 +1,50 @@ + + + + + 1 + + 1 + + Desktop + + 0 + + + + 8 + 500 + 0 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + C-g + + diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..4972481 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,43 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:xvnc] +command=/usr/bin/Xvnc :0 -geometry %(ENV_VNC_RESOLUTION)s -depth %(ENV_VNC_DEPTH)s -SecurityTypes None -rfbport %(ENV_VNC_PORT)s -AcceptSetDesktopSize=1 +user=%(ENV_USER)s +autostart=true +autorestart=true +priority=10 +stdout_logfile=/var/log/supervisor/xvnc.log +stderr_logfile=/var/log/supervisor/xvnc_err.log + +[program:openbox] +command=/usr/bin/openbox +environment=DISPLAY=":0" +user=%(ENV_USER)s +autostart=true +autorestart=true +priority=20 +stdout_logfile=/var/log/supervisor/openbox.log +stderr_logfile=/var/log/supervisor/openbox_err.log + +[program:novnc] +command=/usr/share/novnc/utils/novnc_proxy --vnc localhost:%(ENV_VNC_PORT)s --listen %(ENV_NOVNC_PORT)s +user=%(ENV_USER)s +autostart=true +autorestart=true +priority=30 +stdout_logfile=/var/log/supervisor/novnc.log +stderr_logfile=/var/log/supervisor/novnc_err.log + +[program:nicotine] +command=/usr/local/bin/nicotine +environment=DISPLAY=":0",HOME="/home/%(ENV_USER)s",XDG_CONFIG_HOME="/home/%(ENV_USER)s/.config",XDG_DATA_HOME="/home/%(ENV_USER)s/.local/share" +user=%(ENV_USER)s +autostart=true +autorestart=true +priority=40 +stdout_logfile=/var/log/supervisor/nicotine.log +stderr_logfile=/var/log/supervisor/nicotine_err.log