From 1471d58385c239be7e77c17b32262c378a064ac2 Mon Sep 17 00:00:00 2001 From: emergent-agent-e1 Date: Thu, 23 Jul 2026 22:01:21 +0000 Subject: [PATCH] auto-commit for 28f1e986-c2af-4742-ad64-e0661980843c --- .emergent/emergent.yml | 2 +- .gitignore | 1 + DEPLOY.md | 189 +++++++++++++++++++++++++++++++++++++ README.md | 5 + backend/Dockerfile | 23 +++++ docker-compose.yml | 71 ++++++++++++++ frontend/Dockerfile | 29 ++++++ frontend/nginx.conf | 45 +++++++++ frontend/public/index.html | 47 +-------- 9 files changed, 365 insertions(+), 47 deletions(-) create mode 100644 DEPLOY.md create mode 100644 backend/Dockerfile create mode 100644 docker-compose.yml create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf diff --git a/.emergent/emergent.yml b/.emergent/emergent.yml index 6fca254..079d728 100644 --- a/.emergent/emergent.yml +++ b/.emergent/emergent.yml @@ -1,5 +1,5 @@ { "env_image_name": "fastapi_react_mongo_shadcn_base_image_cloud_arm:release-17042026-1", "job_id": "08d9a7a1-2a0c-4502-9939-fc5d643c04c4", - "created_at": "2026-04-29T16:22:36.278232+00:00Z" + "created_at": "2026-07-23T21:54:52.675455+00:00Z" } diff --git a/.gitignore b/.gitignore index 102eeae..801e859 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,4 @@ credentials.json *.pem *.key .credentials +frontend/node_modules/.cache/default-development/0.pack diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..51fe98f --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,189 @@ +# Kino — Deployment on a Proxmox LAMP host + +Kino is FastAPI + MongoDB + React. It runs happily alongside your existing +LAMP stack (Apache/MySQL/PHP) because we run everything in Docker containers +on their own network. Apache keeps port 80/443, MySQL is untouched, Kino +uses whatever port you pick. + +--- + +## Prerequisites on the Proxmox host (or LXC / VM) + +```bash +# Ubuntu/Debian +sudo apt update +sudo apt install -y docker.io docker-compose-plugin git +sudo systemctl enable --now docker + +# Verify +docker compose version +``` + +If you already have Docker and Compose v2, skip this step. + +--- + +## Deploy + +```bash +# 1. Clone the repo +git clone https://github.com/YOU/kino-media-server.git /opt/kino +cd /opt/kino + +# 2. Configure +cp .env.compose.example .env +nano .env +# - Set KINO_HTTP_PORT (default 8080; anything not used by Apache) +# - Set MEDIA_ROOT_HOST to your bulk storage path (e.g. /mnt/nas/movies) +# - Set a strong JWT_SECRET: openssl rand -hex 32 +# - Set a strong ADMIN_PASSWORD +# - Optional: change ADMIN_EMAIL + +# 3. Start +docker compose up -d + +# 4. Check +docker compose ps +docker compose logs -f backend # confirm "Transcode worker started" +``` + +Visit `http://:8080` (or whatever `KINO_HTTP_PORT` you set). +Log in with the admin credentials from `.env`. + +--- + +## Running behind your existing Apache (optional, recommended) + +If you already have Apache on port 80/443 with virtual hosts, add a Kino vhost +that reverse-proxies to the container. This gives you `https://kino.example.com` +instead of `http://ip:8080`. + +**`/etc/apache2/sites-available/kino.conf`** +```apache + + ServerName kino.local # or kino.yourdomain.tld + + ProxyPreserveHost On + ProxyRequests Off + + # SPA + API + ProxyPass / http://127.0.0.1:8080/ + ProxyPassReverse / http://127.0.0.1:8080/ + + # Streaming needs disabled buffering + SetEnv proxy-sendchunked 1 + + # Multi-GB uploads + LimitRequestBody 21474836480 + +``` + +```bash +sudo a2enmod proxy proxy_http headers +sudo a2ensite kino +sudo systemctl reload apache2 +``` + +For HTTPS, either terminate TLS at Apache (add certbot/`SSLEngine on`) or +put Caddy in front of it. + +--- + +## Common ops + +```bash +# Update to latest code +cd /opt/kino +git pull +docker compose build +docker compose up -d + +# Stop +docker compose down + +# Logs +docker compose logs -f backend # FastAPI + transcode worker +docker compose logs -f frontend # nginx access +docker compose logs -f mongo + +# Shell into backend +docker compose exec backend bash + +# Backup MongoDB +docker compose exec mongo mongodump --out /data/db/backup-$(date +%F) +``` + +--- + +## Where does data live? + +| What | Where | +| -------------------------- | ---------------------------------------------------- | +| Movie files, HLS, subs | `$MEDIA_ROOT_HOST` on host (bind-mounted) | +| MongoDB data | Docker volume `mongo-data` (managed by Docker) | +| TMDB / Radarr keys | In MongoDB (`db.settings` collection) | +| Uploads via web UI | `$MEDIA_ROOT_HOST/videos/` | + +To reset everything: `docker compose down -v` (deletes the mongo-data volume). + +--- + +## Coexistence with your LAMP stack + +| Kino uses | LAMP uses | Conflict? | +| ------------------------ | ----------------------- | --------- | +| Port `KINO_HTTP_PORT` | Apache 80/443 | No — different ports | +| MongoDB (internal only) | MySQL | No — different DBs, Mongo not exposed to host | +| ffmpeg (in container) | anything | No | +| Docker networks | native host services | No — bridged network | + +You can happily keep running Wordpress, phpMyAdmin, Nextcloud, whatever on the +same box. + +--- + +## Radarr integration + +If Radarr is running on the same Proxmox host (native or container), point Kino +at it via **Admin → Settings → Radarr**: + +- **URL**: `http://:7878` (use the host IP, not `localhost` — containers + can't reach the host's `localhost` unless you add `host.docker.internal`) +- **API key**: from Radarr → Settings → General → Security + +**Important**: For Kino to actually play the imported movies, the path Radarr +reports (e.g. `/movies/Inception (2010)/Inception.mkv`) must be **readable from +inside the Kino backend container**. Mount Radarr's media path into Kino: + +```yaml +# docker-compose.yml — extend the backend volumes +backend: + volumes: + - ${MEDIA_ROOT_HOST:-./media}:/media + - /mnt/radarr-movies:/mnt/radarr-movies:ro # <— add this +``` + +Radarr's file paths must be identical inside both Radarr and Kino containers +(easiest: match the paths exactly). + +--- + +## Health check + +```bash +curl -s http://localhost:${KINO_HTTP_PORT:-8080}/api/ | jq +# {"app":"Kino","status":"ok"} +``` + +--- + +## Firewalling + +Kino has no built-in rate limiting or IP allowlist. Since you confirmed +**internal network only**, keep it that way: + +```bash +# ufw example — allow only your LAN +sudo ufw allow from 192.168.0.0/16 to any port ${KINO_HTTP_PORT:-8080} +sudo ufw deny ${KINO_HTTP_PORT:-8080} +``` diff --git a/README.md b/README.md index a1573e5..3060e9d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ A self-hosted Netflix-style streaming app for movies you legally own. Built with FastAPI + React + MongoDB. +## 📖 Deployment + +**→ See [DEPLOY.md](./DEPLOY.md) for full Proxmox + Docker Compose instructions** +(runs alongside your existing LAMP stack without conflicts). + ## Features - Cinematic browse UI with hero banner, horizontal carousels, hover details diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..6a11a2f --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,23 @@ +# Kino backend — FastAPI + ffmpeg +FROM python:3.11-slim + +# ffmpeg for HLS transcoding; util-linux for `nice` (already in base but explicit) +RUN apt-get update && apt-get install -y --no-install-recommends \ + ffmpeg \ + curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Install Python deps first (layer cache) +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy app source +COPY . . + +# Create media dirs (will be overridden by volume mount at runtime) +RUN mkdir -p /media/videos /media/posters /media/subtitles /media/hls + +EXPOSE 8001 +CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8001", "--proxy-headers"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..855464e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,71 @@ +# Kino — Docker Compose deployment +# +# Runs alongside existing LAMP stacks without conflicts: +# - Apache keeps port 80/443 +# - MySQL is untouched (Kino uses its own MongoDB in a container) +# - Kino exposes a single configurable port (default 8080) +# +# Usage: +# cp .env.compose.example .env +# # edit .env — set JWT_SECRET, ADMIN_PASSWORD, KINO_HTTP_PORT, MEDIA_ROOT_HOST +# docker compose up -d +# # visit http://:8080 (or whatever KINO_HTTP_PORT you set) + +services: + mongo: + image: mongo:7 + container_name: kino-mongo + restart: unless-stopped + volumes: + - mongo-data:/data/db + networks: + - kino + # No ports exposed to host — only reachable inside kino network + + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: kino-backend + restart: unless-stopped + depends_on: + - mongo + environment: + MONGO_URL: mongodb://mongo:27017 + DB_NAME: ${DB_NAME:-kino} + CORS_ORIGINS: "*" + JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in .env} + JWT_ALG: HS256 + JWT_EXPIRE_HOURS: ${JWT_EXPIRE_HOURS:-720} + ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@kino.local} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:?set ADMIN_PASSWORD in .env} + ADMIN_NAME: ${ADMIN_NAME:-Admin} + MEDIA_ROOT: /media + volumes: + # Host media directory (movies, HLS, subtitles) — point at your NAS/ZFS mount + - ${MEDIA_ROOT_HOST:-./media}:/media + networks: + - kino + # No direct host port — frontend container reverse-proxies /api → backend:8001 + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: kino-frontend + restart: unless-stopped + depends_on: + - backend + ports: + # Only Kino's external port. Change KINO_HTTP_PORT to avoid clashes with Apache. + - "${KINO_HTTP_PORT:-8080}:80" + networks: + - kino + +networks: + kino: + driver: bridge + +volumes: + mongo-data: + driver: local diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..ee277ce --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,29 @@ +# Kino frontend — React build + nginx reverse proxy +# Multi-stage: build with Node, serve with nginx + +# ---------- Build stage ---------- +FROM node:20-alpine AS build + +WORKDIR /app + +# When BACKEND_URL is empty, frontend uses relative /api which nginx below proxies. +# This keeps auth cookies / CORS trivial and works behind any reverse proxy. +ENV REACT_APP_BACKEND_URL="" + +COPY package.json yarn.lock* ./ +RUN yarn install --frozen-lockfile --network-timeout 600000 + +COPY . . +RUN yarn build + +# ---------- Serve stage ---------- +FROM nginx:1.27-alpine + +# Custom config: SPA fallback + /api reverse proxy + large uploads + streaming timeouts +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Static build +COPY --from=build /app/build /usr/share/nginx/html + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..31ecdcb --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,45 @@ +server { + listen 80; + server_name _; + + # Allow multi-GB movie uploads + client_max_body_size 20g; + + # Long timeouts for streaming + transcode responses + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 60s; + + # gzip static + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + # Reverse proxy /api → backend service on the compose network + location /api/ { + proxy_pass http://backend:8001; + proxy_http_version 1.1; + 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; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Streaming needs unbuffered responses for Range requests + proxy_buffering off; + proxy_request_buffering off; + } + + # SPA static files with client-side routing fallback + location / { + root /usr/share/nginx/html; + try_files $uri /index.html; + } + + # Cache static assets aggressively (hashed filenames from CRA) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { + root /usr/share/nginx/html; + expires 30d; + add_header Cache-Control "public, immutable"; + } +} diff --git a/frontend/public/index.html b/frontend/public/index.html index 427ea43..4741de5 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -38,52 +38,7 @@ To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> - - - - - -

- Made with Emergent -

-
-