diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..564a79f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,35 @@ +.git +.gitignore +.env +.env.* +!.env.example +.venv +venv +__pycache__ +*.py[cod] +*.db +*.db-wal +*.db-shm +*.sqlite +*.sqlite3 +data +files +.thumbs +.meta +.hls +.hls_live +.vodcache +.gunicorn +logs +*.log +*.mp4 +*.mkv +*.ts +*.m3u8 +*.part +*.tmp +docs/screenshots +node_modules +.pytest_cache +.coverage +htmlcov diff --git a/.env.example b/.env.example index 32af36a..7cc1fee 100644 --- a/.env.example +++ b/.env.example @@ -6,18 +6,43 @@ # --- Core WebUI Server Settings --- VOD_HOST=0.0.0.0 VOD_PORT=8080 -VOD_PUBLIC_BASE=http://localhost:8080 -VOD_SECRET_KEY=change-this-to-a-very-secure-random-secret-key-32-chars-minimum +# Reverse proxies whose X-Real-IP / X-Forwarded-For headers are trusted +# (comma-separated IPs or CIDR networks). Rate limits and login bans use the +# client IP these proxies report; never list addresses clients can reach directly. +VOD_TRUSTED_PROXIES=127.0.0.1,::1 +# Public base URL used for share links and OAuth callbacks. +# Leave empty to derive it from the incoming request (recommended behind a proxy). +# For OAuth providers set the exact public HTTPS origin, e.g. https://recorder.example.com +VOD_PUBLIC_BASE= +# Required. Generate with: python -c "import secrets; print(secrets.token_hex(32))" +VOD_SECRET_KEY= VOD_SECURE_COOKIES=false +# --- Cloud / External Storage (optional) --- +# Dedicated AES-GCM key for provider credentials. Falls back to VOD_SECRET_KEY. +# Generate with: python -c "import secrets; print(secrets.token_hex(32))" +# STORAGE_ENCRYPTION_KEY= +# Background worker threads for uploads/restores (default: 1) +# WORKER_STORAGE_THREADS=1 +# Auto-upload, local deletion and retention are configured in the WebUI +# (Systemsteuerung -> Cloud-Speicher) or via `streamvault storage settings`. + # --- Authentication & Initial Bootstrap --- # NOTE: Required in production. Cannot be 'admin'/'admin'. VOD_USER=vaultadmin -VOD_PASS=GenerateAStrongPasswordHere123! +# Required. Generate with: python -c "import secrets; print(secrets.token_urlsafe(24))" +VOD_PASS= VOD_ALLOW_ENV_LOGIN=false VOD_SESSION_MAX_AGE_SEC=0 VOD_SESSION_COOKIE_MAX_AGE_SEC=34560000 +# --- Error Monitoring (optional) --- +# SENTRY_DSN= +# PII is opt-in; keep disabled unless your privacy policy allows it. +# SENTRY_SEND_DEFAULT_PII=false +# Performance tracing is sampled and disabled by default (0.0 - 1.0). +# SENTRY_TRACES_SAMPLE_RATE=0.0 + # --- Storage Paths --- # Base directory for recordings, metadata, and database # Default: ./data (or the app directory) @@ -40,10 +65,22 @@ VOD_USE_X_ACCEL=true # LIVE_TWITCH_OAUTH= # LIVE_TWITCH_ANONYMOUS=true +# Optional playlist proxy. Off by default and no proxy is built in: name one +# you operate or trust yourself. Check the platform's terms before enabling. +# STREAM_RECORDER_USE_PROXY=false +# TWITCH_PROXY_PLAYLIST=https://proxy.example.com +# STREAMLINK_PLUGIN_DIR= + # Optional Twitch API Client (for metadata auto-sync and EventSub) # TWITCH_CLIENT_ID=your_twitch_client_id_here # TWITCH_CLIENT_SECRET=your_twitch_client_secret_here +# --- System Backups (Admin -> Backups) --- +# Directory listed for streamrecorder_*.tar.gz files (default: /backups) +# BACKUP_DIR=/var/backups/streamvault +# Script run by "Backup erstellen"; it must write streamrecorder_*.tar.gz into BACKUP_DIR +# VOD_BACKUP_SCRIPT=/usr/local/bin/streamvault-backup.sh + # --- Performance & Caching --- # Optional Redis cache for high-concurrency deployments # REDIS_URL=redis://127.0.0.1:6379/0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1c9e1cd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,27 @@ +# Keep repository content LF-normalised regardless of core.autocrlf. +* text=auto + +# Executed on Linux (VPS, Docker): CRLF breaks the shebang / bash parsing. +*.sh text eol=lf +bin/streamvault text eol=lf +Dockerfile text eol=lf +*.conf text eol=lf +.env.example text eol=lf + +# Windows helpers keep CRLF. +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Binary assets. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.webp binary +*.mp3 binary +*.mp4 binary +*.woff binary +*.woff2 binary +*.db binary diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..efe2f99 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +buy_me_a_coffee: blackwellcode diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..9c7c163 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,52 @@ +name: Bug report +description: Something does not work as documented +labels: [bug] +body: + - type: markdown + attributes: + value: | + Security issues: please do **not** file them here, see SECURITY.md. + - type: textarea + id: what + attributes: + label: What happened? + description: What did you do, what did you expect, what happened instead? + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: | + 1. ... + 2. ... + validations: + required: true + - type: dropdown + id: deployment + attributes: + label: Deployment + options: + - install.sh (systemd) + - Docker Compose + - Manual + validations: + required: true + - type: input + id: version + attributes: + label: Version or commit + placeholder: e.g. v1.0.0 or 1a2b3c4 + validations: + required: true + - type: input + id: environment + attributes: + label: OS and Python version + placeholder: e.g. Ubuntu 22.04, Python 3.10.12 + - type: textarea + id: logs + attributes: + label: Relevant log output + description: "`streamvault logs ` or `docker compose logs`. Remove tokens and passwords." + render: text diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..bfba653 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/Blackwellcode/streamvault/security/advisories/new + about: Report vulnerabilities privately, not as a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..202b3b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,21 @@ +name: Feature request +description: Suggest an improvement +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What are you trying to do, and what gets in the way today? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed solution + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5d273c1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: pip + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6566525 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +## What and why + + + +## Checks + +- [ ] `ruff check .` passes +- [ ] `python -m unittest discover -s tests` passes +- [ ] New behaviour has a test +- [ ] Operator-visible changes (env vars, migrations, manual steps) are in `CHANGELOG.md` +- [ ] UI changes: screenshots at desktop and phone width attached diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..74f6f63 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # 3.10 = Ubuntu 22.04, 3.11 = Debian 12 (production targets); 3.12 = current. + python-version: ["3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: requirements.txt + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt ruff==0.17.3 + + - name: Compile (target interpreter catches PEP 701 f-strings) + run: python -m compileall -q core recorder WEBUI bin run_transcribe.py tests + + - name: Lint + run: ruff check . + + - name: Unit tests + run: python -m unittest discover -s tests -v + + shell: + name: Shell scripts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Syntax check + run: | + for script in install.sh scripts/streamvault_vps.sh bin/streamvault; do + bash -n "$script" + done diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..acf041f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,51 @@ +# Changelog + +All notable changes to this project are documented here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [Unreleased] + +### Breaking changes + +- **Docker data directory.** `docker-compose.yml` now uses + `VOD_BASE_DIR=/app/data`. Before upgrading, move archives, + `streamrecorder.db` and the dot-directories (`.meta`, `.thumbs`, `.hls`, + `.vodcache`) from the project root into `./data`. +- **Casting removed.** Chromecast/AirPlay buttons, the signed `?cast=` media + URLs and `/api/live/cast-url` are gone. Media is only served to logged-in + sessions and without `Access-Control-Allow-Origin: *`. +- **OBS pages need a login.** `/live/obs/` and `/chat/obs/` + require a session; log in once inside the OBS browser source + ("Interact"). +- **Smart-TV mode removed**, including `/login/tv` and the gatekeeper + auto-login. +- **Playlist proxy is opt-in only.** No proxy hosts are built in; set + `TWITCH_PROXY_PLAYLIST` (and `STREAMLINK_PLUGIN_DIR` if needed) together + with `STREAM_RECORDER_USE_PROXY=true`. +- **Backups.** `BACKUP_DIR` defaults to `/backups`; "create + backup" runs the script named in `VOD_BACKUP_SCRIPT`. + +### Security + +- Client IPs behind a proxy come from `X-Real-IP` or the right-most + `X-Forwarded-For` hop; rotating `X-Forwarded-For` no longer bypasses login + rate limits and bans. `VOD_TRUSTED_PROXIES` accepts CIDR networks. +- Code and dot-directories below `VOD_BASE_DIR` can no longer be deleted as + streamer archives. + +### Fixed + +- `/agb` and `/datenschutz` returned HTTP 500 on fresh installs. +- The worker could remux a recording that was still being written when its + status file was half-written or owned by another user. +- A corrupt JSON state file (favorites, profiles, shares, settings) was + silently replaced by an empty one; it is now kept as `.corrupt-`. +- Login heading overflowed its card; mobile admin pages clipped buttons, + tabs and tables. +- Browsers kept stale CSS/JS after updates; static URLs now carry a version + derived from the shipped files. + +### Added + +- GitHub Actions CI (Python 3.10–3.12, ruff, unit tests, shell syntax), + Dependabot, contribution and security guidelines. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..50ae8ee --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,175 @@ +# CLAUDE.md — StreamVault (nachtsilo-hub) Engine Guide + +## Project Overview +StreamVault is an automated 24/7 multi-platform live stream recording, HLS live restreaming, VOD transcoding, and cloud archival system. +- **Languages & Frameworks:** Python 3.10+ (Ubuntu 22.04 / Debian 12 system Python) (Flask, SQLite WAL, Streamlink, yt-dlp, FFmpeg, Whisper ASR, Boto3, PyCryptodome). +- **Core Services:** + - `Streamrecorder.py`: Daemon managing per-channel recording processes (`flock`-isolated). + - `worker.py`: Multi-threaded background job runner (IO, Remux, ASR, Cloud Storage). + - `WEBUI/webui.py`: Flask dashboard, X-Accel-Redirect streaming, user traffic monitoring, supervisor health checks. + - `run_transcribe.py`: Isolated subprocess runner for Whisper ASR (`STREAMVAULT_IMPORT_ONLY=1`). + - `core/storage/`: Provider-agnostic cloud storage service (AES-GCM encryption; S3, WebDAV, Google Drive, OneDrive, Dropbox). + - `bin/streamvault`: Linux CLI for service orchestration (`start`, `stop`, `restart`, `status`, `logs`, `storage`). + +--- + +## Essential Commands + +### Environment Setup +```bash +# Linux +python3 -m venv .venv +source .venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt + +# Windows (Audit Host) +.\.venv\Scripts\activate +pip install -r requirements.txt +``` + +### Running Tests +The test suite uses Python's standard `unittest` discovery: +```bash +# Run entire test suite (all 167 tests) +python -m unittest discover -s tests -p "test_*.py" -v + +# Run specific test modules +python -m unittest tests/test_recorder_reliability.py -v +python -m unittest tests/test_storage.py -v + +# Run single test class or test method +python -m unittest tests.test_recorder_reliability.DiskSpaceGuardTests -v +python -m unittest tests.test_recorder_reliability.DiskSpaceGuardTests.test_disk_blocks_recording_on_nonexistent_dir_when_disk_full -v +``` + +### Syntax & Compilation Verification +Always verify Python syntax before committing: +```bash +# Lint (config in pyproject.toml; CI runs the same check) +ruff check . + +# Single file +python -m py_compile recorder/Streamrecorder.py + +# Complete repository +python -c "import os, py_compile; [py_compile.compile(os.path.join(r, f), doraise=True) for r, _, fs in os.walk('.') if not any(x in r for x in ('.venv', '__pycache__', '.git')) for f in fs if f.endswith('.py')]" +``` + +### Service Orchestration +```bash +# Linux CLI +streamvault status +streamvault start +streamvault stop +streamvault restart [webui|recorder|worker] +streamvault logs [webui|recorder|worker] -f +streamvault storage sync + +# Direct CLI / Manual Invocation +python recorder/Streamrecorder.py +python recorder/worker.py +python -m WEBUI.webui +``` + +--- + +## Architectural Map & Directory Structure + +``` +nachtsilo-hub/ +├── bin/ +│ └── streamvault # Global bash CLI wrapper (/usr/local/bin/streamvault) +├── core/ +│ ├── config.py # Env parsing, default directories (BASE_DIR, HLS_ROOT, etc.) +│ ├── database.py # SQLite WAL access, schema init, @db_retry, task queues +│ ├── health.py # Lightweight supervisorctl status parser (no heavy imports) +│ ├── redis_client.py # Optional high-speed task queue & pub/sub fallback +│ ├── mp4_utils.py # Fast moov atom shifting & faststart verification +│ └── storage/ # Cloud storage subsystem +│ ├── base.py # Abstract BaseStorageProvider, mask_error, path normalization +│ ├── crypto.py # AES-GCM credential encryption (STORAGE_ENCRYPTION_KEY) +│ ├── http.py # Resilient HTTP session, backoff, sanitized error logs +│ ├── registry.py # Provider registration & runtime dispatch +│ ├── service.py # Scheduled background sync, retention pruning, task handlers +│ └── providers/ # S3, WebDAV, Google Drive, OneDrive, Dropbox implementations +├── recorder/ +│ ├── Streamrecorder.py # Multi-channel recording engine, flock locking, FFmpeg pipelines +│ ├── worker.py # Background task consumer (pools: io, remux, asr, storage) +│ ├── clip_engine.py # Chat-overlay and highlight clipping +│ └── chat_events.py # Chat event ingestion & IRC listeners +├── WEBUI/ +│ ├── webui.py # Flask application, auth, routes, X-Accel-Redirect streaming +│ ├── templates/ # Jinja2 HTML templates +│ └── static/ # CSS, JS, alert audio, icons +├── run_transcribe.py # Subprocess wrapper for Whisper ASR to keep worker memory clean +├── scripts/ +│ └── streamvault_vps.sh # VPS deploy/rollback + Linux validation (supervisor, X-Accel, HLS RAM disk) +└── tests/ + ├── test_recorder_reliability.py # 74 tests: process reaping, locking, folder safety, health, client IP, regressions + └── test_storage.py # 93 tests: crypto, providers, retention, error masking +``` + +--- + +## Critical Rules & Architectural Invariants + +### 1. Zero Silent Failures (Anti-Bypass Policy) +- **STRICTLY FORBIDDEN:** `except Exception: pass`, `except: pass`, or returning empty values without logging. +- Catch specific error classes (e.g., `sqlite3.OperationalError`, `subprocess.TimeoutExpired`, `OSError`). +- Always log caught unexpected exceptions using `logger.error(...)` or `logger.warning(...)`. + +### 1a. Python 3.10 Compatibility +- Production runs on the distro Python (3.10 on Ubuntu 22.04, 3.11 on Debian 12). +- **No PEP 701 f-strings:** never put backslashes (`\n`), comments or the enclosing quote character inside an f-string `{...}` expression. Build the value in a variable first. Python 3.12+ accepts this silently, so the Windows audit host (3.12) will not catch it; `scripts/streamvault_vps.sh validate` compiles with the target interpreter. +- No 3.11+ stdlib APIs (`tomllib`, `datetime.UTC`, `typing.Self`, `ExceptionGroup`, `except*`). + +### 2. Immutable Tests Rule +- **NEVER** weaken, comment out, or delete assertions in `tests/` to make a test pass. +- Fix bugs in the application code, not by relaxing tests. +- When adding new features, add corresponding tests in `tests/test_*.py`. + +### 3. Process Management & Lock Hierarchy +- **File Lock Ordering:** When stopping a channel process, **always stop the child process before releasing the channel `flock`**: + ```python + # CORRECT: + stop_channel_and_release_lock(recordings_dir, channel, proc) + # WRONG: + release_held_channel_lock(recordings_dir, channel) + stop_channel_process(proc, channel) # Races with next recorder process! + ``` +- **Bounded Waiting:** Never call indefinite `process.wait()`. Always use `wait_for_process_exit(proc, timeout=...)` with a hard deadline (default 30s) and fallback to `kill()`. +- **No `fuser`:** Never execute `fuser -k -9` or kill parent process groups. + +### 4. Database Concurrency & SQLite WAL +- SQLite is configured with WAL mode (`PRAGMA journal_mode=WAL`). +- All write operations that may experience busy/locked errors must use the `@db_retry(max_retries=5, delay=0.1, backoff=2.0)` decorator and `BEGIN IMMEDIATE`. +- In multi-threaded environments (`worker.py`), each thread **must** obtain its own connection via `get_db()`. Never share SQLite connection handles across threads. +- Stale running tasks are claimed using `claim_next_task()` and must respect `max_attempts_by_type` to avoid endless retry loops on permanently crashing jobs. + +### 5. Worker & File Safety +- **Directory Protection:** Before deleting or emptying an archive folder, always check: + - `_folder_has_active_recording(folder_path)` + - `_folder_has_active_remux(folder_path)` + If active, raise `TaskDeferred(delay_sec=120)`. +- **In-Progress Files:** Files ending in `.part` or `.remux.lock` that have a fresh modification timestamp (< 10 min) must **never** be deleted or pruned. +- **Disk Space Protection:** `disk_blocks_recording(path)` walks up to the nearest existing parent directory if `path` does not exist yet. New recordings are paused if disk usage reaches `STORAGE_PAUSE_PCT` (default 90%, env `STREAM_RECORDER_STORAGE_PAUSE_PCT`) or free space falls to `STORAGE_PAUSE_FREE_GB` (default 20 GB, env `STREAM_RECORDER_STORAGE_PAUSE_FREE_GB`). + +### 6. Memory & Process Isolation (Whisper ASR) +- Never load heavy ML frameworks (Whisper, PyTorch, CTranslate2) directly into the main `worker.py` process. +- All transcription tasks must execute via `run_transcribe.py` in a separate child process. +- `run_transcribe.py` sets `STREAMVAULT_IMPORT_ONLY=1` to prevent import-time side effects (admin creation, background threads) when loading `WEBUI.webui`. + +### 7. Security & Cloud Storage Hygiene +- **Credential Storage:** Provider credentials and OAuth refresh tokens must be encrypted with AES-GCM via `core.storage.crypto.encrypt_secret()`. +- **Log Sanitization:** All cloud errors must be masked via `mask_error(exc)` before writing to database or logs to strip URLs, auth tokens, and S3 credentials. +- **Path Traversal Protection:** All client-supplied filesystem paths must be validated with `_is_direct_child(config.BASE_DIR, path)` or `classify_local_path()`. +- **Cookies:** `VOD_SECURE_COOKIES` defaults to `false` in `.env.example` to prevent redirect loops when Nginx listens on plain HTTP port 80. Set to `true` only when HTTPS is configured. +- **Sentry / Telemetry:** PII sending is disabled by default (`SENTRY_SEND_DEFAULT_PII=false`). + +--- + +## Common Handoff & Troubleshooting Notes +- On Windows hosts, `fcntl` is mocked automatically in test suites via shims in `test_recorder_reliability.py`. In production, the system targets Debian/Ubuntu Linux. +- If Redis is unavailable, task queuing automatically falls back to SQLite `tasks` table with zero configuration needed. +- If Supervisor reports degraded status, check `parse_supervisor_status()` in `core/health.py`. Standard monitored services are `webui`, `recorder`, `worker`. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8a116ad --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,15 @@ +# Code of Conduct + +This project follows the +[Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). + +In short: be respectful, assume good intent, keep discussions about the work, +and do not harass anyone in issues, pull requests or any other project space. + +## Reporting + +Report unacceptable behaviour privately to the maintainers through GitHub's +private vulnerability reporting (see [SECURITY.md](SECURITY.md)) and mark the +report as a Code of Conduct matter. Reports are handled confidentially. +Maintainers may remove, edit or reject contributions and may ban contributors +who violate this code. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..900f264 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to StreamVault + +Thanks for helping. This file covers how to get a working checkout, the rules +the code base relies on, and what a pull request needs. + +## Development setup + +StreamVault targets Linux in production (Ubuntu 22.04 / Debian 12) but the +test suite also runs on Windows and macOS. + +```bash +git clone https://github.com/Blackwellcode/streamvault.git +cd streamvault +python3 -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -r requirements.txt ruff +``` + +Run the app locally: + +```bash +export VOD_USER=admin VOD_PASS='choose-a-password' \ + VOD_SECRET_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')" \ + VOD_BASE_DIR="$PWD/data" +python -m WEBUI.webui --host 127.0.0.1 --port 8080 +``` + +## Checks before you open a pull request + +CI runs exactly these on Python 3.10, 3.11 and 3.12: + +```bash +python -m compileall -q core recorder WEBUI bin run_transcribe.py tests +ruff check . +python -m unittest discover -s tests -v +``` + +## Rules the code base depends on + +- **Python 3.10 compatible.** No PEP 701 f-strings (no quotes, backslashes or + comments inside `{...}`), no `tomllib`, `datetime.UTC`, `typing.Self` or + `except*`. Python 3.12 accepts PEP 701 silently, so run the compile step on + 3.10 or rely on CI. +- **No silent failures.** No `except Exception: pass`. Catch the specific + error class and log it; if a fallback value is returned, the log must say so. +- **Fail closed around recordings.** Anything that deletes, remuxes or moves + media must treat "unknown" as "still recording". +- **Tests are not weakened to go green.** Fix the code instead. New behaviour + gets a test. +- **Stop before unlock.** When stopping a channel, stop the child process + before releasing its `flock`. Waits are bounded; never `fuser -k`. +- **Paths from clients** are validated against `config.BASE_DIR` + (`_is_direct_child`, `safe_join`) before touching the file system. + +## Pull requests + +- One topic per pull request; small, focused commits. +- Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) + (`fix(worker): ...`, `feat(webui): ...`), written in English. +- Describe what changed for operators: new environment variables, migrations, + or anything that needs a manual step goes into `CHANGELOG.md` under + *Unreleased*. +- UI changes: include a screenshot at desktop and phone width. + +## Reporting security issues + +Please do not open public issues for vulnerabilities. See [SECURITY.md](SECURITY.md). diff --git a/INSTALL.md b/INSTALL.md index 1286dab..9b366e6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -30,14 +30,15 @@ sudo ./install.sh - Installs all apt dependencies (`ffmpeg`, `python3-venv`, `nginx`, `sqlite3`). - Sets up system user `streamvault` and directories (`/opt/streamvault`, `/var/lib/streamvault`). - Generates cryptographically secure secrets in `/etc/streamvault/env`. -- Deploys and enables systemd daemons (`streamvault-webui.service`, `streamvault-recorder.service`). +- Deploys and enables systemd daemons (`streamvault-webui.service`, `streamvault-recorder.service`, `streamvault-worker.service`). - Configures Nginx with `X-Accel-Redirect` byte-range media offloading. - **Registers the global Linux management command** `/usr/local/bin/streamvault`: ```bash streamvault status # Check daemons and live health streamvault logs webui # View WebUI log stream -streamvault logs recorder# View Stream Recorder log stream +streamvault logs recorder # View Stream Recorder log stream +streamvault logs worker # View background task worker log stream streamvault restart # Restart application daemons streamvault update # Pull updates, upgrade dependencies, and reload streamvault check # Validate Python syntax & Nginx configurations @@ -65,7 +66,9 @@ nano .env ```bash docker compose up -d --build ``` -Access the application at `http://localhost:8090` (proxied by Nginx) or directly at `http://localhost:8080`. +Access the application at `http://localhost:8090` (proxied by Nginx). The +Compose stack runs WebUI, recorder and worker under supervisord; the `hls_live` +volume is shared between the app and Nginx containers. --- @@ -114,12 +117,25 @@ Ensure `/etc/streamvault/env` contains: ```ini VOD_HOST=127.0.0.1 VOD_PORT=8080 +# Leave empty to derive from the request, or set the public HTTPS origin. +VOD_PUBLIC_BASE= VOD_BASE_DIR=/var/lib/streamvault/data LIVE_HLS_DIR=/dev/shm/streamvault_hls VOD_USER=vaultadmin -VOD_PASS=YourSuperSecretPasswordHere987! -VOD_SECRET_KEY=generate_a_random_32_character_string_here +VOD_PASS= +VOD_SECRET_KEY= +# The bundled Nginx site listens on HTTP port 80. Keep secure-only cookies +# disabled until HTTPS is enabled, otherwise login redirects loop. +VOD_SECURE_COOKIES=false VOD_USE_X_ACCEL=true +# Optional dedicated key for cloud credentials (falls back to VOD_SECRET_KEY). +# STORAGE_ENCRYPTION_KEY= +``` + +Fill the two empty secret values with freshly generated ones: +```bash +python3 -c "import secrets; print('VOD_PASS=' + secrets.token_urlsafe(24))" +python3 -c "import secrets; print('VOD_SECRET_KEY=' + secrets.token_hex(32))" ``` --- @@ -176,11 +192,33 @@ RestartSec=10s WantedBy=multi-user.target ``` +### Background Task Worker (`/etc/systemd/system/streamvault-worker.service`) +```ini +[Unit] +Description=StreamVault - Background Task Worker +After=network.target streamvault-webui.service streamvault-recorder.service + +[Service] +Type=simple +User=streamvault +Group=streamvault +WorkingDirectory=/opt/streamvault/app +EnvironmentFile=/etc/streamvault/env +Environment="PYTHONPATH=/opt/streamvault/app" +ExecStart=/opt/streamvault/venv/bin/python3 recorder/worker.py +Restart=always +RestartSec=10s + +[Install] +WantedBy=multi-user.target +``` + Enable and start services: ```bash sudo systemctl daemon-reload sudo systemctl enable --now streamvault-webui sudo systemctl enable --now streamvault-recorder +sudo systemctl enable --now streamvault-worker ``` --- @@ -215,24 +253,79 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # 2. X-Accel-Redirect Internal Media Offload + # 2. X-Accel-Redirect media offload. + # WebUI emits /_protected//; archives live directly below + # VOD_BASE_DIR (not below VOD_BASE_DIR/files). location /_protected/ { + internal; + alias /var/lib/streamvault/data/; + sendfile on; + sendfile_max_chunk 1m; + output_buffers 2 1m; + gzip off; + add_header Accept-Ranges bytes always; + } + + # Legacy archives below VOD_FILES_DIR (default VOD_BASE_DIR/files). + location /_protected_files/ { internal; alias /var/lib/streamvault/data/files/; - mp4; - mp4_buffer_size 1m; - mp4_max_buffer_size 10m; + sendfile on; + sendfile_max_chunk 1m; + output_buffers 2 1m; + gzip off; + add_header Accept-Ranges bytes always; } - # 3. Live HLS RAM-Disk Offload - location /_hls_live/ { + location /_protected_vodcache/ { + internal; + alias /var/lib/streamvault/data/.vodcache/; + sendfile on; + sendfile_max_chunk 1m; + output_buffers 2 1m; + gzip off; + } + + location /_protected_thumbs/ { + internal; + alias /var/lib/streamvault/data/.thumbs/; + expires 1d; + add_header Cache-Control "public"; + } + + location /_protected_hls/ { + internal; + alias /var/lib/streamvault/data/.hls/; + types { + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + video/mp4 m4s mp4; + } + default_type application/octet-stream; + add_header Cache-Control "no-cache"; + } + + # 3. Live HLS RAM-Disk offload + location /_protected_hls_live/ { + internal; alias /dev/shm/streamvault_hls/; + open_file_cache off; + types { + application/vnd.apple.mpegurl m3u8; + video/mp2t ts; + video/mp4 m4s mp4; + } + default_type application/octet-stream; add_header Cache-Control "no-cache, no-store, must-revalidate"; - add_header Access-Control-Allow-Origin *; } } ``` +Keep the aliases in sync with `VOD_BASE_DIR`, `VOD_FILES_DIR`, `VOD_THUMB_DIR`, +`VOD_HLS_DIR` and `LIVE_HLS_DIR` if you changed those environment variables. +The `/_protected_files/` alias is only needed for legacy archives stored below +`VOD_FILES_DIR`; new recordings normally live directly below `VOD_BASE_DIR`. + Enable site and restart Nginx: ```bash sudo ln -sf /etc/nginx/sites-available/streamvault.conf /etc/nginx/sites-enabled/ @@ -241,7 +334,40 @@ sudo nginx -t && sudo systemctl reload nginx --- -## 7. Local Developer Setup +## 7. Cloud / External Storage + +Cloud offloading is optional and requires the `streamvault-worker` daemon +(installed by `install.sh`). Configure it in **Systemsteuerung → Cloud-Speicher** +(`/admin/storage`) or via the console: + +```bash +streamvault storage types +streamvault storage add --type google_drive --name "Drive Archive" +streamvault storage add --type webdav --name "Nextcloud" \ + --set base_url=https://cloud.example.com/remote.php/dav/files/vault/ \ + --set username=vault --set password=app-password +streamvault storage test 1 +streamvault storage sync +streamvault storage objects +streamvault storage restore 42 +``` + +For Google Drive, Dropbox and OneDrive, register this redirect URI in the +provider console and set `VOD_PUBLIC_BASE` to the public HTTPS origin: + +``` +https:///admin/storage/oauth/callback +``` + +Credentials are encrypted with AES-GCM. Set a dedicated +`STORAGE_ENCRYPTION_KEY` (32+ random characters) or rely on the existing strong +`VOD_SECRET_KEY`. Keep the key stable; changing it makes stored credentials +unreadable. Safety defaults are automatic upload **off**, local deletion +**off**, and 14 days local retention. + +--- + +## 8. Local Developer Setup To run the application locally for development: @@ -257,7 +383,10 @@ pip install -r requirements.txt # Configure environment variables cp .env.example .env -# Edit .env with your desired VOD_USER and VOD_PASS credentials +# Edit .env and set VOD_USER, VOD_PASS, and VOD_SECRET_KEY. +# Generate them with: +python -c "import secrets; print('VOD_PASS=' + secrets.token_urlsafe(24))" +python -c "import secrets; print('VOD_SECRET_KEY=' + secrets.token_hex(32))" # Start WebUI server python WEBUI/webui.py --port 8080 diff --git a/README.md b/README.md index d5f5ffc..436a583 100644 --- a/README.md +++ b/README.md @@ -1,169 +1,177 @@
# StreamVault -### High-Performance Self-Hosted Stream Archiver, Live Proxy & Interactive VOD Vault -[![Python Version](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue.svg)](https://www.python.org/) -[![Framework](https://img.shields.io/badge/framework-Flask%203.x-green.svg)](https://flask.palletsprojects.com/) -[![Offload](https://img.shields.io/badge/streaming-Nginx%20X--Accel%20%28HTTP%20206%29-orange.svg)](https://nginx.org/) -[![Database](https://img.shields.io/badge/storage-SQLite%20WAL%20%2B%20Redis-red.svg)](https://www.sqlite.org/) -[![Docker](https://img.shields.io/badge/docker-compose%20ready-blue.svg)](https://www.docker.com/) -[![License](https://img.shields.io/badge/license-MIT-purple.svg)](LICENSE) +**Self-hosted recorder and archive for Twitch and YouTube live streams, with chat replay.** -*A privacy-first, enterprise-grade streaming infrastructure for creator collectives, esports teams, and media archives.* +[![CI](https://github.com/Blackwellcode/streamvault/actions/workflows/ci.yml/badge.svg)](https://github.com/Blackwellcode/streamvault/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml) +[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-blackwellcode-FFDD00?logo=buymeacoffee&logoColor=black)](https://buymeacoffee.com/blackwellcode)
---- +StreamVault watches the channels you choose, records every broadcast with +Streamlink and FFmpeg, archives the chat alongside it, and serves both through +a web interface where you can replay a stream with its chat, cut clips and +manage who may watch what. It runs on a single Linux server; video delivery is +handed to Nginx so the Python process stays small. -## 🌟 Overview +> The web interface is currently available in **German only**. -**StreamVault** is a modular, high-efficiency streaming platform engineered to capture, archive, and replay live streams alongside synchronized chat timelines. Designed to operate on low-cost VPS instances or dedicated bare-metal servers, it combines kernel-level Nginx offloading, SQLite concurrency in WAL mode, and sub-second live HLS transmuxing. +## Screenshots -This open-core edition is **100% sanitized**: free of private streamer configurations, tracking tools, hardcoded tokens, or proprietary credentials. - ---- - -## 📸 Interface Showcase - -| Live Dashboard & Telemetry | VOD Player with Chat Replay | +| Dashboard | VOD player with chat replay | | :---: | :---: | -| ![Live Hub](docs/screenshots/dashboard.png) | ![VOD Player](docs/screenshots/vod_player.png) | +| ![Dashboard](docs/screenshots/dashboard.png) | ![VOD player](docs/screenshots/vod_player.png) | -| Streamer Channel Vault | RBAC User & Group Management | +| Channel archive | Users and groups | | :---: | :---: | -| ![Streamer Hub](docs/screenshots/streamer_hub.png) | ![User Management](docs/screenshots/admin_users.png) | +| ![Channel archive](docs/screenshots/streamer_hub.png) | ![User management](docs/screenshots/admin_users.png) | -| System Health & Metrics | Mobile Responsive PWA Layout | +| System status | Phone layout | | :---: | :---: | -| ![Admin Panel](docs/screenshots/admin_panel.png) | ![Mobile View](docs/screenshots/mobile_view.png) | - ---- - -## ⚡ Core Capabilities - -- **Automated 24/7 Stream Recording**: Background worker daemons monitor target channels (Twitch, Kick, YouTube) and capture broadcasts via `streamlink` and `ffmpeg` with automatic stream reconnect and segmented salvage. -- **Synchronized Chat Replay**: Archives live chat messages in real time (`.chat.json`), aligning badges, emotes, and timestamps exactly with video scrubbing. -- **Kernel-Level Media Offloading**: Integrates with Nginx via `X-Accel-Redirect` (HTTP 206 Partial Content / Range Requests). Python workers release memory immediately while the Linux kernel streams gigabyte-scale MP4 files directly to viewers. -- **Live RAM-Disk Transmuxing**: Low-latency HLS restreaming (`/dev/shm`) allows authenticated viewers to watch private live streams without exposing individual client IPs. -- **Lossless In-Browser Clip Cutter**: Web-based timeline selector exports shareable MP4 clips in seconds via keyframe-aligned FFmpeg slicing (`-c copy`). -- **Granular RBAC Security**: Multi-tier permissions (`Admin`, `Viewer`, `Member`, `Live-only`) with channel-specific allowlists and brute-force protection. - ---- - -## 🏗️ Architecture & Pipeline +| ![System status](docs/screenshots/admin_panel.png) | ![Phone layout](docs/screenshots/mobile_view.png) | + +## Features + +- **Automatic recording** of Twitch and YouTube channels (Streamlink, yt-dlp, + FFmpeg), with reconnects, segment salvage and a disk-space guard that pauses + new recordings before the disk fills up. +- **Chat archive and replay**: chat is stored next to each recording and + replayed in sync with the video, including badges and emotes. +- **Live viewing** through a low-latency HLS restream from a RAM disk, so + viewers watch through your server instead of the platform. +- **Clips**: cut lossless clips in the browser (keyframe-aligned `-c copy`). +- **Transcripts** with Whisper, run in a separate process. +- **Cloud archive**: offload recordings to S3-compatible storage (AWS, R2, B2, + Wasabi, MinIO, Hetzner), Google Drive, Dropbox, OneDrive or WebDAV. + Credentials are encrypted with AES-GCM; uploads and local deletion are off + until you enable them. +- **Access control**: roles (Admin, Member, Viewer, Live-only), groups and + per-channel allowlists, login rate limits and temporary IP bans. +- **OBS overlays** for chat, alert sounds and text-to-speech. +- **Efficient delivery**: Nginx serves video files via `X-Accel-Redirect` + with byte ranges; SQLite in WAL mode, optional Redis cache. + +## How it works ```mermaid -flowchart TD - subgraph Ingestion ["1. Stream Ingestion"] - Twitch["Twitch / Kick / YouTube"] -->|Streamlink CLI| Recorder["Stream Recorder Daemon"] - Twitch -->|IRC / WebSocket| ChatWorker["Chat Archiver"] - end - - subgraph Storage ["2. Storage Engine"] - Recorder -->|H.264 Faststart MP4| Disk[("NVMe Storage / VOD Vault")] - ChatWorker -->|JSONL / JSON| Disk - Recorder -->|Segmented HLS| RAMDisk[("Live RAM-Disk /dev/shm")] - Metadata["State & RBAC"] --> SQLite[("SQLite WAL Mode")] - end - - subgraph WebEngine ["3. Application & Offload Layer"] - Flask["Flask WebUI Engine"] <--> SQLite - Nginx["Nginx Reverse Proxy"] <-->|Auth & Routing| Flask - Nginx -->|X-Accel-Redirect / Range HTTP 206| Disk - Nginx -->|Direct Static HLS| RAMDisk - end - - subgraph Clients ["4. Client Experience"] - Nginx --> Desktop["Desktop Browser Player"] - Nginx --> Mobile["Mobile PWA (Dynamic Island Safe)"] - Nginx --> ClipEditor["In-Browser Clip Cutter"] - end +flowchart LR + P["Twitch / YouTube"] -->|Streamlink, yt-dlp| R["Recorder"] + P -->|IRC| C["Chat archiver"] + R -->|MP4| D[("Archive")] + R -->|HLS| M[("RAM disk")] + C --> D + W["Worker: remux, thumbnails,
transcripts, cloud sync"] <--> D + U["Web UI (Flask)"] <--> S[("SQLite")] + N["Nginx"] <-->|auth| U + N -->|X-Accel-Redirect| D + N --> M + B["Browser"] <--> N ``` ---- +Three services run side by side: `recorder`, `worker` and `webui`. -## ⚡ 1-Command Linux Installation & System Registration +## Installation -For production installations on Ubuntu 22.04+ or Debian 12+, run the automated installer: +### Ubuntu 22.04+ / Debian 12+ ```bash -# Clone and run the turnkey installer: git clone https://github.com/Blackwellcode/streamvault.git /tmp/streamvault cd /tmp/streamvault && sudo ./install.sh ``` -The installer automatically: -1. Installs all system dependencies (`ffmpeg`, `python3-venv`, `nginx`, `sqlite3`). -2. Configures the isolated service user and directories (`/opt/streamvault`, `/var/lib/streamvault`). -3. Registers systemd services (`streamvault-webui.service`, `streamvault-recorder.service`). -4. Configures Nginx with `X-Accel-Redirect` kernel video offload. -5. **Registers the global Linux management command** `/usr/local/bin/streamvault`: +The installer sets up FFmpeg, Nginx and a Python environment, creates the +`streamvault` service user, registers the systemd services and prints a +generated admin password. Afterwards manage the installation with: ```bash -# Manage your installation from anywhere: -streamvault status # Check daemons, health, and RAM-disk -streamvault logs all # Follow live daemon logs -streamvault restart # Restart services -streamvault update # Pull Git updates, upgrade deps, and restart -streamvault config # Edit /etc/streamvault/env -streamvault check # Validate Python syntax & Nginx integrity +streamvault status # services, health, RAM disk +streamvault logs all # follow all service logs +streamvault restart # restart services +streamvault update # pull updates, upgrade dependencies, restart +streamvault config # edit /etc/streamvault/env ``` ---- - -## 🐳 Docker Deployment +Step-by-step manual installation and TLS setup: [INSTALL.md](INSTALL.md). -To run StreamVault in production via Docker Compose: +### Docker Compose ```bash -# 1. Clone repository git clone https://github.com/Blackwellcode/streamvault.git cd streamvault - -# 2. Copy environment template cp .env.example .env - -# 3. Set your production secrets in .env -nano .env - -# 4. Build and run +python3 -c "import secrets; print('VOD_PASS=' + secrets.token_urlsafe(24))" +python3 -c "import secrets; print('VOD_SECRET_KEY=' + secrets.token_hex(32))" +# put both values and VOD_USER into .env, then: docker compose up -d --build ``` -Access the service at `http://localhost:8090`. +The UI is served on `http://localhost:8090`. Recordings, the database and +caches are stored in `./data`. -For detailed step-by-step production instructions (including bare-metal Ubuntu/Debian deployment, systemd daemons, and SSL setup), see [INSTALL.md](INSTALL.md). +**Put StreamVault behind HTTPS before exposing it to the internet** and set +`VOD_SECURE_COOKIES=true` once it is. ---- +## Configuration -## ⚙️ Configuration Reference - -Key variables configurable via `.env`: +All settings are environment variables (`/etc/streamvault/env`, or `.env` for +Docker). The most important ones: | Variable | Default | Purpose | | :--- | :--- | :--- | -| `VOD_HOST` | `0.0.0.0` | Bind IP for the Python WebUI. | -| `VOD_PORT` | `8080` | Bind port for the Python WebUI. | -| `VOD_USER` | *required* | Initial administrator username (production). | -| `VOD_PASS` | *required* | Initial administrator password (production). | -| `VOD_SECRET_KEY` | *required* | Secret key for signed session cookies. | -| `VOD_BASE_DIR` | `./data` | Root path for databases and video storage. | -| `LIVE_HLS_DIR` | `/dev/shm/...` | High-speed RAM-disk path for live streaming. | -| `VOD_USE_X_ACCEL` | `true` | Enables Nginx `X-Accel-Redirect` byte-range streaming. | -| `REDIS_URL` | *none* | Optional Redis connection string for caching. | +| `VOD_USER`, `VOD_PASS` | *required* | Initial administrator account. | +| `VOD_SECRET_KEY` | *required* | Signs session cookies. Weak values are rejected. | +| `VOD_BASE_DIR` | `./data` if present, else the app directory | Archives, database and caches. | +| `VOD_PUBLIC_BASE` | *empty* | Public origin for share links and OAuth callbacks. | +| `VOD_SECURE_COOKIES` | `false` | Set to `true` once HTTPS is in place. | +| `VOD_TRUSTED_PROXIES` | `127.0.0.1,::1` | Proxies (IPs or CIDR) whose `X-Real-IP` is trusted for rate limits. | +| `LIVE_HLS_DIR` | `/dev/shm/streamvault_hls` | RAM disk for live HLS segments. | +| `VOD_USE_X_ACCEL` | `true` | Let Nginx serve video files. | +| `STORAGE_ENCRYPTION_KEY` | `VOD_SECRET_KEY` | Key for stored cloud credentials. Keep it stable. | +| `REDIS_URL` | *none* | Optional Redis cache. | + +The full list with explanations is in [.env.example](.env.example). + +## Cloud storage + +Add providers in the web UI (**Systemsteuerung → Cloud-Speicher**) or on the +command line: + +```bash +streamvault storage add --type s3 --name "R2" \ + --set bucket=streamvault --set access_key=... --set secret_key=... \ + --set endpoint_url=https://.r2.cloudflarestorage.com +streamvault storage test 1 +streamvault storage settings --set auto_upload=true +``` + +The local copy stays authoritative: files are only removed locally after a +successful upload and an optional size check, and only if you enable it. OAuth +providers (Google Drive, Dropbox, OneDrive) need the callback URL +`https:///admin/storage/oauth/callback`. + +## Legal notice + +StreamVault is a tool for archiving streams you are allowed to archive, such +as your own channel or content whose rights holder agreed. Recording, +storing or redistributing other people's streams can violate copyright and the +platforms' terms of service. You are responsible for how you use it. + +## Contributing ---- +Bug reports, fixes and improvements are welcome. Read +[CONTRIBUTING.md](CONTRIBUTING.md) first; security issues go through +[SECURITY.md](SECURITY.md). Changes are listed in [CHANGELOG.md](CHANGELOG.md). -## 🔒 Security & Privacy Invariants +## Support the project -1. **Zero Secret Leakage**: No API tokens, passwords, or OAuth tokens are ever logged, passed via CLI flags visible in `ps aux`, or stored in source control. -2. **Subprocess Isolation**: All FFmpeg and Streamlink subroutines execute with `shell=False` and strict execution timeouts. -3. **Process Safety**: Daemon termination utilizes process-group validation preventing self-kill or supervisor collisions. -4. **Range Request Protection**: Offloading to Nginx ensures memory exhaustion attacks via concurrent scrubbing cannot starve web application threads. +StreamVault is free and developed in spare time. If it is useful to you, you +can support further development: ---- +Buy me a coffee -## 📄 License +## License -This project is licensed under the [MIT License](LICENSE). +[MIT](LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..78cc6a7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,26 @@ +# Security Policy + +## Reporting a vulnerability + +Please report vulnerabilities privately through GitHub: +**Security → Report a vulnerability** on this repository +([private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)). + +Do not open a public issue or pull request for a vulnerability before a fix +is released. + +Useful details: + +- affected version or commit +- deployment type (installer, Docker, manual) and whether a reverse proxy is used +- steps to reproduce and the impact you observed + +## Supported versions + +Only the latest release on the default branch receives security fixes. + +## Scope + +StreamVault is self-hosted. Operators are responsible for TLS, firewalling and +keeping `VOD_SECRET_KEY` / `STORAGE_ENCRYPTION_KEY` secret. Reports about +insecure defaults that ship with this repository are in scope. diff --git a/WEBUI/static/cast_playback.js b/WEBUI/static/cast_playback.js deleted file mode 100644 index 8c17c0c..0000000 --- a/WEBUI/static/cast_playback.js +++ /dev/null @@ -1,467 +0,0 @@ -(function (global) { - "use strict"; - - const RECEIVER_APP_ID = "CC1AD845"; - let castReady = false; - let castUnavailable = false; - let senderScriptRequested = false; - - function isMobileCastSurface() { - const body = document.body; - if (!body || !body.classList) return false; - // Only dedicated mobile player / mobile watch templates. - return ( - body.classList.contains("page-mobile-player") || - body.classList.contains("page-twitch-watch-mobile") - ); - } - - function canNativeHls(video) { - if (!video || typeof video.canPlayType !== "function") return false; - return !!( - video.canPlayType("application/vnd.apple.mpegurl") || - video.canPlayType("application/x-mpegURL") - ); - } - - function preferNativeHls(video) { - if (!canNativeHls(video)) return false; - // Only genuine Apple devices have reliable native HLS decoding in