init
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
# NGINX HLS VOD
|
||||
|
||||
Docker Compose deployment for local MP4 to HLS VOD packaging using Kaltura
|
||||
`nginx-vod-module`. The service maps a media tree read-only into the container
|
||||
and preserves its hierarchy in HLS URLs.
|
||||
|
||||
## Scope
|
||||
|
||||
- ARM64 Linux Docker host
|
||||
- Trusted LAN HTTP service on port `8124`
|
||||
- One H.264 video track and one AAC audio track per MP4
|
||||
- One configured browser origin for CORS
|
||||
- No authentication, TLS, ABR, external subtitles, or multiple audio/video
|
||||
tracks
|
||||
|
||||
An attached-picture video stream such as MP4 cover art is ignored by the media
|
||||
validator.
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy the environment template and replace the example player origin:
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Required values:
|
||||
|
||||
```dotenv
|
||||
PLAYER_ORIGIN=http://192.168.1.20:3000
|
||||
MEDIA_DIR=/srv/media
|
||||
CATALOG_CACHE_DIR=/srv/nginx-hls/catalog-cache
|
||||
HLS_PORT=8124
|
||||
```
|
||||
|
||||
`MEDIA_DIR=./data` is appropriate only for local development. Production uses
|
||||
an absolute path on the Docker host. The container user must be able to read
|
||||
the mounted files and directories. `CATALOG_CACHE_DIR` stores catalog snapshots
|
||||
and generated covers. Create it before startup, for example
|
||||
`install -d -m 0700 /srv/nginx-hls/catalog-cache`.
|
||||
`CATALOG_REFRESH_INTERVAL_SECONDS` defaults to `21600` (6 hours).
|
||||
|
||||
The Dockerfile deliberately fixes NGINX `1.30.5` and Kaltura module commit
|
||||
`26f06877b0f2a2336e59cda93a3de18d7b23a3e2`. Change either only as a deliberate
|
||||
upgrade, then rebuild and run the smoke test.
|
||||
|
||||
## Run
|
||||
|
||||
Start the stack after creating the configured cache directory:
|
||||
|
||||
```sh
|
||||
set -a
|
||||
. ./.env
|
||||
set +a
|
||||
install -d -m 0700 "$CATALOG_CACHE_DIR"
|
||||
docker compose --env-file .env up -d --build
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
The catalog validates files during its background refresh and omits invalid
|
||||
MP4s from the published snapshot. Use `./scripts/validate-media.sh "$MEDIA_DIR"`
|
||||
before publishing only when an update must contain no invalid files at all.
|
||||
|
||||
For a media file at:
|
||||
|
||||
```text
|
||||
/srv/media/movies/example.mp4
|
||||
```
|
||||
|
||||
request this HLS master playlist:
|
||||
|
||||
```text
|
||||
http://<vod-host>:8124/hls/movies/example.mp4/master.m3u8
|
||||
```
|
||||
|
||||
Path segments must be URL encoded by clients. There is intentionally no
|
||||
directory listing or progressive-download route.
|
||||
|
||||
## Media Catalog
|
||||
|
||||
Clients discover validated MP4 files through the same origin:
|
||||
|
||||
```text
|
||||
GET /api/media
|
||||
```
|
||||
|
||||
The response is a flat array from the latest completed catalog snapshot:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"path": "movies/example.mp4",
|
||||
"name": "example",
|
||||
"playlistUrl": "/hls/movies/example.mp4/master.m3u8",
|
||||
"coverUrl": "/api/media/covers/<cache-key>.jpg"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Catalog requests never scan media or run FFmpeg. A background worker scans the
|
||||
tree at startup and then every six hours, validates one H.264 video track plus
|
||||
one AAC audio track, and pre-generates covers for every valid file. It publishes
|
||||
the completed JSON and JPEGs atomically, so clients continue to read the last
|
||||
successful snapshot while a refresh runs or fails. Without any completed
|
||||
snapshot, media and cover requests return `503`.
|
||||
|
||||
Use the `coverUrl` from the media item to request a pre-generated cover:
|
||||
|
||||
```text
|
||||
GET /api/media/covers/<cache-key>.jpg
|
||||
```
|
||||
|
||||
`GET /api/media/status` reports the refresh state, latest successful timestamp,
|
||||
valid and skipped file counts, and the latest build error. Trigger an immediate
|
||||
asynchronous rebuild with `POST /api/media/rescan`; concurrent requests do not
|
||||
start duplicate builds. The service is intended for a trusted LAN, so this
|
||||
endpoint has no additional authentication. Browser caching remains disabled.
|
||||
|
||||
```sh
|
||||
curl -sS http://<vod-host>:8124/api/media/status
|
||||
curl -i -X POST http://<vod-host>:8124/api/media/rescan
|
||||
```
|
||||
|
||||
Run the smoke test after the container becomes healthy. Its default test media
|
||||
is `data/aa/1170193193-1-192.mp4`; override it with a relative, URL-encoded
|
||||
`TEST_MEDIA_PATH` when needed.
|
||||
|
||||
```sh
|
||||
export PLAYER_ORIGIN=http://192.168.1.20:3000
|
||||
./scripts/smoke-test.sh
|
||||
```
|
||||
|
||||
## Publishing Updates
|
||||
|
||||
Use a staging directory on the same filesystem as the media library, validate
|
||||
the staged MP4, and atomically rename it into place. Restart the container only
|
||||
after the rename completes:
|
||||
|
||||
```sh
|
||||
./scripts/validate-media.sh /srv/media/.staging
|
||||
mv -T /srv/media/.staging/example.mp4 /srv/media/movies/example.mp4
|
||||
./scripts/refresh-vod.sh
|
||||
```
|
||||
|
||||
The service sends `Cache-Control: no-store` and the restart clears VOD caches.
|
||||
This makes a replacement visible promptly, but intentionally interrupts active
|
||||
playback for a short period. Do not overwrite MP4 files in place.
|
||||
|
||||
## Operations
|
||||
|
||||
- Health endpoint: `GET /healthz`
|
||||
- Logs: `docker compose logs -f vod`
|
||||
- Stop: `docker compose down`
|
||||
- The host firewall should limit TCP `8124` to the trusted LAN. This service
|
||||
does not provide TLS or authentication.
|
||||
|
||||
The first build downloads the fixed source inputs and base image. The ARM64
|
||||
target host therefore needs access to Docker registries, `nginx.org`, and
|
||||
GitHub during `docker compose build`.
|
||||
Reference in New Issue
Block a user