From 51c37fe6746775156cda440398c202070489fce2 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 07:50:38 +0200 Subject: [PATCH 01/24] Add GitHub Actions workflows for building and publishing Docker images --- .github/workflows/build.yaml | 57 ++++++++++++++++++++++++++++++++++ .github/workflows/package.yaml | 37 ++++++++++++++++++++++ .github/workflows/publish.yaml | 29 +++++++++++++++++ docker/Dockerfile | 20 ++++++++++++ package.json | 1 + 5 files changed, 144 insertions(+) create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/package.yaml create mode 100644 .github/workflows/publish.yaml create mode 100644 docker/Dockerfile diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..8734c46 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,57 @@ +name: Build Client and Server + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Install client dependencies + run: | + cd client + npm ci + + - name: Build client + run: | + cd client + npm run build + + - name: Install server dependencies + run: | + cd server + npm ci + + - name: Build server (if applicable) + run: | + cd server + # Add build command if needed, e.g., npm run build + echo "No build step for server" + + - name: Upload client build artifact + uses: actions/upload-artifact@v3 + with: + name: client-build + path: client/build + + - name: Upload server artifact + uses: actions/upload-artifact@v3 + with: + name: server + path: server diff --git a/.github/workflows/package.yaml b/.github/workflows/package.yaml new file mode 100644 index 0000000..e54b0cf --- /dev/null +++ b/.github/workflows/package.yaml @@ -0,0 +1,37 @@ +name: Docker Package + +description: | + This workflow builds a Docker image containing both the client build and the server, then stores the image as an artifact. + +runs: + using: "docker" + image: "docker/Dockerfile" + +inputs: + tag: + description: "Tag for the Docker image" + required: false + default: "latest" + +outputs: + image: + description: "Built Docker image" + value: ${{ steps.build.outputs.image }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Build Docker image + run: | + docker build -t subarr:${{ inputs.tag }} -f docker/Dockerfile . + - name: Save Docker image as artifact + run: | + docker save subarr:${{ inputs.tag }} | gzip > subarr-image.tar.gz + - name: Upload Docker image artifact + uses: actions/upload-artifact@v3 + with: + name: subarr-docker-image + path: subarr-image.tar.gz diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..8dff0f5 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,29 @@ +name: Publish Docker Image + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Docker image + run: | + docker build -t ghcr.io/${{ github.repository }}/subarr:latest -f docker/Dockerfile . + + - name: Push Docker image + run: | + docker push ghcr.io/${{ github.repository }}/subarr:latest diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..3e79d87 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,20 @@ +FROM node:latest + +WORKDIR /app +# Copy server files +COPY server/package.json server/package-lock.json ./server/ +COPY server/ ./server/ + +# Copy client build +COPY client/build ./client/build + +# Install server dependencies +WORKDIR /app/server +RUN npm ci + +# Expose port (change if your server uses a different port) +EXPOSE 3001 + +# Start the server +CMD ["node", "index.js"] + diff --git a/package.json b/package.json index 9602d7c..dd2c008 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "server" ], "scripts": { + "build": "npm --workspace client run build", "start-server": "npm --workspace client run build && NODE_ENV=production node server/index.js", "start-server-win": "npm --workspace client run build && SET NODE_ENV=production && node server/index.js", "update": "git pull && npm install && npm run start-server" From 9cbccad340d96dc62ce9ed9d54328d11861b2991 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 07:51:54 +0200 Subject: [PATCH 02/24] Update upload-artifact action to version 4 in Docker package workflow --- .github/workflows/package.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package.yaml b/.github/workflows/package.yaml index e54b0cf..570c770 100644 --- a/.github/workflows/package.yaml +++ b/.github/workflows/package.yaml @@ -31,7 +31,7 @@ jobs: run: | docker save subarr:${{ inputs.tag }} | gzip > subarr-image.tar.gz - name: Upload Docker image artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: subarr-docker-image path: subarr-image.tar.gz From ca71329788022f91cebb07f5d21b45b650f53453 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 07:52:44 +0200 Subject: [PATCH 03/24] Update upload-artifact action to version 4 in build workflow --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8734c46..4129945 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,13 +45,13 @@ jobs: echo "No build step for server" - name: Upload client build artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: client-build path: client/build - name: Upload server artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: server path: server From 3918fec1136991b271885691ba69c62094bc5fa1 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 07:57:13 +0200 Subject: [PATCH 04/24] Add caching for client dependencies and remove obsolete Docker package workflow --- .github/workflows/build.yaml | 4 +++- .github/workflows/package.yaml | 37 ---------------------------------- 2 files changed, 3 insertions(+), 38 deletions(-) delete mode 100644 .github/workflows/package.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4129945..7010885 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -27,7 +27,9 @@ jobs: run: | cd client npm ci - + cache: + paths: + - client/node_modules - name: Build client run: | cd client diff --git a/.github/workflows/package.yaml b/.github/workflows/package.yaml deleted file mode 100644 index 570c770..0000000 --- a/.github/workflows/package.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Docker Package - -description: | - This workflow builds a Docker image containing both the client build and the server, then stores the image as an artifact. - -runs: - using: "docker" - image: "docker/Dockerfile" - -inputs: - tag: - description: "Tag for the Docker image" - required: false - default: "latest" - -outputs: - image: - description: "Built Docker image" - value: ${{ steps.build.outputs.image }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - name: Build Docker image - run: | - docker build -t subarr:${{ inputs.tag }} -f docker/Dockerfile . - - name: Save Docker image as artifact - run: | - docker save subarr:${{ inputs.tag }} | gzip > subarr-image.tar.gz - - name: Upload Docker image artifact - uses: actions/upload-artifact@v4 - with: - name: subarr-docker-image - path: subarr-image.tar.gz From 6efa38d24b49fc55a2dc3d00dafbc69fc7dda368 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 07:58:36 +0200 Subject: [PATCH 05/24] Refactor Node.js setup to enable caching for client dependencies --- .github/workflows/build.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7010885..43f1bce 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,14 +22,13 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: "npm" + cache-dependency-path: client/package-lock.json - name: Install client dependencies run: | cd client npm ci - cache: - paths: - - client/node_modules - name: Build client run: | cd client From ab74585bb2bf24569d87fc3446f75e345789e4a9 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:02:06 +0200 Subject: [PATCH 06/24] Add Docker image build and push steps to build workflow; remove obsolete publish workflow --- .github/workflows/build.yaml | 18 ++++++++++++++++++ .github/workflows/publish.yaml | 29 ----------------------------- 2 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 .github/workflows/publish.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 43f1bce..7a1edd9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -56,3 +56,21 @@ jobs: with: name: server path: server + + - name: Log in to GitHub Container Registry + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Docker image + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + run: | + docker build -t ghcr.io/${{ github.repository }}/subarr:latest -f docker/Dockerfile . + + - name: Push Docker image + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + run: | + docker push ghcr.io/${{ github.repository }}/subarr:latest diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index 8dff0f5..0000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: Publish Docker Image - -on: - push: - branches: - - master - workflow_dispatch: - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Docker image - run: | - docker build -t ghcr.io/${{ github.repository }}/subarr:latest -f docker/Dockerfile . - - - name: Push Docker image - run: | - docker push ghcr.io/${{ github.repository }}/subarr:latest From f8804bec11608266809234ad7ea4f002b15a8f52 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:05:32 +0200 Subject: [PATCH 07/24] Remove npm ci command from Dockerfile to streamline server dependency installation --- docker/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3e79d87..0fedff0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,7 +10,6 @@ COPY client/build ./client/build # Install server dependencies WORKDIR /app/server -RUN npm ci # Expose port (change if your server uses a different port) EXPOSE 3001 From 3abd7138bf54d10c86f5df3fa4da25c1bb0def79 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:06:26 +0200 Subject: [PATCH 08/24] Remove artifact upload steps for client and server from build workflow --- .github/workflows/build.yaml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7a1edd9..b292be5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,18 +45,6 @@ jobs: # Add build command if needed, e.g., npm run build echo "No build step for server" - - name: Upload client build artifact - uses: actions/upload-artifact@v4 - with: - name: client-build - path: client/build - - - name: Upload server artifact - uses: actions/upload-artifact@v4 - with: - name: server - path: server - - name: Log in to GitHub Container Registry if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: docker/login-action@v3 From 1df338fe19d2a202d94bbb876390f2fbe3a779c3 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:08:52 +0200 Subject: [PATCH 09/24] Update cache-dependency-path to include server package-lock.json --- .github/workflows/build.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b292be5..21c8f97 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -23,7 +23,9 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: "npm" - cache-dependency-path: client/package-lock.json + cache-dependency-path: | + client/package-lock.json + server/package-lock.json - name: Install client dependencies run: | From 8f34ad947c36410f440c4314be9499fe81c66862 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:12:15 +0200 Subject: [PATCH 10/24] Add NODE_ENV environment variable to Dockerfile --- docker/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0fedff0..cb5cf9d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,8 @@ FROM node:latest +# Set NODE_ENV to production +ENV NODE_ENV=production + WORKDIR /app # Copy server files COPY server/package.json server/package-lock.json ./server/ From c245dd905f3c3ffa1fc583fb6f4f3129c1af942f Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:42:03 +0200 Subject: [PATCH 11/24] Remove server dependency installation and build steps from workflow --- .github/workflows/build.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 21c8f97..fcbcec3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -36,17 +36,6 @@ jobs: cd client npm run build - - name: Install server dependencies - run: | - cd server - npm ci - - - name: Build server (if applicable) - run: | - cd server - # Add build command if needed, e.g., npm run build - echo "No build step for server" - - name: Log in to GitHub Container Registry if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: docker/login-action@v3 From a06094546d1beb56cafe6bae8701c0ddb799eb43 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 08:42:17 +0200 Subject: [PATCH 12/24] Refactor Dockerfile to separate build and runtime stages, ensuring proper installation of dependencies and setting NODE_ENV --- docker/Dockerfile | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index cb5cf9d..194f4f2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,22 +1,31 @@ -FROM node:latest +FROM node:latest AS builder -# Set NODE_ENV to production -ENV NODE_ENV=production +# Install build dependencies for native modules +RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/* WORKDIR /app -# Copy server files -COPY server/package.json server/package-lock.json ./server/ -COPY server/ ./server/ +COPY server/package.json ./server/ +WORKDIR /app/server +RUN npm install && npm rebuild -# Copy client build +FROM node:latest AS runtime + +# Install build dependencies for native modules +RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/* + +ENV NODE_ENV=production +WORKDIR /app + +# Copy built node_modules from builder stage +COPY --from=builder /app/server/node_modules ./server/node_modules +COPY server/ ./server/ COPY client/build ./client/build -# Install server dependencies WORKDIR /app/server -# Expose port (change if your server uses a different port) -EXPOSE 3001 +# Rebuild native modules for the runtime environment +RUN npm rebuild -# Start the server +EXPOSE 3001 CMD ["node", "index.js"] From efae50140275f84cb9c6e804ba5dacd584eca0cb Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 09:26:51 +0200 Subject: [PATCH 13/24] Refactor Dockerfile to use Alpine image and update package installation commands for improved efficiency --- docker/Dockerfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 194f4f2..74bd224 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,17 +1,18 @@ -FROM node:latest AS builder +FROM node:alpine AS builder # Install build dependencies for native modules -RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/* +RUN apk add --no-cache python3 make g++ py3-pip WORKDIR /app COPY server/package.json ./server/ WORKDIR /app/server RUN npm install && npm rebuild -FROM node:latest AS runtime +FROM node:alpine AS runtime -# Install build dependencies for native modules -RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/* +# Install build dependencies for native modules and yt-dlp +RUN apk add --no-cache python3 make g++ py3-pip && \ + python3 -m pip install --break-system-packages -U "yt-dlp[default]" ENV NODE_ENV=production WORKDIR /app From 954597d99c504143d1ff0e8225d1b21094f583f5 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 09:56:52 +0200 Subject: [PATCH 14/24] Add data directory to .gitignore to exclude local data files from version control --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d65c795..d5d7a33 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,5 @@ Thumbs.db **/*.crt **/*.key **/secrets.json + +data/* \ No newline at end of file From b09b6702cab78e95a352f2084b0d252b74125dc2 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:16:59 +0200 Subject: [PATCH 15/24] Refactor database path resolution to use a dedicated data directory for improved organization --- server/db.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/db.js b/server/db.js index d4323cb..59a2a07 100644 --- a/server/db.js +++ b/server/db.js @@ -3,9 +3,11 @@ const path = require('path'); const Database = require('better-sqlite3'); // Always use subarr.db from the server folder (and support youtubarr.db if it still exists from before the app rename) -const dbPath = fs.existsSync(path.join(__dirname, 'youtubarr.db')) - ? path.join(__dirname, 'youtubarr.db') - : path.join(__dirname, 'subarr.db'); +const dir = path.resolve("/data/db"); + +const dbPath = fs.existsSync(path.join(dir, 'youtubarr.db')) + ? path.join(dir, 'youtubarr.db') + : path.join(dir, 'subarr.db'); const db = new Database(dbPath); From ce2b07002a59e3a88bf91517ae34689b810dc229 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:20:14 +0200 Subject: [PATCH 16/24] Add Docker usage instructions to README for building and running the container --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/README.md b/README.md index c4526b9..f461814 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,44 @@ npm run start-server # On Windows, use 'npm run start-server-win' And if you'd like to set it to run at startup, put that last command into a .service file (Linux) or your Startup folder (Windows). +--- + +## Docker Usage + +### Build the Docker image + +```bash +docker build -t subarr -f docker/Dockerfile . +``` + +### Run the container + +```bash +docker run -p 3001:3001 subarr +``` + +### Mount a data directory for persistent storage + +```bash +docker run -p 3001:3001 -v /path/to/host/data:/data subarr +``` +This will store all database and downloaded files in `/path/to/host/data` on your host. + +### Set environment variables + +You can pass environment variables (such as API keys, config, etc) using the `-e` flag or a `.env` file: + +```bash +docker run -p 3001:3001 -v /path/to/host/data:/data -e NODE_ENV=production subarr +``` + +Or with a custom `.env` file: +```bash +docker run -p 3001:3001 -v /path/to/host/data:/data --env-file /path/to/.env subarr +``` + +--- + ### Plans for future maintenance I am currently just building this as a hobby project for myself and I already have about 10x the amount of hobby projects that I can handle. I'll probably fix some bugs or maintenance issues as they arise, but I don't plan to work on any major features. If you'd like to contribute, please reach out! From e077f763b8605acc4514b01614cc27eced21f413 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:22:16 +0200 Subject: [PATCH 17/24] Refactor Dockerfile to remove yt-dlp installation, simplifying the runtime stage --- docker/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 74bd224..f83bc68 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,8 +11,7 @@ RUN npm install && npm rebuild FROM node:alpine AS runtime # Install build dependencies for native modules and yt-dlp -RUN apk add --no-cache python3 make g++ py3-pip && \ - python3 -m pip install --break-system-packages -U "yt-dlp[default]" +RUN apk add --no-cache python3 make g++ ENV NODE_ENV=production WORKDIR /app From a573d4ef82253131f2e013b4388cb520a7dd5db1 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:24:34 +0200 Subject: [PATCH 18/24] Add warning against mounting NFS storage in Docker usage section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f461814..bb7fa5a 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ And if you'd like to set it to run at startup, put that last command into a .ser --- ## Docker Usage +> Do not mount nfs storage! The application uses sqllite. ### Build the Docker image From 6e9d1c3c09f64d69ee615dc728ddb21fe0b16613 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:28:45 +0200 Subject: [PATCH 19/24] Create data directories for database and configuration in Dockerfile --- docker/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index f83bc68..cab5292 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -26,6 +26,8 @@ WORKDIR /app/server # Rebuild native modules for the runtime environment RUN npm rebuild +RUN mkdir -p /data/db && mkdir -p /data/config + EXPOSE 3001 CMD ["node", "index.js"] From dc25fd9b5523f9dfa3ed7568a96975b71a198b9b Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:55:00 +0200 Subject: [PATCH 20/24] Update README to clarify data directory path for persistent storage --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb7fa5a..a7d54f0 100644 --- a/README.md +++ b/README.md @@ -113,9 +113,9 @@ docker run -p 3001:3001 subarr ### Mount a data directory for persistent storage ```bash -docker run -p 3001:3001 -v /path/to/host/data:/data subarr +docker run -p 3001:3001 -v /path/to/host/data:/data/db subarr ``` -This will store all database and downloaded files in `/path/to/host/data` on your host. +This will store all database in `/path/to/host/data/data` on your host. ### Set environment variables From c74c1b86327f9cd95e2492f09cd29d8a6354252c Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 12:59:17 +0200 Subject: [PATCH 21/24] Add Docker Compose example to README for easier setup --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index a7d54f0..8e94eb9 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ And if you'd like to set it to run at startup, put that last command into a .ser ## Docker Usage > Do not mount nfs storage! The application uses sqllite. + + ### Build the Docker image ```bash @@ -132,6 +134,38 @@ docker run -p 3001:3001 -v /path/to/host/data:/data --env-file /path/to/.env sub --- +### Docker Compose Example + +Create a `docker-compose.yml` file in your project directory: + +```yaml +services: + subarr: + image: ghcr.io/dnlrsr/subarr/subarr:latest # Todo: Change after merge + ports: + - "3001:3001" + volumes: + - db-data:/data/db + env_file: + - .env + +db-data: + driver: local # Or what ever +``` + +Then start with: +```bash +docker compose up --build +``` + +This will: +- Build and run the Subarr container +- Mount the local `./data` folder to `/data` in the container for persistence +- Load environment variables from `.env` +- Expose port 3001 + +--- + ### Plans for future maintenance I am currently just building this as a hobby project for myself and I already have about 10x the amount of hobby projects that I can handle. I'll probably fix some bugs or maintenance issues as they arise, but I don't plan to work on any major features. If you'd like to contribute, please reach out! From 129e6e2958993ed3f1e0f924cd142a48dd82a525 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Thu, 4 Sep 2025 15:23:56 +0200 Subject: [PATCH 22/24] Set user to non-root for improved security in Dockerfile --- docker/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index cab5292..4abace2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -28,6 +28,8 @@ RUN npm rebuild RUN mkdir -p /data/db && mkdir -p /data/config +USER 1000:1000 + EXPOSE 3001 CMD ["node", "index.js"] From 502d8a4a38cd94d364387397dd0c0a49e74cdcf7 Mon Sep 17 00:00:00 2001 From: Dani <5970108+dnlrsr@users.noreply.github.com> Date: Fri, 5 Sep 2025 07:10:35 +0200 Subject: [PATCH 23/24] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8e94eb9..2d84fd3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Subarr +### This is a fork of [derekantrican/subarr](https://github.com/derekantrican/subarr) . I will continue implement features when they fit my needs. + *After [a lot of feedback](https://reddit.com/r/selfhosted/comments/1myldh3/i_built_youtubarr_the_sonarr_for_youtube/nacw3am/), I've decided to rename this project from "YouTubarr" to "Subarr". The name "Subarr" also helps define the project a little clearer in how it's based on RSS subscriptions (intended to be a "subscribe to playlists/channels & take action on new uploads") rather than a full PVR & media management system. If you're looking for a more true "Sonarr for YouTube", I recommend checking out one of the solutions below.* Subarr is a **lightweight** YouTube channel/playlist/subscription follower that will take action on new uploads (actions can be a webhook - like Discord - or a process - like downloading through yt-dlp). "Lightweight" means it needs few resources and can run on something like a raspberry pi. From dafc7b1b5eac7d386f1b823cb9eab5c1215948b2 Mon Sep 17 00:00:00 2001 From: Dnl Rsr Date: Fri, 5 Sep 2025 07:31:55 +0200 Subject: [PATCH 24/24] Revise README for clarity and structure; enhance project description and add Docker usage details --- README.md | 203 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 2d84fd3..8b97b1e 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,49 @@ -# Subarr +
-### This is a fork of [derekantrican/subarr](https://github.com/derekantrican/subarr) . I will continue implement features when they fit my needs. +# 🎎 Subarr -*After [a lot of feedback](https://reddit.com/r/selfhosted/comments/1myldh3/i_built_youtubarr_the_sonarr_for_youtube/nacw3am/), I've decided to rename this project from "YouTubarr" to "Subarr". The name "Subarr" also helps define the project a little clearer in how it's based on RSS subscriptions (intended to be a "subscribe to playlists/channels & take action on new uploads") rather than a full PVR & media management system. If you're looking for a more true "Sonarr for YouTube", I recommend checking out one of the solutions below.* +**A lightweight YouTube channel/playlist subscription manager** -Subarr is a **lightweight** YouTube channel/playlist/subscription follower that will take action on new uploads (actions can be a webhook - like Discord - or a process - like downloading through yt-dlp). "Lightweight" means it needs few resources and can run on something like a raspberry pi. +[![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white)](https://github.com/dnlrsr/subarr/pkgs/container/subarr%2Fsubarr) +[![Node.js](https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)](https://nodejs.org/) +[![License](https://img.shields.io/github/license/dnlrsr/subarr?style=for-the-badge)](LICENSE) +[![Stars](https://img.shields.io/github/stars/dnlrsr/subarr?style=for-the-badge)](https://github.com/dnlrsr/subarr/stargazers) -image +
+--- + +> ### 🔗 **Fork Notice** +> +> This is a fork of [derekantrican/subarr](https://github.com/derekantrican/subarr). I will continue to implement features when they fit my needs. + +--- + +## 📖 Overview + +*After [a lot of feedback](https://reddit.com/r/selfhosted/comments/1myldh3/i_built_youtubarr_the_sonarr_for_youtube/nacw3am/), the original project was renamed from "YouTubarr" to "Subarr". The name "Subarr" helps define the project clearer - it's based on RSS subscriptions (intended to be a "subscribe to playlists/channels & take action on new uploads") rather than a full PVR & media management system.* + +Subarr is a **ðŸŠķ lightweight** YouTube channel/playlist/subscription follower that will take action on new uploads (actions can be a webhook - like Discord). + +**"Lightweight"** means it needs few resources and can run on something like a Raspberry Pi. + +
+ +![Subarr Interface](https://github.com/user-attachments/assets/dd9b42d8-08e9-4d9a-a175-acf7219d059a) -### Background +*Clean, Sonarr-inspired interface for managing your YouTube subscriptions* -This is an attempt to create a Sonarr-like application for YouTube videos. A lot of inspiration has been taken from Sonarr (mostly the UI), but this has been written entirely from scratch. +
-_Why not use one of the existing solutions?_ +--- + +## ðŸŽŊ Background + +This is an attempt to create a **Sonarr-like application for YouTube videos**. A lot of inspiration has been taken from Sonarr (mostly the UI), but this has been written entirely from scratch. -Here are all the similar things I could find and how this is different: +### ðŸĪ” Why not use one of the existing solutions? + +Here are all the similar projects and how Subarr is different: | Name | Active? | Stars | Indexer | Comment | |----------------------|---------|-------|---------|---------| | [Tube Archivist](https://github.com/tubearchivist/tubearchivist) | ✅ | ![](https://img.shields.io/github/stars/tubearchivist/tubearchivist?label=&style=flat-square&color=white) | yt-dlp | Based on yt-dlp | @@ -32,118 +60,129 @@ Here are all the similar things I could find and how this is different: | [Tubarr (Capstone Project)](https://vc.bridgew.edu/cgi/viewcontent.cgi?article=1691&context=honors_proj) | ❌ | N/A | yt-dlp | Private (a student's capstone project) | -As you can see, there's a few other solutions, but most are based on [yt-dlp](https://github.com/yt-dlp/yt-dlp) for getting playlist information. This works fine if you want to index an _entire playlist_ (which a user may, very well, want to do) but it can require a LOT of polling activity (particularly for large channels). _Tube Archivist above even calls out 2-4GB of memory dedicated to its functionality._ **This project is lightweight and can easily run on a raspberry pi or similar.** Additionally, none of the above services can automatically keep your actual subscriptions on YouTube in sync with the app. +As you can see, there are several other solutions, but most are based on [yt-dlp](https://github.com/yt-dlp/yt-dlp) for getting playlist information. This works fine if you want to index an _entire playlist_ (which users may want to do) but it can require **a LOT of polling activity** (particularly for large channels). + +> 📊 _Tube Archivist above even calls out 2-4GB of memory dedicated to its functionality._ -Sonarr is based on RSS feeds - explicitly designed for this purpose of getting new updates from subscription-like sources. This is much lighter in processing requirements. I've also tried to make this UI as similar as possible to the other *arr apps for familiarity. +**🚀 This project is lightweight and can easily run on a Raspberry Pi or similar.** Additionally, none of the above services can automatically keep your actual subscriptions on YouTube in sync with the app. -_What are the limitations of the RSS feed approach?_ +Sonarr is based on **RSS feeds** - explicitly designed for this purpose of getting new updates from subscription-like sources. This is much lighter in processing requirements. The UI has been made as similar as possible to the other *arr apps for familiarity. -YouTube already provides RSS feeds for playlists (eg https://www.youtube.com/feeds/videos.xml?playlist_id=PLopY4n17t8RDoFQPcjBKWDEblGH1sOH2h). However, they can be severly limited: +### ⚠ïļ What are the limitations of the RSS feed approach? -- Feeds seem to be limited to only the last 15 items +YouTube already provides RSS feeds for playlists (e.g., `https://www.youtube.com/feeds/videos.xml?playlist_id=PLopY4n17t8RDoFQPcjBKWDEblGH1sOH2h`). However, they can be severely limited: + +- **📊 Limited to 15 items**: Feeds seem to be limited to only the last 15 items - _However, Subarr will list more items as they are found because the internal database will be updated_ - - Since the feed is limited to only the last 15 items, if (for some reason) Subarr is down for an extended period of time (or a large amount of videos are published to the playlist in a short period of time), Subarr may miss some videos entirely. -- YouTube RSS feed items are always in "playlist order", meaning a new video added outside of the first 15 items will not be seen as an update to the RSS feed - - This means that, currently, RSS feeds _**will not work**_ in the following situations: (issue logged with YouTube: https://issuetracker.google.com/issues/429563457) - - YouTube playlists greater than 15 items where items are added outside the "top 15" (regular playlists can be in _any order_ - as determined by the playlist owner - which means that "newly added items" aren't necessarily at the top. Some creators' playlists are ordered oldest -> newest) - - \>=15 items added to a playlist quickly (eg if a playlist has 1 item, then when it updates 15 minutes later it has 20 items, 4 of those items will be missed because only the top 15 will be in the RSS feed) -- RSS feeds may be somewhat slow to update (updates are approximately every 15 minutes). _15 minutes is pretty fast, but other methods (like the direct YouTube API - or perhaps even TubeSync's yt-dl method) could check for new videos even faster_ -- _[May not be a big deal]_ RSS feeds will not include "Members Only" items + - If Subarr is down for an extended period or many videos are published quickly, some videos may be missed entirely -However, this works perfectly fine for mine (and maybe other people's) needs. +- **🔄 Playlist order issues**: YouTube RSS feed items are always in "playlist order", meaning a new video added outside of the first 15 items will not be seen as an update + - This means RSS feeds **will not work** in these situations: ([issue logged with YouTube](https://issuetracker.google.com/issues/429563457)) + - YouTube playlists >15 items where items are added outside the "top 15" + - â‰Ĩ15 items added to a playlist quickly +- **⏰ Update delays**: RSS feeds may be somewhat slow to update (~15 minutes) -### Notes +- **ðŸ‘Ĩ Members-only content**: RSS feeds will not include "Members Only" items -⚠ïļ **Subarr currently does not implement any sort of authetication. It is highly recommended that you do not expose your instance to the internet (or, at least, put it behind a form of authentication like nginx or Cloudflare)** ⚠ïļ +**However, this works perfectly fine for our needs and many others' use cases.** -Subarr is NOT intended to do the following: -- Index an entire channel/playlist or get "older" videos. Subarr's RSS approach is specifically for "subscriptions": new video is posted, take some action -- Media management. Once Subarr kicks off the post-processor (like yt-dlp), its job is done. Use Plex/Jellyfin/etc or another one of the linked solutions above if you require more control over your media +--- +## 📋 Important Notes -### Current features +🔐 **Security Notice**: Subarr currently does not implement any sort of authentication. It is **highly recommended** that you do not expose your instance to the internet (or, at least, put it behind a form of authentication like nginx or Cloudflare). -- Add playlists -- Limit playlist items by regular expression -- Exclude shorts -- [ytsubs.app](https://github.com/derekantrican/ytsubs) integration to import user's YouTube subscriptions and keep them in sync -- Post processors (an action to run when a new video is found) - - Webhook: call a webhook (eg Discord, Raindrop.io, etc) - - Process: execute a process (eg yt-dlp to download the video) +### ðŸšŦ What Subarr is NOT intended for: -### Future features +- **📚 Indexing entire channels/playlists** or getting "older" videos. Subarr's RSS approach is specifically for "subscriptions": new video is posted → take some action +- **🎎 Media management**. Once Subarr triggers the post-processor (webhook), its job is done. Use Plex/Jellyfin/etc or another solution above if you require more control over your media -- API key for authenticating calls to the server -- It would be neat to have some sort of web socket or other real-time communication between the server & client that will do things like updating the "last checked" or video list on the UI -- Native "url base" support like sonarr (for reverse proxies or cloudflare tunnels) -- Backup & Restore functionality (_should_ be pretty easy by just giving a copy of the sqlite db?) -- _Index more than 15 items initially? (this would require significant up-front processing power like TubeSync does - so...unlikely)_ +--- +## âœĻ Current Features -### Installation +- 📝 **Add playlists** - Subscribe to YouTube playlists and channels +- ðŸŽŊ **Regex filtering** - Limit playlist items by regular expression +- ðŸšŦ **Exclude shorts** - Filter out YouTube Shorts automatically +- 🔗 **[ytsubs.app](https://github.com/derekantrican/ytsubs) integration** - Import user's YouTube subscriptions and keep them in sync +- ⚡ **Post processors** - Actions to run when a new video is found: + - 🊝 **Webhook**: Call a webhook (e.g., Discord, Raindrop.io, etc.) -Make sure you have Node >= 18 installed, then run the following: +## 🚀 Future Features -``` -git clone https://github.com/derekantrican/subarr.git +- 🔐 **API key authentication** for secure server calls +- 🔄 **Real-time updates** via WebSocket communication between server & client +- 🌐 **Native URL base support** like Sonarr (for reverse proxies or Cloudflare tunnels) +- ðŸ’ū **Backup & Restore functionality** (should be easy with SQLite database) +- 📊 _Index more than 15 items initially?_ (would require significant processing power like TubeSync - unlikely) + +--- + +## 🛠ïļ Installation + +### Prerequisites +- **Node.js** >= 18 + +### Quick Start + +```bash +git clone https://github.com/dnlrsr/subarr.git cd subarr npm install -# optionally create server/.env with PORT=5000 or whatever +# Optionally create server/.env with PORT=5000 or whatever npm run start-server # On Windows, use 'npm run start-server-win' ``` -And if you'd like to set it to run at startup, put that last command into a .service file (Linux) or your Startup folder (Windows). +### 🔄 Run at Startup +To set it to run at startup, put the last command into: +- **Linux**: A `.service` file +- **Windows**: Your Startup folder --- -## Docker Usage -> Do not mount nfs storage! The application uses sqllite. - - +## ðŸģ Docker Usage +⚠ïļ **Important**: Do not mount NFS storage! The application uses SQLite. -### Build the Docker image +### 🏗ïļ Build the Docker Image ```bash docker build -t subarr -f docker/Dockerfile . ``` -### Run the container +### 🚀 Run the Container ```bash docker run -p 3001:3001 subarr ``` -### Mount a data directory for persistent storage +### ðŸ’ū Mount Data Directory for Persistent Storage ```bash docker run -p 3001:3001 -v /path/to/host/data:/data/db subarr ``` -This will store all database in `/path/to/host/data/data` on your host. +This stores all database files in `/path/to/host/data/data` on your host. -### Set environment variables - -You can pass environment variables (such as API keys, config, etc) using the `-e` flag or a `.env` file: +### 🔧 Set Environment Variables +Using the `-e` flag: ```bash docker run -p 3001:3001 -v /path/to/host/data:/data -e NODE_ENV=production subarr ``` -Or with a custom `.env` file: +Using a custom `.env` file: ```bash docker run -p 3001:3001 -v /path/to/host/data:/data --env-file /path/to/.env subarr ``` ---- - -### Docker Compose Example +### 🐙 Docker Compose Example Create a `docker-compose.yml` file in your project directory: ```yaml services: subarr: - image: ghcr.io/dnlrsr/subarr/subarr:latest # Todo: Change after merge + image: ghcr.io/dnlrsr/subarr/subarr:latest # TODO: Change after merge ports: - "3001:3001" volumes: @@ -151,23 +190,41 @@ services: env_file: - .env -db-data: - driver: local # Or what ever +volumes: + db-data: + driver: local ``` -Then start with: +Start with: ```bash docker compose up --build ``` -This will: -- Build and run the Subarr container -- Mount the local `./data` folder to `/data` in the container for persistence -- Load environment variables from `.env` -- Expose port 3001 +**This will:** +- 🏗ïļ Build and run the Subarr container +- ðŸ’ū Mount the local `./data` folder to `/data` in the container for persistence +- 🔧 Load environment variables from `.env` +- 🌐 Expose port 3001 --- -### Plans for future maintenance +## ðŸ”Ū Plans for Future Maintenance + +I am currently building this as a **hobby project** for myself and I already have about 10x the amount of hobby projects that I can handle. I'll probably fix some bugs or maintenance issues as they arise, but I don't plan to work on any major features. + +**ðŸĪ Want to contribute?** Please reach out! + +--- + +
+ +## 💖 Support + +If you find Subarr useful, consider: + +⭐ **Starring this repository** +🐛 **Reporting bugs** +ðŸ’Ą **Suggesting features** +🔧 **Contributing code** -I am currently just building this as a hobby project for myself and I already have about 10x the amount of hobby projects that I can handle. I'll probably fix some bugs or maintenance issues as they arise, but I don't plan to work on any major features. If you'd like to contribute, please reach out! +