diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b0c8b59 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git +__pycache__ +server.log +*.pyc diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4236a06 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.13-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY server.py index.html README.md ./ + +ENV C41_SOURCE=/data/source \ + C41_OUTPUT=/data/encoded \ + C41_HOST=0.0.0.0 \ + C41_PORT=8765 + +EXPOSE 8765 +VOLUME ["/data/source", "/data/encoded"] + +CMD ["python3", "server.py"] diff --git a/README.md b/README.md index 0e7e2f3..1c46fa7 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,46 @@ python3 server.py Open in a browser. +## Docker + +Docker keeps the application and FFmpeg inside a container while the recorder +files remain on the host. From this directory, run: + +```sh +docker compose up -d --build +``` + +Open . The Compose configuration mounts the original +archive read-only at `/data/source` and stores encoded files persistently in +`/data/encoded`. Stop it with: + +```sh +docker compose down +``` + +To use different host folders, edit the two `volumes` entries in +`compose.yaml`. Do not remove `:ro` from the source mount. + +## LXC + +An unprivileged LXC works without Docker. Install Python and FFmpeg inside the +container, then bind-mount the host folders into the LXC and run: + +```sh +python3 server.py --host 0.0.0.0 --source /mnt/c41-media --root /mnt/c41-media-mp4 +``` + +The LXC user needs read permission on the source mount and write permission on +the encoded mount. Run the player as a system service or with a process +supervisor so it restarts after reboot. If the LXC should be reachable from +other machines, bind the port explicitly: + +```sh +python3 server.py --host 0.0.0.0 --source /mnt/c41-media --root /mnt/c41-media-mp4 --port 8765 +``` + +Then open `http://LXC-IP:8765`. No privileged container features are required. + The default source archive is: `/home/human/Videos/Yassine Salon/C41/media` diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..7d9f025 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,12 @@ +services: + c41-player: + build: . + container_name: c41-player + environment: + C41_HOST: 0.0.0.0 + ports: + - "8765:8765" + volumes: + - "/home/human/Videos/Yassine Salon/C41/media:/data/source:ro" + - "/home/human/Videos/Yassine Salon/C41/media-mp4:/data/encoded" + restart: unless-stopped diff --git a/server.py b/server.py index 557fbac..a35d05c 100644 --- a/server.py +++ b/server.py @@ -5,6 +5,7 @@ import json import re import subprocess import threading +import os from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import parse_qs, urlparse @@ -196,20 +197,21 @@ def main(): "root", nargs="?", type=Path, - default=Path("/home/human/Videos/Yassine Salon/C41/media-mp4"), + default=Path(os.environ.get("C41_OUTPUT", "/home/human/Videos/Yassine Salon/C41/media-mp4")), help="folder containing YYYYMMDD recording directories", ) - parser.add_argument("--port", type=int, default=8765) + parser.add_argument("--port", type=int, default=int(os.environ.get("C41_PORT", "8765"))) + parser.add_argument("--host", default=os.environ.get("C41_HOST", "127.0.0.1")) parser.add_argument( "--source", type=Path, - default=Path("/home/human/Videos/Yassine Salon/C41/media"), + default=Path(os.environ.get("C41_SOURCE", "/home/human/Videos/Yassine Salon/C41/media")), help="folder containing source AVI files for manual encoding", ) args = parser.parse_args() recordings = scan_recordings(args.root.expanduser().resolve()) - server = ThreadingHTTPServer(("127.0.0.1", args.port), PlayerHandler) + server = ThreadingHTTPServer((args.host, args.port), PlayerHandler) server.recordings = recordings server.source_root = args.source.expanduser().resolve() server.output_root = args.root.expanduser().resolve()