From 244bdce58631b299a05cbc64f73e0b5e1107db06 Mon Sep 17 00:00:00 2001 From: Esa Kataja Date: Sat, 16 May 2026 20:17:10 +0300 Subject: [PATCH] add: WebDL / TVRip media types and filename-token override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- MANUAL.md | 39 +++++++++++++++++++++++++++++++---- cmd/videnc/main.go | 23 +++++++++++++++------ internal/config/config.go | 12 +++++++++++ internal/metadata/metadata.go | 29 +++++++++++++++++++++++--- pkg/types/types.go | 4 ++++ 5 files changed, 94 insertions(+), 13 deletions(-) diff --git a/MANUAL.md b/MANUAL.md index 6345cc6..ac68d76 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -48,6 +48,12 @@ encoding: bluray: crf: 29 preset: 3 + webdl: + crf: 30 + preset: 3 + tvrip: + crf: 32 + preset: 2 paths: input: "./input" @@ -65,6 +71,10 @@ paths: | `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` | @@ -131,6 +141,27 @@ the-wire.TVM75.s2e5.mkv 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..crf` / `encoding..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 @@ -143,10 +174,10 @@ For every `.mkv` found in `paths.input`, the program runs these steps in order. - 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** from pixel count: - - `width × height < 600,000` → **DVD** profile. - - Otherwise → **Blu-ray** profile. - The chosen profile selects which CRF/preset pair from the config to use. +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..crf` / `encoding..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`. diff --git a/cmd/videnc/main.go b/cmd/videnc/main.go index 150cbf2..c4874c8 100644 --- a/cmd/videnc/main.go +++ b/cmd/videnc/main.go @@ -95,14 +95,25 @@ func processFile(inputPath string, cfg *types.Config, enc *encoder.Encoder, meta log.ErrorFile(inputPath, "Getting stream languages", err.Error()) } - mediaType := watcher.DetectMediaType(width, height) - crf := cfg.Encoding.DVD.CRF - preset := cfg.Encoding.DVD.Preset - if mediaType == types.MediaTypeBluRay { - crf = cfg.Encoding.Bluray.CRF - preset = cfg.Encoding.Bluray.Preset + mediaType := metadata.ParseMediaType(filename) + if mediaType == "" { + mediaType = watcher.DetectMediaType(width, height) } + var profile types.EncodingParams + switch mediaType { + case types.MediaTypeBluRay: + profile = cfg.Encoding.Bluray + case types.MediaTypeWebDL: + profile = cfg.Encoding.WebDL + case types.MediaTypeTVRip: + profile = cfg.Encoding.TVRip + default: + profile = cfg.Encoding.DVD + } + crf := profile.CRF + preset := profile.Preset + var meta *types.Metadata if isSeries && tvmazeID != "" && season != "" && episode != "" { meta, err = metaClient.FetchSeriesMetadata(tvmazeID, season, episode) diff --git a/internal/config/config.go b/internal/config/config.go index 457faa1..72dc11e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -61,6 +61,18 @@ func Load(configPath string) (*types.Config, error) { if cfg.Encoding.Bluray.Preset == 0 { cfg.Encoding.Bluray.Preset = 3 } + if cfg.Encoding.WebDL.CRF == 0 { + cfg.Encoding.WebDL.CRF = 30 + } + if cfg.Encoding.WebDL.Preset == 0 { + cfg.Encoding.WebDL.Preset = 3 + } + if cfg.Encoding.TVRip.CRF == 0 { + cfg.Encoding.TVRip.CRF = 32 + } + if cfg.Encoding.TVRip.Preset == 0 { + cfg.Encoding.TVRip.Preset = 2 + } return &cfg, nil } diff --git a/internal/metadata/metadata.go b/internal/metadata/metadata.go index b8d39e5..42da193 100644 --- a/internal/metadata/metadata.go +++ b/internal/metadata/metadata.go @@ -19,9 +19,10 @@ const ( ) var ( - imdbRegex = regexp.MustCompile(`tt(\d+)`) - tvmRegex = regexp.MustCompile(`(?i)tvm(\d+)`) - seRegex = regexp.MustCompile(`(?i)s(\d+)e(\d+)`) + imdbRegex = regexp.MustCompile(`tt(\d+)`) + tvmRegex = regexp.MustCompile(`(?i)tvm(\d+)`) + seRegex = regexp.MustCompile(`(?i)s(\d+)e(\d+)`) + mediaTypeRegex = regexp.MustCompile(`(?i)\b(dvd|bluray|webdl|tvrip)\b`) ) type OMDbResponse struct { @@ -86,6 +87,28 @@ func parseInt(s string) int { return n } +// ParseMediaType looks for a `.dvd.` / `.bluray.` / `.webdl.` / `.tvrip.` +// token (case-insensitive) in the filename and returns the matching MediaType. +// Returns an empty MediaType if no token is found, so the caller can fall back +// to the pixel-count heuristic. +func ParseMediaType(filename string) types.MediaType { + m := mediaTypeRegex.FindStringSubmatch(strings.TrimSuffix(filename, ".mkv")) + if m == nil { + return "" + } + switch strings.ToLower(m[1]) { + case "dvd": + return types.MediaTypeDVD + case "bluray": + return types.MediaTypeBluRay + case "webdl": + return types.MediaTypeWebDL + case "tvrip": + return types.MediaTypeTVRip + } + return "" +} + func (c *Client) FetchMovieMetadata(imdbID string) (*types.Metadata, error) { url := fmt.Sprintf("%s?i=%s&apikey=%s", omdbAPIURL, imdbID, c.omdbAPIKey) diff --git a/pkg/types/types.go b/pkg/types/types.go index f597210..db22642 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -9,6 +9,8 @@ type MediaType string const ( MediaTypeDVD MediaType = "DVD" MediaTypeBluRay MediaType = "Blu-ray" + MediaTypeWebDL MediaType = "WebDL" + MediaTypeTVRip MediaType = "TVRip" ) type Job struct { @@ -42,6 +44,8 @@ type Config struct { type EncodingConfig struct { DVD EncodingParams `yaml:"dvd"` Bluray EncodingParams `yaml:"bluray"` + WebDL EncodingParams `yaml:"webdl"` + TVRip EncodingParams `yaml:"tvrip"` } type EncodingParams struct {