Sync from /srv/compose/unified-media-manager
This commit is contained in:
195
internal/service/release.go
Normal file
195
internal/service/release.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ReleaseInfo struct {
|
||||
Title string `json:"title"`
|
||||
Resolution string `json:"resolution"`
|
||||
Source string `json:"source"`
|
||||
VideoCodec string `json:"video_codec"`
|
||||
AudioFormat string `json:"audio_format"`
|
||||
ReleaseGroup string `json:"release_group"`
|
||||
ParseWarning bool `json:"parse_warning"`
|
||||
}
|
||||
|
||||
type ReleaseParser struct{}
|
||||
|
||||
func NewReleaseParser() *ReleaseParser {
|
||||
return &ReleaseParser{}
|
||||
}
|
||||
|
||||
var (
|
||||
bracketRe = regexp.MustCompile(`\[.*?\]`)
|
||||
releaseRe = regexp.MustCompile(`(?i)` +
|
||||
`(?:.*?[-. ])?` +
|
||||
`(?:(?P<resolution>480[p|i]|576[p|i]|720[p|i]|1080[p|i]|2160[p|i]|4K)[-. ])?` +
|
||||
`(?:(?P<source>HDTV|PDTV|SDTV|WEB-DL|WEBDL|WEBRip|WEB\s|BluRay|BDRip|BRRip|REMUX|Remux|DVDRip|DVD|CAM|TS|HDCAM)[-. ])?` +
|
||||
`(?:(?P<codec>x264|h264|X264|H264|x265|h265|X265|HEVC|XviD|MPEG2|VC1|AV1)[-. ])?` +
|
||||
`(?:(?P<audio>DTS-HD\.MA|DTS-HD|DTS\.HD|DTS-X|DTS\.X|ATMOS|Atmos|TrueHD|DDP5\.1|DD\+?5\.1|DolbyDigitalPlus|AAC[. ]?2\.0|AAC[. ]?5\.1|AAC|AC3|DD|FLAC|MP3)[-. ])?` +
|
||||
`.*?(?:-(?P<group>[A-Za-z0-9]+))?$`,
|
||||
)
|
||||
|
||||
resolutionCleanRe = regexp.MustCompile(`(?i)(480)[pi]|(576)[pi]|(720)[pi]|(1080)[pi]|(2160)[pi]|4K`)
|
||||
|
||||
knownSuffixes = []string{"Esubs", "TGx", "ettv", "eztv", "x0r", "FGT", "ION10", "NTb"}
|
||||
)
|
||||
|
||||
func normalizeResolution(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
raw = strings.TrimSpace(raw)
|
||||
raw = strings.TrimRight(raw, "pPiI")
|
||||
if strings.EqualFold(raw, "4K") {
|
||||
return "2160p"
|
||||
}
|
||||
return raw + "p"
|
||||
}
|
||||
|
||||
func normalizeSource(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
raw = strings.TrimSpace(raw)
|
||||
raw = strings.TrimRight(raw, " -.")
|
||||
upper := strings.ToUpper(raw)
|
||||
switch {
|
||||
case upper == "HDTV", upper == "PDTV", upper == "SDTV":
|
||||
return upper
|
||||
case upper == "WEB-DL", upper == "WEBDL", strings.HasPrefix(upper, "WEB"):
|
||||
return "WEB-DL"
|
||||
case strings.HasPrefix(upper, "BLURAY"), upper == "BDRIP", upper == "BRRIP":
|
||||
return "BluRay"
|
||||
case upper == "REMUX":
|
||||
return "REMUX"
|
||||
case strings.HasPrefix(upper, "DVD"):
|
||||
return "DVD"
|
||||
case upper == "CAM", upper == "TS", upper == "HDCAM":
|
||||
return upper
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func normalizeCodec(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
raw = strings.TrimSpace(raw)
|
||||
switch {
|
||||
case strings.EqualFold(raw, "x264"), strings.EqualFold(raw, "h264"):
|
||||
return "x264"
|
||||
case strings.EqualFold(raw, "x265"), strings.EqualFold(raw, "h265"), strings.EqualFold(raw, "HEVC"):
|
||||
return "x265"
|
||||
case strings.EqualFold(raw, "XviD"):
|
||||
return "XviD"
|
||||
case strings.EqualFold(raw, "MPEG2"):
|
||||
return "MPEG2"
|
||||
case strings.EqualFold(raw, "VC1"):
|
||||
return "VC1"
|
||||
case strings.EqualFold(raw, "AV1"):
|
||||
return "AV1"
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func normalizeAudio(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
raw = strings.TrimSpace(raw)
|
||||
upper := strings.ToUpper(raw)
|
||||
switch {
|
||||
case strings.HasPrefix(upper, "DTS-HD"):
|
||||
return "DTS-HD"
|
||||
case strings.HasPrefix(upper, "DTS-X") || strings.HasPrefix(upper, "DTS.X"):
|
||||
return "DTS-X"
|
||||
case upper == "ATMOS":
|
||||
return "ATMOS"
|
||||
case upper == "TRUEHD":
|
||||
return "TrueHD"
|
||||
case strings.HasPrefix(upper, "DDP") || strings.HasPrefix(upper, "DD+") || strings.HasPrefix(upper, "DOLBYDIGITALPLUS"):
|
||||
return "DDP5.1"
|
||||
case strings.HasPrefix(upper, "DD") && (strings.Contains(upper, "5.1") || strings.Contains(upper, "51")):
|
||||
return "DDP5.1"
|
||||
case upper == "AAC":
|
||||
return "AAC"
|
||||
case strings.HasPrefix(upper, "AAC"):
|
||||
return "AAC"
|
||||
case upper == "AC3":
|
||||
return "AC3"
|
||||
case upper == "DD":
|
||||
return "DD"
|
||||
case upper == "FLAC":
|
||||
return "FLAC"
|
||||
case upper == "MP3":
|
||||
return "MP3"
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func cleanGroup(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
for _, suffix := range knownSuffixes {
|
||||
if strings.EqualFold(raw, suffix) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func (p *ReleaseParser) Parse(title string) ReleaseInfo {
|
||||
info := ReleaseInfo{Title: title}
|
||||
|
||||
cleaned := bracketRe.ReplaceAllString(title, " ")
|
||||
cleaned = strings.TrimSpace(cleaned)
|
||||
|
||||
match := releaseRe.FindStringSubmatch(cleaned)
|
||||
if match == nil {
|
||||
info.ParseWarning = true
|
||||
return info
|
||||
}
|
||||
|
||||
for i, name := range releaseRe.SubexpNames() {
|
||||
if i == 0 || name == "" {
|
||||
continue
|
||||
}
|
||||
value := match[i]
|
||||
switch name {
|
||||
case "resolution":
|
||||
info.Resolution = normalizeResolution(value)
|
||||
case "source":
|
||||
info.Source = normalizeSource(value)
|
||||
case "codec":
|
||||
info.VideoCodec = normalizeCodec(value)
|
||||
case "audio":
|
||||
info.AudioFormat = normalizeAudio(value)
|
||||
case "group":
|
||||
info.ReleaseGroup = cleanGroup(value)
|
||||
}
|
||||
}
|
||||
|
||||
if info.Resolution == "" {
|
||||
info.ParseWarning = true
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func (p *ReleaseParser) MatchQuality(info ReleaseInfo) *QualityTier {
|
||||
for i := len(QualityTiers) - 1; i >= 0; i-- {
|
||||
tier := &QualityTiers[i]
|
||||
if tier.Resolution != "" && tier.Resolution != info.Resolution {
|
||||
continue
|
||||
}
|
||||
if info.Source == "" || SourceMatch(tier.Source, info.Source) {
|
||||
return tier
|
||||
}
|
||||
}
|
||||
|
||||
return &QualityTiers[0]
|
||||
}
|
||||
Reference in New Issue
Block a user