Files
av1dae/MANUAL.md
T
Esa Kataja 244bdce586 add: WebDL / TVRip media types and filename-token override
Source kind is now declared per-file via a `dvd` / `bluray` / `webdl` /
`tvrip` token in the basename (case-insensitive, word-bounded). Matches
the existing `tt…` / `tvm…` filename convention. When no token is
present, falls back to the pixel-count guess in DetectMediaType, which
still only chooses between DVD and Blu-ray.

The parsed media type drives both ORIGINAL_MEDIA_TYPE in the muxed
metadata and the CRF/preset profile used for encoding. EncodingConfig
gains `webdl` and `tvrip` sub-blocks with conservative defaults
(WebDL 30/3, TVRip 32/2); user can override in config.yaml.

Manual updated with the new tokens, config fields, and pipeline
description.
2026-05-16 20:17:10 +03:00

266 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# videnc-vibe — User Manual
A Go CLI that watches a folder for `.mkv` rips, transcodes them to SVT-AV1 video + Opus audio, embeds metadata fetched from OMDb (movies) or TVmaze (series), and files the results into output/originals/failed directories.
---
## 1. Requirements
External binaries must be on `$PATH` (the program checks them at startup and exits if any are missing):
- `ffmpeg`
- `ffprobe`
- `opusenc`
Go (only to build):
- Go 1.x with module support.
API keys:
- OMDb API key (free at https://www.omdbapi.com/). Required for movie metadata. TVmaze is unauthenticated.
---
## 2. Build
```bash
go build -o videnc-vibe ./cmd/videnc/
```
This produces a single static binary `./videnc-vibe`.
---
## 3. Configuration
By default the config is loaded from `~/.config/videnc-vibe/config.yaml`. Pass `-c /path/to/config.yaml` to override.
Example (`config.example.yaml`):
```yaml
omdb_api_key: "YOUR_API_KEY_HERE"
encoding:
dvd:
crf: 30
preset: 2
bluray:
crf: 29
preset: 3
webdl:
crf: 30
preset: 3
tvrip:
crf: 32
preset: 2
paths:
input: "./input"
output: "./output"
originals: "./originals"
failed: "./failed"
```
### Field reference
| Field | Meaning | Default |
|---|---|---|
| `omdb_api_key` | OMDb API key for movie metadata lookup | (none — movies won't get metadata without it) |
| `encoding.dvd.crf` | SVT-AV1 CRF for SD sources | `30` |
| `encoding.dvd.preset` | SVT-AV1 preset for SD sources | `2` |
| `encoding.bluray.crf` | SVT-AV1 CRF for HD sources | `29` |
| `encoding.bluray.preset` | SVT-AV1 preset for HD sources | `3` |
| `encoding.webdl.crf` | SVT-AV1 CRF for WebDL sources | `30` |
| `encoding.webdl.preset` | SVT-AV1 preset for WebDL sources | `3` |
| `encoding.tvrip.crf` | SVT-AV1 CRF for TVRip sources | `32` |
| `encoding.tvrip.preset` | SVT-AV1 preset for TVRip sources | `2` |
| `paths.input` | Folder polled for new `.mkv` files | `./input` |
| `paths.output` | Destination for finished encodes | `./output` |
| `paths.originals` | Where source files are moved on success (unless `-d`) | `./originals` |
| `paths.failed` | Where source + partial output go on failure | `./failed` |
All four directories are created on startup if they don't exist.
---
## 4. Running
```bash
./videnc-vibe # default config path, keep originals
./videnc-vibe -d # delete originals after successful encode
./videnc-vibe -c /etc/videnc.yaml # custom config
./videnc-vibe -c /etc/videnc.yaml -d
```
Flags:
- `-d` — delete the source `.mkv` after a successful encode instead of moving it to `originals/`.
- `-c PATH` — path to config file.
Stop with `Ctrl+C` (SIGINT) or `SIGTERM`. A signal triggers a clean shutdown after the current poll cycle.
The program runs as a foreground daemon. It scans the input directory on startup and every 15 seconds thereafter.
---
## 5. Input filename convention
The base name of each `.mkv` in `paths.input` is parsed to decide what metadata to fetch. Three forms are recognized:
### 5.1 Movies — IMDb ID
Filename must contain an IMDb tag of the form `tt<digits>`.
Examples:
```
Heat.tt0113277.mkv
some-rip-tt0114369.mkv
```
→ OMDb is queried for that IMDb ID. Title, release date, and IMDb ID are embedded.
### 5.2 Series — TVmaze ID + season/episode
Filename must contain BOTH:
- `tvm<digits>` — the TVmaze show ID (case-insensitive).
- `s<digits>e<digits>` — season and episode (case-insensitive).
Examples:
```
Breaking.Bad.tvm169.S01E01.mkv
the-wire.TVM75.s2e5.mkv
```
→ TVmaze is queried for that show/season/episode. Show name (Collection), episode title, season, episode, airdate, and the show's IMDb ID are embedded.
### 5.3 No recognizable tags
If neither pattern matches, encoding still proceeds but the file is treated as having no metadata. The output is named with a random hex string and an `.nometadata.mkv` suffix.
### 5.4 Optional: source media type
Any filename can additionally contain a media-type token (case-insensitive, word-bounded):
- `dvd`
- `bluray`
- `webdl`
- `tvrip`
Examples:
```
Heat.tt0113277.bluray.mkv
some-rip.tvm169.S01E01.webdl.mkv
old.broadcast.tvrip.tt0066026.mkv
```
The token controls **both** the `ORIGINAL_MEDIA_TYPE` metadata tag written into the output and which `encoding.<type>.crf` / `encoding.<type>.preset` pair is used.
If no token is present, the program falls back to guessing from pixel count: `width × height < 600,000` → DVD, otherwise Blu-ray. WebDL and TVRip are never auto-detected — they must be declared via the token.
---
## 6. The processing pipeline
For every `.mkv` found in `paths.input`, the program runs these steps in order. Any error sends the source file (and the partial `output.mkv`, if any) to `paths.failed`.
1. **Parse filename** → determines whether this is a movie or series, and what IDs to use.
2. **Probe video** with `ffprobe`:
- Picks the first video stream whose codec is `mpeg2video`, `h264`, or `hevc`.
- Records width, height, and sample aspect ratio (SAR).
- Runs `ffmpeg -vf idet` to detect interlacing (presence of `TFF`/`BFF` in stderr).
3. **Probe stream languages** with `ffprobe` — collects `language` tags for every audio/subtitle stream so they can be re-applied after encoding (FFmpeg's `-map_metadata -1` strips them otherwise).
4. **Detect media type**:
- First, check the filename for a `dvd` / `bluray` / `webdl` / `tvrip` token (case-insensitive). If present, that wins.
- Otherwise, fall back to pixel count: `width × height < 600,000` → DVD, else Blu-ray.
The chosen profile selects which `encoding.<type>.crf` / `encoding.<type>.preset` pair from the config to use, and is written into the `ORIGINAL_MEDIA_TYPE` metadata tag.
5. **Fetch metadata** from OMDb or TVmaze depending on the parsed filename. Failures here are logged but do not abort the encode — the file is just encoded without metadata.
6. **Extract audio** to `audio.wav` (PCM s16le, 48 kHz) in the input directory.
7. **Encode audio** with `opusenc --bitrate 128k``audio.opus`.
8. **Calculate display width** from SAR. If SAR ≠ `1:1`, the width is rescaled so the output has square pixels, using a `zscale` filter (`spline36`). Height is preserved.
9. **Encode video** with FFmpeg:
- Video filter chain: `bwdif=mode=0:par=-1:-1,zscale=w=W:h=H:filter=spline36` if interlaced, else just the `zscale` step.
- Codec: `libsvtav1`, `-pix_fmt yuv420p10le`.
- `-crf` and `-preset` from the selected profile.
- `-svtav1-params film-grain=10:film-grain-denoise=1:scd=1:qm-min=4:qm-max=15:keyint=10s`.
- Streams mapped: video from input, subtitles from input (`0:s?` — optional), audio from the re-encoded Opus files.
- Audio copied (`-c:a copy`), subtitles copied (`-c:s copy`).
- `-map_metadata -1` strips global metadata; per-stream language tags are then re-applied from the ffprobe pass.
- Container metadata written: `TITLE`, `DATE_RELEASED`, `IMDBID`, `ORIGINAL_MEDIA_TYPE`. For series: also `COLLECTION`, `SEASON`, `EPISODE`, `TVMAZE_ID`.
- Output written to `output.mkv` in the input directory.
10. **Clean up** intermediate `.wav` and `.opus` files.
11. **Rename and move** `output.mkv` to `paths.output` with a final name:
- Series → `<sanitized-show-name>.S<NN>E<NN>.mkv`
- Movie → `<sanitized-title>.<imdbID>.mkv`
- No metadata → `<8-hex-chars>.nometadata.mkv`
Sanitization keeps `az AZ 09 - ä ö Ä Ö` only.
12. **Dispose of the source**:
- `-d` flag set → delete the original `.mkv`.
- Otherwise → move it to `paths.originals`.
---
## 7. Output naming examples
| Input filename | Result in `paths.output` |
|---|---|
| `Heat.tt0113277.mkv` | `Heat.tt0113277.mkv` |
| `Breaking.Bad.tvm169.S01E01.mkv` | `BreakingBad.S01E01.mkv` |
| `unrecognized-rip.mkv` | `a1b2c3d4.nometadata.mkv` |
The IMDb ID returned by the API is used, not the one from the filename, so a typo in the filename would surface there.
---
## 8. Logs
Three log files are written to the **current working directory** (not the config paths):
- `info_YYYY-MM-DD.log` — INFO messages, dated.
- `error_YYYY-MM-DD.log` — ERROR messages, dated.
- `structured.json` — one JSON object per line, every entry (info + error), with `timestamp`, `level`, `message`, optional `error` and `file` fields.
Run the program from the directory where you want the logs to land.
Note: a number of `DEBUG` lines are printed to stdout/stderr (ffprobe output, calculated zscale width, the full ffmpeg command, etc.). These are intentional but not written to the log files.
---
## 9. Failure handling
If any step from probing through encoding through renaming fails:
- The source `.mkv` is moved to `paths.failed`.
- Any partial `output.mkv` left in the input directory is also moved to `paths.failed`.
- The error is logged to `error_*.log` and `structured.json` with the source file path.
The watcher continues with the next file; one bad rip won't stop the daemon.
---
## 10. Polling behavior
- Input directory is scanned every **15 seconds** (clamped to a 1030 s range).
- Only files matching `*.mkv` directly in `paths.input` are picked up — no recursion.
- Files are processed **sequentially**, one at a time, in the order `filepath.Glob` returns them (alphabetical on Linux).
- There is no atomic-write detection. If you're copying a large file into `paths.input`, copy it to a different name first and `mv` it into place once complete, otherwise the watcher may try to encode a half-written file.
---
## 11. Project layout
```
cmd/videnc/main.go CLI entrypoint and per-file orchestration
internal/config/ YAML config load + defaults + mkdir
internal/watcher/ Polling loop, media-type detection by pixel count
internal/encoder/ ffprobe/ffmpeg/opusenc wrapper, transcode pipeline
internal/metadata/ Filename parsing, OMDb + TVmaze clients
internal/mover/ File rename/move/delete helpers
internal/logger/ Plain + JSON logging
pkg/types/types.go Shared structs (Config, Job, Metadata, …)
```