diff --git a/go.mod b/go.mod index f366fff..0f19ed6 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,7 @@ require ( github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/moistari/rls v0.2.0 // indirect github.com/nxadm/tail v1.4.6 // indirect github.com/onsi/ginkgo v1.14.2 // indirect github.com/onsi/gomega v1.10.1 // indirect @@ -65,6 +66,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index 611a103..db57d0c 100644 --- a/go.sum +++ b/go.sum @@ -299,6 +299,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -471,6 +472,10 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/moistari/rls v0.1.20 h1:RsLbJqzev3O/BnSuuiAkSL1VcsymHW1i1z6/rB4Lz6c= +github.com/moistari/rls v0.1.20/go.mod h1:2oVpWLhkuUzu2xqRINGnvvlcAGizZGMfMv8UYnntUCg= +github.com/moistari/rls v0.2.0 h1:0+aJk8yNBKi/eAOhnQSz1pZoYcYYVnBUicl0WE75oAg= +github.com/moistari/rls v0.2.0/go.mod h1:2oVpWLhkuUzu2xqRINGnvvlcAGizZGMfMv8UYnntUCg= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -771,6 +776,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/internal/action/macros.go b/internal/action/macros.go index bf00e18..a3f3f8a 100644 --- a/internal/action/macros.go +++ b/internal/action/macros.go @@ -2,6 +2,7 @@ package action import ( "bytes" + "strings" "text/template" "time" @@ -14,6 +15,7 @@ type Macro struct { TorrentHash string TorrentUrl string Indexer string + Title string Resolution string Source string HDR string @@ -36,9 +38,10 @@ func NewMacro(release domain.Release) Macro { TorrentPathName: release.TorrentTmpFile, TorrentHash: release.TorrentHash, Indexer: release.Indexer, + Title: release.Title, Resolution: release.Resolution, Source: release.Source, - HDR: release.HDR, + HDR: strings.Join(release.HDR, ", "), Season: release.Season, Episode: release.Episode, Year: currentTime.Year(), diff --git a/internal/action/macros_test.go b/internal/action/macros_test.go index ebece92..9766596 100644 --- a/internal/action/macros_test.go +++ b/internal/action/macros_test.go @@ -133,7 +133,7 @@ func TestMacros_Parse(t *testing.T) { TorrentURL: "https://some.site/download/fakeid", Indexer: "mock1", Resolution: "2160p", - HDR: "DV", + HDR: []string{"DV"}, }, args: args{text: "movies-{{.Resolution}}{{ if .HDR }}-{{.HDR}}{{ end }}"}, want: "movies-2160p-DV", @@ -146,7 +146,7 @@ func TestMacros_Parse(t *testing.T) { TorrentURL: "https://some.site/download/fakeid", Indexer: "mock1", Resolution: "2160p", - HDR: "HDR", + HDR: []string{"HDR"}, }, args: args{text: "movies-{{.Resolution}}{{ if .HDR }}-{{.HDR}}{{ end }}"}, want: "movies-2160p-HDR", diff --git a/internal/announce/announce.go b/internal/announce/announce.go index bf8f2e0..4f58840 100644 --- a/internal/announce/announce.go +++ b/internal/announce/announce.go @@ -98,21 +98,21 @@ func (a *announceProcessor) processQueue(queue chan string) { continue } - newRelease, err := domain.NewRelease(a.indexer.Identifier, "") + rls, err := domain.NewRelease(a.indexer.Identifier) if err != nil { log.Error().Err(err).Msg("could not create new release") continue } // on lines matched - err = a.onLinesMatched(a.indexer, tmpVars, newRelease) + err = a.onLinesMatched(a.indexer, tmpVars, rls) if err != nil { log.Debug().Msgf("error match line: %v", "") continue } // process release in a new go routine - go a.releaseSvc.Process(newRelease) + go a.releaseSvc.Process(rls) } } @@ -166,24 +166,24 @@ func (a *announceProcessor) parseExtract(pattern string, vars []string, tmpVars } // onLinesMatched process vars into release -func (a *announceProcessor) onLinesMatched(def domain.IndexerDefinition, vars map[string]string, release *domain.Release) error { +func (a *announceProcessor) onLinesMatched(def domain.IndexerDefinition, vars map[string]string, rls *domain.Release) error { var err error - err = release.MapVars(def, vars) + err = rls.MapVars(def, vars) if err != nil { log.Error().Stack().Err(err).Msg("announce: could not map vars for release") return err } // parse fields - err = release.Parse() + err = rls.ParseString(rls.TorrentName) if err != nil { log.Error().Stack().Err(err).Msg("announce: could not parse release") return err } // parse torrentUrl - err = release.ParseTorrentUrl(def.Parse.Match.TorrentURL, vars, def.SettingsMap, def.Parse.Match.Encode) + err = def.Parse.ParseTorrentUrl(vars, def.SettingsMap, rls) if err != nil { log.Error().Stack().Err(err).Msg("announce: could not parse torrent url") return err @@ -235,41 +235,6 @@ func (a *announceProcessor) processTorrentUrl(match string, vars map[string]stri return b.String(), nil } -func split(r rune) bool { - return r == ' ' || r == '.' -} - -func Splitter(s string, splits string) []string { - m := make(map[rune]int) - for _, r := range splits { - m[r] = 1 - } - - splitter := func(r rune) bool { - return m[r] == 1 - } - - return strings.FieldsFunc(s, splitter) -} - -func canonicalizeString(s string) []string { - //a := strings.FieldsFunc(s, split) - a := Splitter(s, " .") - - return a -} - -func cleanReleaseName(input string) string { - // Make a Regex to say we only want letters and numbers - reg, err := regexp.Compile("[^a-zA-Z0-9]+") - if err != nil { - //log.Fatal(err) - } - processedString := reg.ReplaceAllString(input, " ") - - return processedString -} - func removeElement(s []string, i int) ([]string, error) { // s is [1,2,3,4,5,6], i is 2 diff --git a/internal/database/filter.go b/internal/database/filter.go index 02ab7fd..f7ccf20 100644 --- a/internal/database/filter.go +++ b/internal/database/filter.go @@ -3,10 +3,9 @@ package database import ( "context" "database/sql" - sq "github.com/Masterminds/squirrel" - "strings" "time" + sq "github.com/Masterminds/squirrel" "github.com/lib/pq" "github.com/rs/zerolog/log" @@ -100,6 +99,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter "containers", "match_hdr", "except_hdr", + "match_other", + "except_other", "years", "artists", "albums", @@ -117,6 +118,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter "except_uploaders", "tags", "except_tags", + "origins", "created_at", "updated_at", ). @@ -140,7 +142,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool var delay, logScore sql.NullInt32 - if err := row.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), &years, &artists, &albums, pq.Array(&f.MatchReleaseTypes), pq.Array(&f.Formats), pq.Array(&f.Quality), pq.Array(&f.Media), &logScore, &hasLog, &hasCue, &perfectFlac, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &f.CreatedAt, &f.UpdatedAt); err != nil { + if err := row.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), pq.Array(&f.MatchOther), pq.Array(&f.ExceptOther), &years, &artists, &albums, pq.Array(&f.MatchReleaseTypes), pq.Array(&f.Formats), pq.Array(&f.Quality), pq.Array(&f.Media), &logScore, &hasLog, &hasCue, &perfectFlac, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, pq.Array(&f.Origins), &f.CreatedAt, &f.UpdatedAt); err != nil { log.Error().Stack().Err(err).Msgf("filter.findByID: %v : error scanning row", filterID) return nil, err } @@ -204,6 +206,8 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e "f.containers", "f.match_hdr", "f.except_hdr", + "f.match_other", + "f.except_other", "f.years", "f.artists", "f.albums", @@ -221,6 +225,7 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e "f.except_uploaders", "f.tags", "f.except_tags", + "f.origins", "f.created_at", "f.updated_at", ). @@ -254,7 +259,7 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool var delay, logScore sql.NullInt32 - if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), &years, &artists, &albums, pq.Array(&f.MatchReleaseTypes), pq.Array(&f.Formats), pq.Array(&f.Quality), pq.Array(&f.Media), &logScore, &hasLog, &hasCue, &perfectFlac, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &f.CreatedAt, &f.UpdatedAt); err != nil { + if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), pq.Array(&f.MatchOther), pq.Array(&f.ExceptOther), &years, &artists, &albums, pq.Array(&f.MatchReleaseTypes), pq.Array(&f.Formats), pq.Array(&f.Quality), pq.Array(&f.Media), &logScore, &hasLog, &hasCue, &perfectFlac, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, pq.Array(&f.Origins), &f.CreatedAt, &f.UpdatedAt); err != nil { log.Error().Stack().Err(err).Msg("filter.findByIndexerIdentifier: error scanning row") return nil, err } @@ -320,6 +325,8 @@ func (r *FilterRepo) Store(ctx context.Context, filter domain.Filter) (*domain.F "containers", "match_hdr", "except_hdr", + "match_other", + "except_other", "years", "match_categories", "except_categories", @@ -337,6 +344,7 @@ func (r *FilterRepo) Store(ctx context.Context, filter domain.Filter) (*domain.F "has_log", "has_cue", "perfect_flac", + "origins", ). Values( filter.Name, @@ -362,6 +370,8 @@ func (r *FilterRepo) Store(ctx context.Context, filter domain.Filter) (*domain.F pq.Array(filter.Containers), pq.Array(filter.MatchHDR), pq.Array(filter.ExceptHDR), + pq.Array(filter.MatchOther), + pq.Array(filter.ExceptOther), filter.Years, filter.MatchCategories, filter.ExceptCategories, @@ -379,6 +389,7 @@ func (r *FilterRepo) Store(ctx context.Context, filter domain.Filter) (*domain.F filter.Log, filter.Cue, filter.PerfectFlac, + pq.Array(filter.Origins), ). Suffix("RETURNING id").RunWith(r.db.handler) @@ -424,6 +435,8 @@ func (r *FilterRepo) Update(ctx context.Context, filter domain.Filter) (*domain. Set("containers", pq.Array(filter.Containers)). Set("match_hdr", pq.Array(filter.MatchHDR)). Set("except_hdr", pq.Array(filter.ExceptHDR)). + Set("match_other", pq.Array(filter.MatchOther)). + Set("except_other", pq.Array(filter.ExceptOther)). Set("years", filter.Years). Set("match_categories", filter.MatchCategories). Set("except_categories", filter.ExceptCategories). @@ -441,6 +454,7 @@ func (r *FilterRepo) Update(ctx context.Context, filter domain.Filter) (*domain. Set("has_log", filter.Log). Set("has_cue", filter.Cue). Set("perfect_flac", filter.PerfectFlac). + Set("origins", pq.Array(filter.Origins)). Set("updated_at", time.Now().Format(time.RFC3339)). Where("id = ?", filter.ID) @@ -596,14 +610,14 @@ func (r *FilterRepo) Delete(ctx context.Context, filterID int) error { } // Split string to slice. We store comma separated strings and convert to slice -func stringToSlice(str string) []string { - if str == "" { - return []string{} - } else if !strings.Contains(str, ",") { - return []string{str} - } - - split := strings.Split(str, ",") - - return split -} +//func stringToSlice(str string) []string { +// if str == "" { +// return []string{} +// } else if !strings.Contains(str, ",") { +// return []string{str} +// } +// +// split := strings.Split(str, ",") +// +// return split +//} diff --git a/internal/database/migrate.go b/internal/database/migrate.go index b76e359..0ee12c4 100644 --- a/internal/database/migrate.go +++ b/internal/database/migrate.go @@ -81,6 +81,8 @@ CREATE TABLE filter containers TEXT [] DEFAULT '{}' NOT NULL, match_hdr TEXT [] DEFAULT '{}', except_hdr TEXT [] DEFAULT '{}', + match_other TEXT [] DEFAULT '{}', + except_other TEXT [] DEFAULT '{}', years TEXT, artists TEXT, albums TEXT, @@ -99,6 +101,7 @@ CREATE TABLE filter except_uploaders TEXT, tags TEXT, except_tags TEXT, + origins TEXT [] DEFAULT '{}' NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); @@ -169,7 +172,6 @@ CREATE TABLE "release" torrent_id TEXT, torrent_name TEXT, size INTEGER, - raw TEXT, title TEXT, category TEXT, season INTEGER, @@ -180,28 +182,13 @@ CREATE TABLE "release" codec TEXT, container TEXT, hdr TEXT, - audio TEXT, - release_group TEXT, - region TEXT, - language TEXT, - edition TEXT, - unrated BOOLEAN, - hybrid BOOLEAN, + group TEXT, proper BOOLEAN, repack BOOLEAN, website TEXT, - artists TEXT [] DEFAULT '{}' NOT NULL, type TEXT, - format TEXT, - quality TEXT, - log_score INTEGER, - has_log BOOLEAN, - has_cue BOOLEAN, - is_scene BOOLEAN, origin TEXT, tags TEXT [] DEFAULT '{}' NOT NULL, - freeleech BOOLEAN, - freeleech_percent INTEGER, uploader TEXT, pre_time TEXT ); @@ -595,6 +582,68 @@ ALTER TABLE release_action_status_dg_tmp ALTER TABLE indexer ADD COLUMN implementation TEXT; `, + ` + ALTER TABLE release + RENAME COLUMN release_group TO "group"; + + ALTER TABLE release + DROP COLUMN raw; + + ALTER TABLE release + DROP COLUMN audio; + + ALTER TABLE release + DROP COLUMN region; + + ALTER TABLE release + DROP COLUMN language; + + ALTER TABLE release + DROP COLUMN edition; + + ALTER TABLE release + DROP COLUMN unrated; + + ALTER TABLE release + DROP COLUMN hybrid; + + ALTER TABLE release + DROP COLUMN artists; + + ALTER TABLE release + DROP COLUMN format; + + ALTER TABLE release + DROP COLUMN quality; + + ALTER TABLE release + DROP COLUMN log_score; + + ALTER TABLE release + DROP COLUMN has_log; + + ALTER TABLE release + DROP COLUMN has_cue; + + ALTER TABLE release + DROP COLUMN is_scene; + + ALTER TABLE release + DROP COLUMN freeleech; + + ALTER TABLE release + DROP COLUMN freeleech_percent; + + ALTER TABLE "filter" + ADD COLUMN origins TEXT [] DEFAULT '{}'; + `, + ` + ALTER TABLE "filter" + ADD COLUMN match_other TEXT [] DEFAULT '{}'; + + ALTER TABLE "filter" + ADD COLUMN except_other TEXT [] DEFAULT '{}'; + `, } const postgresSchema = ` @@ -678,6 +727,8 @@ CREATE TABLE filter containers TEXT [] DEFAULT '{}' NOT NULL, match_hdr TEXT [] DEFAULT '{}', except_hdr TEXT [] DEFAULT '{}', + match_other TEXT [] DEFAULT '{}', + except_other TEXT [] DEFAULT '{}', years TEXT, artists TEXT, albums TEXT, @@ -696,6 +747,7 @@ CREATE TABLE filter except_uploaders TEXT, tags TEXT, except_tags TEXT, + origins TEXT [] DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); @@ -926,4 +978,66 @@ var postgresMigrations = []string{ ALTER TABLE indexer ADD COLUMN implementation TEXT; `, + ` + ALTER TABLE release + RENAME COLUMN release_group TO "group"; + + ALTER TABLE release + DROP COLUMN raw; + + ALTER TABLE release + DROP COLUMN audio; + + ALTER TABLE release + DROP COLUMN region; + + ALTER TABLE release + DROP COLUMN language; + + ALTER TABLE release + DROP COLUMN edition; + + ALTER TABLE release + DROP COLUMN unrated; + + ALTER TABLE release + DROP COLUMN hybrid; + + ALTER TABLE release + DROP COLUMN artists; + + ALTER TABLE release + DROP COLUMN format; + + ALTER TABLE release + DROP COLUMN quality; + + ALTER TABLE release + DROP COLUMN log_score; + + ALTER TABLE release + DROP COLUMN has_log; + + ALTER TABLE release + DROP COLUMN has_cue; + + ALTER TABLE release + DROP COLUMN is_scene; + + ALTER TABLE release + DROP COLUMN freeleech; + + ALTER TABLE release + DROP COLUMN freeleech_percent; + + ALTER TABLE "filter" + ADD COLUMN origins TEXT [] DEFAULT '{}'; + `, + ` + ALTER TABLE "filter" + ADD COLUMN match_other TEXT [] DEFAULT '{}'; + + ALTER TABLE "filter" + ADD COLUMN except_other TEXT [] DEFAULT '{}'; + `, } diff --git a/internal/database/release.go b/internal/database/release.go index 9e1b673..9ce9bc4 100644 --- a/internal/database/release.go +++ b/internal/database/release.go @@ -3,6 +3,8 @@ package database import ( "context" "database/sql" + "strings" + sq "github.com/Masterminds/squirrel" "github.com/autobrr/autobrr/internal/domain" "github.com/lib/pq" @@ -18,10 +20,13 @@ func NewReleaseRepo(db *DB) domain.ReleaseRepo { } func (repo *ReleaseRepo) Store(ctx context.Context, r *domain.Release) (*domain.Release, error) { + codecStr := strings.Join(r.Codec, ",") + hdrStr := strings.Join(r.HDR, ",") + queryBuilder := repo.db.squirrel. Insert("release"). - Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "torrent_name", "size", "raw", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "audio", "release_group", "region", "language", "edition", "unrated", "hybrid", "proper", "repack", "website", "artists", "type", "format", "quality", "log_score", "has_log", "has_cue", "is_scene", "origin", "tags", "freeleech", "freeleech_percent", "uploader", "pre_time"). - Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer, r.FilterName, r.Protocol, r.Implementation, r.Timestamp, r.GroupID, r.TorrentID, r.TorrentName, r.Size, r.Raw, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, r.Codec, r.Container, r.HDR, r.Audio, r.Group, r.Region, r.Language, r.Edition, r.Unrated, r.Hybrid, r.Proper, r.Repack, r.Website, pq.Array(r.Artists), r.Type, r.Format, r.Quality, r.LogScore, r.HasLog, r.HasCue, r.IsScene, r.Origin, pq.Array(r.Tags), r.Freeleech, r.FreeleechPercent, r.Uploader, r.PreTime). + Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "torrent_name", "size", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "group", "proper", "repack", "website", "type", "origin", "tags", "uploader", "pre_time"). + Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer, r.FilterName, r.Protocol, r.Implementation, r.Timestamp, r.GroupID, r.TorrentID, r.TorrentName, r.Size, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, codecStr, r.Container, hdrStr, r.Group, r.Proper, r.Repack, r.Website, r.Type, r.Origin, pq.Array(r.Tags), r.Uploader, r.PreTime). Suffix("RETURNING id").RunWith(repo.db.handler) // return values diff --git a/internal/domain/filter.go b/internal/domain/filter.go index 58c4191..3ec98b0 100644 --- a/internal/domain/filter.go +++ b/internal/domain/filter.go @@ -2,7 +2,13 @@ package domain import ( "context" + "strconv" + "strings" "time" + + "github.com/autobrr/autobrr/pkg/wildcard" + + "github.com/dustin/go-humanize" ) /* @@ -39,7 +45,7 @@ type Filter struct { MatchReleaseGroups string `json:"match_release_groups"` ExceptReleaseGroups string `json:"except_release_groups"` Scene bool `json:"scene"` - Origins string `json:"origins"` + Origins []string `json:"origins"` Freeleech bool `json:"freeleech"` FreeleechPercent string `json:"freeleech_percent"` Shows string `json:"shows"` @@ -51,6 +57,8 @@ type Filter struct { Containers []string `json:"containers"` MatchHDR []string `json:"match_hdr"` ExceptHDR []string `json:"except_hdr"` + MatchOther []string `json:"match_other"` + ExceptOther []string `json:"except_other"` Years string `json:"years"` Artists string `json:"artists"` Albums string `json:"albums"` @@ -74,3 +82,433 @@ type Filter struct { Actions []*Action `json:"actions"` Indexers []Indexer `json:"indexers"` } + +func (f Filter) CheckFilter(r *Release) ([]string, bool) { + // reset rejections first to clean previous checks + r.resetRejections() + + if f.Freeleech && r.Freeleech != f.Freeleech { + r.addRejection("wanted: freeleech") + } + + if f.FreeleechPercent != "" && !checkFreeleechPercent(r.FreeleechPercent, f.FreeleechPercent) { + r.addRejectionF("freeleech percent not matching. got: %v want: %v", r.FreeleechPercent, f.FreeleechPercent) + } + + if len(f.Origins) > 0 && !containsSlice(r.Origin, f.Origins) { + r.addRejectionF("origin not matching. got: %v want: %v", r.Origin, f.Origins) + } + + // title is the parsed title + if f.Shows != "" && !contains(r.Title, f.Shows) { + r.addRejectionF("shows not matching. got: %v want: %v", r.Title, f.Shows) + } + + if f.Seasons != "" && !containsIntStrings(r.Season, f.Seasons) { + r.addRejectionF("season not matching. got: %d want: %v", r.Season, f.Seasons) + } + + if f.Episodes != "" && !containsIntStrings(r.Episode, f.Episodes) { + r.addRejectionF("episodes not matching. got: %d want: %v", r.Episode, f.Episodes) + } + + // matchRelease + // TODO allow to match against regex + if f.MatchReleases != "" && !containsFuzzy(r.TorrentName, f.MatchReleases) { + r.addRejectionF("match release not matching. got: %v want: %v", r.TorrentName, f.MatchReleases) + } + + if f.ExceptReleases != "" && containsFuzzy(r.TorrentName, f.ExceptReleases) { + r.addRejectionF("except releases: unwanted release. got: %v want: %v", r.TorrentName, f.ExceptReleases) + } + + if f.MatchReleaseGroups != "" && !contains(r.Group, f.MatchReleaseGroups) { + r.addRejectionF("release groups not matching. got: %v want: %v", r.Group, f.MatchReleaseGroups) + } + + if f.ExceptReleaseGroups != "" && contains(r.Group, f.ExceptReleaseGroups) { + r.addRejectionF("unwanted release group. got: %v unwanted: %v", r.Group, f.ExceptReleaseGroups) + } + + if f.MatchUploaders != "" && !contains(r.Uploader, f.MatchUploaders) { + r.addRejectionF("uploaders not matching. got: %v want: %v", r.Uploader, f.MatchUploaders) + } + + if f.ExceptUploaders != "" && contains(r.Uploader, f.ExceptUploaders) { + r.addRejectionF("unwanted uploaders. got: %v unwanted: %v", r.Uploader, f.ExceptUploaders) + } + + if len(f.Resolutions) > 0 && !containsSlice(r.Resolution, f.Resolutions) { + r.addRejectionF("resolution not matching. got: %v want: %v", r.Resolution, f.Resolutions) + } + + if len(f.Codecs) > 0 && !sliceContainsSlice(r.Codec, f.Codecs) { + r.addRejectionF("codec not matching. got: %v want: %v", r.Codec, f.Codecs) + } + + if len(f.Sources) > 0 && !containsSlice(r.Source, f.Sources) { + r.addRejectionF("source not matching. got: %v want: %v", r.Source, f.Sources) + } + + if len(f.Containers) > 0 && !containsSlice(r.Container, f.Containers) { + r.addRejectionF("container not matching. got: %v want: %v", r.Container, f.Containers) + } + + // HDR is parsed into the Codec slice from rls + if len(f.MatchHDR) > 0 && !sliceContainsSlice(r.HDR, f.MatchHDR) { + r.addRejectionF("hdr not matching. got: %v want: %v", r.HDR, f.MatchHDR) + } + + // HDR is parsed into the Codec slice from rls + if len(f.ExceptHDR) > 0 && sliceContainsSlice(r.HDR, f.ExceptHDR) { + r.addRejectionF("hdr unwanted. got: %v want: %v", r.HDR, f.ExceptHDR) + } + + if f.Years != "" && !containsIntStrings(r.Year, f.Years) { + r.addRejectionF("year not matching. got: %d want: %v", r.Year, f.Years) + } + + if f.MatchCategories != "" && !contains(r.Category, f.MatchCategories) { + r.addRejectionF("category not matching. got: %v want: %v", r.Category, f.MatchCategories) + } + + if f.ExceptCategories != "" && contains(r.Category, f.ExceptCategories) { + r.addRejectionF("category unwanted. got: %v want: %v", r.Category, f.ExceptCategories) + } + + if len(f.MatchReleaseTypes) > 0 && !containsSlice(r.Category, f.MatchReleaseTypes) { + r.addRejectionF("release type not matching. got: %v want: %v", r.Category, f.MatchReleaseTypes) + } + + if (f.MinSize != "" || f.MaxSize != "") && !f.checkSizeFilter(r, f.MinSize, f.MaxSize) { + r.addRejectionF("size not matching. got: %v want min: %v max: %v", r.Size, f.MinSize, f.MaxSize) + } + + if f.Tags != "" && !containsAny(r.Tags, f.Tags) { + r.addRejectionF("tags not matching. got: %v want: %v", r.Tags, f.Tags) + } + + if f.ExceptTags != "" && containsAny(r.Tags, f.ExceptTags) { + r.addRejectionF("tags unwanted. got: %v want: %v", r.Tags, f.ExceptTags) + } + + if len(f.Artists) > 0 && !containsFuzzy(r.TorrentName, f.Artists) { + r.addRejectionF("artists not matching. got: %v want: %v", r.TorrentName, f.Artists) + } + + if len(f.Albums) > 0 && !containsFuzzy(r.TorrentName, f.Albums) { + r.addRejectionF("albums not matching. got: %v want: %v", r.TorrentName, f.Albums) + } + + // Perfect flac requires Cue, Log, Log Score 100, FLAC and 24bit Lossless + if f.PerfectFlac && !f.isPerfectFLAC(r) { + r.addRejectionF("wanted: perfect flac. got: %v", r.Audio) + } + + if len(f.Formats) > 0 && !sliceContainsSlice(r.Audio, f.Formats) { + r.addRejectionF("formats not matching. got: %v want: %v", r.Audio, f.Formats) + } + + if len(f.Quality) > 0 && !sliceContainsSlice(r.Audio, f.Quality) { + r.addRejectionF("quality not matching. got: %v want: %v", r.Audio, f.Quality) + } + + if len(f.Media) > 0 && !containsSlice(r.Source, f.Media) { + r.addRejectionF("media not matching. got: %v want: %v", r.Source, f.Media) + } + + if f.Cue && !containsAny(r.Audio, "Cue") { + r.addRejection("wanted: cue") + } + + if f.Log && !containsAny(r.Audio, "Log") { + r.addRejection("wanted: log") + } + + if f.Log && f.LogScore != 0 && r.LogScore != f.LogScore { + r.addRejectionF("log score. got: %v want: %v", r.LogScore, f.LogScore) + } + + if len(r.Rejections) > 0 { + return r.Rejections, false + } + + return nil, true +} + +// isPerfectFLAC Perfect is "CD FLAC Cue Log 100% Lossless or 24bit Lossless" +func (f Filter) isPerfectFLAC(r *Release) bool { + if !contains(r.Source, "CD") { + return false + } + if !containsAny(r.Audio, "Cue") { + return false + } + if !containsAny(r.Audio, "Log") { + return false + } + if !containsAny(r.Audio, "Log100") { + return false + } + if !containsAny(r.Audio, "FLAC") { + return false + } + if !containsAnySlice(r.Audio, []string{"Lossless", "24bit Lossless"}) { + return false + } + + return true +} + +// checkSizeFilter additional size check +// for indexers that doesn't announce size, like some gazelle based +// set flag r.AdditionalSizeCheckRequired if there's a size in the filter, otherwise go a head +// implement API for ptp,btn,ggn to check for size if needed +// for others pull down torrent and do check +func (f Filter) checkSizeFilter(r *Release, minSize string, maxSize string) bool { + + if r.Size == 0 { + r.AdditionalSizeCheckRequired = true + + return true + } else { + r.AdditionalSizeCheckRequired = false + } + + // if r.Size parse filter to bytes and compare + // handle both min and max + if minSize != "" { + // string to bytes + minSizeBytes, err := humanize.ParseBytes(minSize) + if err != nil { + // log could not parse into bytes + } + + if r.Size <= minSizeBytes { + r.addRejection("size: smaller than min size") + return false + } + + } + + if maxSize != "" { + // string to bytes + maxSizeBytes, err := humanize.ParseBytes(maxSize) + if err != nil { + // log could not parse into bytes + } + + if r.Size >= maxSizeBytes { + r.addRejection("size: larger than max size") + return false + } + } + + return true +} + +// checkFilterIntStrings "1,2,3-20" +func containsIntStrings(value int, filterList string) bool { + filters := strings.Split(filterList, ",") + + for _, s := range filters { + s = strings.Replace(s, "%", "", -1) + s = strings.Trim(s, " ") + + if strings.Contains(s, "-") { + minMax := strings.Split(s, "-") + + // to int + min, err := strconv.ParseInt(minMax[0], 10, 32) + if err != nil { + return false + } + + max, err := strconv.ParseInt(minMax[1], 10, 32) + if err != nil { + return false + } + + if min > max { + // handle error + return false + } else { + // if announcePercent is greater than min and less than max return true + if value >= int(min) && value <= int(max) { + return true + } + } + } + + filterInt, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return false + } + + if int(filterInt) == value { + return true + } + } + + return false +} + +func contains(tag string, filter string) bool { + return containsMatch([]string{tag}, strings.Split(filter, ",")) +} + +func containsFuzzy(tag string, filter string) bool { + return containsMatchFuzzy([]string{tag}, strings.Split(filter, ",")) +} + +func containsSlice(tag string, filters []string) bool { + return containsMatch([]string{tag}, filters) +} + +func containsAny(tags []string, filter string) bool { + return containsMatch(tags, strings.Split(filter, ",")) +} + +func sliceContainsSlice(tags []string, filters []string) bool { + return containsMatchBasic(tags, filters) +} + +func containsMatchFuzzy(tags []string, filters []string) bool { + for _, tag := range tags { + tag = strings.ToLower(tag) + + for _, filter := range filters { + filter = strings.ToLower(filter) + filter = strings.Trim(filter, " ") + // check if line contains * or ?, if so try wildcard match, otherwise try substring match + a := strings.ContainsAny(filter, "?|*") + if a { + match := wildcard.Match(filter, tag) + if match { + return true + } + } else if strings.Contains(tag, filter) { + return true + } + } + } + + return false +} + +func containsMatch(tags []string, filters []string) bool { + for _, tag := range tags { + tag = strings.ToLower(tag) + + for _, filter := range filters { + filter = strings.ToLower(filter) + filter = strings.Trim(filter, " ") + // check if line contains * or ?, if so try wildcard match, otherwise try substring match + a := strings.ContainsAny(filter, "?|*") + if a { + match := wildcard.Match(filter, tag) + if match { + return true + } + } else if tag == filter { + return true + } + } + } + + return false +} + +func containsMatchBasic(tags []string, filters []string) bool { + for _, tag := range tags { + tag = strings.ToLower(tag) + + for _, filter := range filters { + filter = strings.ToLower(filter) + filter = strings.Trim(filter, " ") + + if tag == filter { + return true + } + } + } + + return false +} + +func containsAnySlice(tags []string, filters []string) bool { + for _, tag := range tags { + tag = strings.ToLower(tag) + + for _, filter := range filters { + filter = strings.ToLower(filter) + filter = strings.Trim(filter, " ") + // check if line contains * or ?, if so try wildcard match, otherwise try substring match + wild := strings.ContainsAny(filter, "?|*") + if wild { + match := wildcard.Match(filter, tag) + if match { + return true + } + } else if tag == filter { + return true + } + } + } + + return false +} + +func checkFreeleechPercent(announcePercent int, filterPercent string) bool { + filters := strings.Split(filterPercent, ",") + + // remove % and trim spaces + //announcePercent = strings.Replace(announcePercent, "%", "", -1) + //announcePercent = strings.Trim(announcePercent, " ") + + //announcePercentInt, err := strconv.ParseInt(announcePercent, 10, 32) + //if err != nil { + // return false + //} + + for _, s := range filters { + s = strings.Replace(s, "%", "", -1) + s = strings.Trim(s, " ") + + if strings.Contains(s, "-") { + minMax := strings.Split(s, "-") + + // to int + min, err := strconv.ParseInt(minMax[0], 10, 32) + if err != nil { + return false + } + + max, err := strconv.ParseInt(minMax[1], 10, 32) + if err != nil { + return false + } + + if min > max { + // handle error + return false + } else { + // if announcePercent is greater than min and less than max return true + if announcePercent >= int(min) && announcePercent <= int(max) { + return true + } + } + } + + filterPercentInt, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return false + } + + if int(filterPercentInt) == announcePercent { + return true + } + } + + return false +} diff --git a/internal/domain/filter_test.go b/internal/domain/filter_test.go new file mode 100644 index 0000000..1b63313 --- /dev/null +++ b/internal/domain/filter_test.go @@ -0,0 +1,1537 @@ +package domain + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestFilter_CheckFilter(t *testing.T) { + type args struct { + filter Filter + rejections []string + } + tests := []struct { + name string + fields *Release + args args + want bool + }{ + //{ + // name: "test_no_size", + // fields: &Release{Size: uint64(0)}, + // args: args{ + // filter: Filter{ + // Enabled: true, + // FilterGeneral: FilterGeneral{MinSize: "10 GB", MaxSize: "20GB"}, + // }, + // }, + // want: false, // additional checks + //}, + { + name: "movie_parse_1", + fields: &Release{ + TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + }, + }, + want: true, + }, + + { + name: "movie_parse_2", + fields: &Release{ + TorrentName: "That Movie 2020 2160p Blu-Ray DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + }, + }, + want: true, + }, + { + name: "movie_parse_3", + fields: &Release{ + TorrentName: "That Movie 2020 2160p WEBDL DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + }, + }, + want: true, + }, + { + name: "movie_parse_shows", + fields: &Release{ + TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + Shows: "That Movie", + }, + }, + want: true, + }, + { + name: "movie_parse_shows_1", + fields: &Release{ + TorrentName: "That.Movie.2020.2160p.BluRay.DD5.1.x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + Shows: "That Movie", + }, + }, + want: true, + }, + { + name: "movie_parse_multiple_shows", + fields: &Release{ + TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + Shows: "That Movie, good story, bad movie", + }, + }, + want: true, + }, + { + name: "movie_parse_multiple_shows_1", + fields: &Release{ + TorrentName: "That.Movie.2020.2160p.BluRay.DD5.1.x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2020", + MatchReleaseGroups: "GROUP1", + Shows: "That Movie, good story, bad movie", + }, + }, + want: true, + }, + { + name: "movie_parse_wildcard_shows", + fields: &Release{ + TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), // 30GB + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Movies, tv", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"1080p", "2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2015,2018-2022", + MatchReleaseGroups: "GROUP1,BADGROUP", + Shows: "*Movie*, good story, bad movie", + }, + }, + want: true, + }, + { + name: "movie_bad_category", + fields: &Release{ + TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", + Category: "Movies", + Freeleech: true, + Size: uint64(30000000001), // 30GB + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + Freeleech: true, + MinSize: "10 GB", + MaxSize: "40GB", + Resolutions: []string{"1080p", "2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x264"}, + Years: "2015,2018-2022", + MatchReleaseGroups: "GROUP1,BADGROUP", + Shows: "*Movie*, good story, bad movie", + }, + rejections: []string{"category not matching. got: Movies want: *tv*"}, + }, + want: false, + }, + { + name: "tv_match_season_episode", + fields: &Release{ + TorrentName: "Good show S01E01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + Resolutions: []string{"1080p", "2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"HEVC"}, + MatchReleaseGroups: "GROUP1,GROUP2", + Seasons: "1,2", + Episodes: "1", + }, + }, + want: true, + }, + { + name: "tv_match_season", + fields: &Release{ + TorrentName: "Good show S01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + Resolutions: []string{"1080p", "2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"HEVC"}, + MatchReleaseGroups: "GROUP1,GROUP2", + Seasons: "1,2", + }, + }, + want: true, + }, + { + name: "tv_bad_match_season", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + Resolutions: []string{"1080p", "2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"HEVC"}, + MatchReleaseGroups: "GROUP1,GROUP2", + Seasons: "1", + }, + rejections: []string{"season not matching. got: 2 want: 1"}, + }, + want: false, + }, + { + name: "match_uploader", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1", + }, + }, + want: true, + }, + { + name: "except_uploader", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Anonymous", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + ExceptUploaders: "Anonymous", + }, + rejections: []string{"unwanted uploaders. got: Anonymous unwanted: Anonymous"}, + }, + want: false, + }, + { + name: "match_except_uploader", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show", + }, + }, + want: true, + }, + { + name: "match_tags", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Uploader1", + Tags: []string{"tv"}, + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show", + Tags: "tv", + }, + }, + want: true, + }, + { + name: "match_tags_bad", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Uploader1", + Tags: []string{"foreign"}, + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show", + Tags: "tv", + }, + rejections: []string{"tags not matching. got: [foreign] want: tv"}, + }, + want: false, + }, + { + name: "match_except_tags", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Uploader1", + Tags: []string{"foreign"}, + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show", + ExceptTags: "tv", + }, + }, + want: true, + }, + { + name: "match_except_tags_2", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + Category: "TV", + Uploader: "Uploader1", + Tags: []string{"foreign"}, + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show", + ExceptTags: "foreign", + }, + rejections: []string{"tags unwanted. got: [foreign] want: foreign"}, + }, + want: false, + }, + { + name: "match_group_1", + fields: &Release{ + TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show", + MatchReleaseGroups: "GROUP", + }, + }, + want: true, + }, + { + name: "match_group_potential_partial_1", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-ift", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "ift", + }, + }, + want: true, + }, + { + name: "match_group_potential_partial_2", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "ift", + }, + rejections: []string{"release groups not matching. got: GROUP want: ift"}, + }, + want: false, + }, + //{ + // name: "match_group_potential_partial_3", + // fields: &Release{ + // TorrentName: "[AnimeGroup] Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC", + // Category: "TV", + // Uploader: "Uploader1", + // }, + // args: args{ + // filter: Filter{ + // Enabled: true, + // MatchCategories: "*tv*", + // MatchUploaders: "Uploader1,Uploader2", + // ExceptUploaders: "Anonymous", + // Shows: "Good show shift", + // MatchReleaseGroups: "[AnimeGroup]", + // }, + // }, + // want: true, + //}, + { + name: "except_release_1", + fields: &Release{ + TorrentName: "Good show shift S02 NORDiC 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "Good show shift", + }, + rejections: []string{"except releases: unwanted release. got: Good show shift S02 NORDiC 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP want: Good show shift"}, + }, + want: false, + }, + { + name: "except_release_2", + fields: &Release{ + TorrentName: "Good show shift S02 NORDiC 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + }, + rejections: []string{"except releases: unwanted release. got: Good show shift S02 NORDiC 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP want: NORDiC"}, + }, + want: false, + }, + { + name: "except_release_3", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + }, + }, + want: true, + }, + { + name: "except_release_4", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC,*shift*", + }, + rejections: []string{"except releases: unwanted release. got: Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP want: NORDiC,*shift*"}, + }, + want: false, + }, + { + name: "match_hdr_1", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + MatchHDR: []string{"DV", "HDR"}, + }, + }, + want: true, + }, + { + name: "match_hdr_2", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DoVi HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + MatchHDR: []string{"DV", "HDR"}, + }, + }, + want: true, + }, + { + name: "match_hdr_3", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DoVi HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + ExceptHDR: []string{"DV", "HDR", "DoVi"}, + }, + rejections: []string{"hdr unwanted. got: [DV] want: [DV HDR DoVi]"}, + }, + want: false, + }, + { + name: "match_hdr_4", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + MatchHDR: []string{"DV", "HDR", "DoVi"}, + }, + rejections: []string{"hdr not matching. got: [] want: [DV HDR DoVi]"}, + }, + want: false, + }, + { + name: "match_hdr_5", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + ExceptHDR: []string{"DV", "HDR", "DoVi"}, + }, + }, + want: true, + }, + { + name: "match_hdr_6", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HDR HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + ExceptHDR: []string{"DV", "DoVi"}, + }, + }, + want: true, + }, + { + name: "match_hdr_7", + fields: &Release{ + TorrentName: "Good show dvorak shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HDR HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show dvorak shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + ExceptHDR: []string{"DV", "DoVi"}, + }, + }, + want: true, + }, + { + name: "match_hdr_8", + fields: &Release{ + TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HDR10+ HEVC-GROUP", + Category: "TV", + Uploader: "Uploader1", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "*tv*", + MatchUploaders: "Uploader1,Uploader2", + ExceptUploaders: "Anonymous", + Shows: "Good show shift", + MatchReleaseGroups: "GROUP", + ExceptReleases: "NORDiC", + MatchHDR: []string{"DV", "DoVi", "HDR10+"}, + }, + }, + want: true, + }, + { + name: "match_music_1", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "FLAC / 24bit Lossless / Log / 100% / Cue / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Album", + Artists: "Artist", + Media: []string{"CD"}, + Formats: []string{"FLAC"}, + Quality: []string{"24bit Lossless"}, + Log: true, + Cue: true, + //LogScore: 100, + }, + }, + want: true, + }, + { + name: "match_music_2", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "MP3 / 320 / WEB", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Album", + Artists: "Artist", + PerfectFlac: true, + }, + rejections: []string{"wanted: perfect flac. got: [320 MP3]"}, + }, + want: false, + }, + { + name: "match_music_3", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "FLAC / Lossless / Log / 100% / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Album", + Artists: "Artist", + PerfectFlac: true, + }, + rejections: []string{"wanted: perfect flac. got: [FLAC Lossless Log100 Log]"}, + }, + want: false, + }, + { + name: "match_music_4", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "FLAC / Lossless / Log / 100% / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchCategories: "Album", + Artists: "Artist", + Media: []string{"CD"}, + Formats: []string{"FLAC"}, + Quality: []string{"24bit Lossless"}, + //PerfectFlac: true, + Log: true, + LogScore: 100, + Cue: true, + }, + rejections: []string{"quality not matching. got: [FLAC Lossless Log100 Log] want: [24bit Lossless]", "wanted: cue", "log score. got: 0 want: 100"}, + }, + want: false, + }, + { + name: "match_music_5", + fields: &Release{ + TorrentName: "Artist - Albumname", + Year: 2022, + ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchReleaseTypes: []string{"Album"}, + Years: "2020-2022", + Artists: "Artist", + Media: []string{"CD"}, + Formats: []string{"FLAC"}, + Quality: []string{"24bit Lossless", "Lossless"}, + PerfectFlac: true, + Log: true, + //LogScore: 100, + Cue: true, + }, + }, + want: true, + }, + { + name: "match_music_6", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchReleaseTypes: []string{"Single"}, + Artists: "Artist", + Media: []string{"CD"}, + Formats: []string{"FLAC"}, + Quality: []string{"24bit Lossless", "Lossless"}, + PerfectFlac: true, + Log: true, + LogScore: 100, + Cue: true, + }, + rejections: []string{"release type not matching. got: Album want: [Single]", "log score. got: 0 want: 100"}, + }, + want: false, + }, + { + name: "match_music_7", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchReleaseTypes: []string{"Album"}, + Artists: "Artiiiist", + Media: []string{"CD"}, + Formats: []string{"FLAC"}, + Quality: []string{"24bit Lossless", "Lossless"}, + PerfectFlac: true, + Log: true, + LogScore: 100, + Cue: true, + }, + rejections: []string{"artists not matching. got: Artist - Albumname want: Artiiiist", "log score. got: 0 want: 100"}, + }, + want: false, + }, + { + name: "match_music_8", + fields: &Release{ + TorrentName: "Artist - Albumname", + ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", + Category: "Album", + }, + args: args{ + filter: Filter{ + Enabled: true, + MatchReleaseTypes: []string{"Album"}, + Artists: "Artist", + Albums: "Albumname", + Media: []string{"CD"}, + Formats: []string{"FLAC"}, + Quality: []string{"24bit Lossless", "Lossless"}, + PerfectFlac: true, + Log: true, + //LogScore: 100, + Cue: true, + }, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := tt.fields // Release + + _ = r.ParseString(tt.fields.TorrentName) // Parse TorrentName into struct + rejections, got := tt.args.filter.CheckFilter(r) + + assert.Equal(t, tt.want, got) + assert.Equal(t, tt.args.rejections, rejections) + }) + } +} + +func TestFilter_CheckFilter1(t *testing.T) { + type fields struct { + ID int + Name string + Enabled bool + CreatedAt time.Time + UpdatedAt time.Time + MinSize string + MaxSize string + Delay int + Priority int32 + MatchReleases string + ExceptReleases string + UseRegex bool + MatchReleaseGroups string + ExceptReleaseGroups string + Scene bool + Origins []string + Freeleech bool + FreeleechPercent string + Shows string + Seasons string + Episodes string + Resolutions []string + Codecs []string + Sources []string + Containers []string + MatchHDR []string + ExceptHDR []string + Years string + Artists string + Albums string + MatchReleaseTypes []string + ExceptReleaseTypes string + Formats []string + Quality []string + Media []string + PerfectFlac bool + Cue bool + Log bool + LogScore int + MatchCategories string + ExceptCategories string + MatchUploaders string + ExceptUploaders string + Tags string + ExceptTags string + TagsAny string + ExceptTagsAny string + Actions []*Action + Indexers []Indexer + } + type args struct { + r *Release + } + tests := []struct { + name string + fields fields + args args + wantRejections []string + wantMatch bool + }{ + { + name: "test_1", + fields: fields{ + Shows: "WeCrashed", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + MatchHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_2", + fields: fields{ + Shows: "WeCrashed", + Seasons: "1", + Episodes: "2-8", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"episodes not matching. got: 0 want: 2-8"}, + wantMatch: false, + }, + { + name: "test_3", + fields: fields{ + Shows: "WeCrashed", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + MatchHDR: []string{"HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"hdr not matching. got: [DV] want: [HDR]"}, + wantMatch: false, + }, + { + name: "test_4", + fields: fields{ + Shows: "WeCrashed", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + ExceptHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"hdr unwanted. got: [DV] want: [DV HDR]"}, + wantMatch: false, + }, + { + name: "test_5", + fields: fields{ + Shows: "WeWork", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + ExceptHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"shows not matching. got: WeCrashed want: WeWork", "hdr unwanted. got: [DV] want: [DV HDR]"}, + wantMatch: false, + }, + { + name: "test_6", + fields: fields{ + Shows: "WeWork", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + ExceptReleaseGroups: "NOSiViD", + ExceptHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"shows not matching. got: WeCrashed want: WeWork", "unwanted release group. got: NOSiViD unwanted: NOSiViD", "hdr unwanted. got: [DV] want: [DV HDR]"}, + wantMatch: false, + }, + { + name: "test_7", + fields: fields{ + Shows: "WeWork", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + ExceptReleaseGroups: "NOSiViD", + ExceptHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"shows not matching. got: WeCrashed want: WeWork", "unwanted release group. got: NOSiViD unwanted: NOSiViD", "source not matching. got: WEB want: [WEB-DL]", "hdr unwanted. got: [DV] want: [DV HDR]"}, + wantMatch: false, + }, + { + name: "test_8", + fields: fields{ + Shows: "WeCrashed", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + MatchHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"source not matching. got: WEB-DL want: [WEB]"}, + wantMatch: false, + }, + { + name: "test_9", + fields: fields{ + Shows: "WeCrashed", + Seasons: "1", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB"}, + Codecs: []string{"x265"}, + MatchReleaseGroups: "NOSiViD", + MatchHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "WeCrashed.S01.DV.2160p.Blu-ray.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"source not matching. got: BluRay want: [WEB]"}, + wantMatch: false, + }, + { + name: "test_10", + fields: fields{ + Resolutions: []string{"2160p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"x265", "HEVC"}, + MatchHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "Stranger Things S02 UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-FraMeSToR"}}, + wantRejections: []string{"source not matching. got: UHD.BluRay want: [BluRay]"}, + wantMatch: false, + }, + { + name: "test_10", + fields: fields{ + Resolutions: []string{"2160p"}, + Sources: []string{"UHD.BluRay"}, + Codecs: []string{"x265", "HEVC"}, + MatchHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "Stranger Things S02 UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-FraMeSToR"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_11", + fields: fields{ + Resolutions: []string{"1080p"}, + Sources: []string{"BluRay"}, + Codecs: []string{"HEVC"}, + //MatchHDR: []string{"DV", "HDR"}, + }, + args: args{&Release{TorrentName: "Food Wars!: Shokugeki no Soma S05 2020 1080p BluRay HEVC 10-Bit DD2.0 Dual Audio -ZR-"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_12", + fields: fields{ + Resolutions: []string{"2160p"}, + Codecs: []string{"h.265"}, + }, + args: args{&Release{TorrentName: "The.First.Lady.S01E01.DV.2160p.WEB-DL.DD5.1.H265-GLHF"}}, + wantRejections: nil, + wantMatch: true, + }, + + { + name: "test_13", + fields: fields{ + Resolutions: []string{"2160p"}, + Codecs: []string{"h.265"}, + }, + args: args{&Release{TorrentName: "The First Lady S01E01 DV 2160p WEB-DL DD5.1 H 265-GLHF"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_14", + fields: fields{ + Sources: []string{"WEBRip"}, + }, + args: args{&Release{TorrentName: "Halt and Catch Fire S04 1080p WEBRip x265-HiQVE"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_15", + fields: fields{ + Sources: []string{"WEB"}, + }, + args: args{&Release{TorrentName: "Dominik Walter-Cocktail Girl-(NS1083)-WEB-2022-AFO"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_16", + fields: fields{ + Sources: []string{"ViNYL"}, + }, + args: args{&Release{TorrentName: "Love Unlimited - Under the Influence of Love Unlimited [1973] [Album] - MP3 / V0 (VBR) / Vinyl"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_17", + fields: fields{ + Resolutions: []string{"1080p"}, + Sources: []string{"BluRay"}, + }, + args: args{&Release{TorrentName: "A Movie [2015] - GROUP", ReleaseTags: "Type: Movie / 1080p / Encode / Freeleech: 100 Size: 7.00GB"}}, + wantRejections: []string{"source not matching. got: want: [BluRay]"}, + wantMatch: false, + }, + { + name: "test_18", + fields: fields{ + Resolutions: []string{"2160p"}, + }, + args: args{&Release{TorrentName: "The Green Mile [1999] - playBD", ReleaseTags: "Type: Movie / 2160p / Remux / Freeleech: 100 Size: 72.78GB"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_19", + fields: fields{ + Shows: "Reacher", + Seasons: "1", + Episodes: "0", + Resolutions: []string{"2160p"}, + Sources: []string{"WEB-DL"}, + Codecs: []string{"x265"}, + }, + args: args{&Release{TorrentName: "Preacher.S01.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}}, + wantRejections: []string{"shows not matching. got: Preacher want: Reacher"}, + wantMatch: false, + }, + { + name: "test_20", + fields: fields{ + Shows: "Atlanta", + Resolutions: []string{"1080p"}, + Sources: []string{"WEB-DL", "WEB"}, + }, + args: args{&Release{TorrentName: "NBA.2022.04.19.Atlanta.Hawks.vs.Miami.Heat.1080p.WEB.H264-SPLASH"}}, + wantRejections: []string{"shows not matching. got: NBA want: Atlanta"}, + wantMatch: false, + }, + { + name: "test_21", + fields: fields{ + Formats: []string{"FLAC"}, + Quality: []string{"Lossless"}, + Media: []string{"CD"}, + Log: true, + //LogScore: 100, + Cue: true, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_22", + fields: fields{ + PerfectFlac: true, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_23", + fields: fields{ + Origins: []string{"Internal"}, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene", Origin: "Internal"}}, + wantRejections: nil, + wantMatch: true, + }, + { + name: "test_24", + fields: fields{ + Origins: []string{"P2P"}, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene", Origin: "Internal"}}, + wantRejections: []string{"origin not matching. got: Internal want: [P2P]"}, + wantMatch: false, + }, + { + name: "test_25", + fields: fields{ + Origins: []string{"O-SCENE"}, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene", Origin: "SCENE"}}, + wantRejections: []string{"origin not matching. got: SCENE want: [O-SCENE]"}, + wantMatch: false, + }, + { + name: "test_26", + fields: fields{ + Origins: []string{"SCENE"}, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene", Origin: "O-SCENE"}}, + wantRejections: []string{"origin not matching. got: O-SCENE want: [SCENE]"}, + wantMatch: false, + }, + { + name: "test_26", + fields: fields{ + Origins: []string{"SCENE"}, + }, + args: args{&Release{TorrentName: "Gillan - Future Shock", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD / Scene"}}, + wantRejections: []string{"origin not matching. got: want: [SCENE]"}, + wantMatch: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := Filter{ + ID: tt.fields.ID, + Name: tt.fields.Name, + Enabled: tt.fields.Enabled, + CreatedAt: tt.fields.CreatedAt, + UpdatedAt: tt.fields.UpdatedAt, + MinSize: tt.fields.MinSize, + MaxSize: tt.fields.MaxSize, + Delay: tt.fields.Delay, + Priority: tt.fields.Priority, + MatchReleases: tt.fields.MatchReleases, + ExceptReleases: tt.fields.ExceptReleases, + UseRegex: tt.fields.UseRegex, + MatchReleaseGroups: tt.fields.MatchReleaseGroups, + ExceptReleaseGroups: tt.fields.ExceptReleaseGroups, + Scene: tt.fields.Scene, + Origins: tt.fields.Origins, + Freeleech: tt.fields.Freeleech, + FreeleechPercent: tt.fields.FreeleechPercent, + Shows: tt.fields.Shows, + Seasons: tt.fields.Seasons, + Episodes: tt.fields.Episodes, + Resolutions: tt.fields.Resolutions, + Codecs: tt.fields.Codecs, + Sources: tt.fields.Sources, + Containers: tt.fields.Containers, + MatchHDR: tt.fields.MatchHDR, + ExceptHDR: tt.fields.ExceptHDR, + Years: tt.fields.Years, + Artists: tt.fields.Artists, + Albums: tt.fields.Albums, + MatchReleaseTypes: tt.fields.MatchReleaseTypes, + ExceptReleaseTypes: tt.fields.ExceptReleaseTypes, + Formats: tt.fields.Formats, + Quality: tt.fields.Quality, + Media: tt.fields.Media, + PerfectFlac: tt.fields.PerfectFlac, + Cue: tt.fields.Cue, + Log: tt.fields.Log, + LogScore: tt.fields.LogScore, + MatchCategories: tt.fields.MatchCategories, + ExceptCategories: tt.fields.ExceptCategories, + MatchUploaders: tt.fields.MatchUploaders, + ExceptUploaders: tt.fields.ExceptUploaders, + Tags: tt.fields.Tags, + ExceptTags: tt.fields.ExceptTags, + TagsAny: tt.fields.TagsAny, + ExceptTagsAny: tt.fields.ExceptTagsAny, + Actions: tt.fields.Actions, + Indexers: tt.fields.Indexers, + } + tt.args.r.ParseString(tt.args.r.TorrentName) + rejections, match := f.CheckFilter(tt.args.r) + assert.Equalf(t, tt.wantRejections, rejections, "CheckFilter(%v)", tt.args.r) + assert.Equalf(t, tt.wantMatch, match, "CheckFilter(%v)", tt.args.r) + }) + } +} + +func Test_contains(t *testing.T) { + type args struct { + tag string + filter string + } + tests := []struct { + name string + args args + want bool + }{ + {name: "test_1", args: args{tag: "group1", filter: "group1,group2"}, want: true}, + {name: "test_2", args: args{tag: "group1", filter: "group1,group2"}, want: true}, + {name: "test_3", args: args{tag: "group1", filter: "group2,group3"}, want: false}, + {name: "test_4", args: args{tag: "something test something", filter: "*test*"}, want: true}, + {name: "test_5", args: args{tag: "something.test.something", filter: "*test*"}, want: true}, + {name: "test_6", args: args{tag: "that movie", filter: "that?movie"}, want: true}, + {name: "test_7", args: args{tag: "that.movie", filter: "that?movie"}, want: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, contains(tt.args.tag, tt.args.filter), "contains(%v, %v)", tt.args.tag, tt.args.filter) + }) + } +} + +func Test_containsSlice(t *testing.T) { + type args struct { + tag string + filters []string + } + tests := []struct { + name string + args args + want bool + }{ + {name: "test_1", args: args{tag: "group1", filters: []string{"group1", "group2"}}, want: true}, + {name: "test_2", args: args{tag: "group1", filters: []string{"group2", "group3"}}, want: false}, + {name: "test_3", args: args{tag: "2160p", filters: []string{"1080p", "2160p"}}, want: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, containsSlice(tt.args.tag, tt.args.filters), "containsSlice(%v, %v)", tt.args.tag, tt.args.filters) + }) + } +} + +func Test_containsAny(t *testing.T) { + type args struct { + tags []string + filter string + } + tests := []struct { + name string + args args + want bool + }{ + {name: "test_1", args: args{tags: []string{"HDR", "DV"}, filter: "DV"}, want: true}, + {name: "test_2", args: args{tags: []string{"HDR"}, filter: "DV"}, want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, containsAny(tt.args.tags, tt.args.filter), "containsAny(%v, %v)", tt.args.tags, tt.args.filter) + }) + } +} + +func Test_sliceContainsSlice(t *testing.T) { + type args struct { + tags []string + filters []string + } + tests := []struct { + name string + args args + want bool + }{ + {name: "test_1", args: args{tags: []string{"HDR", "DV"}, filters: []string{"HDR", "DoVi"}}, want: true}, + {name: "test_2", args: args{tags: []string{"HDR10+", "DV"}, filters: []string{"HDR"}}, want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, sliceContainsSlice(tt.args.tags, tt.args.filters), "sliceContainsSlice(%v, %v)", tt.args.tags, tt.args.filters) + }) + } +} + +func Test_containsIntStrings(t *testing.T) { + type args struct { + value int + filterList string + } + tests := []struct { + name string + args args + want bool + }{ + {name: "test_1", args: args{value: 2, filterList: "1,2,3"}, want: true}, + {name: "test_2", args: args{value: 2, filterList: "1-3"}, want: true}, + {name: "test_3", args: args{value: 2, filterList: "2"}, want: true}, + {name: "test_4", args: args{value: 2, filterList: "2-5"}, want: true}, + {name: "test_5", args: args{value: 2, filterList: "3-5"}, want: false}, + {name: "test_6", args: args{value: 2, filterList: "3-5"}, want: false}, + {name: "test_7", args: args{value: 0, filterList: "3-5"}, want: false}, + {name: "test_8", args: args{value: 0, filterList: "0"}, want: true}, + {name: "test_9", args: args{value: 100, filterList: "1-1000"}, want: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, containsIntStrings(tt.args.value, tt.args.filterList), "containsIntStrings(%v, %v)", tt.args.value, tt.args.filterList) + }) + } +} diff --git a/internal/domain/indexer.go b/internal/domain/indexer.go index 93fd40d..feae247 100644 --- a/internal/domain/indexer.go +++ b/internal/domain/indexer.go @@ -1,9 +1,13 @@ package domain import ( + "bytes" "context" + "net/url" + "text/template" "github.com/dustin/go-humanize" + "github.com/rs/zerolog/log" ) type IndexerRepo interface { @@ -115,6 +119,56 @@ type IndexerParseMatch struct { Encode []string `json:"encode"` } +func (p *IndexerParse) ParseTorrentUrl(vars map[string]string, extraVars map[string]string, release *Release) error { + tmpVars := map[string]string{} + + // copy vars to new tmp map + for k, v := range vars { + tmpVars[k] = v + } + + // merge extra vars with vars + if extraVars != nil { + for k, v := range extraVars { + tmpVars[k] = v + } + } + + // handle url encode of values + if p.Match.Encode != nil { + for _, e := range p.Match.Encode { + if v, ok := tmpVars[e]; ok { + // url encode value + t := url.QueryEscape(v) + tmpVars[e] = t + } + } + } + + // setup text template to inject variables into + tmpl, err := template.New("torrenturl").Parse(p.Match.TorrentURL) + if err != nil { + log.Error().Err(err).Msg("could not create torrent url template") + return err + } + + var urlBytes bytes.Buffer + err = tmpl.Execute(&urlBytes, &tmpVars) + if err != nil { + log.Error().Err(err).Msg("could not write torrent url template output") + return err + } + + release.TorrentURL = urlBytes.String() + + // handle cookies + if v, ok := extraVars["cookie"]; ok { + release.RawCookie = v + } + + return nil +} + type TorrentBasic struct { Id string `json:"Id"` TorrentId string `json:"TorrentId,omitempty"` diff --git a/internal/domain/release.go b/internal/domain/release.go index 76da3f2..86f7d78 100644 --- a/internal/domain/release.go +++ b/internal/domain/release.go @@ -1,7 +1,6 @@ package domain import ( - "bytes" "context" "crypto/tls" "fmt" @@ -9,20 +8,17 @@ import ( "io" "net/http" "net/http/cookiejar" - "net/url" "os" "regexp" "strconv" "strings" - "text/template" "time" "golang.org/x/net/publicsuffix" - "github.com/autobrr/autobrr/pkg/wildcard" - "github.com/anacrolix/torrent/metainfo" "github.com/dustin/go-humanize" + "github.com/moistari/rls" "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -53,44 +49,37 @@ type Release struct { TorrentHash string `json:"-"` TorrentName string `json:"torrent_name"` // full release name Size uint64 `json:"size"` - Raw string `json:"raw"` // Raw release - Clean string `json:"clean"` // cleaned release name Title string `json:"title"` // Parsed title Category string `json:"category"` Season int `json:"season"` Episode int `json:"episode"` Year int `json:"year"` Resolution string `json:"resolution"` - Source string `json:"source"` // CD, DVD, Vinyl, DAT, Cassette, WEB, Other - Codec string `json:"codec"` + Source string `json:"source"` + Codec []string `json:"codec"` Container string `json:"container"` - HDR string `json:"hdr"` - Audio string `json:"audio"` + HDR []string `json:"hdr"` + Audio []string `json:"-"` + AudioChannels string `json:"-"` Group string `json:"group"` - Region string `json:"region"` - Language string `json:"language"` - Edition string `json:"edition"` // Extended, directors cut - Unrated bool `json:"unrated"` - Hybrid bool `json:"hybrid"` + Region string `json:"-"` + Language string `json:"-"` Proper bool `json:"proper"` Repack bool `json:"repack"` Website string `json:"website"` - ThreeD bool `json:"-"` - Artists []string `json:"artists"` - Type string `json:"type"` // Album,Single,EP - Format string `json:"format"` // music only - Quality string `json:"quality"` // quality - LogScore int `json:"log_score"` - HasLog bool `json:"has_log"` - HasCue bool `json:"has_cue"` - IsScene bool `json:"is_scene"` + Artists string `json:"-"` + Type string `json:"type"` // Album,Single,EP + LogScore int `json:"-"` + IsScene bool `json:"-"` Origin string `json:"origin"` // P2P, Internal - Tags []string `json:"tags"` + Tags []string `json:"-"` ReleaseTags string `json:"-"` - Freeleech bool `json:"freeleech"` - FreeleechPercent int `json:"freeleech_percent"` + Freeleech bool `json:"-"` + FreeleechPercent int `json:"-"` + Bonus []string `json:"-"` Uploader string `json:"uploader"` PreTime string `json:"pre_time"` + Other []string `json:"-"` RawCookie string `json:"-"` AdditionalSizeCheckRequired bool `json:"-"` FilterID int `json:"-"` @@ -108,50 +97,147 @@ type ReleaseActionStatus struct { ReleaseID int64 `json:"-"` } -func NewRelease(indexer string, line string) (*Release, error) { +type DownloadTorrentFileResponse struct { + MetaInfo *metainfo.MetaInfo + TmpFileName string +} + +type ReleaseStats struct { + TotalCount int64 `json:"total_count"` + FilteredCount int64 `json:"filtered_count"` + FilterRejectedCount int64 `json:"filter_rejected_count"` + PushApprovedCount int64 `json:"push_approved_count"` + PushRejectedCount int64 `json:"push_rejected_count"` +} + +type ReleasePushStatus string + +const ( + ReleasePushStatusApproved ReleasePushStatus = "PUSH_APPROVED" + ReleasePushStatusRejected ReleasePushStatus = "PUSH_REJECTED" + ReleasePushStatusErr ReleasePushStatus = "PUSH_ERROR" + ReleasePushStatusPending ReleasePushStatus = "PENDING" // Initial status +) + +func (r ReleasePushStatus) String() string { + switch r { + case ReleasePushStatusApproved: + return "Approved" + case ReleasePushStatusRejected: + return "Rejected" + case ReleasePushStatusErr: + return "Error" + default: + return "Unknown" + } +} + +type ReleaseFilterStatus string + +const ( + ReleaseStatusFilterApproved ReleaseFilterStatus = "FILTER_APPROVED" + ReleaseStatusFilterRejected ReleaseFilterStatus = "FILTER_REJECTED" + ReleaseStatusFilterPending ReleaseFilterStatus = "PENDING" +) + +type ReleaseProtocol string + +const ( + ReleaseProtocolTorrent ReleaseProtocol = "torrent" +) + +type ReleaseImplementation string + +const ( + ReleaseImplementationIRC ReleaseImplementation = "IRC" + ReleaseImplementationTorznab ReleaseImplementation = "TORZNAB" +) + +type ReleaseQueryParams struct { + Limit uint64 + Offset uint64 + Cursor uint64 + Sort map[string]string + Filters struct { + Indexers []string + PushStatus string + } + Search string +} + +func NewRelease(indexer string) (*Release, error) { r := &Release{ Indexer: indexer, - Raw: line, FilterStatus: ReleaseStatusFilterPending, Rejections: []string{}, Protocol: ReleaseProtocolTorrent, Implementation: ReleaseImplementationIRC, Timestamp: time.Now(), - Artists: []string{}, Tags: []string{}, } return r, nil } -func (r *Release) Parse() error { - var err error +func (r *Release) ParseString(title string) error { + rel := rls.ParseString(title) - err = r.extractYear() - err = r.extractSeason() - err = r.extractEpisode() - err = r.extractResolution() - err = r.extractSource() - err = r.extractCodec() - err = r.extractContainer() - err = r.extractHDR() - err = r.extractAudio() - err = r.extractGroup() - err = r.extractRegion() - err = r.extractLanguage() - err = r.extractEdition() - err = r.extractUnrated() - err = r.extractHybrid() - err = r.extractProper() - err = r.extractRepack() - err = r.extractWebsite() - err = r.extractReleaseTags() + r.TorrentName = title + r.Title = rel.Title + r.Source = rel.Source + r.Resolution = rel.Resolution + r.Season = rel.Series + r.Episode = rel.Episode + r.Group = rel.Group + r.Region = rel.Region + r.Audio = rel.Audio + r.AudioChannels = rel.Channels + r.Codec = rel.Codec + r.Container = rel.Container + r.HDR = rel.HDR + r.Other = rel.Other + r.Artists = rel.Artist - r.Clean = cleanReleaseName(r.TorrentName) + if r.Year == 0 { + r.Year = rel.Year + } - if err != nil { - log.Trace().Msgf("could not parse release: %v", r.TorrentName) - return err + r.ParseReleaseTagsString(r.ReleaseTags) + + return nil +} + +func (r *Release) ParseReleaseTagsString(tags string) error { + // trim delimiters and closest space + re := regexp.MustCompile(`\| |\/ |, `) + cleanTags := re.ReplaceAllString(tags, "") + + t := ParseReleaseTagString(cleanTags) + + if len(t.Audio) > 0 { + r.Audio = append(r.Audio, t.Audio...) + } + if len(t.Bonus) > 0 { + r.Bonus = append(r.Bonus, t.Bonus...) + } + if len(t.Codec) > 0 { + r.Codec = append(r.Codec, t.Codec) + } + if len(t.Other) > 0 { + r.Other = append(r.Other, t.Other...) + } + + if r.Container == "" && t.Container != "" { + r.Container = t.Container + } + if r.Resolution == "" && t.Resolution != "" { + r.Resolution = t.Resolution + } + if r.Source == "" && t.Source != "" { + r.Source = t.Source + } + if r.AudioChannels == "" && t.Channels != "" { + r.AudioChannels = t.Channels } return nil @@ -166,466 +252,6 @@ func (r *Release) ParseSizeBytesString(size string) { r.Size = s } -func (r *Release) extractYear() error { - if r.Year > 0 { - return nil - } - - y, err := findLastInt(r.TorrentName, `\b(((?:19[0-9]|20[0-9])[0-9]))\b`) - if err != nil { - return err - } - r.Year = y - - return nil -} - -func (r *Release) extractSeason() error { - s, err := findLastInt(r.TorrentName, `(?i)(?:S|Season\s*)(\d{1,3})`) - if err != nil { - return err - } - r.Season = s - - return nil -} - -func (r *Release) extractEpisode() error { - e, err := findLastInt(r.TorrentName, `(?i)[ex]([0-9]{2})(?:[^0-9]|$)`) - if err != nil { - return err - } - r.Episode = e - - return nil -} - -func (r *Release) extractResolution() error { - v, err := findLast(r.TorrentName, `\b(?i)[0-9]{3,4}(?:p|i)\b`) - if err != nil { - return err - } - r.Resolution = v - - return nil -} - -func (r *Release) extractResolutionFromTags(tag string) error { - if r.Resolution != "" { - return nil - } - v, err := findLast(tag, `\b(?i)(([0-9]{3,4}p|i))\b`) - if err != nil { - return err - } - r.Resolution = v - - return nil -} - -func (r *Release) extractSource() error { - v, err := findLast(r.TorrentName, `(?i)\b(((?:PPV\.)?[HP]DTV|(?:HD)?CAM|B[DR]Rip|(?:HD-?)?TS|(?:PPV )?WEB-?DL(?: DVDRip)?|HDRip|DVDRip|DVDRIP|CamRip|WEB|W[EB]BRip|Blu-?Ray|DvDScr|telesync|CD|DVD|Vinyl|DAT|Cassette))\b`) - if err != nil { - return err - } - r.Source = v - - return nil -} - -func (r *Release) extractSourceFromTags(tag string) error { - if r.Source != "" { - return nil - } - v, err := findLast(tag, `(?i)\b(((?:PPV\.)?[HP]DTV|(?:HD)?CAM|B[DR]Rip|(?:HD-?)?TS|(?:PPV )?WEB-?DL(?: DVDRip)?|HDRip|DVDRip|DVDRIP|CamRip|WEB|W[EB]BRip|Blu-?Ray|DvDScr|telesync|CD|DVD|Vinyl|DAT|Cassette))\b`) - if err != nil { - return err - } - r.Source = v - - return nil -} - -func (r *Release) extractCodec() error { - v, err := findLast(r.TorrentName, `(?i)\b(HEVC|[hx]\.?26[45] 10-bit|[hx]\.?26[45]|xvid|divx|AVC|MPEG-?2|AV1|VC-?1|VP9|WebP)\b`) - if err != nil { - return err - } - r.Codec = v - - return nil -} - -func (r *Release) extractCodecFromTags(tag string) error { - if r.Codec != "" { - return nil - } - - v, err := findLast(tag, `(?i)\b(HEVC|[hx]\.?26[45] 10-bit|[hx]\.?26[45]|xvid|divx|AVC|MPEG-?2|AV1|VC-?1|VP9|WebP)\b`) - if err != nil { - return err - } - r.Codec = v - - return nil -} - -func (r *Release) extractContainer() error { - v, err := findLast(r.TorrentName, `(?i)\b(AVI|MPG|MKV|MP4|VOB|m2ts|ISO|IMG)\b`) - if err != nil { - return err - } - r.Container = v - - return nil -} - -func (r *Release) extractContainerFromTags(tag string) error { - if r.Container != "" { - return nil - } - - v, err := findLast(tag, `(?i)\b(AVI|MPG|MKV|MP4|VOB|m2ts|ISO|IMG)\b`) - if err != nil { - return err - } - r.Container = v - - return nil -} - -func (r *Release) extractHDR() error { - v, err := findLast(r.TorrentName, `(?i)[\. ](HDR10\+|HDR10|DoVi[\. ]HDR|DV[\. ]HDR10\+|DV[\. ]HDR10|DV[\. ]HDR|HDR|DV|DoVi|Dolby[\. ]Vision[\. ]\+[\. ]HDR10|Dolby[\. ]Vision)[\. ]`) - if err != nil { - return err - } - r.HDR = v - - return nil -} - -func (r *Release) extractAudio() error { - v, err := findLast(r.TorrentName, `(?i)(FLAC[\. ][1-7][\. ][0-2]|FLAC|Opus|DD-EX|DDP[\. ]?[124567][\. ][012] Atmos|DDP[\. ]?[124567][\. ][012]|DDP|DD[1-7][\. ][0-2]|Dual[\- ]Audio|LiNE|PCM|Dolby TrueHD [0-9][\. ][0-4]|TrueHD [0-9][\. ][0-4] Atmos|TrueHD [0-9][\. ][0-4]|DTS X|DTS-HD MA [0-9][\. ][0-4]|DTS-HD MA|DTS-ES|DTS [1-7][\. ][0-2]|DTS|DD|DD[12][\. ]0|Dolby Atmos|TrueHD ATMOS|TrueHD|Atmos|Dolby Digital Plus|Dolby Digital Audio|Dolby Digital|AAC[.-]LC|AAC (?:\.?[1-7]\.[0-2])?|AAC|eac3|AC3(?:\.5\.1)?)`) - if err != nil { - return err - } - r.Audio = v - - return nil -} - -func (r *Release) extractAudioFromTags(tag string) error { - if r.Audio != "" { - return nil - } - - v, err := findLast(tag, `(?i)(FLAC[\. ][1-7][\. ][0-2]|FLAC|Opus|DD-EX|DDP[\. ]?[124567][\. ][012] Atmos|DDP[\. ]?[124567][\. ][012]|DDP|DD[1-7][\. ][0-2]|Dual[\- ]Audio|LiNE|PCM|Dolby TrueHD [0-9][\. ][0-4]|TrueHD [0-9][\. ][0-4] Atmos|TrueHD [0-9][\. ][0-4]|DTS X|DTS-HD MA [0-9][\. ][0-4]|DTS-HD MA|DTS-ES|DTS [1-7][\. ][0-2]|DTS|DD|DD[12][\. ]0|Dolby Atmos|TrueHD ATMOS|TrueHD|Atmos|Dolby Digital Plus|Dolby Digital Audio|Dolby Digital|AAC[.-]LC|AAC (?:\.?[1-7]\.[0-2])?|AAC|eac3|AC3(?:\.5\.1)?)`) - if err != nil { - return err - } - r.Audio = v - - return nil -} - -func (r *Release) extractFormatsFromTags(tag string) error { - if r.Format != "" { - return nil - } - - v, err := findLast(tag, `(?i)(?:MP3|FLAC|Ogg Vorbis|AAC|AC3|DTS)`) - if err != nil { - return err - } - r.Format = v - - return nil -} - -//func (r *Release) extractCueFromTags(tag string) error { -// v, err := findLast(tag, `Cue`) -// if err != nil { -// return err -// } -// r.HasCue = v -// -// return nil -//} - -func (r *Release) extractGroup() error { - // try first for wierd anime group names [group] show name, or in brackets at the end - - //g, err := findLast(r.Clean, `\[(.*?)\]`) - group, err := findLast(r.TorrentName, `\-([a-zA-Z0-9_\.]+)$`) - if err != nil { - return err - } - - r.Group = group - - return nil -} - -func (r *Release) extractAnimeGroupFromTags(tag string) error { - if r.Group != "" { - return nil - } - v, err := findLast(tag, `(?:RAW|Softsubs|Hardsubs)\s\((.+)\)`) - if err != nil { - return err - } - r.Group = v - - return nil -} - -func (r *Release) extractRegion() error { - v, err := findLast(r.TorrentName, `(?i)\b(R([0-9]))\b`) - if err != nil { - return err - } - r.Region = v - - return nil -} - -func (r *Release) extractLanguage() error { - v, err := findLast(r.TorrentName, `(?i)\b((DK|DKSUBS|DANiSH|DUTCH|NL|NLSUBBED|ENG|FI|FLEMiSH|FiNNiSH|DE|FRENCH|GERMAN|HE|HEBREW|HebSub|HiNDi|iCELANDiC|KOR|MULTi|MULTiSUBS|NORWEGiAN|NO|NORDiC|PL|PO|POLiSH|PLDUB|RO|ROMANiAN|RUS|SPANiSH|SE|SWEDiSH|SWESUB||))\b`) - if err != nil { - return err - } - r.Language = v - - return nil -} - -func (r *Release) extractEdition() error { - v, err := findLast(r.TorrentName, `(?i)\b((?:DIRECTOR'?S|EXTENDED|INTERNATIONAL|THEATRICAL|ORIGINAL|FINAL|BOOTLEG)(?:.CUT)?)\b`) - if err != nil { - return err - } - r.Edition = v - - return nil -} - -func (r *Release) extractUnrated() error { - v, err := findLastBool(r.TorrentName, `(?i)\b((UNRATED))\b`) - if err != nil { - return err - } - r.Unrated = v - - return nil -} - -func (r *Release) extractHybrid() error { - v, err := findLastBool(r.TorrentName, `(?i)\b((HYBRID))\b`) - if err != nil { - return err - } - r.Hybrid = v - - return nil -} - -func (r *Release) extractProper() error { - v, err := findLastBool(r.TorrentName, `(?i)\b((PROPER))\b`) - if err != nil { - return err - } - r.Proper = v - - return nil -} - -func (r *Release) extractRepack() error { - v, err := findLastBool(r.TorrentName, `(?i)\b((REPACK))\b`) - if err != nil { - return err - } - r.Repack = v - - return nil -} - -func (r *Release) extractWebsite() error { - // Start with the basic most common ones - v, err := findLast(r.TorrentName, `(?i)\b((AMBC|AS|AMZN|AMC|ANPL|ATVP|iP|CORE|BCORE|CMOR|CN|CBC|CBS|CMAX|CNBC|CC|CRIT|CR|CSPN|CW|DAZN|DCU|DISC|DSCP|DSNY|DSNP|DPLY|ESPN|FOX|FUNI|PLAY|HBO|HMAX|HIST|HS|HOTSTAR|HULU|iT|MNBC|MTV|NATG|NBC|NF|NICK|NRK|PMNT|PMNP|PCOK|PBS|PBSK|PSN|QIBI|SBS|SHO|STAN|STZ|SVT|SYFY|TLC|TRVL|TUBI|TV3|TV4|TVL|VH1|VICE|VMEO|UFC|USAN|VIAP|VIAPLAY|VL|WWEN|XBOX|YHOO|YT|RED))\b`) - if err != nil { - return err - } - r.Website = v - - return nil -} - -func (r *Release) extractFreeleechFromTags(tag string) error { - if r.Freeleech == true { - return nil - } - - // Start with the basic most common ones - v, err := findLast(tag, `(?i)(Freeleech!|Freeleech)`) - if err != nil { - return err - } - if v != "" { - r.Freeleech = true - return nil - } - - r.Freeleech = false - - return nil -} - -func (r *Release) extractLogScoreFromTags(tag string) error { - if r.LogScore > 0 { - return nil - } - - // Start with the basic most common ones - - rxp, err := regexp.Compile(`([\d\.]+)%`) - if err != nil { - return err - //return errors.Wrapf(err, "invalid regex: %s", value) - } - - matches := rxp.FindStringSubmatch(tag) - if matches != nil { - // first value is the match, second value is the text - if len(matches) >= 1 { - last := matches[len(matches)-1] - score, err := strconv.ParseInt(last, 10, 32) - if err != nil { - return err - } - - r.LogScore = int(score) - return nil - } - } - - return nil -} - -func (r *Release) extractQualityFromTags(tag string) error { - if r.Quality != "" { - return nil - } - - // Start with the basic most common ones - - rxp, err := regexp.Compile(`(?i)(Lossless|24bit Lossless|V0 \(VBR\)|V1 \(VBR\)|V2 \(VBR\)|APS \(VBR\)|APX \(VBR\)|320|256|192)`) - if err != nil { - return err - //return errors.Wrapf(err, "invalid regex: %s", value) - } - - matches := rxp.FindStringSubmatch(tag) - if matches != nil { - // first value is the match, second value is the text - if len(matches) >= 1 { - last := matches[len(matches)-1] - - r.Quality = last - return nil - } - } - - return nil -} - -func (r *Release) extractReleaseTags() error { - if r.ReleaseTags == "" { - return nil - } - - tags := SplitAny(r.ReleaseTags, ",|/") - - for _, t := range tags { - t = strings.Trim(t, " ") - - var err error - err = r.extractAudioFromTags(t) - err = r.extractFormatsFromTags(t) - err = r.extractResolutionFromTags(t) - err = r.extractCodecFromTags(t) - err = r.extractContainerFromTags(t) - err = r.extractSourceFromTags(t) - err = r.extractFreeleechFromTags(t) - err = r.extractLogScoreFromTags(t) - err = r.extractQualityFromTags(t) - err = r.extractAnimeGroupFromTags(t) - - if err != nil { - continue - } - - switch t { - case "Cue": - r.HasCue = true - case "Log": - r.HasLog = true - // check percent - } - } - - return nil -} - -func (r *Release) ParseTorrentUrl(match string, vars map[string]string, extraVars map[string]string, encode []string) error { - tmpVars := map[string]string{} - - // copy vars to new tmp map - for k, v := range vars { - tmpVars[k] = v - } - - // merge extra vars with vars - if extraVars != nil { - for k, v := range extraVars { - tmpVars[k] = v - } - } - - // handle url encode of values - if encode != nil { - for _, e := range encode { - if v, ok := tmpVars[e]; ok { - // url encode value - t := url.QueryEscape(v) - tmpVars[e] = t - } - } - } - - // setup text template to inject variables into - tmpl, err := template.New("torrenturl").Parse(match) - if err != nil { - log.Error().Err(err).Msg("could not create torrent url template") - return err - } - - var urlBytes bytes.Buffer - err = tmpl.Execute(&urlBytes, &tmpVars) - if err != nil { - log.Error().Err(err).Msg("could not write torrent url template output") - return err - } - - r.TorrentURL = urlBytes.String() - - // handle cookies - if v, ok := extraVars["cookie"]; ok { - r.RawCookie = v - } - - return nil -} - func (r *Release) DownloadTorrentFile() error { if r.TorrentURL == "" { return errors.New("download_file: url can't be empty") @@ -731,211 +357,6 @@ func (r *Release) RejectionsString() string { return "" } -func (r *Release) CheckFilter(filter Filter) ([]string, bool) { - // reset rejections first to clean previous checks - r.resetRejections() - - if !filter.Enabled { - return nil, false - } - - // FIXME what if someone explicitly doesnt want scene, or toggles in filter. Make enum? 0,1,2? Yes, No, Dont care - if filter.Scene && r.IsScene != filter.Scene { - r.addRejection("wanted: scene") - } - - if filter.Freeleech && r.Freeleech != filter.Freeleech { - r.addRejection("wanted: freeleech") - } - - if filter.FreeleechPercent != "" && !checkFreeleechPercent(r.FreeleechPercent, filter.FreeleechPercent) { - r.addRejectionF("freeleech percent not matching. wanted: %v got: %v", filter.FreeleechPercent, r.FreeleechPercent) - } - - // check against TorrentName and Clean which is a cleaned name without (. _ -) - if filter.Shows != "" && !checkMultipleFilterStrings(filter.Shows, r.TorrentName, r.Clean) { - r.addRejection("shows not matching") - } - - if filter.Seasons != "" && !checkFilterIntStrings(r.Season, filter.Seasons) { - r.addRejectionF("season not matching. wanted: %v got: %d", filter.Seasons, r.Season) - } - - if filter.Episodes != "" && !checkFilterIntStrings(r.Episode, filter.Episodes) { - r.addRejectionF("episodes not matching. wanted: %v got: %d", filter.Seasons, r.Season) - } - - // matchRelease - // TODO allow to match against regex - if filter.MatchReleases != "" && !checkMultipleFilterStrings(filter.MatchReleases, r.TorrentName, r.Clean) { - r.addRejection("match release not matching") - } - - if filter.ExceptReleases != "" && checkMultipleFilterStrings(filter.ExceptReleases, r.TorrentName, r.Clean) { - r.addRejection("except_releases: unwanted release") - } - - if filter.MatchReleaseGroups != "" && !checkMultipleFilterGroups(filter.MatchReleaseGroups, r.Group, r.Clean) { - r.addRejectionF("release groups not matching. wanted: %v got: %v", filter.MatchReleaseGroups, r.Group) - } - - if filter.ExceptReleaseGroups != "" && checkMultipleFilterGroups(filter.ExceptReleaseGroups, r.Group, r.Clean) { - r.addRejectionF("unwanted release group. unwanted: %v got: %v", filter.ExceptReleaseGroups, r.Group) - } - - if filter.MatchUploaders != "" && !checkFilterStrings(r.Uploader, filter.MatchUploaders) { - r.addRejectionF("uploaders not matching. wanted: %v got: %v", filter.MatchUploaders, r.Uploader) - } - - if filter.ExceptUploaders != "" && checkFilterStrings(r.Uploader, filter.ExceptUploaders) { - r.addRejectionF("unwanted uploaders. unwanted: %v got: %v", filter.MatchUploaders, r.Uploader) - } - - if len(filter.Resolutions) > 0 && !checkFilterSlice(r.Resolution, filter.Resolutions) { - r.addRejectionF("resolution not matching. wanted: %v got: %v", filter.Resolutions, r.Resolution) - } - - if len(filter.Codecs) > 0 && !checkFilterSlice(r.Codec, filter.Codecs) { - r.addRejectionF("codec not matching. wanted: %v got: %v", filter.Codecs, r.Codec) - } - - if len(filter.Sources) > 0 && !checkFilterSource(r.Source, filter.Sources) { - r.addRejectionF("source not matching. wanted: %v got: %v", filter.Sources, r.Source) - } - - if len(filter.Containers) > 0 && !checkFilterSlice(r.Container, filter.Containers) { - r.addRejectionF("container not matching. wanted: %v got: %v", filter.Containers, r.Container) - } - - if len(filter.MatchHDR) > 0 && !checkMultipleFilterHDR(filter.MatchHDR, r.HDR, r.TorrentName) { - r.addRejectionF("hdr not matching. wanted: %v got: %v", filter.MatchHDR, r.HDR) - } - - if len(filter.ExceptHDR) > 0 && checkMultipleFilterHDR(filter.ExceptHDR, r.HDR, r.TorrentName) { - r.addRejectionF("hdr unwanted. unwanted: %v got: %v", filter.ExceptHDR, r.HDR) - } - - if filter.Years != "" && !checkFilterIntStrings(r.Year, filter.Years) { - r.addRejectionF("year not matching. wanted: %v got: %d", filter.Years, r.Year) - } - - if filter.MatchCategories != "" && !checkFilterStrings(r.Category, filter.MatchCategories) { - r.addRejectionF("category not matching. wanted: %v got: %v", filter.MatchCategories, r.Category) - } - - if filter.ExceptCategories != "" && checkFilterStrings(r.Category, filter.ExceptCategories) { - r.addRejectionF("category unwanted. unwanted: %v got: %v", filter.ExceptCategories, r.Category) - } - - if len(filter.MatchReleaseTypes) > 0 && !checkFilterSlice(r.Category, filter.MatchReleaseTypes) { - r.addRejectionF("release type not matching. wanted: %v got: %v", filter.MatchReleaseTypes, r.Category) - } - - if (filter.MinSize != "" || filter.MaxSize != "") && !r.CheckSizeFilter(filter.MinSize, filter.MaxSize) { - r.addRejectionF("size not matching. wanted min: %v max: %v got: %v", filter.MinSize, filter.MaxSize, r.Size) - } - - if filter.Tags != "" && !checkFilterTags(r.Tags, filter.Tags) { - r.addRejectionF("tags not matching. wanted: %v got: %v", filter.Tags, r.Tags) - } - - if filter.ExceptTags != "" && checkFilterTags(r.Tags, filter.ExceptTags) { - r.addRejectionF("tags unwanted. wanted: %v got: %v", filter.ExceptTags, r.Tags) - } - - if len(filter.Artists) > 0 && !checkFilterStrings(r.TorrentName, filter.Artists) { - r.addRejection("artists not matching") - } - - if len(filter.Albums) > 0 && !checkFilterStrings(r.TorrentName, filter.Albums) { - r.addRejection("albums not matching") - } - - // Perfect flac requires Cue, Log, Log Score 100, FLAC and 24bit Lossless - if filter.PerfectFlac { - if !r.HasLog || !r.HasCue || r.LogScore != 100 || r.Format != "FLAC" && !checkFilterSlice(r.Quality, []string{"Lossless", "24bit Lossless"}) { - r.addRejectionF("wanted: perfect flac. got: cue %v log %v log score %v format %v quality %v", r.HasCue, r.HasLog, r.LogScore, r.Format, r.Quality) - } - } - - if len(filter.Formats) > 0 && !checkFilterSlice(r.Format, filter.Formats) { - r.addRejectionF("formats not matching. wanted: %v got: %v", filter.Formats, r.Format) - } - - if len(filter.Quality) > 0 && !checkFilterSlice(r.Quality, filter.Quality) { - r.addRejectionF("quality not matching. wanted: %v got: %v", filter.Quality, r.Quality) - } - - if len(filter.Media) > 0 && !checkFilterSource(r.Source, filter.Media) { - r.addRejectionF("media not matching. wanted: %v got: %v", filter.Media, r.Source) - } - - if filter.Log && r.HasLog != filter.Log { - r.addRejection("wanted: log") - } - - if filter.Log && filter.LogScore != 0 && r.LogScore != filter.LogScore { - r.addRejectionF("wanted: log score %v got: %v", filter.LogScore, r.LogScore) - } - - if filter.Cue && r.HasCue != filter.Cue { - r.addRejection("wanted: cue") - } - - if len(r.Rejections) > 0 { - return r.Rejections, false - } - - return nil, true -} - -// CheckSizeFilter additional size check -// for indexers that doesn't announce size, like some gazelle based -// set flag r.AdditionalSizeCheckRequired if there's a size in the filter, otherwise go a head -// implement API for ptp,btn,ggn to check for size if needed -// for others pull down torrent and do check -func (r *Release) CheckSizeFilter(minSize string, maxSize string) bool { - - if r.Size == 0 { - r.AdditionalSizeCheckRequired = true - - return true - } else { - r.AdditionalSizeCheckRequired = false - } - - // if r.Size parse filter to bytes and compare - // handle both min and max - if minSize != "" { - // string to bytes - minSizeBytes, err := humanize.ParseBytes(minSize) - if err != nil { - // log could not parse into bytes - } - - if r.Size <= minSizeBytes { - r.addRejection("size: smaller than min size") - return false - } - - } - - if maxSize != "" { - // string to bytes - maxSizeBytes, err := humanize.ParseBytes(maxSize) - if err != nil { - // log could not parse into bytes - } - - if r.Size >= maxSizeBytes { - r.addRejection("size: larger than max size") - return false - } - } - - return true -} - // MapVars better name func (r *Release) MapVars(def IndexerDefinition, varMap map[string]string) error { @@ -954,7 +375,11 @@ func (r *Release) MapVars(def IndexerDefinition, varMap map[string]string) error } if freeleech, err := getStringMapValue(varMap, "freeleech"); err == nil { - r.Freeleech = strings.EqualFold(freeleech, "freeleech") || strings.EqualFold(freeleech, "yes") || strings.EqualFold(freeleech, "1") + fl := strings.EqualFold(freeleech, "freeleech") || strings.EqualFold(freeleech, "yes") || strings.EqualFold(freeleech, "1") + if fl { + r.Freeleech = true + r.Bonus = append(r.Bonus, "Freeleech") + } } if freeleechPercent, err := getStringMapValue(varMap, "freeleechPercent"); err == nil { @@ -968,6 +393,22 @@ func (r *Release) MapVars(def IndexerDefinition, varMap map[string]string) error } r.FreeleechPercent = freeleechPercentInt + + switch freeleechPercentInt { + case 25: + r.Bonus = append(r.Bonus, "Freeleech25") + break + case 50: + r.Bonus = append(r.Bonus, "Freeleech50") + break + case 75: + r.Bonus = append(r.Bonus, "Freeleech75") + break + case 100: + r.Bonus = append(r.Bonus, "Freeleech100") + break + } + } if uploader, err := getStringMapValue(varMap, "uploader"); err == nil { @@ -976,7 +417,7 @@ func (r *Release) MapVars(def IndexerDefinition, varMap map[string]string) error if torrentSize, err := getStringMapValue(varMap, "torrentSize"); err == nil { // handling for indexer who doesn't explicitly set which size unit is used like (AR) - if def.Parse.ForceSizeUnit != "" { + if def.Parse != nil && def.Parse.ForceSizeUnit != "" { torrentSize = fmt.Sprintf("%v %v", torrentSize, def.Parse.ForceSizeUnit) } @@ -991,6 +432,15 @@ func (r *Release) MapVars(def IndexerDefinition, varMap map[string]string) error r.IsScene = strings.EqualFold(scene, "true") || strings.EqualFold(scene, "yes") } + // set origin. P2P, SCENE, O-SCENE and Internal + if origin, err := getStringMapValue(varMap, "origin"); err == nil { + r.Origin = origin + + if r.IsScene { + r.Origin = "SCENE" + } + } + if yearVal, err := getStringMapValue(varMap, "year"); err == nil { year, err := strconv.Atoi(yearVal) if err != nil { @@ -1016,301 +466,9 @@ func (r *Release) MapVars(def IndexerDefinition, varMap map[string]string) error return nil } -func checkFilterSlice(name string, filterList []string) bool { - name = strings.ToLower(name) - - for _, filter := range filterList { - filter = strings.ToLower(filter) - filter = strings.Trim(filter, " ") - // check if line contains * or ?, if so try wildcard match, otherwise try substring match - a := strings.ContainsAny(filter, "?|*") - if a { - match := wildcard.Match(filter, name) - if match { - return true - } - } else { - b := strings.Contains(name, filter) - if b { - return true - } - } - } - - return false -} - -func checkFilterStrings(name string, filterList string) bool { - filterSplit := strings.Split(filterList, ",") - name = strings.ToLower(name) - - for _, s := range filterSplit { - s = strings.ToLower(s) - s = strings.Trim(s, " ") - // check if line contains * or ?, if so try wildcard match, otherwise try substring match - a := strings.ContainsAny(s, "?|*") - if a { - match := wildcard.Match(s, name) - if match { - return true - } - } else { - b := strings.Contains(name, s) - if b { - return true - } - } - - } - - return false -} - -// checkMultipleFilterStrings check against multiple vars of unknown length -func checkMultipleFilterStrings(filterList string, vars ...string) bool { - filterSplit := strings.Split(filterList, ",") - - for _, name := range vars { - name = strings.ToLower(name) - - for _, s := range filterSplit { - s = strings.ToLower(s) - s = strings.Trim(s, " ") - // check if line contains * or ?, if so try wildcard match, otherwise try substring match - a := strings.ContainsAny(s, "?|*") - if a { - match := wildcard.Match(s, name) - if match { - return true - } - } else { - b := strings.Contains(name, s) - if b { - return true - } - } - } - } - - return false -} - -// checkFilterIntStrings "1,2,3-20" -func checkFilterIntStrings(value int, filterList string) bool { - filters := strings.Split(filterList, ",") - - for _, s := range filters { - s = strings.Replace(s, "%", "", -1) - s = strings.Trim(s, " ") - - if strings.Contains(s, "-") { - minMax := strings.Split(s, "-") - - // to int - min, err := strconv.ParseInt(minMax[0], 10, 32) - if err != nil { - return false - } - - max, err := strconv.ParseInt(minMax[1], 10, 32) - if err != nil { - return false - } - - if min > max { - // handle error - return false - } else { - // if announcePercent is greater than min and less than max return true - if value >= int(min) && value <= int(max) { - return true - } - } - } - - filterInt, err := strconv.ParseInt(s, 10, 32) - if err != nil { - return false - } - - if int(filterInt) == value { - return true - } - } - - return false -} - -func checkMultipleFilterGroups(filterList string, vars ...string) bool { - filterSplit := strings.Split(filterList, ",") - - for _, name := range vars { - name = strings.ToLower(name) - - for _, s := range filterSplit { - s = strings.ToLower(strings.Trim(s, " ")) - // check if line contains * or ?, if so try wildcard match, otherwise try substring match - a := strings.ContainsAny(s, "?|*") - if a { - match := wildcard.Match(s, name) - if match { - return true - } - } else { - split := SplitAny(name, " .-") - for _, c := range split { - if c == s { - return true - } - } - continue - } - } - } - - return false -} - -func checkMultipleFilterHDR(filterList []string, vars ...string) bool { - for _, name := range vars { - name = strings.ToLower(name) - - for _, s := range filterList { - s = strings.ToLower(strings.Trim(s, " ")) - // check if line contains * or ?, if so try wildcard match, otherwise try substring match - a := strings.ContainsAny(s, "?|*") - if a { - match := wildcard.Match(s, name) - if match { - return true - } - } else { - split := SplitAny(name, " .-") - for _, c := range split { - if c == s { - return true - } - } - continue - } - } - } - - return false -} - -func checkFilterSource(name string, filterList []string) bool { - // remove dash (-) in blu-ray web-dl and make lowercase - name = strings.ToLower(strings.ReplaceAll(name, "-", "")) - - for _, filter := range filterList { - // remove dash (-) in blu-ray web-dl, trim spaces and make lowercase - filter = strings.ToLower(strings.Trim(strings.ReplaceAll(filter, "-", ""), " ")) - - b := strings.Contains(name, filter) - if b { - return true - } - } - - return false -} - -func checkFilterTags(tags []string, filter string) bool { - filterTags := strings.Split(filter, ",") - - for _, tag := range tags { - tag = strings.ToLower(tag) - - for _, filter := range filterTags { - filter = strings.ToLower(filter) - filter = strings.Trim(filter, " ") - // check if line contains * or ?, if so try wildcard match, otherwise try substring match - a := strings.ContainsAny(filter, "?|*") - if a { - match := wildcard.Match(filter, tag) - if match { - return true - } - } else { - b := strings.Contains(tag, filter) - if b { - return true - } - } - } - } - - return false -} - -func checkFreeleechPercent(announcePercent int, filterPercent string) bool { - filters := strings.Split(filterPercent, ",") - - // remove % and trim spaces - //announcePercent = strings.Replace(announcePercent, "%", "", -1) - //announcePercent = strings.Trim(announcePercent, " ") - - //announcePercentInt, err := strconv.ParseInt(announcePercent, 10, 32) - //if err != nil { - // return false - //} - - for _, s := range filters { - s = strings.Replace(s, "%", "", -1) - s = strings.Trim(s, " ") - - if strings.Contains(s, "-") { - minMax := strings.Split(s, "-") - - // to int - min, err := strconv.ParseInt(minMax[0], 10, 32) - if err != nil { - return false - } - - max, err := strconv.ParseInt(minMax[1], 10, 32) - if err != nil { - return false - } - - if min > max { - // handle error - return false - } else { - // if announcePercent is greater than min and less than max return true - if announcePercent >= int(min) && announcePercent <= int(max) { - return true - } - } - } - - filterPercentInt, err := strconv.ParseInt(s, 10, 32) - if err != nil { - return false - } - - if int(filterPercentInt) == announcePercent { - return true - } - } - - return false -} - func getStringMapValue(stringMap map[string]string, key string) (string, error) { lowerKey := strings.ToLower(key) - // case-sensitive match - //if caseSensitive { - // v, ok := stringMap[key] - // if !ok { - // return "", fmt.Errorf("key was not found in map: %q", key) - // } - // - // return v, nil - //} - // case-insensitive match for k, v := range stringMap { if strings.ToLower(k) == lowerKey { @@ -1321,16 +479,6 @@ func getStringMapValue(stringMap map[string]string, key string) (string, error) return "", fmt.Errorf("key was not found in map: %q", lowerKey) } -func getFirstStringMapValue(stringMap map[string]string, keys []string) (string, error) { - for _, k := range keys { - if val, err := getStringMapValue(stringMap, k); err == nil { - return val, nil - } - } - - return "", fmt.Errorf("key were not found in map: %q", strings.Join(keys, ", ")) -} - func findLast(input string, pattern string) (string, error) { matched := make([]string, 0) //for _, s := range arr { @@ -1364,177 +512,9 @@ func findLast(input string, pattern string) (string, error) { return "", nil } -func findLastBool(input string, pattern string) (bool, error) { - matched := make([]string, 0) - - rxp, err := regexp.Compile(pattern) - if err != nil { - return false, err - } - - matches := rxp.FindStringSubmatch(input) - if matches != nil { - // first value is the match, second value is the text - if len(matches) >= 1 { - last := matches[len(matches)-1] - - // add to temp slice - matched = append(matched, last) - } - } - - //} - - // check if multiple values in temp slice, if so get the last one - if len(matched) >= 1 { - //last := matched[len(matched)-1] - - return true, nil - } - - return false, nil -} - -func findLastInt(input string, pattern string) (int, error) { - matched := make([]string, 0) - //for _, s := range arr { - - rxp, err := regexp.Compile(pattern) - if err != nil { - return 0, err - //return errors.Wrapf(err, "invalid regex: %s", value) - } - - matches := rxp.FindStringSubmatch(input) - if matches != nil { - // first value is the match, second value is the text - if len(matches) >= 1 { - last := matches[len(matches)-1] - - // add to temp slice - matched = append(matched, last) - } - } - - //} - - // check if multiple values in temp slice, if so get the last one - if len(matched) >= 1 { - last := matched[len(matched)-1] - - i, err := strconv.Atoi(last) - if err != nil { - return 0, err - } - - return i, nil - } - - return 0, nil -} - func SplitAny(s string, seps string) []string { splitter := func(r rune) bool { return strings.ContainsRune(seps, r) } return strings.FieldsFunc(s, splitter) } - -//func Splitter(s string, splits string) []string { -// m := make(map[rune]int) -// for _, r := range splits { -// m[r] = 1 -// } -// -// splitter := func(r rune) bool { -// return m[r] == 1 -// } -// -// return strings.FieldsFunc(s, splitter) -//} -// -//func canonicalizeString(s string) []string { -// //a := strings.FieldsFunc(s, split) -// a := Splitter(s, " .") -// -// return a -//} - -func cleanReleaseName(input string) string { - // Make a Regex to say we only want letters and numbers - reg, err := regexp.Compile(`[\x00-\x1F\x2D\x2E\x5F\x7F]`) - if err != nil { - return "" - } - processedString := reg.ReplaceAllString(input, " ") - - return processedString -} - -type DownloadTorrentFileResponse struct { - MetaInfo *metainfo.MetaInfo - TmpFileName string -} - -type ReleaseStats struct { - TotalCount int64 `json:"total_count"` - FilteredCount int64 `json:"filtered_count"` - FilterRejectedCount int64 `json:"filter_rejected_count"` - PushApprovedCount int64 `json:"push_approved_count"` - PushRejectedCount int64 `json:"push_rejected_count"` -} - -type ReleasePushStatus string - -const ( - ReleasePushStatusApproved ReleasePushStatus = "PUSH_APPROVED" - ReleasePushStatusRejected ReleasePushStatus = "PUSH_REJECTED" - ReleasePushStatusErr ReleasePushStatus = "PUSH_ERROR" - ReleasePushStatusPending ReleasePushStatus = "PENDING" // Initial status -) - -func (r ReleasePushStatus) String() string { - switch r { - case ReleasePushStatusApproved: - return "Approved" - case ReleasePushStatusRejected: - return "Rejected" - case ReleasePushStatusErr: - return "Error" - default: - return "Unknown" - } -} - -type ReleaseFilterStatus string - -const ( - ReleaseStatusFilterApproved ReleaseFilterStatus = "FILTER_APPROVED" - ReleaseStatusFilterRejected ReleaseFilterStatus = "FILTER_REJECTED" - ReleaseStatusFilterPending ReleaseFilterStatus = "PENDING" -) - -type ReleaseProtocol string - -const ( - ReleaseProtocolTorrent ReleaseProtocol = "torrent" -) - -type ReleaseImplementation string - -const ( - ReleaseImplementationIRC ReleaseImplementation = "IRC" - ReleaseImplementationTorznab ReleaseImplementation = "TORZNAB" -) - -type ReleaseQueryParams struct { - Limit uint64 - Offset uint64 - Cursor uint64 - Sort map[string]string - Filters struct { - Indexers []string - PushStatus string - } - Search string -} diff --git a/internal/domain/release_test.go b/internal/domain/release_test.go index b3121be..d191374 100644 --- a/internal/domain/release_test.go +++ b/internal/domain/release_test.go @@ -19,17 +19,18 @@ func TestRelease_Parse(t *testing.T) { TorrentName: "Servant S01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-FLUX", }, want: Release{ - TorrentName: "Servant S01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-FLUX", - Clean: "Servant S01 2160p ATVP WEB DL DDP 5 1 Atmos DV HEVC FLUX", - Season: 1, - Episode: 0, - Resolution: "2160p", - Source: "WEB-DL", - Codec: "HEVC", - HDR: "DV", - Audio: "DDP 5.1 Atmos", - Group: "FLUX", - Website: "ATVP", + TorrentName: "Servant S01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-FLUX", + Title: "Servant", + Season: 1, + Episode: 0, + Resolution: "2160p", + Source: "WEB-DL", + Codec: []string{"HEVC"}, + Audio: []string{"DDP", "Atmos"}, + AudioChannels: "5.1", + HDR: []string{"DV"}, + Group: "FLUX", + //Website: "ATVP", }, wantErr: false, }, @@ -39,17 +40,17 @@ func TestRelease_Parse(t *testing.T) { TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", }, want: Release{ - TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", - Clean: "Servant S01 2160p ATVP WEB DL DDP 5 1 Atmos DV HEVC FLUX", - Season: 1, - Episode: 0, - Resolution: "2160p", - Source: "WEB-DL", - Codec: "HEVC", - HDR: "DV", - Audio: "DDP.5.1", // need to fix audio parsing - Group: "FLUX", - Website: "ATVP", + TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", + Title: "Servant", + Season: 1, + Episode: 0, + Resolution: "2160p", + Source: "WEB-DL", + Codec: []string{"HEVC"}, + Audio: []string{"DDP", "Atmos"}, + AudioChannels: "5.1", + HDR: []string{"DV"}, + Group: "FLUX", }, wantErr: false, }, @@ -60,19 +61,19 @@ func TestRelease_Parse(t *testing.T) { ReleaseTags: "MKV / 2160p / WEB-DL", }, want: Release{ - TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", - Clean: "Servant S01 2160p ATVP WEB DL DDP 5 1 Atmos DV HEVC FLUX", - ReleaseTags: "MKV / 2160p / WEB-DL", - Container: "MKV", - Season: 1, - Episode: 0, - Resolution: "2160p", - Source: "WEB-DL", - Codec: "HEVC", - HDR: "DV", - Audio: "DDP.5.1", // need to fix audio parsing - Group: "FLUX", - Website: "ATVP", + TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", + ReleaseTags: "MKV / 2160p / WEB-DL", + Title: "Servant", + Season: 1, + Episode: 0, + Resolution: "2160p", + Source: "WEB-DL", + Container: "mkv", + Codec: []string{"HEVC"}, + Audio: []string{"DDP", "Atmos"}, + AudioChannels: "5.1", + HDR: []string{"DV"}, + Group: "FLUX", }, wantErr: false, }, @@ -83,19 +84,19 @@ func TestRelease_Parse(t *testing.T) { ReleaseTags: "MKV | 2160p | WEB-DL", }, want: Release{ - TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", - Clean: "Servant S01 2160p ATVP WEB DL DDP 5 1 Atmos DV HEVC FLUX", - ReleaseTags: "MKV | 2160p | WEB-DL", - Container: "MKV", - Season: 1, - Episode: 0, - Resolution: "2160p", - Source: "WEB-DL", - Codec: "HEVC", - HDR: "DV", - Audio: "DDP.5.1", // need to fix audio parsing - Group: "FLUX", - Website: "ATVP", + TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", + ReleaseTags: "MKV | 2160p | WEB-DL", + Title: "Servant", + Season: 1, + Episode: 0, + Resolution: "2160p", + Source: "WEB-DL", + Container: "mkv", + Codec: []string{"HEVC"}, + Audio: []string{"DDP", "Atmos"}, + AudioChannels: "5.1", + HDR: []string{"DV"}, + Group: "FLUX", }, wantErr: false, }, @@ -106,19 +107,19 @@ func TestRelease_Parse(t *testing.T) { ReleaseTags: "MP4 | 2160p | WEB-DL", }, want: Release{ - TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", - Clean: "Servant S01 2160p ATVP WEB DL DDP 5 1 Atmos DV HEVC FLUX", - ReleaseTags: "MP4 | 2160p | WEB-DL", - Container: "MP4", - Season: 1, - Episode: 0, - Resolution: "2160p", - Source: "WEB-DL", - Codec: "HEVC", - HDR: "DV", - Audio: "DDP.5.1", // need to fix audio parsing - Group: "FLUX", - Website: "ATVP", + TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", + ReleaseTags: "MP4 | 2160p | WEB-DL", + Title: "Servant", + Season: 1, + Episode: 0, + Resolution: "2160p", + Source: "WEB-DL", + Container: "mp4", + Codec: []string{"HEVC"}, + Audio: []string{"DDP", "Atmos"}, + AudioChannels: "5.1", + HDR: []string{"DV"}, + Group: "FLUX", }, wantErr: false, }, @@ -129,20 +130,20 @@ func TestRelease_Parse(t *testing.T) { ReleaseTags: "MP4 | 2160p | WEB-DL | Freeleech!", }, want: Release{ - TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", - Clean: "Servant S01 2160p ATVP WEB DL DDP 5 1 Atmos DV HEVC FLUX", - ReleaseTags: "MP4 | 2160p | WEB-DL | Freeleech!", - Container: "MP4", - Season: 1, - Episode: 0, - Resolution: "2160p", - Source: "WEB-DL", - Codec: "HEVC", - HDR: "DV", - Audio: "DDP.5.1", // need to fix audio parsing - Group: "FLUX", - Website: "ATVP", - Freeleech: true, + TorrentName: "Servant.S01.2160p.ATVP.WEB-DL.DDP.5.1.Atmos.DV.HEVC-FLUX", + ReleaseTags: "MP4 | 2160p | WEB-DL | Freeleech!", + Title: "Servant", + Season: 1, + Episode: 0, + Resolution: "2160p", + Source: "WEB-DL", + Container: "mp4", + Codec: []string{"HEVC"}, + Audio: []string{"DDP", "Atmos"}, + AudioChannels: "5.1", + HDR: []string{"DV"}, + Group: "FLUX", + Bonus: []string{"Freeleech"}, }, wantErr: false, }, @@ -154,16 +155,11 @@ func TestRelease_Parse(t *testing.T) { }, want: Release{ TorrentName: "Artist - Albumname", - Clean: "Artist Albumname", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", - Group: "", - Audio: "FLAC", - Format: "FLAC", - Quality: "Lossless", + Title: "Artist", + Group: "Albumname", + Audio: []string{"Cue", "FLAC", "Lossless", "Log100", "Log"}, Source: "CD", - HasCue: true, - HasLog: true, - LogScore: 100, }, wantErr: false, }, @@ -176,13 +172,11 @@ func TestRelease_Parse(t *testing.T) { }, want: Release{ TorrentName: "Various Artists - Music '21", - Clean: "Various Artists Music '21", Tags: []string{"house, techno, tech.house, electro.house, future.house, bass.house, melodic.house"}, ReleaseTags: "MP3 / 320 / Cassette", - Group: "", - Format: "MP3", + Title: "Various Artists - Music '21", Source: "Cassette", - Quality: "320", + Audio: []string{"320", "MP3"}, }, wantErr: false, }, @@ -194,12 +188,11 @@ func TestRelease_Parse(t *testing.T) { }, want: Release{ TorrentName: "The artist (ザ・フリーダムユニティ) - Long album name", - Clean: "The artist (ザ・フリーダムユニティ) Long album name", ReleaseTags: "MP3 / V0 (VBR) / CD", - Group: "", - Format: "MP3", - Quality: "V0 (VBR)", + Title: "The artist", + Group: "name", Source: "CD", + Audio: []string{"MP3", "VBR"}, }, wantErr: false, }, @@ -211,16 +204,11 @@ func TestRelease_Parse(t *testing.T) { }, want: Release{ TorrentName: "Artist - Albumname", - Clean: "Artist Albumname", ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", - Group: "", - Audio: "FLAC", - Format: "FLAC", - Quality: "Lossless", + Title: "Artist", + Group: "Albumname", + Audio: []string{"Cue", "FLAC", "Lossless", "Log100", "Log"}, Source: "CD", - HasCue: true, - HasLog: true, - LogScore: 100, }, wantErr: false, }, @@ -232,16 +220,11 @@ func TestRelease_Parse(t *testing.T) { }, want: Release{ TorrentName: "Artist - Albumname", - Clean: "Artist Albumname", ReleaseTags: "FLAC / 24bit Lossless / Log / 100% / Cue / CD", - Group: "", - Audio: "FLAC", - Format: "FLAC", - Quality: "24bit Lossless", + Title: "Artist", + Group: "Albumname", + Audio: []string{"24BIT Lossless", "Cue", "FLAC", "Lossless", "Log100", "Log"}, Source: "CD", - HasCue: true, - HasLog: true, - LogScore: 100, }, wantErr: false, }, @@ -251,17 +234,17 @@ func TestRelease_Parse(t *testing.T) { TorrentName: "I Am Movie 2007 Theatrical UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-GROUP1", }, want: Release{ - TorrentName: "I Am Movie 2007 Theatrical UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-GROUP1", - Clean: "I Am Movie 2007 Theatrical UHD BluRay 2160p DTS HD MA 5 1 DV HEVC HYBRID REMUX GROUP1", - Resolution: "2160p", - Source: "BluRay", - Codec: "HEVC", - HDR: "DV", - Audio: "DTS-HD MA 5.1", // need to fix audio parsing - Edition: "Theatrical", - Hybrid: true, - Year: 2007, - Group: "GROUP1", + TorrentName: "I Am Movie 2007 Theatrical UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-GROUP1", + Title: "I Am Movie", + Resolution: "2160p", + Source: "UHD.BluRay", + Codec: []string{"HEVC"}, + HDR: []string{"DV"}, + Audio: []string{"DTS-HD.MA"}, + AudioChannels: "5.1", + Year: 2007, + Group: "GROUP1", + Other: []string{"HYBRiD", "REMUX"}, }, wantErr: false, }, @@ -269,7 +252,7 @@ func TestRelease_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := tt.fields - if err := r.Parse(); (err != nil) != tt.wantErr { + if err := r.ParseString(tt.fields.TorrentName); (err != nil) != tt.wantErr { t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) } @@ -278,1004 +261,6 @@ func TestRelease_Parse(t *testing.T) { } } -func TestRelease_CheckFilter(t *testing.T) { - type args struct { - filter Filter - } - tests := []struct { - name string - fields *Release - args args - want bool - }{ - { - name: "size_between_max_min", - fields: &Release{Size: uint64(10000000001)}, - args: args{ - filter: Filter{ - Enabled: true, - MinSize: "10 GB", - MaxSize: "20GB", - }, - }, - want: true, - }, - { - name: "size_larger_than_max", - fields: &Release{Size: uint64(30000000001)}, - args: args{ - filter: Filter{ - Enabled: true, - MinSize: "10 GB", - MaxSize: "20GB", - }, - }, - want: false, - }, - //{ - // name: "test_no_size", - // fields: &Release{Size: uint64(0)}, - // args: args{ - // filter: Filter{ - // Enabled: true, - // FilterGeneral: FilterGeneral{MinSize: "10 GB", MaxSize: "20GB"}, - // }, - // }, - // want: false, // additional checks - //}, - { - name: "movie_parse_1", - fields: &Release{ - TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - }, - }, - want: true, - }, - - { - name: "movie_parse_2", - fields: &Release{ - TorrentName: "That Movie 2020 2160p Blu-Ray DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - }, - }, - want: true, - }, - { - name: "movie_parse_3", - fields: &Release{ - TorrentName: "That Movie 2020 2160p WEBDL DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"WEB-DL"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - }, - }, - want: true, - }, - { - name: "movie_parse_shows", - fields: &Release{ - TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - Shows: "That Movie", - }, - }, - want: true, - }, - { - name: "movie_parse_shows_1", - fields: &Release{ - TorrentName: "That.Movie.2020.2160p.BluRay.DD5.1.x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - Shows: "That Movie", - }, - }, - want: true, - }, - { - name: "movie_parse_multiple_shows", - fields: &Release{ - TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - Shows: "That Movie, good story, bad movie", - }, - }, - want: true, - }, - { - name: "movie_parse_multiple_shows_1", - fields: &Release{ - TorrentName: "That.Movie.2020.2160p.BluRay.DD5.1.x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2020", - MatchReleaseGroups: "GROUP1", - Shows: "That Movie, good story, bad movie", - }, - }, - want: true, - }, - { - name: "movie_parse_wildcard_shows", - fields: &Release{ - TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), // 30GB - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Movies, tv", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"1080p", "2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2015,2018-2022", - MatchReleaseGroups: "GROUP1,BADGROUP", - Shows: "*Movie*, good story, bad movie", - }, - }, - want: true, - }, - { - name: "movie_bad_category", - fields: &Release{ - TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", - Category: "Movies", - Freeleech: true, - Size: uint64(30000000001), // 30GB - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - Freeleech: true, - MinSize: "10 GB", - MaxSize: "40GB", - Resolutions: []string{"1080p", "2160p"}, - Sources: []string{"BluRay"}, - Codecs: []string{"x264"}, - Years: "2015,2018-2022", - MatchReleaseGroups: "GROUP1,BADGROUP", - Shows: "*Movie*, good story, bad movie", - }, - }, - want: false, - }, - { - name: "tv_match_season_episode", - fields: &Release{ - TorrentName: "Good show S01E01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - Resolutions: []string{"1080p", "2160p"}, - Sources: []string{"WEB-DL"}, - Codecs: []string{"HEVC"}, - MatchReleaseGroups: "GROUP1,GROUP2", - Seasons: "1,2", - Episodes: "1", - }, - }, - want: true, - }, - { - name: "tv_match_season", - fields: &Release{ - TorrentName: "Good show S01 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - Resolutions: []string{"1080p", "2160p"}, - Sources: []string{"WEB-DL"}, - Codecs: []string{"HEVC"}, - MatchReleaseGroups: "GROUP1,GROUP2", - Seasons: "1,2", - }, - }, - want: true, - }, - { - name: "tv_bad_match_season", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - Resolutions: []string{"1080p", "2160p"}, - Sources: []string{"WEB-DL"}, - Codecs: []string{"HEVC"}, - MatchReleaseGroups: "GROUP1,GROUP2", - Seasons: "1", - }, - }, - want: false, - }, - { - name: "match_uploader", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1", - }, - }, - want: true, - }, - { - name: "except_uploader", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Anonymous", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - ExceptUploaders: "Anonymous", - }, - }, - want: false, - }, - { - name: "match_except_uploader", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show", - }, - }, - want: true, - }, - { - name: "match_tags", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Uploader1", - Tags: []string{"tv"}, - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show", - Tags: "tv", - }, - }, - want: true, - }, - { - name: "match_tags_bad", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Uploader1", - Tags: []string{"foreign"}, - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show", - Tags: "tv", - }, - }, - want: false, - }, - { - name: "match_except_tags", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Uploader1", - Tags: []string{"foreign"}, - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show", - ExceptTags: "tv", - }, - }, - want: true, - }, - { - name: "match_except_tags_2", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - Category: "TV", - Uploader: "Uploader1", - Tags: []string{"foreign"}, - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show", - ExceptTags: "foreign", - }, - }, - want: false, - }, - { - name: "match_group_1", - fields: &Release{ - TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show", - MatchReleaseGroups: "GROUP", - }, - }, - want: true, - }, - { - name: "match_group_potential_partial_1", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-ift", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "ift", - }, - }, - want: true, - }, - { - name: "match_group_potential_partial_2", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "ift", - }, - }, - want: false, - }, - { - name: "match_group_potential_partial_3", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-de[42]", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "de[42]", - }, - }, - want: true, - }, - { - name: "match_group_potential_partial_3", - fields: &Release{ - TorrentName: "[AnimeGroup] Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "[AnimeGroup]", - }, - }, - want: true, - }, - { - name: "except_release_1", - fields: &Release{ - TorrentName: "Good show shift S02 NORDiC 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "Good show shift", - }, - }, - want: false, - }, - { - name: "except_release_2", - fields: &Release{ - TorrentName: "Good show shift S02 NORDiC 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - }, - }, - want: false, - }, - { - name: "except_release_3", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - }, - }, - want: true, - }, - { - name: "except_release_4", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC,*shift*", - }, - }, - want: false, - }, - { - name: "match_hdr_1", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - MatchHDR: []string{"DV", "HDR"}, - }, - }, - want: true, - }, - { - name: "match_hdr_2", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DoVi HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - MatchHDR: []string{"DV", "HDR"}, - }, - }, - want: false, - }, - { - name: "match_hdr_3", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos DoVi HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - ExceptHDR: []string{"DV", "HDR", "DoVi"}, - }, - }, - want: false, - }, - { - name: "match_hdr_4", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - MatchHDR: []string{"DV", "HDR", "DoVi"}, - }, - }, - want: false, - }, - { - name: "match_hdr_5", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - ExceptHDR: []string{"DV", "HDR", "DoVi"}, - }, - }, - want: true, - }, - { - name: "match_hdr_6", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HDR HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - ExceptHDR: []string{"DV", "DoVi"}, - }, - }, - want: true, - }, - { - name: "match_hdr_7", - fields: &Release{ - TorrentName: "Good show dvorak shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HDR HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show dvorak shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - ExceptHDR: []string{"DV", "DoVi"}, - }, - }, - want: true, - }, - { - name: "match_hdr_8", - fields: &Release{ - TorrentName: "Good show shift S02 2160p ATVP WEB-DL DDP 5.1 Atmos HDR10+ HEVC-GROUP", - Category: "TV", - Uploader: "Uploader1", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "*tv*", - MatchUploaders: "Uploader1,Uploader2", - ExceptUploaders: "Anonymous", - Shows: "Good show shift", - MatchReleaseGroups: "GROUP", - ExceptReleases: "NORDiC", - MatchHDR: []string{"DV", "DoVi", "HDR10+"}, - }, - }, - want: true, - }, - { - name: "match_music_1", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "FLAC / 24bit Lossless / Log / 100% / Cue / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Album", - Artists: "Artist", - Media: []string{"CD"}, - Formats: []string{"FLAC"}, - Quality: []string{"24bit Lossless"}, - Log: true, - LogScore: 100, - Cue: true, - }, - }, - want: true, - }, - { - name: "match_music_2", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "MP3 / 320 / WEB", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Album", - Artists: "Artist", - //Sources: []string{"CD"}, - //Formats: []string{"FLAC"}, - //Quality: []string{"24bit Lossless"}, - PerfectFlac: true, - //Log: true, - //LogScore: 100, - //Cue: true, - }, - }, - want: false, - }, - { - name: "match_music_3", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "FLAC / Lossless / Log / 100% / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Album", - Artists: "Artist", - //Sources: []string{"CD"}, - //Formats: []string{"FLAC"}, - //Quality: []string{"24bit Lossless"}, - PerfectFlac: true, - //Log: true, - //LogScore: 100, - //Cue: true, - }, - }, - want: false, - }, - { - name: "match_music_4", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "FLAC / Lossless / Log / 100% / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchCategories: "Album", - Artists: "Artist", - Media: []string{"CD"}, - Formats: []string{"FLAC"}, - Quality: []string{"24bit Lossless"}, - //PerfectFlac: true, - Log: true, - LogScore: 100, - Cue: true, - }, - }, - want: false, - }, - { - name: "match_music_5", - fields: &Release{ - TorrentName: "Artist - Albumname", - Year: 2022, - ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchReleaseTypes: []string{"Album"}, - Years: "2020-2022", - Artists: "Artist", - Media: []string{"CD"}, - Formats: []string{"FLAC"}, - Quality: []string{"24bit Lossless", "Lossless"}, - PerfectFlac: true, - Log: true, - LogScore: 100, - Cue: true, - }, - }, - want: true, - }, - { - name: "match_music_6", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchReleaseTypes: []string{"Single"}, - Artists: "Artist", - Media: []string{"CD"}, - Formats: []string{"FLAC"}, - Quality: []string{"24bit Lossless", "Lossless"}, - PerfectFlac: true, - Log: true, - LogScore: 100, - Cue: true, - }, - }, - want: false, - }, - { - name: "match_music_7", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchReleaseTypes: []string{"Album"}, - Artists: "Artiiiist", - Media: []string{"CD"}, - Formats: []string{"FLAC"}, - Quality: []string{"24bit Lossless", "Lossless"}, - PerfectFlac: true, - Log: true, - LogScore: 100, - Cue: true, - }, - }, - want: false, - }, - { - name: "match_music_8", - fields: &Release{ - TorrentName: "Artist - Albumname", - ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD", - Category: "Album", - }, - args: args{ - filter: Filter{ - Enabled: true, - MatchReleaseTypes: []string{"Album"}, - Artists: "Artist", - Albums: "Albumname", - Media: []string{"CD"}, - Formats: []string{"FLAC"}, - Quality: []string{"24bit Lossless", "Lossless"}, - PerfectFlac: true, - Log: true, - LogScore: 100, - Cue: true, - }, - }, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := tt.fields // Release - - _ = r.Parse() // Parse TorrentName into struct - _, got := r.CheckFilter(tt.args.filter) - - assert.Equal(t, tt.want, got) - }) - } -} - func TestRelease_MapVars(t *testing.T) { type args struct { varMap map[string]string @@ -1302,16 +287,19 @@ func TestRelease_MapVars(t *testing.T) { TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", Category: "tv", Freeleech: true, + Bonus: []string{"Freeleech"}, Uploader: "Anon", Size: uint64(10000000000), }, - args: args{varMap: map[string]string{ - "torrentName": "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", - "category": "tv", - "freeleech": "freeleech", - "uploader": "Anon", - "torrentSize": "10GB", - }}, + args: args{ + varMap: map[string]string{ + "torrentName": "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", + "category": "tv", + "freeleech": "freeleech", + "uploader": "Anon", + "torrentSize": "10GB", + }, + }, }, { name: "3", @@ -1320,6 +308,7 @@ func TestRelease_MapVars(t *testing.T) { TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", Category: "tv", FreeleechPercent: 100, + Bonus: []string{"Freeleech100"}, Uploader: "Anon", Size: uint64(10000000000), }, @@ -1337,7 +326,8 @@ func TestRelease_MapVars(t *testing.T) { want: &Release{ TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", Category: "tv", - FreeleechPercent: 100, + FreeleechPercent: 50, + Bonus: []string{"Freeleech50"}, Uploader: "Anon", Size: uint64(10000000000), Tags: []string{"foreign", "tv"}, @@ -1345,7 +335,7 @@ func TestRelease_MapVars(t *testing.T) { args: args{varMap: map[string]string{ "torrentName": "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", "category": "tv", - "freeleechPercent": "100%", + "freeleechPercent": "50%", "uploader": "Anon", "torrentSize": "10GB", "tags": "foreign,tv", @@ -1358,6 +348,7 @@ func TestRelease_MapVars(t *testing.T) { TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", Category: "tv", FreeleechPercent: 100, + Bonus: []string{"Freeleech100"}, Uploader: "Anon", Size: uint64(10000000000), Tags: []string{"foreign", "tv"}, @@ -1379,6 +370,7 @@ func TestRelease_MapVars(t *testing.T) { Category: "tv", Year: 2020, FreeleechPercent: 100, + Bonus: []string{"Freeleech100"}, Uploader: "Anon", Size: uint64(10000000000), Tags: []string{"foreign", "tv"}, @@ -1400,7 +392,8 @@ func TestRelease_MapVars(t *testing.T) { TorrentName: "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", Category: "tv", Year: 2020, - FreeleechPercent: 100, + FreeleechPercent: 25, + Bonus: []string{"Freeleech25"}, Uploader: "Anon", Size: uint64(10000000000), Tags: []string{"hip.hop", "rhythm.and.blues", "2000s"}, @@ -1409,7 +402,7 @@ func TestRelease_MapVars(t *testing.T) { "torrentName": "Good show S02 2160p ATVP WEB-DL DDP 5.1 Atmos DV HEVC-GROUP2", "category": "tv", "year": "2020", - "freeleechPercent": "100%", + "freeleechPercent": "25%", "uploader": "Anon", "torrentSize": "10GB", "tags": "hip.hop,rhythm.and.blues, 2000s", @@ -1423,6 +416,7 @@ func TestRelease_MapVars(t *testing.T) { Category: "tv", Year: 2020, FreeleechPercent: 100, + Bonus: []string{"Freeleech100"}, Uploader: "Anon", Size: uint64(10000000000), Tags: []string{"hip.hop", "rhythm.and.blues", "2000s"}, @@ -1437,7 +431,7 @@ func TestRelease_MapVars(t *testing.T) { "torrentSize": "10000", "tags": "hip.hop,rhythm.and.blues, 2000s", }, - definition: IndexerDefinition{Parse: IndexerParse{ForceSizeUnit: "MB"}}, + definition: IndexerDefinition{Parse: &IndexerParse{ForceSizeUnit: "MB"}}, }, }, } @@ -1492,3 +486,94 @@ func TestSplitAny(t *testing.T) { }) } } + +func TestRelease_ParseString(t *testing.T) { + type fields struct { + Release + } + type args struct { + title string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + {name: "parse_1", fields: fields{}, args: args{title: "Phenomena 1985 International Cut UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-FraMeSToR"}, wantErr: false}, + {name: "parse_2", fields: fields{}, args: args{title: "Justice League: Dark 2017 UHD BluRay 2160p DTS-HD MA 5.1 DV HEVC HYBRID REMUX-FraMeSToR"}, wantErr: false}, + {name: "parse_3", fields: fields{}, args: args{title: "Outer.Range.S01E02.The.Land.1080p.AMZN.WEB-DL.DDP5.1.H.264-TOMMY"}, wantErr: false}, + {name: "parse_4", fields: fields{}, args: args{title: "WeCrashed S01E07 The Power of We 2160p ATVP WEB-DL DDP 5.1 Atmos HDR H.265-NOSiViD"}, wantErr: false}, + {name: "parse_5", fields: fields{}, args: args{title: "WeCrashed.S01E07.The.Power.of.We.DV.2160p.ATVP.WEB-DL.DDPA5.1.H.265-NOSiViD"}, wantErr: false}, + {name: "parse_6", fields: fields{}, args: args{title: "WeCrashed.S01E07.The.Power.of.We.DV.2160p.ATVP.WEB-DL.DDPA5.1.H265-NOSiViD"}, wantErr: false}, + {name: "parse_7", fields: fields{}, args: args{title: "WeCrashed.S01E07.The.Power.of.We.DV.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}, wantErr: false}, + {name: "parse_8", fields: fields{}, args: args{title: "WeCrashed.S01E07.The.Power.of.We.HDR.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}, wantErr: false}, + {name: "parse_9", fields: fields{}, args: args{title: "WeCrashed.S01.HDR.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}, wantErr: false}, + {name: "parse_10", fields: fields{}, args: args{title: "WeCrashed.S01.DV.HDR+.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}, wantErr: false}, + {name: "parse_11", fields: fields{}, args: args{title: "WeCrashed.S01.DoVi.HDR10+.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}, wantErr: false}, + {name: "parse_12", fields: fields{}, args: args{title: "WeCrashed.S01.Dolby.Vision.2160p.ATVP.WEB-DL.DDPA5.1.x265-NOSiViD"}, wantErr: false}, + {name: "parse_13", fields: fields{}, args: args{title: "WeCrashed.S01.Dolby.Vision.1080p.ATVP.WEB-DL.DDPA5.1.x264-NOSiViD"}, wantErr: false}, + {name: "parse_14", fields: fields{}, args: args{title: "Without Remorse 2021 1080p Blu-ray AVC DTS-HD MA 5.1-MTeam"}, wantErr: false}, + {name: "parse_15", fields: fields{}, args: args{title: "Annette 2021 2160p GER UHD Blu-ray SDR HEVC DTS-HD MA 5.1-UNTOUCHED"}, wantErr: false}, + {name: "parse_16", fields: fields{}, args: args{title: "Sing 2 2021 MULTi COMPLETE UHD Blu-ray TrueHD Atmos 7.1-MMCLX"}, wantErr: false}, + {name: "parse_17", fields: fields{}, args: args{title: "NBC.Nightly.News.2022.04.12.1080p.NBC.WEB-DL.AAC2.0.H.264-TEPES"}, wantErr: false}, + {name: "parse_18", fields: fields{}, args: args{title: "[SubsPlease] Heroine Tarumono! Kiraware Heroine to Naisho no Oshigoto - 04 (1080p) [17083ED9]"}, wantErr: false}, + {name: "parse_19", fields: fields{}, args: args{title: "The World is Not Enough 1999 2160p WEB-DL HEVC DTS-HD MA 5.1 H.265-DEFLATE"}, wantErr: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Release{ + ID: tt.fields.ID, + FilterStatus: tt.fields.FilterStatus, + Rejections: tt.fields.Rejections, + Indexer: tt.fields.Indexer, + FilterName: tt.fields.FilterName, + Protocol: tt.fields.Protocol, + Implementation: tt.fields.Implementation, + Timestamp: tt.fields.Timestamp, + GroupID: tt.fields.GroupID, + TorrentID: tt.fields.TorrentID, + TorrentURL: tt.fields.TorrentURL, + TorrentTmpFile: tt.fields.TorrentTmpFile, + TorrentHash: tt.fields.TorrentHash, + TorrentName: tt.fields.TorrentName, + Size: tt.fields.Size, + Title: tt.fields.Title, + Category: tt.fields.Category, + Season: tt.fields.Season, + Episode: tt.fields.Episode, + Year: tt.fields.Year, + Resolution: tt.fields.Resolution, + Source: tt.fields.Source, + Codec: tt.fields.Codec, + Container: tt.fields.Container, + HDR: tt.fields.HDR, + Audio: tt.fields.Audio, + Group: tt.fields.Group, + Region: tt.fields.Region, + Language: tt.fields.Language, + Proper: tt.fields.Proper, + Repack: tt.fields.Repack, + Website: tt.fields.Website, + Artists: tt.fields.Artists, + Type: tt.fields.Type, + LogScore: tt.fields.LogScore, + IsScene: tt.fields.IsScene, + Origin: tt.fields.Origin, + Tags: tt.fields.Tags, + ReleaseTags: tt.fields.ReleaseTags, + Freeleech: tt.fields.Freeleech, + FreeleechPercent: tt.fields.FreeleechPercent, + Uploader: tt.fields.Uploader, + PreTime: tt.fields.PreTime, + RawCookie: tt.fields.RawCookie, + AdditionalSizeCheckRequired: tt.fields.AdditionalSizeCheckRequired, + FilterID: tt.fields.FilterID, + Filter: tt.fields.Filter, + ActionStatus: tt.fields.ActionStatus, + } + _ = r.ParseString(tt.args.title) + //fmt.Sprintf("ParseString(%v)", tt.args.title) + }) + } +} diff --git a/internal/domain/releasetags.go b/internal/domain/releasetags.go new file mode 100644 index 0000000..58e9aff --- /dev/null +++ b/internal/domain/releasetags.go @@ -0,0 +1,377 @@ +package domain + +import ( + "fmt" + "regexp" +) + +var types map[string][]*TagInfo + +func init() { + types = make(map[string][]*TagInfo) + + audio := []*TagInfo{ + {tag: "24BIT", title: "", regexp: "(?-i:24BIT)", re: nil}, + {tag: "24BIT Lossless", title: "", regexp: "(?:24BIT lossless)", re: nil}, + {tag: "16BIT", title: "", regexp: "(?-i:16BIT)", re: nil}, + {tag: "320", title: "320 Kbps", regexp: "320[\\\\-\\\\._ kbps]?", re: nil}, + {tag: "256", title: "256 Kbps", regexp: "256[\\\\-\\\\._ kbps]?", re: nil}, + {tag: "192", title: "192 Kbps", regexp: "192[\\\\-\\\\._ kbps]?", re: nil}, + {tag: "128", title: "128 Kbps", regexp: "128[\\\\-\\\\._ kbps]?", re: nil}, + {tag: "AAC-LC", title: "Advanced Audio Coding (LC)", regexp: "aac[\\-\\._ ]?lc", re: nil}, + {tag: "AAC", title: "Advanced Audio Coding (LC)", regexp: "", re: nil}, + {tag: "AC3D", title: "", regexp: "ac[\\-\\._ ]?3d", re: nil}, + {tag: "Atmos", title: "Dolby Atmos", regexp: "", re: nil}, + {tag: "CBR", title: "Constant Bit Rate", regexp: "", re: nil}, + {tag: "Cue", title: "Cue File", regexp: "", re: nil}, + {tag: "DDPA", title: "Dolby Digital+ Atmos (E-AC-3+Atmos)", regexp: "dd[p\\+]a", re: nil}, + {tag: "DDP", title: "Dolby Digital+ (E-AC-3)", regexp: "dd[p\\+]|e[\\-\\._ ]?ac3", re: nil}, + {tag: "DD", title: "Dolby Digital (AC-3)", regexp: "dd|ac3|dolby[\\-\\._ ]?digital", re: nil}, + {tag: "DTS-HD.HRA", title: "DTS (HD HRA)", regexp: "dts[\\-\\._ ]?hd[\\-\\._ ]?hra", re: nil}, + {tag: "DTS-HD.HR", title: "DTS (HD HR)", regexp: "dts[\\-\\._ ]?hd[\\-\\._ ]?hr", re: nil}, + {tag: "DTS-HD.MA", title: "DTS (HD MA)", regexp: "dts[\\-\\._ ]?hd[\\-\\._ ]?ma", re: nil}, + {tag: "DTS-HD", title: "DTS (HD)", regexp: "dts[\\-\\._ ]?hd[\\-\\._ ]?", re: nil}, + {tag: "DTS-MA", title: "DTS (MA)", regexp: "dts[\\-\\._ ]?ma[\\-\\._ ]?", re: nil}, + {tag: "DTS-X", title: "DTS (X)", regexp: "dts[\\-\\._ ]?x", re: nil}, + {tag: "DTS", title: "", regexp: "", re: nil}, + {tag: "DUAL.AUDIO", title: "Dual Audio", regexp: "dual(?:[\\-\\._ ]?audio)?", re: nil}, + {tag: "EAC3D", title: "", regexp: "", re: nil}, + {tag: "ES", title: "Dolby Digital (ES)", regexp: "(?-i:ES)", re: nil}, + {tag: "EX", title: "Dolby Digital (EX)", regexp: "(?-i:EX)", re: nil}, + {tag: "FLAC", title: "Free Lossless Audio Codec", regexp: "", re: nil}, + {tag: "LiNE", title: "Line", regexp: "(?-i:L[iI]NE)", re: nil}, + {tag: "Lossless", title: "", regexp: "(?i:Lossless)", re: nil}, + {tag: "Log100", title: "", regexp: "(log 100%)", re: nil}, + {tag: "Log", title: "", regexp: "(?:log)", re: nil}, + {tag: "LPCM", title: "Linear Pulse-Code Modulation", regexp: "", re: nil}, + {tag: "MP3", title: "", regexp: "", re: nil}, + {tag: "OGG", title: "", regexp: "", re: nil}, + {tag: "OPUS", title: "", regexp: "", re: nil}, + {tag: "TrueHD", title: "Dolby TrueHD", regexp: "(?:dolby[\\-\\._ ]?)?true[\\-\\._ ]?hd", re: nil}, + {tag: "VBR", title: "Variable Bit Rate", regexp: "", re: nil}, + } + types["audio"] = audio + + bonus := []*TagInfo{ + {tag: "Freeleech", title: "Freeleech", regexp: "freeleech", re: nil}, + } + types["bonus"] = bonus + + channels := []*TagInfo{ + {tag: "7.1", title: "", regexp: "7\\.1(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "6.1", title: "", regexp: "6\\.1(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "6.0", title: "", regexp: "6\\.0(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "5.1", title: "", regexp: "5\\.1(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "5.0", title: "", regexp: "5\\.0(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "4.1", title: "", regexp: "4\\.1(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "4.0", title: "", regexp: "4\\.0(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "3.1", title: "", regexp: "3\\.1(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "3.0", title: "", regexp: "3\\.0(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "2.1", title: "", regexp: "2\\.1(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "2.0", title: "", regexp: "2\\.0(?:[\\-\\._ ]?audios)?", re: nil}, + {tag: "1.0", title: "", regexp: "1\\.0(?:[\\-\\._ ]?audios)?", re: nil}, + } + types["channels"] = channels + + codecs := []*TagInfo{ + {tag: "DiVX.SBC", title: "DivX SBC", regexp: "(?:divx[\\-\\._ ]?)?sbc", re: nil}, + {tag: "x264.HQ", title: "x264 (HQ)", regexp: "x[\\\\-\\\\._ ]?264[\\\\-\\\\._ ]?hq", re: nil}, + {tag: "MPEG-2", title: "", regexp: "mpe?g(?:[\\-\\._ ]?2)?", re: nil}, + {tag: "H.265", title: "", regexp: "h[\\-\\._ ]?265", re: nil}, + {tag: "H.264", title: "", regexp: "h[\\-\\._ ]?264", re: nil}, + {tag: "H.263", title: "", regexp: "h[\\-\\._ ]?263", re: nil}, + {tag: "H.262", title: "", regexp: "h[\\-\\._ ]?2[26]2", re: nil}, + {tag: "H.261", title: "", regexp: "h[\\-\\._ ]?261", re: nil}, + {tag: "dxva", title: "Direct-X Video Acceleration", regexp: "", re: nil}, + {tag: "HEVC", title: "High Efficiency Video Coding", regexp: "", re: nil}, + {tag: "VC-1", title: "", regexp: "vc[\\-\\._ ]?1", re: nil}, + {tag: "x265", title: "", regexp: "x[\\-\\._ ]?265", re: nil}, + {tag: "x264", title: "", regexp: "x[\\-\\._ ]?264", re: nil}, + {tag: "XViD", title: "Xvid", regexp: "", re: nil}, + {tag: "AVC", title: "Advanced Video Coding", regexp: "avc(?:[\\-\\._ ]?1)?", re: nil}, + {tag: "VP9", title: "", regexp: "vp[\\-\\._ ]?9", re: nil}, + {tag: "VP8", title: "", regexp: "vp[\\-\\._ ]?8", re: nil}, + {tag: "VP7", title: "", regexp: "vp[\\-\\._ ]?7", re: nil}, + } + types["codecs"] = codecs + + container := []*TagInfo{ + {tag: "avi", title: "Audio Video Interleave (avi)", regexp: "", re: nil}, + {tag: "img", title: "IMG", regexp: "", re: nil}, + {tag: "iso", title: "ISO", regexp: "", re: nil}, + {tag: "mkv", title: "Matroska (mkv)", regexp: "", re: nil}, + {tag: "mov", title: "MOV", regexp: "", re: nil}, + {tag: "mp4", title: "MP4", regexp: "", re: nil}, + {tag: "mpg", title: "MPEG", regexp: "mpe?g", re: nil}, + {tag: "m2ts", title: "BluRay Disc (m2ts)", regexp: "", re: nil}, + {tag: "vob", title: "VOB", regexp: "", re: nil}, + } + types["container"] = container + + hdr := []*TagInfo{ + {tag: "HDR10+", title: "High Dynamic Range (10-bit+)", regexp: "hdr[\\-\\.]?10\\+|10\\+[\\-\\.]?bit|hdr10plus|hi10p", re: nil}, + {tag: "HDR10", title: "High Dynamic Range (10-bit)", regexp: "hdr[\\-\\.]?10|10[\\-\\.]?bit|hi10", re: nil}, + {tag: "HDR+", title: "High Dynamic Range+", regexp: "hdr\\+", re: nil}, + {tag: "HDR", title: "High Dynamic Range", regexp: "", re: nil}, + {tag: "SDR", title: "Standard Dynamic Range", regexp: "", re: nil}, + {tag: "DV", title: "Dolby Vision", regexp: "(?i:dolby[\\-\\._ ]vision|dovi|\\Qdv\\E\\b)", re: nil}, + } + types["hdr"] = hdr + + other := []*TagInfo{ + {tag: "HYBRID", title: "Hybrid", regexp: "", re: nil}, + {tag: "REMUX", title: "Remux", regexp: "", re: nil}, + {tag: "REPACK", title: "Repack", regexp: "repack(?:ed)?", re: nil}, + {tag: "REREPACK", title: "Rerepack", regexp: "rerepack(?:ed)?", re: nil}, + } + types["other"] = other + + source := []*TagInfo{ + {tag: "Cassette", title: "Cassette", regexp: "", re: nil}, + {tag: "CD", title: "Compact Disc", regexp: "cd[\\-\\._ ]?(?:album)?", re: nil}, + {tag: "BDRiP", title: "BluRay (rip)", regexp: "b[dr]?[\\-\\._ ]?rip", re: nil}, + {tag: "BDSCR", title: "BluRay (screener)", regexp: "b[dr][\\-\\._ ]?scr(?:eener)?", re: nil}, + {tag: "BluRay3D", title: "", regexp: "blu[\\-\\._ ]?ray[\\-\\._ ]?3d|bd3d", re: nil}, + {tag: "BluRayRiP", title: "BluRay (rip)", regexp: "", re: nil}, + {tag: "BluRay", title: "", regexp: "blu[\\-\\._ ]?ray|bd", re: nil}, + {tag: "BRDRip", title: "BluRay Disc (rip)", regexp: "", re: nil}, + {tag: "DAT", title: "Datacable", regexp: "(?-i:DAT)", re: nil}, + {tag: "DVBRiP", title: "Digital Video Broadcasting (rip)", regexp: "dvb[\\-\\._ ]?rip", re: nil}, + {tag: "DVDA", title: "Audio DVD", regexp: "", re: nil}, + {tag: "DVDRiP", title: "Digital Video Disc (rip)", regexp: "dvd[\\-\\._ ]?rip", re: nil}, + {tag: "DVDSCRRiP", title: "Digital Video Disc (screener rip)", regexp: "(?:dvd[\\-\\._ ]?)?scr(?:eener)?[\\-\\._ ]?rip", re: nil}, + {tag: "DVDSCR", title: "Digital Video Disc (screener)", regexp: "(?:dvd[\\-\\._ ]?)?scr(?:eener)?", re: nil}, + {tag: "DVDS", title: "Digital Video Disc (single)", regexp: "dvds(?:ingle)?", re: nil}, + {tag: "DVD", title: "Digital Video Disc", regexp: "dvd", re: nil}, + {tag: "SACD", title: "Super Audio Compact Disc", regexp: "", re: nil}, + {tag: "RADIO", title: "Radio", regexp: "(?-i:R[aA]D[iI][oO])", re: nil}, + {tag: "SATRiP", title: "Satellite (rip)", regexp: "sat[\\-\\._ ]?rip", re: nil}, + {tag: "SAT", title: "Satellite Radio", regexp: "(?-i:SAT)", re: nil}, + {tag: "SBD", title: "Soundboard", regexp: "(?-i:SBD|DAB|Soundboard)", re: nil}, + {tag: "UHD.BDRiP", title: "Ultra High-Definition BluRay (rip)", regexp: "uhd[\\-\\._ ]?(?:bd)?rip", re: nil}, + {tag: "UHD.BluRay", title: "Ultra High-Definition BluRay", regexp: "uhd[\\-\\._ ]?(?:blu[\\-\\._ ]?ray|bd)", re: nil}, + {tag: "UHDTV", title: "Ultra High-Definition TV", regexp: "", re: nil}, + {tag: "UMDMOVIE", title: "Universal Media Disc Movie", regexp: "", re: nil}, + {tag: "Vinyl", title: "Vinyl", regexp: "vinyl|vl", re: nil}, + {tag: "WEB-DL", title: "Web (DL)", regexp: "web[\\-\\._ ]?dl", re: nil}, + {tag: "WEB-HD", title: "Web (HD)", regexp: "web[\\-\\._ ]?hd", re: nil}, + {tag: "WEBFLAC", title: "Web (FLAC)", regexp: "", re: nil}, + {tag: "WebHDRiP", title: "Web (HD rip)", regexp: "", re: nil}, + {tag: "WEBRiP", title: "Web (rip)", regexp: "web[\\-\\._ ]?rip", re: nil}, + {tag: "WEBSCR", title: "Web (screener)", regexp: "web[\\-\\._ ]?scr(?:eener)?", re: nil}, + {tag: "WebUHD", title: "Web (UHD)", regexp: "", re: nil}, + {tag: "WEB", title: "Web", regexp: "", re: nil}, + } + types["source"] = source + + resolution := []*TagInfo{ + {tag: "PN", title: "Selector", regexp: "p(?:al)?[\\-\\._ ]?n(?:tsc)?[\\-\\._ ]selector", re: nil}, + {tag: "DCI4K", title: "DCI 4k", regexp: "dci[\\-\\._ ]?4k|4096x2160", re: nil}, + {tag: "DCI2K", title: "DCI 2k", regexp: "dci[\\-\\._ ]?2k|2048x1080", re: nil}, + {tag: "4320p", title: "UltraHD 8K (4320p)", regexp: "4320p|7680x4320", re: nil}, + {tag: "2880p", title: "5k (2880p)", regexp: "2880p|5k|5120x2880", re: nil}, + {tag: "2160p", title: "UltraHD 4K (2160p)", regexp: "2160p|3840x2160|uhd|4k", re: nil}, + {tag: "1800p", title: "QHD+ (1800p)", regexp: "1800p|3200x1800", re: nil}, + {tag: "1440p", title: "QHD (1440p)", regexp: "1440p|2560x1440", re: nil}, + {tag: "1080p", title: "FullHD (1080p)", regexp: "1080[ip]|1920x1080", re: nil}, + {tag: "900p", title: "900[ip]|1600x900", regexp: "900[ip]|1600x900", re: nil}, + {tag: "720p", title: "HD (720p)", regexp: "720[ip]|1280x720", re: nil}, + {tag: "576p", title: "PAL (576p)", regexp: "576[ip]|720x576|pal", re: nil}, + {tag: "540p", title: "qHD (540p)", regexp: "540[ip]|960x540", re: nil}, + {tag: "480p", title: "NTSC (480p)", regexp: "480[ip]|720x480|848x480|854x480|ntsc", re: nil}, + {tag: "360p", title: "nHD (360p)", regexp: "360[ip]|640x360", re: nil}, + {tag: "$1p", title: "Other ($1p)", regexp: "([123]\\d{3})p", re: nil}, + } + types["resolution"] = resolution + + // language `(?i)\b((DK|DKSUBS|DANiSH|DUTCH|NL|NLSUBBED|ENG|FI|FLEMiSH|FiNNiSH|DE|FRENCH|GERMAN|HE|HEBREW|HebSub|HiNDi|iCELANDiC|KOR|MULTi|MULTiSUBS|NORWEGiAN|NO|NORDiC|PL|PO|POLiSH|PLDUB|RO|ROMANiAN|RUS|SPANiSH|SE|SWEDiSH|SWESUB||))\b`) + // websites `(?i)\b((AMBC|AS|AMZN|AMC|ANPL|ATVP|iP|CORE|BCORE|CMOR|CN|CBC|CBS|CMAX|CNBC|CC|CRIT|CR|CSPN|CW|DAZN|DCU|DISC|DSCP|DSNY|DSNP|DPLY|ESPN|FOX|FUNI|PLAY|HBO|HMAX|HIST|HS|HOTSTAR|HULU|iT|MNBC|MTV|NATG|NBC|NF|NICK|NRK|PMNT|PMNP|PCOK|PBS|PBSK|PSN|QIBI|SBS|SHO|STAN|STZ|SVT|SYFY|TLC|TRVL|TUBI|TV3|TV4|TVL|VH1|VICE|VMEO|UFC|USAN|VIAP|VIAPLAY|VL|WWEN|XBOX|YHOO|YT|RED))\b`) + + for s, infos := range types { + for _, info := range infos { + var err error + //if info.re, err = regexp.Compile(`(?i)^(?:` + info.RE() + `)$`); err != nil { + if info.re, err = regexp.Compile(`(?i)(?:` + info.RE() + `)`); err != nil { + fmt.Errorf("tag %q has invalid regexp %q\n", s, info.re) + } + } + } +} + +type TagInfo struct { + tag string + title string + regexp string + re *regexp.Regexp +} + +// Tag returns the tag info tag. +func (info *TagInfo) Tag() string { + return info.tag +} + +// Title returns the tag info title. +func (info *TagInfo) Title() string { + return info.title +} + +// Regexp returns the tag info regexp. +func (info *TagInfo) Regexp() string { + return info.regexp +} + +//// Other returns the tag info other. +//func (info *TagInfo) Other() string { +// return info.other +//} +// +//// Type returns the tag info type. +//func (info *TagInfo) Type() int { +// return info.typ +//} + +//// Excl returns the tag info excl. +//func (info *TagInfo) Excl() bool { +// return info.excl +//} + +// RE returns the tag info regexp string. +func (info *TagInfo) RE() string { + if info.regexp != "" { + return info.regexp + } + return `\Q` + info.tag + `\E` +} + +// Match matches the tag info to s. +func (info *TagInfo) Match(s string) bool { + return info.re.MatchString(s) +} + +// FindFunc is the find signature.. +type FindFunc func(string) *TagInfo + +// Find returns a func to find tag info. +func Find(infos ...*TagInfo) FindFunc { + n := len(infos) + return func(s string) *TagInfo { + for i := 0; i < n; i++ { + if infos[i].Match(s) { + return infos[i] + } + } + return nil + } +} + +type ReleaseTags struct { + Audio []string + Channels string + Source string + Resolution string + Container string + Codec string + HDR []string + Other []string + Bonus []string +} + +func ParseReleaseTags(tags []string) ReleaseTags { + releaseTags := ReleaseTags{} + + for _, tag := range tags { + + for tagType, tagInfos := range types { + + for _, info := range tagInfos { + // check tag + match := info.Match(tag) + if match { + fmt.Printf("match: %v, info: %v\n", tag, info.Tag()) + switch tagType { + case "audio": + releaseTags.Audio = append(releaseTags.Audio, info.Tag()) + continue + case "bonus": + releaseTags.Bonus = append(releaseTags.Bonus, info.Tag()) + continue + case "channels": + releaseTags.Channels = info.Tag() + break + case "codecs": + releaseTags.Codec = info.Tag() + break + case "container": + releaseTags.Container = info.Tag() + break + case "hdr": + releaseTags.HDR = append(releaseTags.HDR, info.Tag()) + continue + case "other": + releaseTags.Other = append(releaseTags.Other, info.Tag()) + continue + case "source": + releaseTags.Source = info.Tag() + break + case "resolution": + releaseTags.Resolution = info.Tag() + break + } + break + } + } + } + } + + return releaseTags +} +func ParseReleaseTagString(tags string) ReleaseTags { + releaseTags := ReleaseTags{} + + for tagType, tagInfos := range types { + //fmt.Printf("tagType: %v\n", tagType) + + for _, info := range tagInfos { + // check tag + match := info.Match(tags) + if !match { + continue + } + + //fmt.Printf("match: info: %v\n", info.Tag()) + switch tagType { + case "audio": + releaseTags.Audio = append(releaseTags.Audio, info.Tag()) + continue + case "bonus": + releaseTags.Bonus = append(releaseTags.Bonus, info.Tag()) + continue + case "channels": + releaseTags.Channels = info.Tag() + break + case "codecs": + releaseTags.Codec = info.Tag() + break + case "container": + releaseTags.Container = info.Tag() + break + case "hdr": + releaseTags.HDR = append(releaseTags.HDR, info.Tag()) + continue + case "other": + releaseTags.Other = append(releaseTags.Other, info.Tag()) + continue + case "source": + releaseTags.Source = info.Tag() + break + case "resolution": + releaseTags.Resolution = info.Tag() + break + } + break + } + + } + + return releaseTags +} diff --git a/internal/domain/releasetags_test.go b/internal/domain/releasetags_test.go new file mode 100644 index 0000000..dfa42e5 --- /dev/null +++ b/internal/domain/releasetags_test.go @@ -0,0 +1,56 @@ +package domain + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseReleaseTags(t *testing.T) { + type args struct { + tags []string + } + tests := []struct { + name string + args args + want ReleaseTags + }{ + {name: "test_1", args: args{tags: []string{"CD", "FLAC", "Lossless"}}, want: ReleaseTags{Source: "CD", Audio: []string{"FLAC", "Lossless"}}}, + {name: "test_2", args: args{tags: []string{"MP4", "2160p", "BluRay", "DV"}}, want: ReleaseTags{Source: "BluRay", Resolution: "2160p", Container: "mp4", HDR: []string{"DV"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, ParseReleaseTags(tt.args.tags), "ParseReleaseTags(%v)", tt.args.tags) + }) + } +} + +func TestParseReleaseTagString(t *testing.T) { + type args struct { + tags string + } + tests := []struct { + name string + args args + want ReleaseTags + }{ + {name: "music_1", args: args{tags: "FLAC / Lossless / Log / 80% / Cue / CD"}, want: ReleaseTags{Audio: []string{"Cue", "FLAC", "Lossless", "Log"}, Source: "CD"}}, + {name: "music_2", args: args{tags: "FLAC Lossless Log 80% Cue CD"}, want: ReleaseTags{Audio: []string{"Cue", "FLAC", "Lossless", "Log"}, Source: "CD"}}, + {name: "music_3", args: args{tags: "FLAC Lossless Log 100% Cue CD"}, want: ReleaseTags{Audio: []string{"Cue", "FLAC", "Lossless", "Log100", "Log"}, Source: "CD"}}, + {name: "music_4", args: args{tags: "FLAC 24bit Lossless Log 100% Cue CD"}, want: ReleaseTags{Audio: []string{"24BIT Lossless", "Cue", "FLAC", "Lossless", "Log100", "Log"}, Source: "CD"}}, + {name: "music_5", args: args{tags: "MP3 320 WEB"}, want: ReleaseTags{Audio: []string{"320", "MP3"}, Source: "WEB"}}, + {name: "movies_1", args: args{tags: "x264 Blu-ray MKV 1080p"}, want: ReleaseTags{Codec: "x264", Source: "BluRay", Resolution: "1080p", Container: "mkv"}}, + {name: "movies_2", args: args{tags: "HEVC HDR Blu-ray mp4 2160p"}, want: ReleaseTags{Codec: "HEVC", Source: "BluRay", Resolution: "2160p", Container: "mp4", HDR: []string{"HDR"}}}, + {name: "movies_3", args: args{tags: "HEVC HDR DV Blu-ray mp4 2160p"}, want: ReleaseTags{Codec: "HEVC", Source: "BluRay", Resolution: "2160p", Container: "mp4", HDR: []string{"HDR", "DV"}}}, + {name: "movies_4", args: args{tags: "H.264, Blu-ray/HD DVD"}, want: ReleaseTags{Codec: "H.264", Source: "BluRay"}}, + {name: "movies_5", args: args{tags: "H.264, Remux"}, want: ReleaseTags{Codec: "H.264", Other: []string{"REMUX"}}}, + {name: "movies_6", args: args{tags: "H.264, DVD"}, want: ReleaseTags{Codec: "H.264", Source: "DVD"}}, + {name: "movies_7", args: args{tags: "H.264, DVD, Freeleech"}, want: ReleaseTags{Codec: "H.264", Source: "DVD", Bonus: []string{"Freeleech"}}}, + {name: "movies_8", args: args{tags: "H.264, DVD, Freeleech!"}, want: ReleaseTags{Codec: "H.264", Source: "DVD", Bonus: []string{"Freeleech"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, ParseReleaseTagString(tt.args.tags), "ParseReleaseTagString(%v)", tt.args.tags) + }) + } +} diff --git a/internal/feed/torznab.go b/internal/feed/torznab.go index 7aa2f89..6a6f2b0 100644 --- a/internal/feed/torznab.go +++ b/internal/feed/torznab.go @@ -56,7 +56,7 @@ func (j *TorznabJob) process() error { releases := make([]*domain.Release, 0) for _, item := range items { - rls, err := domain.NewRelease(item.Title, "") + rls, err := domain.NewRelease(j.IndexerIdentifier) if err != nil { continue } @@ -69,7 +69,7 @@ func (j *TorznabJob) process() error { // parse size bytes string rls.ParseSizeBytesString(item.Size) - if err := rls.Parse(); err != nil { + if err := rls.ParseString(item.Title); err != nil { j.Log.Error().Err(err).Msgf("torznab.process: error parsing release") continue } diff --git a/internal/filter/service.go b/internal/filter/service.go index 29ec0f7..af38a1c 100644 --- a/internal/filter/service.go +++ b/internal/filter/service.go @@ -234,7 +234,7 @@ func (s *service) CheckFilter(f domain.Filter, release *domain.Release) (bool, e log.Trace().Msgf("filter.Service.CheckFilter: checking filter: %v %+v", f.Name, f) log.Trace().Msgf("filter.Service.CheckFilter: checking filter: %v for release: %+v", f.Name, release) - rejections, matchedFilter := release.CheckFilter(f) + rejections, matchedFilter := f.CheckFilter(release) if len(rejections) > 0 { log.Trace().Msgf("filter.Service.CheckFilter: (%v) for release: %v rejections: (%v)", f.Name, release.TorrentName, release.RejectionsString()) return false, nil diff --git a/internal/filter/service_test.go b/internal/filter/service_test.go index e63facd..9d45440 100644 --- a/internal/filter/service_test.go +++ b/internal/filter/service_test.go @@ -1,644 +1,38 @@ package filter -//func Test_checkFilterStrings(t *testing.T) { -// type args struct { -// name string -// filterList string -// } -// tests := []struct { -// name string -// args args -// want bool -// }{ -// { -// name: "test_01", -// args: args{ -// name: "The End", -// filterList: "The End, Other movie", -// }, -// want: true, -// }, -// { -// name: "test_02", -// args: args{ -// name: "The Simpsons S12", -// filterList: "The End, Other movie", -// }, -// want: false, -// }, -// { -// name: "test_03", -// args: args{ -// name: "The.Simpsons.S12", -// filterList: "The?Simpsons*, Other movie", -// }, -// want: true, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// got := checkFilterStrings(tt.args.name, tt.args.filterList) -// assert.Equal(t, tt.want, got) -// }) -// } -//} +import "testing" -//func Test_service_checkFilter(t *testing.T) { -// type args struct { -// filter domain.Filter -// announce domain.Announce -// } -// -// svcMock := &service{ -// repo: nil, -// actionRepo: nil, -// indexerSvc: nil, -// } -// -// tests := []struct { -// name string -// args args -// expected bool -// }{ -// { -// name: "freeleech", -// args: args{ -// announce: domain.Announce{ -// Freeleech: true, -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// Freeleech: true, -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "scene", -// args: args{ -// announce: domain.Announce{ -// Scene: true, -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// Scene: true, -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "not_scene", -// args: args{ -// announce: domain.Announce{ -// Scene: false, -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// Scene: true, -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "shows_1", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "That show", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "shows_2", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "That show, The Other show", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "shows_3", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "That?show*, The?Other?show", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "shows_4", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "The Other show", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "shows_5", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "*show*", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "shows_6", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That.Show.S06.1080p.BluRay.DD5.1.x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "*show*", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "shows_7", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That.Show.S06.1080p.BluRay.DD5.1.x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Shows: "That?show*", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_releases_single", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// MatchReleases: "That show", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_releases_single_wildcard", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// MatchReleases: "That show*", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_releases_multiple", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// MatchReleases: "That show*, Other one", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_release_groups", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// ReleaseGroup: "GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// MatchReleaseGroups: "GROUP1", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_release_groups_multiple", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// ReleaseGroup: "GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// MatchReleaseGroups: "GROUP1,GROUP2", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_release_groups_dont_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// ReleaseGroup: "GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// MatchReleaseGroups: "GROUP2", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "except_release_groups", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// ReleaseGroup: "GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterP2P: domain.FilterP2P{ -// ExceptReleaseGroups: "GROUP1", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "match_uploaders", -// args: args{ -// announce: domain.Announce{ -// Uploader: "Uploader1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchUploaders: "Uploader1", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "non_match_uploaders", -// args: args{ -// announce: domain.Announce{ -// Uploader: "Uploader2", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchUploaders: "Uploader1", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "except_uploaders", -// args: args{ -// announce: domain.Announce{ -// Uploader: "Uploader1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// ExceptUploaders: "Uploader1", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "resolutions_1080p", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 1080p BluRay DD5.1 x264-GROUP1", -// Resolution: "1080p", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Resolutions: []string{"1080p"}, -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "resolutions_2160p", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// Resolution: "2160p", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Resolutions: []string{"2160p"}, -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "resolutions_no_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// Resolution: "2160p", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Resolutions: []string{"1080p"}, -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "codecs_1_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Codecs: []string{"x264"}, -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "codecs_2_no_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Codecs: []string{"h264"}, -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "sources_1_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Sources: []string{"BluRay"}, -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "sources_2_no_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Sources: []string{"WEB"}, -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "years_1", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Years: "2020", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "years_2", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Years: "2020,1990", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "years_3_no_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Years: "1990", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "years_4_no_match", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Show S06 2160p BluRay DD5.1 x264-GROUP1", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterTVMovies: domain.FilterTVMovies{ -// Years: "2020", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "match_categories_1", -// args: args{ -// announce: domain.Announce{ -// Category: "TV", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchCategories: "TV", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_categories_2", -// args: args{ -// announce: domain.Announce{ -// Category: "TV :: HD", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchCategories: "*TV*", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_categories_3", -// args: args{ -// announce: domain.Announce{ -// Category: "TV :: HD", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchCategories: "*TV*, *HD*", -// }, -// }, -// }, -// expected: true, -// }, -// { -// name: "match_categories_4_no_match", -// args: args{ -// announce: domain.Announce{ -// Category: "TV :: HD", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchCategories: "Movies", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "except_categories_1", -// args: args{ -// announce: domain.Announce{ -// Category: "Movies", -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// ExceptCategories: "Movies", -// }, -// }, -// }, -// expected: false, -// }, -// { -// name: "match_multiple_fields_1", -// args: args{ -// announce: domain.Announce{ -// TorrentName: "That Movie 2020 2160p BluRay DD5.1 x264-GROUP1", -// Category: "Movies", -// Freeleech: true, -// }, -// filter: domain.Filter{ -// Enabled: true, -// FilterAdvanced: domain.FilterAdvanced{ -// MatchCategories: "Movies", -// }, -// FilterTVMovies: domain.FilterTVMovies{ -// Resolutions: []string{"2160p"}, -// Sources: []string{"BluRay"}, -// Years: "2020", -// }, -// FilterP2P: domain.FilterP2P{ -// MatchReleaseGroups: "GROUP1", -// MatchReleases: "That movie", -// Freeleech: true, -// }, -// }, -// }, -// expected: true, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// got := svcMock.checkFilter(tt.args.filter, tt.args.announce) -// assert.Equal(t, tt.expected, got) -// }) -// } -//} +func Test_checkSizeFilter(t *testing.T) { + type args struct { + minSize string + maxSize string + releaseSize uint64 + } + tests := []struct { + name string + args args + want bool + wantErr bool + }{ + {name: "test_1", args: args{minSize: "1GB", maxSize: "", releaseSize: 100}, want: false, wantErr: false}, + {name: "test_2", args: args{minSize: "1GB", maxSize: "", releaseSize: 2000000000}, want: true, wantErr: false}, + {name: "test_3", args: args{minSize: "1GB", maxSize: "2.2GB", releaseSize: 2000000000}, want: true, wantErr: false}, + {name: "test_4", args: args{minSize: "1GB", maxSize: "2GIB", releaseSize: 2000000000}, want: true, wantErr: false}, + {name: "test_5", args: args{minSize: "1GB", maxSize: "2GB", releaseSize: 2000000010}, want: false, wantErr: false}, + {name: "test_6", args: args{minSize: "1GB", maxSize: "2GB", releaseSize: 2000000000}, want: false, wantErr: false}, + {name: "test_7", args: args{minSize: "", maxSize: "2GB", releaseSize: 2500000000}, want: false, wantErr: false}, + {name: "test_8", args: args{minSize: "", maxSize: "20GB", releaseSize: 2500000000}, want: true, wantErr: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := checkSizeFilter(tt.args.minSize, tt.args.maxSize, tt.args.releaseSize) + if (err != nil) != tt.wantErr { + t.Errorf("checkSizeFilter() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("checkSizeFilter() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/indexer/definitions/filelist.yaml b/internal/indexer/definitions/filelist.yaml index d4c81d2..688dfbd 100644 --- a/internal/indexer/definitions/filelist.yaml +++ b/internal/indexer/definitions/filelist.yaml @@ -50,7 +50,7 @@ parse: vars: - torrentName - freeleech - - internal + - origin - category - torrentSize - baseUrl diff --git a/internal/indexer/definitions/hdb.yaml b/internal/indexer/definitions/hdb.yaml index 5ff6474..d5898c3 100644 --- a/internal/indexer/definitions/hdb.yaml +++ b/internal/indexer/definitions/hdb.yaml @@ -53,12 +53,12 @@ parse: - "New Torrent: PilotsEYE tv: QUITO 2014 1080p Blu-ray AVC DD 2.0 - Type: Documentary (H.264, Blu-ray/HD DVD) - Uploaded by: Anonymous - Size: 23.14 GiB - https://hdbits.org/details.php?id=12345&hit=1" - "New Torrent: Xiao Q 2019 720p BluRay DD-EX 5.1 x264-Anonymous - Type: Movie (H.264, Encode) Internal! - Uploaded by: Anonymous - Size: 4.54 GiB - https://hdbits.org/details.php?id=12345&hit=1" - "New Torrent: The Gentlemen 2019 UHD Blu-ray English TrueHD 7.1 - Type: Audio Track - Uploaded by: Anonymous - Size: 3.19 GiB - https://hdbits.org/details.php?id=519896&hit=1" - pattern: '^New Torrent: (.+) - Type: (.+?) (?:\((.+)\))?\s?(Internal!)?\s?- Uploaded by: (.+) - Size: (.+) - (https://.+?/).+id=(\d+)' + pattern: '^New Torrent: (.+) - Type: (.+?) (?:\((.+)\))?\s?(?:(Internal)!?)?\s?- Uploaded by: (.+) - Size: (.+) - (https://.+?/).+id=(\d+)' vars: - torrentName - category - releaseTags - - internal + - origin - uploader - torrentSize - baseUrl diff --git a/web/package.json b/web/package.json index 4694a93..b416ee7 100644 --- a/web/package.json +++ b/web/package.json @@ -14,7 +14,7 @@ "react-cookie": "^4.1.1", "react-dom": "^17.0.2", "react-hot-toast": "^2.1.1", - "react-multi-select-component": "^4.0.2", + "react-multi-select-component": "4.2.5", "react-query": "^3.18.1", "react-ridge-state": "4.2.2", "react-router-dom": "^5.2.0", @@ -124,4 +124,4 @@ }, "globals": {} } -} \ No newline at end of file +} diff --git a/web/src/components/inputs/select.tsx b/web/src/components/inputs/select.tsx index a60c10f..d2e5e25 100644 --- a/web/src/components/inputs/select.tsx +++ b/web/src/components/inputs/select.tsx @@ -8,10 +8,11 @@ import { classNames, COL_WIDTHS } from "../../utils"; import { SettingsContext } from "../../utils/Context"; interface MultiSelectProps { + name: string; label?: string; options?: [] | any; - name: string; columns?: COL_WIDTHS; + creatable?: boolean; } export const MultiSelect = ({ @@ -19,8 +20,16 @@ export const MultiSelect = ({ label, options, columns, + creatable, }: MultiSelectProps) => { const settingsContext = SettingsContext.useValue(); + + const handleNewField = (value: string) => ({ + value: value.toUpperCase(), + label: value.toUpperCase(), + key: value, + }); + return (
Match only certain release names and/or ignore other release names
-Match only certain groups and/or ignore other groups
-{subtitle}
Match or ignore categories or tags
-Match or ignore uploaders
-Match only freeleech and freeleech percent
-