From c58a038daf1a00dbe33a56520c571ec36581f246 Mon Sep 17 00:00:00 2001 From: Simon Ding Date: Mon, 28 Apr 2025 14:31:46 +0800 Subject: [PATCH] feat: match tv release year --- engine/torrent.go | 41 ++++++++++++++++++++++++++++++++++ pkg/metadata/movie.go | 49 ++++++++++++++++++++++++++++++----------- pkg/metadata/tv.go | 10 +++++++-- pkg/metadata/tv_test.go | 10 +++++++++ 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/engine/torrent.go b/engine/torrent.go index 6c48803..a30c132 100644 --- a/engine/torrent.go +++ b/engine/torrent.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/pkg/errors" ) @@ -71,6 +72,26 @@ func names2Query(media *ent.Media) []string { return names } +func getSeasonReleaseYear(series *db.MediaDetails, seasonNum int) int { + if seasonNum == 0 { + return 0 + } + releaseYear := 0 + for _, s := range series.Episodes { + if s.SeasonNumber == seasonNum && s.AirDate != "" { + ss := strings.Split(s.AirDate, "-")[0] + y, err := strconv.Atoi(ss) + if err != nil { + continue + } + releaseYear = y + break + + } + } + return releaseYear +} + func SearchTvSeries(db1 db.Database, param *SearchParam) ([]torznab.Result, error) { series, err := db1.GetMediaDetails(param.MediaId) if err != nil { @@ -87,6 +108,11 @@ func SearchTvSeries(db1 db.Database, param *SearchParam) ([]torznab.Result, erro res := searchWithTorznab(db1, SearchTypeTv, names...) + ss := strings.Split(series.AirDate, "-")[0] + releaseYear, _ := strconv.Atoi(ss) + + seasonYear := getSeasonReleaseYear(series, param.SeasonNum) + var filtered []torznab.Result lo: for _, r := range res { @@ -102,6 +128,17 @@ lo: if !torrentNameOk(series, meta) { continue } + if meta.Year > 0 && releaseYear > 0 { + if meta.Year != releaseYear && meta.Year != releaseYear-1 && meta.Year != releaseYear+1 { //year not match + if seasonYear > 0 { // if tv release year is not match, check season release year + if meta.Year != seasonYear && meta.Year != seasonYear-1 && meta.Year != seasonYear+1 { //season year not match + continue lo + } + } else { + continue lo + } + } + } } if !isNoSeasonSeries(series) && meta.Season != param.SeasonNum { //do not check season on series that only rely on episode number @@ -311,6 +348,10 @@ const ( ) func searchWithTorznab(db db.Database, t SearchType, queries ...string) []torznab.Result { + t1 := time.Now() + defer func() { + log.Infof("search with torznab took %v", time.Since(t1)) + }() var res []torznab.Result allTorznab := db.GetAllIndexers() diff --git a/pkg/metadata/movie.go b/pkg/metadata/movie.go index 4b10ce6..632004e 100644 --- a/pkg/metadata/movie.go +++ b/pkg/metadata/movie.go @@ -34,33 +34,56 @@ func (m *MovieMetadata) IsAcceptable(names... string) bool { return false } - -func ParseMovie(name string) *MovieMetadata { - name = strings.Join(strings.Fields(name), " ") //remove unnessary spaces - name = strings.ToLower(strings.TrimSpace(name)) - var meta = &MovieMetadata{} +func findYear(name string) (year int, index int) { yearRe := regexp.MustCompile(`\(\d{4}\)`) yearMatches := yearRe.FindAllString(name, -1) - var yearIndex = -1 + index = -1 if len(yearMatches) > 0 { - yearIndex = strings.Index(name, yearMatches[0]) + index = strings.Index(name, yearMatches[0]) y := yearMatches[0][1 : len(yearMatches[0])-1] n, err := strconv.Atoi(y) if err != nil { panic(fmt.Sprintf("convert %s error: %v", y, err)) } - meta.Year = n + year = n } else { yearRe := regexp.MustCompile(`\d{4}`) yearMatches := yearRe.FindAllString(name, -1) if len(yearMatches) > 0 { - n, err := strconv.Atoi(yearMatches[0]) - if err != nil { - panic(fmt.Sprintf("convert %s error: %v", yearMatches[0], err)) - } - meta.Year = n + year, index = findYearInMatches(yearMatches, name) + + } } + return +} + +func findYearInMatches(matches []string, name string) (year int, index int) { + if len(matches) == 0 { + return 0, -1 + } + for _, y := range matches { + index = strings.Index(name, y) + n, err := strconv.Atoi(y) + if err != nil { + panic(fmt.Sprintf("convert %s error: %v", y, err)) + } + if n < 1900 || n > 2050 { + continue + } + year = n + + } + return +} + +func ParseMovie(name string) *MovieMetadata { + name = strings.Join(strings.Fields(name), " ") //remove unnessary spaces + name = strings.ToLower(strings.TrimSpace(name)) + var meta = &MovieMetadata{} + year, yearIndex := findYear(name) + + meta.Year = year if yearIndex != -1 { meta.Name = name[:yearIndex] diff --git a/pkg/metadata/tv.go b/pkg/metadata/tv.go index ccceddc..ffeda3a 100644 --- a/pkg/metadata/tv.go +++ b/pkg/metadata/tv.go @@ -272,7 +272,7 @@ func matchResolution(s string) string { func maybeSeasonPack(s string) bool { //season pack - packRe := regexp.MustCompile(`((\d{1,2}-\d{1,2}))|(complete)|(全集)|(\W[sS]\d{1,2}\W)`) + packRe := regexp.MustCompile(`((\d{1,2}-\d{1,2}))|(complete)|(全集)|(合集)|(\W[sS]\d{1,2}\W)`) if packRe.MatchString(s) { return true } @@ -409,6 +409,8 @@ func parseName(name string) *Info { if strings.TrimSpace(name) == "" { return meta } + year, yearP := findYear(name) + meta.Year = year season, p := findSeason(name) if season == -1 { @@ -437,7 +439,11 @@ func parseName(name string) *Info { //tv name if utils.IsASCII(name) && p < len(name) && p-1 > 0 { - meta.NameEn = strings.TrimSpace(name[:p-1]) + p1 := p -1 + if yearP > 0 { + p1 = min(p1, yearP-1) + } + meta.NameEn = strings.TrimSpace(name[:p1]) meta.NameCn = meta.NameEn } else { fields := strings.FieldsFunc(name, func(r rune) bool { diff --git a/pkg/metadata/tv_test.go b/pkg/metadata/tv_test.go index 90b5674..ee94354 100644 --- a/pkg/metadata/tv_test.go +++ b/pkg/metadata/tv_test.go @@ -206,6 +206,16 @@ func Test_ParseTV20(t *testing.T) { assert.Equal(t, true, m.IsSeasonPack) } + +func Test_ParseTV21(t *testing.T) { + s1 := "【东京不够热】基督山伯爵-华丽的复仇-【01~09】【1280x720】【简中/日双语字幕】【2018春季日剧】【合集】 " + m := ParseTv(s1) + log.Infof("results: %+v", m) + assert.Equal(t, 1, m.Season) + assert.Equal(t, 2018, m.Year) + assert.Equal(t, true, m.IsSeasonPack) +} + // The Day of the Jackal (Season 1) WEB-DL 1080​p func Test_ParseTV19(t *testing.T) { s1 := "The Day of the Jackal (Season 1) WEB-DL 1080​p "