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.
This commit is contained in:
@@ -48,6 +48,12 @@ encoding:
|
|||||||
bluray:
|
bluray:
|
||||||
crf: 29
|
crf: 29
|
||||||
preset: 3
|
preset: 3
|
||||||
|
webdl:
|
||||||
|
crf: 30
|
||||||
|
preset: 3
|
||||||
|
tvrip:
|
||||||
|
crf: 32
|
||||||
|
preset: 2
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
input: "./input"
|
input: "./input"
|
||||||
@@ -65,6 +71,10 @@ paths:
|
|||||||
| `encoding.dvd.preset` | SVT-AV1 preset for SD sources | `2` |
|
| `encoding.dvd.preset` | SVT-AV1 preset for SD sources | `2` |
|
||||||
| `encoding.bluray.crf` | SVT-AV1 CRF for HD sources | `29` |
|
| `encoding.bluray.crf` | SVT-AV1 CRF for HD sources | `29` |
|
||||||
| `encoding.bluray.preset` | SVT-AV1 preset for HD sources | `3` |
|
| `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.input` | Folder polled for new `.mkv` files | `./input` |
|
||||||
| `paths.output` | Destination for finished encodes | `./output` |
|
| `paths.output` | Destination for finished encodes | `./output` |
|
||||||
| `paths.originals` | Where source files are moved on success (unless `-d`) | `./originals` |
|
| `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.
|
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
|
## 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).
|
- Records width, height, and sample aspect ratio (SAR).
|
||||||
- Runs `ffmpeg -vf idet` to detect interlacing (presence of `TFF`/`BFF` in stderr).
|
- 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).
|
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:
|
4. **Detect media type**:
|
||||||
- `width × height < 600,000` → **DVD** profile.
|
- First, check the filename for a `dvd` / `bluray` / `webdl` / `tvrip` token (case-insensitive). If present, that wins.
|
||||||
- Otherwise → **Blu-ray** profile.
|
- Otherwise, fall back to pixel count: `width × height < 600,000` → DVD, else Blu-ray.
|
||||||
The chosen profile selects which CRF/preset pair from the config to use.
|
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.
|
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.
|
6. **Extract audio** to `audio.wav` (PCM s16le, 48 kHz) in the input directory.
|
||||||
7. **Encode audio** with `opusenc --bitrate 128k` → `audio.opus`.
|
7. **Encode audio** with `opusenc --bitrate 128k` → `audio.opus`.
|
||||||
|
|||||||
+17
-6
@@ -95,14 +95,25 @@ func processFile(inputPath string, cfg *types.Config, enc *encoder.Encoder, meta
|
|||||||
log.ErrorFile(inputPath, "Getting stream languages", err.Error())
|
log.ErrorFile(inputPath, "Getting stream languages", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType := watcher.DetectMediaType(width, height)
|
mediaType := metadata.ParseMediaType(filename)
|
||||||
crf := cfg.Encoding.DVD.CRF
|
if mediaType == "" {
|
||||||
preset := cfg.Encoding.DVD.Preset
|
mediaType = watcher.DetectMediaType(width, height)
|
||||||
if mediaType == types.MediaTypeBluRay {
|
|
||||||
crf = cfg.Encoding.Bluray.CRF
|
|
||||||
preset = cfg.Encoding.Bluray.Preset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
var meta *types.Metadata
|
||||||
if isSeries && tvmazeID != "" && season != "" && episode != "" {
|
if isSeries && tvmazeID != "" && season != "" && episode != "" {
|
||||||
meta, err = metaClient.FetchSeriesMetadata(tvmazeID, season, episode)
|
meta, err = metaClient.FetchSeriesMetadata(tvmazeID, season, episode)
|
||||||
|
|||||||
@@ -61,6 +61,18 @@ func Load(configPath string) (*types.Config, error) {
|
|||||||
if cfg.Encoding.Bluray.Preset == 0 {
|
if cfg.Encoding.Bluray.Preset == 0 {
|
||||||
cfg.Encoding.Bluray.Preset = 3
|
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
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
imdbRegex = regexp.MustCompile(`tt(\d+)`)
|
imdbRegex = regexp.MustCompile(`tt(\d+)`)
|
||||||
tvmRegex = regexp.MustCompile(`(?i)tvm(\d+)`)
|
tvmRegex = regexp.MustCompile(`(?i)tvm(\d+)`)
|
||||||
seRegex = regexp.MustCompile(`(?i)s(\d+)e(\d+)`)
|
seRegex = regexp.MustCompile(`(?i)s(\d+)e(\d+)`)
|
||||||
|
mediaTypeRegex = regexp.MustCompile(`(?i)\b(dvd|bluray|webdl|tvrip)\b`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type OMDbResponse struct {
|
type OMDbResponse struct {
|
||||||
@@ -86,6 +87,28 @@ func parseInt(s string) int {
|
|||||||
return n
|
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) {
|
func (c *Client) FetchMovieMetadata(imdbID string) (*types.Metadata, error) {
|
||||||
url := fmt.Sprintf("%s?i=%s&apikey=%s", omdbAPIURL, imdbID, c.omdbAPIKey)
|
url := fmt.Sprintf("%s?i=%s&apikey=%s", omdbAPIURL, imdbID, c.omdbAPIKey)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ type MediaType string
|
|||||||
const (
|
const (
|
||||||
MediaTypeDVD MediaType = "DVD"
|
MediaTypeDVD MediaType = "DVD"
|
||||||
MediaTypeBluRay MediaType = "Blu-ray"
|
MediaTypeBluRay MediaType = "Blu-ray"
|
||||||
|
MediaTypeWebDL MediaType = "WebDL"
|
||||||
|
MediaTypeTVRip MediaType = "TVRip"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Job struct {
|
type Job struct {
|
||||||
@@ -42,6 +44,8 @@ type Config struct {
|
|||||||
type EncodingConfig struct {
|
type EncodingConfig struct {
|
||||||
DVD EncodingParams `yaml:"dvd"`
|
DVD EncodingParams `yaml:"dvd"`
|
||||||
Bluray EncodingParams `yaml:"bluray"`
|
Bluray EncodingParams `yaml:"bluray"`
|
||||||
|
WebDL EncodingParams `yaml:"webdl"`
|
||||||
|
TVRip EncodingParams `yaml:"tvrip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EncodingParams struct {
|
type EncodingParams struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user