diff --git a/README.md b/README.md index 20e833d..4956f77 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,12 @@ func main() { // group: NOiR } ``` +## Build standalone cli +``` +go build ./cmd/rls +``` + +## Run tests +``` +go test ./... +``` diff --git a/cmd/rls/main.go b/cmd/rls/main.go new file mode 100644 index 0000000..6452a0f --- /dev/null +++ b/cmd/rls/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "bufio" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + + "github.com/moistari/rls" +) + +var pretty = flag.Bool("pretty", false, "pretty-print JSON output") + +func processLine(line string) error { + line = strings.TrimSpace(line) + if line == "" { + return nil + } + + // Just parse the release directly + r := rls.ParseString(line) + + var b []byte + var err error + + if *pretty { + b, err = json.MarshalIndent(r, "", " ") + } else { + b, err = json.Marshal(r) + } + + if err != nil { + return err + } + fmt.Print(string(b)) + return nil +} + +func main() { + flag.Parse() + args := flag.Args() + if len(args) == 0 { + // read lines from stdin + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + if err := processLine(scanner.Text()); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + os.Exit(1) + } + } + if err := scanner.Err(); err != nil { + fmt.Fprintln(os.Stderr, "error reading stdin:", err) + os.Exit(1) + } + return + } + // treat each arg as a separate release name + for _, a := range args { + if err := processLine(a); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + os.Exit(1) + } + } +} diff --git a/lex.go b/lex.go index 2601117..2fdedab 100644 --- a/lex.go +++ b/lex.go @@ -70,17 +70,17 @@ func DefaultLexers() []Lexer { NewRegexpSourceLexer(TagTypeCollection, true), NewSeriesLexer( // s02, S01E01 - `(?i)^s(?P[0-8]?\d)[\-\._ ]?(?:e(?P\d{1,5}))?\b`, + `(?i)^s(?P\d{1,4})[\-\._ ]?(?:e(?P\d{1,5}))?\b`, // S01E02E03, S01E02-E03, S01E03.E04.E05 - `(?i)^s(?P[0-8]?\d)(?P(?:[\-\._ ]?e\d{1,5}){1,5})\b`, + `(?i)^s(?P\d{1,4})(?P(?:[\-\._ ]?e\d{1,5}){1,5})\b`, // S01S02S03 - `(?i)^(?P(?:s[0-8]?\d){2,4})\b`, + `(?i)^(?P(?:s\d{1,4}){2,4})\b`, // 2x1, 1x01 - `(?i)^(?P[0-8]?\d)x(?P\d{1,3})\b`, + `(?i)^(?P\d{1,4})x(?P\d{1,5})\b`, // S01 - 02v3, S07-06, s03-5v.9 - `(?i)^s(?P[0-8]?\d)[\-\._ ]{1,3}(?P\d{1,5})(?:[\-\._ ]{1,3}(?Pv\d+(?:\.\d+){0,2}))?\b`, + `(?i)^s(?P\d{1,4})[\-\._ ]{1,3}(?P\d{1,5})(?:[\-\._ ]{1,3}(?Pv\d+(?:\.\d+){0,2}))?\b`, // Season.01.Episode.02, Series.01.Ep.02, Series.01, Season.01 - `(?i)^(?:series|season|s)[\-\._ ]?(?P[0-8]?\d)(?:[\-\._ ]?(?:episode|ep)(?P\d{1,5}))?\b`, + `(?i)^(?:series|season|s)[\-\._ ]?(?P\d{1,2})(?:[\-\._ ]?(?:episode|ep)(?P\d{1,5}))?\b`, // Vol.1.No.2, vol1no2 `(?i)^vol(?:ume)?[\-\._ ]?(?P\d{1,3})(?:[\-\._ ]?(?:number|no)[\-\._ ]?(?P\d{1,5}))\b`, // Episode 15, E009, Ep. 007, Ep.05-07 @@ -88,7 +88,7 @@ func DefaultLexers() []Lexer { // 10v1.7, 13v2 `(?i)^(?P\d{1,5})(?Pv[\-\._ ]?\d+(?:\.\d){0,2})\b`, // S01.Disc02, s01D3, Series.01.Disc.02, S02DVD3 - `(?i)^(?:series|season|s)[\-\._ ]?(?P[0-8]?\d)[\-\._ ]?(?P(?:disc|disk|dvd|d)[\-\._ ]?(?:\d{1,3}))\b`, + `(?i)^(?:series|season|s)[\-\._ ]?(?P\d{1,4})[\-\._ ]?(?P(?:disc|disk|dvd|d)[\-\._ ]?(?:\d{1,3}))\b`, // s1957e01 `(?i)^s(?P19\d\d)e(?P\d{2,4})\b`, ), @@ -231,7 +231,7 @@ func NewDateLexer(strs ...string) Lexer { func NewSeriesLexer(strs ...string) Lexer { var sourcef taginfo.FindFunc lexer := NamedCaptureLexer(strs...) - mlt := regexp.MustCompile(`(?i)s(\d?\d)`) + mlt := regexp.MustCompile(`(?i)s(\d{1,4})`) mny := regexp.MustCompile(`(?i)[\-\._ ]?e(\d{1,5})`) dsc := regexp.MustCompile(`(?i)^disc|disk|dvd|d`) return TagLexer{ diff --git a/parse.go b/parse.go index 0a046af..a285f1e 100644 --- a/parse.go +++ b/parse.go @@ -507,6 +507,17 @@ func (b *TagBuilder) fixMusic(r *Release) { // collect collects tags into the release. func (b *TagBuilder) collect(r *Release) { + // determine pivot and the text end prior to pivot — only consider series + // tags that occur before this 'end' (the first text prior to the pivot). + // Note: do not include TagTypeSeries when computing the pivot here — + // including it causes the pivot to point to series tags themselves and + // leads to misclassification when series tags appear early in the name. + // compute pivot based on source/resolution/version so that series tags + // following a title or year (TagTypeDate) are still accepted. Excluding + // TagTypeDate prevents dates from moving the pivot before legitimate + // series tokens. + _, seriesTextEnd := b.pivots(r, TagTypeSource, TagTypeResolution, TagTypeVersion) + for i := 0; i < len(r.tags); i++ { switch r.tags[i].typ { case TagTypeText: @@ -534,12 +545,26 @@ func (b *TagBuilder) collect(r *Release) { case TagTypeDate: r.Year, r.Month, r.Day = r.tags[i].Date() case TagTypeSeries: - series, episode := r.tags[i].Series() + // only accept series tags from the initial part of the release + // name (typically after the title or year). series tokens found + // after the pivot (eg. in codec/version sections) are ignored. + if i >= seriesTextEnd { + // treat as text (leave for unused/group heuristics) + r.tags[i] = r.tags[i].As(TagTypeText, nil) + break + } + // inspect raw tag captures so we can detect explicit episode captures + tag := r.tags[i] + series, episode := tag.Series() if r.Series == 0 { r.Series = series } - if r.Episode == 0 { - r.Episode = episode + // if the tag actually captured an episode (even "0"), set episode + if len(tag.v) > 2 && tag.v[2] != "" { + // prefer first explicit episode capture + if r.Episode == 0 && (episode != 0 || tag.v[2] == "0") { + r.Episode = episode + } } case TagTypeVersion: if r.Version == "" { @@ -627,6 +652,14 @@ func (b *TagBuilder) inspect(r *Release, initial bool) Type { return r.Type } n := len(r.tags) + // detect explicit episode captures in tags (including "0") + episodePresent := false + for _, t := range r.tags { + if t.Is(TagTypeSeries) && len(t.v) > 2 && t.v[2] != "" { + episodePresent = true + break + } + } // inspect types var app, series, movie bool for i := n; i > 0; i-- { @@ -642,12 +675,12 @@ func (b *TagBuilder) inspect(r *Release, initial bool) Type { } return typ case Series, Episode: - if r.Episode != 0 || (r.Series == 0 && r.Episode == 0) && !contains(r.Other, "BOXSET") { + if episodePresent || (r.Series == 0 && !episodePresent) && !contains(r.Other, "BOXSET") { return Episode } return Series case Education: - if r.Series == 0 && r.Episode == 0 { + if r.Series == 0 && !episodePresent { return Education } case Music: @@ -664,7 +697,7 @@ func (b *TagBuilder) inspect(r *Release, initial bool) Type { // exclusive tag not superseded by version/episode/date if r.tags[i-1].InfoExcl() && r.Version == "" && - r.Series == 0 && r.Episode == 0 && + r.Series == 0 && !episodePresent && r.Day == 0 && r.Month == 0 { return typ } @@ -690,7 +723,7 @@ func (b *TagBuilder) inspect(r *Release, initial bool) Type { } // defaults switch { - case r.Episode != 0 || (r.Year != 0 && r.Month != 0 && r.Day != 0): + case episodePresent || (r.Year != 0 && r.Month != 0 && r.Day != 0): return Episode case r.Series != 0 || series: return Series @@ -1180,7 +1213,17 @@ func (b *TagBuilder) unused(r *Release, i int) { case r.Sum == "" && b.sum.MatchString(s) && strings.ContainsAny(s, "0123456789"): r.Sum, r.unused = s, r.unused[:n-1] case r.Group == "" && !b.digits.MatchString(s): - r.Group, r.unused = s, r.unused[:n-1] + // if the unused tag was originally a series token, reconstruct + // a normalized series-like group (eg. "S97") using the numeric + // capture rather than the raw captured text (which may include + // punctuation such as a trailing dot). + if r.tags[r.unused[n-1]].Was(TagTypeSeries) { + series, _ := r.tags[r.unused[n-1]].Series() + r.Group = fmt.Sprintf("S%v", series) + } else { + r.Group = s + } + r.unused = r.unused[:n-1] } } } diff --git a/reutil/reutil.go b/reutil/reutil.go index b9ec69d..644e818 100644 --- a/reutil/reutil.go +++ b/reutil/reutil.go @@ -25,6 +25,7 @@ func Join(quote bool, strs ...string) string { // Build builds a regexp for strings. // // Config options: +// // i - ignore case // ^ - add ^ start anchor // a - add \b start anchor diff --git a/rls.go b/rls.go index 0a7f2a5..9fee48b 100644 --- a/rls.go +++ b/rls.go @@ -3,6 +3,7 @@ package rls import ( "bytes" + "encoding/json" "fmt" "math" "regexp" @@ -261,8 +262,8 @@ func (tag Tag) Info() *taginfo.Taginfo { // SingleEp returns true when the func (tag Tag) SingleEp() bool { if tag.typ.Is(TagTypeSeries) { - s, e := tag.Series() - return s == 0 && e != 0 && tag.v[1] == "" && tag.v[0] == tag.v[2] + s, _ := tag.Series() + return s == 0 && len(tag.v) > 2 && tag.v[2] != "" && tag.v[1] == "" && tag.v[0] == tag.v[2] } return false } @@ -346,7 +347,7 @@ func (tag Tag) Normalize() string { return strconv.Itoa(year) case TagTypeSeries: series, episode := tag.Series() - if episode != 0 { + if len(tag.v) > 2 && tag.v[2] != "" { return fmt.Sprintf("S%02dE%02d", series, episode) } return fmt.Sprintf("S%02d", series) @@ -521,7 +522,10 @@ func (tag Tag) Series() (int, int) { func (tag Tag) Episodes() []int { var v []int for _, b := range tag.v[2:] { - if episode, _ := strconv.Atoi(b); episode != 0 { + if b == "" { + continue + } + if episode, err := strconv.Atoi(b); err == nil { v = append(v, episode) } } @@ -703,6 +707,12 @@ const ( Series ) +// MarshalJSON ensures that when this type is converted to JSON, +// it uses its string representation instead of the integer value. +func (t Type) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} + // ParseType parses a type from s. func ParseType(s string) Type { switch s { diff --git a/taginfo/taginfo.csv b/taginfo/taginfo.csv index 69fc152..c436ffd 100644 --- a/taginfo/taginfo.csv +++ b/taginfo/taginfo.csv @@ -179,6 +179,7 @@ collection,VTC,,,,education,1 collection,WAKA,Wakanim,(?-i:WAKA),,, collection,WiiWare,WiiWare Shop,,,game,1 collection,Wiley,,wiley(?:[\-\._ ]?com)?,,education,1 +collection,WOWP,WOW Presents Plus,(?-i:WOWP)|WOW Presents Plus,,, collection,XBLA,Xbox Live Arcade,,,game,1 collection,XXX,Adult,(?-i:XXX),,, collection,YOUTUBE,YouTube,(?-i:YOUTUBE)|(?-i:YT),,, @@ -331,53 +332,96 @@ hdr,HLG,Hybrid Log-Gamma,,,movie, hdr,SDR,Standard Dynamic Range,,,movie, hdr,DV,Dolby Vision,dolby[\-\._ ]vision|dovi|dv,,movie, language,AUDiO.ADDON,Audio Addon,audio[\-\._ ]?addon,,, -language,BALTIC,Baltic,,,, -language,BRAZiLiAN,Brazilian,BRAZiLiAN|BR,,, -language,BULGARiAN,Bulgarian,(?i:bulgarian)|BG,,, -language,CHiNESE,Chinese,CH[iI]N[eE]S[eE]|CN,,, +language,DL,Dual Language,(?i:dual[\-\._ ]?(?:audio|line|language))|DL,,, +language,DUBBED,Dubbed,(?i:(?:line[\-\._ ]?)?dubbed),,, +language,HARDSUB,Subs (hard),(?i:hard[\-\._ ]?sub(?:s|bed)?),,, +language,HC,Hardcoded,(?i:hard[\-\._ ]?coded)|HC,,, +language,MULTILANG,Multi (lang),,,, +language,MULTISUB,Subs (multi),(?i:multiple[\-\._ ]subtitles?|multi[\-\._ ]?subs?),,, +language,MULTI,Multi,(?i:multi(?:[\-\._ ]?(?:lingual|language))?)|MULT[iI],,, +language,SUBBED,Subbed,(?i:subbed),,, +language,SUBFORCED,Subbed (forced),(?i:sub(?:[\-\._ ]?forced)?|forced(?:[\-\._ ]?sub)?),,, +language,SUBPACK,Subs (pack),(?i:subs?[\-\._ ]?pack),,, +language,SYNCED,Synced,(?i:synced),,, +language,AFRIKAANS,Afrikaans,(?i:afrikaans)|AF,,, +language,ALBANIAN,Albanian,(?i:albanian)|SQ,,, +language,AMHARIC,Amharic,(?i:amharic)|AM,,, +language,ARABIC,Arabic,(?i:ara(?:bic)?)|AR,,, +language,ARMENIAN,Armenian,(?i:armenian)|HY,,, +language,AZERBAIJANI,Azerbaijani,(?i:azerbaijani)|AZ,,, +language,BALTIC,Baltic,(?i:baltic),,, +language,BASQUE,Basque,(?i:basque)|EU,,, +language,BELARUSIAN,Belarusian,(?i:belarusian)|BE,,, +language,BENGALI,Bengali,(?i:bengali)|BN,,, +language,BOSNIAN,Bosnian,(?i:bosnian)|BS,,, +language,BRAZILIAN,Brazilian,(?i:brazilian)|BR,,, +language,BULGARIAN,Bulgarian,(?i:bulgarian)|BG,,, +language,BURMESE,Burmese,(?i:burmese)|MY,,, +language,CATALAN,Catalan,(?i:catalan)|CA,,, +language,CHINESE,Chinese,(?i:chinese)|ZH|CN,,, language,CHS,Chinese (simplified),(?i:chinese[\-\._ ]?simplified)|CHS,,, language,CHT,Chinese (traditional),(?i:chinese[\-\._ ]?traditional)|CHT,,, -language,CZECH,Czech,CZECH|CZ,,, -language,DANiSH,Danish,(?i:danish)|DK,,, -language,DL,Dual Language,(?i:dual[\-\._ ]?language)|DL,,, -language,DUBBED,Dubbed,(?i:(?:line[\-\._ ]?)?dubbed),,, +language,CROATIAN,Croatian,(?i:croatian)|HR,,, +language,CZECH,Czech,(?i:czech)|CZ,,, +language,DANISH,Danish,(?i:danish)|DK,,, language,DUTCH,Dutch,(?i:dutch|flemish)|NL,,, -language,ENGLiSH,English,(?i:eng(?:lish)?)|EN,,, -language,ESTONiAN,Estonian,(?i:estonian)|EE,,, -language,FiNNiSH,Finnish,(?i:finnish)|FI,,, -language,FRENCH,French,(?i:french)|FRE|FR,,, +language,ENGLISH,English,(?i:eng(?:lish)?)|EN,,, +language,ESTONIAN,Estonian,(?i:estonian)|EE,,, +language,FINNISH,Finnish,(?i:finnish)|FI,,, +language,FRENCH,French,(?i:french)|FR,,, +language,GEORGIAN,Georgian,(?i:georgian)|KA,,, language,GERMAN,German,(?i:german)|DE,,, -language,GREEK,Greek,GREEK|(?i:gr),,, -language,HAiTiAN,Hatian,(?i:haitian)|HT,,, -language,HARDSUB,Subs (hard),(?:hardsub),,, -language,HC,Hardcoded,(?i:hard[\-\._ ]?coded|hc),,, -language,HiNDI,Hindi,(?i:hindi)|HI,,, -language,HUNGARiAN,Hungarian,(?i:hun(?:garian)?)|HU,,, -language,iCELANDiC,Icelandic,(?i:icelandic),,, -language,iTALiAN,Italian,(?i:ita(?:lian)?),,, +language,GREEK,Greek,(?i:greek)|GR,,, +language,HAITIAN,Haitian,(?i:haitian)|HT,,, +language,HEBREW,Hebrew,(?i:hebrew)|HE,,, +language,HINDI,Hindi,(?i:hindi)|HI,,, +language,HUNGARIAN,Hungarian,(?i:hun(?:garian)?)|HU,,, +language,ICELANDIC,Icelandic,(?i:icelandic)|IS,,, +language,INDONESIAN,Indonesian,(?i:indonesian)|ID,,, +language,IRISH,Irish,(?i:irish)|GA,,, +language,ITALIAN,Italian,(?i:ita(?:lian)?)|IT,,, language,JAPANESE,Japanese,(?i:japanese),,, -language,KOREAN,Korean,K[oO]R[eE][aA]N|KR,,, -language,LATiN,Latin,,,, -language,MANDARiN,Mandarin,,,, -language,MULTILANG,Multi (lang),,,, -language,MULTiSUB,Subs (multi),(?i:multiple[\-\._ ]subtitles?|multi[\-\._ ]?subs?),,, -language,MULTi,Multi,(?i:multi(?:[\-\._ ]?(?:lingual|language)))|MULT[iI],,, -language,NORDiC,Nordic,N[oO]RD[iI]C,,, -language,NORWEGiAN,Norwegian,(?i:nor(?:wegian)?)|NO,,, -language,POLiSH,Polish,(?i:polish)|PL,,, +language,JAVANESE,Javanese,(?i:javanese)|JV,,, +language,KAZAKH,Kazakh,(?i:kazakh)|KK,,, +language,KHMER,Khmer,(?i:khmer)|KM,,, +language,KOREAN,Korean,(?i:kor(?:ean)?)|KR,,, +language,LAOTIAN,Laotian,(?i:lao(?:tian)?)|LO,,, +language,LATIN,Latin,(?i:latin)|LA,,, +language,LATVIAN,Latvian,(?i:latvian)|LV,,, +language,LITHUANIAN,Lithuanian,(?i:lithuanian)|LT,,, +language,MACEDONIAN,Macedonian,(?i:macedonian)|MK,,, +language,MALAY,Malay,(?i:malay)|MS,,, +language,MALAYALAM,Malayalam,(?i:malayalam)|ML,,, +language,MANDARIN,Mandarin,(?i:mandarin),,, +language,MARATHI,Marathi,(?i:marathi)|MR,,, +language,MONGOLIAN,Mongolian,(?i:mongolian)|MN,,, +language,NEPALI,Nepali,(?i:nepali)|NE,,, +language,NORDIC,Nordic,(?i:nordic),,, +language,NORWEGIAN,Norwegian,(?i:nor(?:wegian)?)|NO,,, +language,PERSIAN,Persian,(?i:persian|farsi)|FA,,, +language,POLISH,Polish,(?i:polish)|PL,,, language,PORTUGUESE,Portuguese,(?i:portuguese)|PT,,, -language,ROMANiAN,Romanian,(?i:romanian)|RO,,, -language,RUSSiAN,Russian,(?i:rus(?:sian)?)|RU,,, -language,SLOVAK,Slovak,SLOVAK|SK,,, -language,SPANiSH,Spanish,(?i:spanish)|SPA|ES,,, -language,SUBBED,Subbed,(?i:subbed),,, -language,SUBFORCED,Subbed (forced),(?i:subforced|forcedsub),,, -language,SUBPACK,Subs (pack),(?i:subs?[\-\._ ]?pack),,, -language,SWEDiSH,Swedish,(?i:swe(?:dish)?)|SE,,, -language,SYNCED,Synced,(?i:synced),,, -language,TURKiSH,Turkish,(?i:turkish)|TR,,, -language,UKRAiNiAN,Ukrainian,(?i:ukrainian)|UA,,, -language,UNSUBBED,Unsubbed,(?i:unsubbed),,, +language,PUNJABI,Punjabi,(?i:punjabi)|PA,,, +language,ROMANIAN,Romanian,(?i:romanian)|RO,,, +language,RUSSIAN,Russian,(?i:rus(?:sian)?)|RU,,, +language,SERBIAN,Serbian,(?i:serbian)|SR,,, +language,SLOVAK,Slovak,(?i:slovak)|SK,,, +language,SLOVENIAN,Slovenian,(?i:slovenian)|SL,,, +language,SOMALI,Somali,(?i:somali)|SO,,, +language,SPANISH,Spanish,(?i:spanish)|SPA|ES,,, +language,SWAHILI,Swahili,(?i:swahili)|SW,,, +language,SWEDISH,Swedish,(?i:swe(?:dish)?)|SE,,, +language,TAGALOG,Tagalog,(?i:tagalog)|TL,,, +language,TAMIL,Tamil,(?i:tamil)|TA,,, +language,TELUGU,Telugu,(?i:telugu)|TE,,, +language,THAI,Thai,(?i:thai)|TH,,, +language,TURKISH,Turkish,(?i:turkish)|TR,,, +language,UKRAINIAN,Ukrainian,(?i:ukrainian)|UA,,, +language,URDU,Urdu,(?i:urdu)|UR,,, +language,UZBEK,Uzbek,(?i:uzbek)|UZ,,, +language,VIETNAMESE,Vietnamese,(?i:vietnamese)|VI,,, +language,WELSH,Welsh,(?i:welsh)|CY,,, +language,ZULU,Zulu,(?i:zulu)|ZU,,, language,VF2,VFF et VFQ,(?i:vf2|fr2),,, language,VFB,Version Francophone Belge,(?i:vfb),,, language,VFF,Version Francophone Français,(?i:vf?f|truefrench),,, diff --git a/tests.yaml b/tests.yaml index 99b442e..286894b 100644 --- a/tests.yaml +++ b/tests.yaml @@ -94,7 +94,7 @@ source: "BluRay" year: 1992 other: "INTERNAL" - language: "SWEDiSH SUBPACK" + language: "SWEDISH SUBPACK" group: "SiN" "(2001)A Space Odyssey(1961).mkv": type: "movie" @@ -127,7 +127,7 @@ codec: "x264" audio: "DUAL.AUDIO" channels: "6.0" - language: "ENGLiSH HiNDI" + language: "ENGLISH HINDI" group: "GOPISAHI" unused: "Esub" "2012(2009).1080p.Dual Audio(Hindi+English) 5.1 Audios": @@ -137,7 +137,7 @@ year: 2009 audio: "DUAL.AUDIO" channels: "5.1" - language: "HiNDI ENGLiSH" + language: "HINDI ENGLISH" "2012 (2009) 1080p BrRip x264 - 1.7GB - YIFY": type: "movie" title: "2012" @@ -227,7 +227,7 @@ year: 2011 codec: "x264.HQ" other: "3D Half-SBS" - language: "FRENCH MULTiSUB" + language: "FRENCH MULTISUB" group: "TUSAHD" "Adam.Carolla.Not.Taco.Bell.Material.2019.WEB-DL": type: "movie" @@ -252,7 +252,7 @@ audio: "DD" channels: "5.1" other: "UPSCALED" - language: "HiNDI" + language: "HINDI" group: "M2Tv" unused: "DesiSCR Rip Mafiaking" "Akte.X.Jenseits.der.Wahrheit.R5.Line.Dubbed.German.READ.NFO.XviD-VCF": @@ -485,7 +485,7 @@ year: 2006 other: "DIRFIX COMPLETE" cut: "Extended.Cut" - language: "MULTi" + language: "MULTI" group: "MONUMENT" "(Comedy) Netflix Originals - Dave Chappelle - Deep in the Heart of Texas (2017) 1080p WEBRip DD5.1 x264-TrollHD.mkv": type: "movie" @@ -671,7 +671,7 @@ source: "BluRay" resolution: "720p" year: 1965 - language: "VOSTFR RUSSiAN" + language: "VOSTFR RUSSIAN" group: "Popo" unused: "Russ Meyer Liosaa" "FAT.A.Documentary.2019.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv": @@ -713,7 +713,7 @@ source: "DVDSCR" resolution: "576p" codec: "H.264" - language: "ENGLiSH HARDSUB" + language: "ENGLISH HARDSUB" site: "ValdikSS" ext: "mkv" "\t foo\nbar\n\f\r1080p \t\nbluray\n\t": @@ -771,7 +771,7 @@ source: "HDTV" resolution: "720p" codec: "x264" - language: "DANiSH" + language: "DANISH" group: "SKANK" "(horror)Heart-.Burn+.-.h-264.D-Z0N3 {{ secret }}": type: "movie" @@ -834,7 +834,7 @@ year: 2014 codec: "x264" audio: "AAC" - language: "ENGLiSH" + language: "ENGLISH" group: "CPG" "Jack.And.The.Cuckoo-Clock.Heart.2013.BRRip XViD": type: "movie" @@ -1119,7 +1119,7 @@ audio: "DDP Atmos" channels: "5.1" other: "HYBRiD" - language: "ENGLiSH" + language: "ENGLISH" group: "HONE" ext: "mkv" "South.Park.Bigger.Longer.and.Uncut.1999.1080p.Blu-ray.AVC.TrueHD.5.1.REMUX-FraMeSToR": @@ -1350,7 +1350,7 @@ disc: "1x" codec: "x264" audio: "AAC" - language: "HiNDI" + language: "HINDI" group: "Hon3y" site: "DDR" "The.Treasure.of.the.Sierra.Madre.1948.BluRay.1080p.FLAC.1.0.VC-1.REMUX-FraMeSToR": @@ -1588,7 +1588,7 @@ series: 1 episode: 1 codec: "XViD" - language: "SWEDiSH" + language: "SWEDISH" group: "HDR" "1899.S01.PROPER.1080p.NF.WEB-DL.DDP5.1.Atmos.H.264-FLUX": type: "series" @@ -1759,7 +1759,7 @@ resolution: "720p" series: 2 episode: 20 - language: "RUSSiAN ENGLiSH" + language: "RUSSIAN ENGLISH" "Core.Kyoto.S05E16.Tatami.The.Flooring.Underlying.Japanese.Culture.HDTV.x264-DARKFLiX": type: "episode" title: "Core Kyoto" @@ -1912,7 +1912,7 @@ audio: "DD" channels: "2.0" other: "BOXSET" - language: "NORDiC" + language: "NORDIC" group: "TWASERiES" "Game of Thrones - 4x03 - Breaker of Chains": type: "episode" @@ -1973,12 +1973,13 @@ site: "SubsPlease" sum: "C00D6C68" "Hold.The.Sunset.S01E00.Christmas.Special.720p.HDTV.X264-MTB": - type: "series" + type: "episode" title: "Hold The Sunset" subtitle: "Christmas Special" source: "HDTV" resolution: "720p" series: 1 + episode: 0 codec: "x264" group: "MTB" "HollyRandall.15.03.09.Black.Angelika.and.Nick.Lang.Take.Me.Home.Tonight.XXX.1080p.x264-GAGViD": @@ -2684,7 +2685,7 @@ episode: 8 audio: "AAC" channels: "2.0" - language: "ENGLiSH SUBBED" + language: "ENGLISH SUBBED" group: "ZR" ext: "mkv" unused: "Shuumatsu no Harem" @@ -3334,7 +3335,7 @@ arch: "x64" year: 2019 version: "v21.0.12" - language: "MULTi" + language: "MULTI" group: "WEBiSO" "Atlassian.Fisheye.and.Crucible.v4.7.0.MultiOS.Incl.KeyMaker.and.Patch.15TH.BIRTHDAY-DVT": type: "app" @@ -3494,7 +3495,7 @@ platform: "NSW" version: "v1.90" other: "UPDATE" - language: "MULTi" + language: "MULTI" group: "SUXXORS" "Super_Mario_3D_World_plus_Bowsers_Fury_Update_v1.1.0_NSW-VENOM": type: "game" @@ -3598,7 +3599,7 @@ source: "AUDiOBOOK" year: 2021 other: "INTERNAL" - language: "SWEDiSH" + language: "SWEDISH" group: "OLDSWE" "PLURALSIGHT.3DS.MAX.RIGGING.FUNDAMENTALS-JGTiSO": type: "education" @@ -3693,3 +3694,40 @@ type: "game" title: "Oddsparks An Automation Adventure Coaster Rush" group: "RUNE" +"Big Fat Quiz S2025E02 1080p ALL4 WEB-DL AAC 2.0 H.264-RAWR": + type: "episode" + title: "Big Fat Quiz" + source: "WEB-DL" + resolution: "1080p" + codec: "H.264" + audio: "AAC" + channels: "2.0" + series: 2025 + Episode: 2 + collection: "ALL4" + group: "RAWR" +"Robotech The Macross Saga E00 Extended Pilot 1080p BluRay x264-PRESENT": + type: "episode" + title: "Robotech The Macross Saga" + subtitle: "Pilot" + source: "BluRay" + resolution: "1080p" + series: 0 + Episode: 0 + cut: "Extended.Cut" + codec: "x264" + group: "PRESENT" +# to do: fix subtitle as "Meet the Queens of Season 1" +"Drag Race Philippines: Slaysian Royale S01E00 Meet the Queens of Season 1 1080p WOWP WEB-DL AAC 2.0 H.264-Kitsune": + type: "episode" + title: "Drag Race Philippines: Slaysian Royale" + subtitle: "Meet the Queens of" + source: "WEB-DL" + resolution: "1080p" + codec: "H.264" + audio: "AAC" + channels: "2.0" + series: 1 + Episode: 0 + group: "Kitsune" + collection: "WOWP" \ No newline at end of file