Filter by audio format; apply filter changes live behind the dialog
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,8 @@ type MediaFile struct {
|
||||
VideoCodec string `json:"videoCodec"`
|
||||
VideoDynamicRange string `json:"videoDynamicRange"`
|
||||
VideoDynamicRangeType string `json:"videoDynamicRangeType"`
|
||||
AudioCodec string `json:"audioCodec"`
|
||||
AudioAdditional string `json:"audioAdditionalFeatures"`
|
||||
} `json:"mediaInfo"`
|
||||
Languages []struct {
|
||||
Name string `json:"name"`
|
||||
@@ -40,16 +42,43 @@ type FileAttrs struct {
|
||||
Codec string // CodecX265, CodecX264, CodecAV1 or ""
|
||||
HDR bool // HDR10, HDR10+, HLG, PQ (also with Dolby Vision on top)
|
||||
DV bool // Dolby Vision
|
||||
Audio []string // audio formats, see the Audio* constants
|
||||
Languages []string // audio languages
|
||||
}
|
||||
|
||||
// Audio formats offered as filters. Atmos and the DTS flavours overlap with
|
||||
// their carrier format: a "TrueHD Atmos" track is both Atmos and TrueHD, and
|
||||
// every DTS flavour also counts as plain DTS.
|
||||
const (
|
||||
AudioAtmos = "Atmos"
|
||||
AudioTrueHD = "TrueHD"
|
||||
AudioDTSX = "DTS:X"
|
||||
AudioDTSHD = "DTS-HD"
|
||||
AudioDTS = "DTS"
|
||||
AudioDDPlus = "DD+"
|
||||
AudioDD = "DD"
|
||||
AudioAAC = "AAC"
|
||||
AudioFLAC = "FLAC"
|
||||
AudioOpus = "Opus"
|
||||
)
|
||||
|
||||
var (
|
||||
reRes = regexp.MustCompile(`(?i)\b(2160|1080|720|576|480)p\b`)
|
||||
re265 = regexp.MustCompile(`(?i)(x\.?265|h\.?265|hevc)`)
|
||||
re264 = regexp.MustCompile(`(?i)(x\.?264|h\.?264|\bavc\b)`)
|
||||
reAV1 = regexp.MustCompile(`(?i)\bav1\b`)
|
||||
reDV = regexp.MustCompile(`(?i)(\bdv\b|dovi|dolby[ .]?vision)`)
|
||||
reHDR = regexp.MustCompile(`(?i)\b(hdr(10(\+|plus)?)?|hlg|pq)\b`)
|
||||
reRes = regexp.MustCompile(`(?i)\b(2160|1080|720|576|480)p\b`)
|
||||
re265 = regexp.MustCompile(`(?i)(x\.?265|h\.?265|hevc)`)
|
||||
re264 = regexp.MustCompile(`(?i)(x\.?264|h\.?264|\bavc\b)`)
|
||||
reAV1 = regexp.MustCompile(`(?i)\bav1\b`)
|
||||
reDV = regexp.MustCompile(`(?i)(\bdv\b|dovi|dolby[ .]?vision)`)
|
||||
reAtmos = regexp.MustCompile(`(?i)(atmos|\bddpa\b)`)
|
||||
reTrueHD = regexp.MustCompile(`(?i)true[ .-]?hd`)
|
||||
reDTSX = regexp.MustCompile(`(?i)\bdts[ .:-]?x\b`)
|
||||
reDTSHD = regexp.MustCompile(`(?i)dts[ .-]?(hd|ma|hra|es)`)
|
||||
reDTS = regexp.MustCompile(`(?i)\bdts([^a-z]|$)`)
|
||||
reDDPlus = regexp.MustCompile(`(?i)(e[ .-]?ac[ .-]?3|\bddp|dd\+|dolby digital plus)`)
|
||||
reDD = regexp.MustCompile(`(?i)(\bac[ .-]?3\b|\bdd[ .]?[0-9]|dolby digital\b)`)
|
||||
reAAC = regexp.MustCompile(`(?i)\baac`)
|
||||
reFLAC = regexp.MustCompile(`(?i)\bflac\b`)
|
||||
reOpus = regexp.MustCompile(`(?i)\bopus\b`)
|
||||
reHDR = regexp.MustCompile(`(?i)\b(hdr(10(\+|plus)?)?|hlg|pq)\b`)
|
||||
)
|
||||
|
||||
// Attrs derives the filterable properties. Radarr/Sonarr's media info is used
|
||||
@@ -77,6 +106,11 @@ func (f MediaFile) Attrs() FileAttrs {
|
||||
}
|
||||
a.DV = reDV.MatchString(dr)
|
||||
a.HDR = reHDR.MatchString(dr)
|
||||
audio := f.MediaInfo.AudioCodec + " " + f.MediaInfo.AudioAdditional
|
||||
if strings.TrimSpace(audio) == "" {
|
||||
audio = names
|
||||
}
|
||||
a.Audio = audioTags(audio)
|
||||
for _, l := range f.Languages {
|
||||
if l.Name != "" {
|
||||
a.Languages = append(a.Languages, l.Name)
|
||||
@@ -85,6 +119,37 @@ func (f MediaFile) Attrs() FileAttrs {
|
||||
return a
|
||||
}
|
||||
|
||||
// audioTags finds the audio formats we offer as filters in free text.
|
||||
func audioTags(s string) []string {
|
||||
var tags []string
|
||||
add := func(re *regexp.Regexp, tag string) {
|
||||
if re.MatchString(s) {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
add(reAtmos, AudioAtmos)
|
||||
add(reTrueHD, AudioTrueHD)
|
||||
dtsx, dtshd := reDTSX.MatchString(s), reDTSHD.MatchString(s)
|
||||
if dtsx {
|
||||
tags = append(tags, AudioDTSX)
|
||||
}
|
||||
if dtshd {
|
||||
tags = append(tags, AudioDTSHD)
|
||||
}
|
||||
if dtsx || dtshd || reDTS.MatchString(s) {
|
||||
tags = append(tags, AudioDTS)
|
||||
}
|
||||
if reDDPlus.MatchString(s) {
|
||||
tags = append(tags, AudioDDPlus)
|
||||
} else {
|
||||
add(reDD, AudioDD)
|
||||
}
|
||||
add(reAAC, AudioAAC)
|
||||
add(reFLAC, AudioFLAC)
|
||||
add(reOpus, AudioOpus)
|
||||
return tags
|
||||
}
|
||||
|
||||
func atoi(s string) (n int) {
|
||||
for _, c := range s {
|
||||
n = n*10 + int(c-'0')
|
||||
|
||||
@@ -71,3 +71,44 @@ func TestReleaseAttrs(t *testing.T) {
|
||||
t.Errorf("HDTV detected as HDR: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAudioTags(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
in string
|
||||
want []string
|
||||
}{
|
||||
{"Movie.2160p.UHD.BluRay.TrueHD.7.1.Atmos.x265-G", []string{"Atmos", "TrueHD"}},
|
||||
{"Movie.1080p.DDP5.1.Atmos.x265", []string{"Atmos", "DD+"}},
|
||||
{"Movie.1080p.DTS-HD.MA.5.1.x264", []string{"DTS-HD", "DTS"}},
|
||||
{"Movie.2160p.DTS-X.7.1", []string{"DTS:X", "DTS"}},
|
||||
{"Movie.1080p.DTS.5.1", []string{"DTS"}},
|
||||
{"Movie.1080p.EAC3.5.1", []string{"DD+"}},
|
||||
{"Movie.1080p.E-AC-3.5.1", []string{"DD+"}},
|
||||
{"Movie.1080p.AC3.5.1", []string{"DD"}},
|
||||
{"Movie.720p.DD5.1.x264", []string{"DD"}},
|
||||
{"Show.S01E01.AAC.2.0", []string{"AAC"}},
|
||||
{"Album.FLAC", []string{"FLAC"}},
|
||||
{"Show.Opus", []string{"Opus"}},
|
||||
{"Movie.1080p.BluRay.x264-DTSAG", nil}, // a group name is not an audio format
|
||||
{"Movie.1080p.x264", nil},
|
||||
} {
|
||||
if got := audioTags(tc.in); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("audioTags(%q) = %v, want %v", tc.in, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMediaFileAudio(t *testing.T) {
|
||||
a := attrs(t, `{"sceneName":"Movie.x264.AC3","mediaInfo":{"audioCodec":"TrueHD Atmos"}}`)
|
||||
if !reflect.DeepEqual(a.Audio, []string{"Atmos", "TrueHD"}) {
|
||||
t.Errorf("media info wins over the name: %v", a.Audio)
|
||||
}
|
||||
a = attrs(t, `{"mediaInfo":{"audioCodec":"EAC3","audioAdditionalFeatures":"Atmos"}}`)
|
||||
if !reflect.DeepEqual(a.Audio, []string{"Atmos", "DD+"}) {
|
||||
t.Errorf("additional features: %v", a.Audio)
|
||||
}
|
||||
a = attrs(t, `{"relativePath":"Movie.1080p.DTS-HD.MA.mkv"}`)
|
||||
if !reflect.DeepEqual(a.Audio, []string{"DTS-HD", "DTS"}) {
|
||||
t.Errorf("fallback to the name: %v", a.Audio)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ func (r Release) Attrs() FileAttrs {
|
||||
}
|
||||
a.DV = reDV.MatchString(text)
|
||||
a.HDR = reHDR.MatchString(text)
|
||||
a.Audio = audioTags(text)
|
||||
for _, l := range r.Languages {
|
||||
if l.Name != "" {
|
||||
a.Languages = append(a.Languages, l.Name)
|
||||
|
||||
Reference in New Issue
Block a user