feat(filters): support Language filtering (#632)

* feat(filters): add support for language

* feat(filters): add db migrations and repo

* feat(filters): fix failing tests

* feat(filters): fix failing tests
This commit is contained in:
ze0s 2023-01-09 00:33:08 +01:00 committed by GitHub
parent 3c4711efa0
commit 5c402b6d6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 120 additions and 8 deletions

View file

@ -217,6 +217,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
"except_categories", "except_categories",
"match_uploaders", "match_uploaders",
"except_uploaders", "except_uploaders",
"match_language",
"except_language",
"tags", "tags",
"except_tags", "except_tags",
"origins", "origins",
@ -250,7 +252,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac, extScriptEnabled, extWebhookEnabled sql.NullBool var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac, extScriptEnabled, extWebhookEnabled sql.NullBool
var delay, maxDownloads, logScore, extWebhookStatus, extScriptStatus sql.NullInt32 var delay, maxDownloads, logScore, extWebhookStatus, extScriptStatus sql.NullInt32
if err := row.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &maxDownloads, &maxDownloadsUnit, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &matchReleaseTags, &exceptReleaseTags, &f.UseRegexReleaseTags, &scene, &freeleech, &freeleechPercent, &f.SmartEpisode, &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), pq.Array(&f.ExceptOrigins), &extScriptEnabled, &extScriptCmd, &extScriptArgs, &extScriptStatus, &extWebhookEnabled, &extWebhookHost, &extWebhookData, &extWebhookStatus, &f.CreatedAt, &f.UpdatedAt); err != nil { if err := row.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &maxDownloads, &maxDownloadsUnit, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &matchReleaseTags, &exceptReleaseTags, &f.UseRegexReleaseTags, &scene, &freeleech, &freeleechPercent, &f.SmartEpisode, &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, pq.Array(&f.MatchLanguage), pq.Array(&f.ExceptLanguage), &tags, &exceptTags, pq.Array(&f.Origins), pq.Array(&f.ExceptOrigins), &extScriptEnabled, &extScriptCmd, &extScriptArgs, &extScriptStatus, &extWebhookEnabled, &extWebhookHost, &extWebhookData, &extWebhookStatus, &f.CreatedAt, &f.UpdatedAt); err != nil {
return nil, errors.Wrap(err, "error scanning row") return nil, errors.Wrap(err, "error scanning row")
} }
@ -378,6 +380,8 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, tx *Tx, indexe
"f.except_categories", "f.except_categories",
"f.match_uploaders", "f.match_uploaders",
"f.except_uploaders", "f.except_uploaders",
"f.match_language",
"f.except_language",
"f.tags", "f.tags",
"f.except_tags", "f.except_tags",
"f.origins", "f.origins",
@ -421,7 +425,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, tx *Tx, indexe
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac, extScriptEnabled, extWebhookEnabled sql.NullBool var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac, extScriptEnabled, extWebhookEnabled sql.NullBool
var delay, maxDownloads, logScore, extWebhookStatus, extScriptStatus sql.NullInt32 var delay, maxDownloads, logScore, extWebhookStatus, extScriptStatus sql.NullInt32
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &maxDownloads, &maxDownloadsUnit, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &matchReleaseTags, &exceptReleaseTags, &f.UseRegexReleaseTags, &scene, &freeleech, &freeleechPercent, &f.SmartEpisode, &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), pq.Array(&f.ExceptOrigins), &extScriptEnabled, &extScriptCmd, &extScriptArgs, &extScriptStatus, &extWebhookEnabled, &extWebhookHost, &extWebhookData, &extWebhookStatus, &f.CreatedAt, &f.UpdatedAt); err != nil { if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &f.Priority, &maxDownloads, &maxDownloadsUnit, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &matchReleaseTags, &exceptReleaseTags, &f.UseRegexReleaseTags, &scene, &freeleech, &freeleechPercent, &f.SmartEpisode, &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, pq.Array(&f.MatchLanguage), pq.Array(&f.ExceptLanguage), &tags, &exceptTags, pq.Array(&f.Origins), pq.Array(&f.ExceptOrigins), &extScriptEnabled, &extScriptCmd, &extScriptArgs, &extScriptStatus, &extWebhookEnabled, &extWebhookHost, &extWebhookData, &extWebhookStatus, &f.CreatedAt, &f.UpdatedAt); err != nil {
return nil, errors.Wrap(err, "error scanning row") return nil, errors.Wrap(err, "error scanning row")
} }
@ -513,6 +517,8 @@ func (r *FilterRepo) Store(ctx context.Context, filter domain.Filter) (*domain.F
"except_categories", "except_categories",
"match_uploaders", "match_uploaders",
"except_uploaders", "except_uploaders",
"match_language",
"except_language",
"tags", "tags",
"except_tags", "except_tags",
"artists", "artists",
@ -573,6 +579,8 @@ func (r *FilterRepo) Store(ctx context.Context, filter domain.Filter) (*domain.F
filter.ExceptCategories, filter.ExceptCategories,
filter.MatchUploaders, filter.MatchUploaders,
filter.ExceptUploaders, filter.ExceptUploaders,
pq.Array(filter.MatchLanguage),
pq.Array(filter.ExceptLanguage),
filter.Tags, filter.Tags,
filter.ExceptTags, filter.ExceptTags,
filter.Artists, filter.Artists,
@ -652,6 +660,8 @@ func (r *FilterRepo) Update(ctx context.Context, filter domain.Filter) (*domain.
Set("except_categories", filter.ExceptCategories). Set("except_categories", filter.ExceptCategories).
Set("match_uploaders", filter.MatchUploaders). Set("match_uploaders", filter.MatchUploaders).
Set("except_uploaders", filter.ExceptUploaders). Set("except_uploaders", filter.ExceptUploaders).
Set("match_language", pq.Array(filter.MatchLanguage)).
Set("except_language", pq.Array(filter.ExceptLanguage)).
Set("tags", filter.Tags). Set("tags", filter.Tags).
Set("except_tags", filter.ExceptTags). Set("except_tags", filter.ExceptTags).
Set("artists", filter.Artists). Set("artists", filter.Artists).
@ -803,6 +813,12 @@ func (r *FilterRepo) UpdatePartial(ctx context.Context, filter domain.FilterUpda
if filter.ExceptUploaders != nil { if filter.ExceptUploaders != nil {
q = q.Set("except_uploaders", filter.ExceptUploaders) q = q.Set("except_uploaders", filter.ExceptUploaders)
} }
if filter.MatchLanguage != nil {
q = q.Set("match_language", pq.Array(filter.MatchLanguage))
}
if filter.ExceptLanguage != nil {
q = q.Set("except_language", pq.Array(filter.ExceptLanguage))
}
if filter.Tags != nil { if filter.Tags != nil {
q = q.Set("tags", filter.Tags) q = q.Set("tags", filter.Tags)
} }

View file

@ -111,6 +111,8 @@ CREATE TABLE filter
except_categories TEXT, except_categories TEXT,
match_uploaders TEXT, match_uploaders TEXT,
except_uploaders TEXT, except_uploaders TEXT,
match_language TEXT [] DEFAULT '{}',
except_language TEXT [] DEFAULT '{}',
tags TEXT, tags TEXT,
except_tags TEXT, except_tags TEXT,
origins TEXT [] DEFAULT '{}', origins TEXT [] DEFAULT '{}',
@ -623,4 +625,10 @@ CREATE INDEX indexer_identifier_index
`ALTER TABLE "filter" `ALTER TABLE "filter"
ADD COLUMN smart_episode BOOLEAN DEFAULT false; ADD COLUMN smart_episode BOOLEAN DEFAULT false;
`, `,
`ALTER TABLE "filter"
ADD COLUMN match_language TEXT [] DEFAULT '{}';
ALTER TABLE "filter"
ADD COLUMN except_language TEXT [] DEFAULT '{}';
`,
} }

View file

@ -111,6 +111,8 @@ CREATE TABLE filter
except_categories TEXT, except_categories TEXT,
match_uploaders TEXT, match_uploaders TEXT,
except_uploaders TEXT, except_uploaders TEXT,
match_language TEXT [] DEFAULT '{}',
except_language TEXT [] DEFAULT '{}',
tags TEXT, tags TEXT,
except_tags TEXT, except_tags TEXT,
origins TEXT [] DEFAULT '{}', origins TEXT [] DEFAULT '{}',
@ -967,4 +969,10 @@ ALTER TABLE irc_network_dg_tmp
`ALTER TABLE "filter" `ALTER TABLE "filter"
ADD COLUMN smart_episode BOOLEAN DEFAULT false; ADD COLUMN smart_episode BOOLEAN DEFAULT false;
`, `,
`ALTER TABLE "filter"
ADD COLUMN match_language TEXT [] DEFAULT '{}';
ALTER TABLE "filter"
ADD COLUMN except_language TEXT [] DEFAULT '{}';
`,
} }

View file

@ -109,6 +109,8 @@ type Filter struct {
ExceptCategories string `json:"except_categories,omitempty"` ExceptCategories string `json:"except_categories,omitempty"`
MatchUploaders string `json:"match_uploaders,omitempty"` MatchUploaders string `json:"match_uploaders,omitempty"`
ExceptUploaders string `json:"except_uploaders,omitempty"` ExceptUploaders string `json:"except_uploaders,omitempty"`
MatchLanguage []string `json:"match_language,omitempty"`
ExceptLanguage []string `json:"except_language,omitempty"`
Tags string `json:"tags,omitempty"` Tags string `json:"tags,omitempty"`
ExceptTags string `json:"except_tags,omitempty"` ExceptTags string `json:"except_tags,omitempty"`
TagsAny string `json:"tags_any,omitempty"` TagsAny string `json:"tags_any,omitempty"`
@ -182,6 +184,8 @@ type FilterUpdate struct {
ExceptCategories *string `json:"except_categories,omitempty"` ExceptCategories *string `json:"except_categories,omitempty"`
MatchUploaders *string `json:"match_uploaders,omitempty"` MatchUploaders *string `json:"match_uploaders,omitempty"`
ExceptUploaders *string `json:"except_uploaders,omitempty"` ExceptUploaders *string `json:"except_uploaders,omitempty"`
MatchLanguage *[]string `json:"match_language,omitempty"`
ExceptLanguage *[]string `json:"except_language,omitempty"`
Tags *string `json:"tags,omitempty"` Tags *string `json:"tags,omitempty"`
ExceptTags *string `json:"except_tags,omitempty"` ExceptTags *string `json:"except_tags,omitempty"`
TagsAny *string `json:"tags_any,omitempty"` TagsAny *string `json:"tags_any,omitempty"`
@ -297,6 +301,14 @@ func (f Filter) CheckFilter(r *Release) ([]string, bool) {
r.addRejectionF("unwanted uploaders. got: %v unwanted: %v", r.Uploader, f.ExceptUploaders) r.addRejectionF("unwanted uploaders. got: %v unwanted: %v", r.Uploader, f.ExceptUploaders)
} }
if len(f.MatchLanguage) > 0 && !sliceContainsSlice(r.Language, f.MatchLanguage) {
r.addRejectionF("language not matching. got: %v want: %v", r.Language, f.MatchLanguage)
}
if len(f.ExceptLanguage) > 0 && sliceContainsSlice(r.Language, f.ExceptLanguage) {
r.addRejectionF("language unwanted. got: %v want: %v", r.Language, f.ExceptLanguage)
}
if len(f.Resolutions) > 0 && !containsSlice(r.Resolution, f.Resolutions) { if len(f.Resolutions) > 0 && !containsSlice(r.Resolution, f.Resolutions) {
r.addRejectionF("resolution not matching. got: %v want: %v", r.Resolution, f.Resolutions) r.addRejectionF("resolution not matching. got: %v want: %v", r.Resolution, f.Resolutions)
} }

View file

@ -67,7 +67,7 @@ type Release struct {
AudioChannels string `json:"-"` AudioChannels string `json:"-"`
Group string `json:"group"` Group string `json:"group"`
Region string `json:"-"` Region string `json:"-"`
Language string `json:"-"` Language []string `json:"-"`
Proper bool `json:"proper"` Proper bool `json:"proper"`
Repack bool `json:"repack"` Repack bool `json:"repack"`
Website string `json:"website"` Website string `json:"website"`
@ -203,6 +203,7 @@ func (r *Release) ParseString(title string) {
r.HDR = rel.HDR r.HDR = rel.HDR
r.Other = rel.Other r.Other = rel.Other
r.Artists = rel.Artist r.Artists = rel.Artist
r.Language = rel.Language
if r.Season == 0 { if r.Season == 0 {
r.Season = rel.Series r.Season = rel.Series

View file

@ -260,6 +260,7 @@ func TestRelease_Parse(t *testing.T) {
Year: 2022, Year: 2022,
Group: "GROUP1", Group: "GROUP1",
Season: 1, Season: 1,
Language: []string{"ENGLiSH"},
}, },
}, },
} }
@ -733,7 +734,7 @@ func TestRelease_DownloadTorrentFile(t *testing.T) {
AudioChannels string AudioChannels string
Group string Group string
Region string Region string
Language string Language []string
Proper bool Proper bool
Repack bool Repack bool
Website string Website string

View file

@ -64,7 +64,7 @@ func TestRSSJob_processItem(t *testing.T) {
Link: "/details.php?id=00000&hit=1", Link: "/details.php?id=00000&hit=1",
GUID: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", GUID: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP",
}}, }},
want: &domain.Release{ID: 0, FilterStatus: "PENDING", Rejections: []string{}, Indexer: "mock-feed", FilterName: "", Protocol: "torrent", Implementation: "RSS", Timestamp: now, GroupID: "", TorrentID: "", TorrentURL: "https://fake-feed.com/details.php?id=00000&hit=1", TorrentTmpFile: "", TorrentDataRawBytes: []uint8(nil), TorrentHash: "", TorrentName: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", Size: 0x0, Title: "Some Release Title", Category: "", Season: 0, Episode: 0, Year: 2022, Resolution: "720p", Source: "WEB", Codec: []string{"H.264"}, Container: "", HDR: []string(nil), Audio: []string(nil), AudioChannels: "", Group: "GROUP", Region: "", Language: "", Proper: false, Repack: false, Website: "", Artists: "", Type: "", LogScore: 0, IsScene: false, Origin: "", Tags: []string{}, ReleaseTags: "", Freeleech: false, FreeleechPercent: 0, Bonus: []string(nil), Uploader: "", PreTime: "", Other: []string(nil), RawCookie: "", AdditionalSizeCheckRequired: false, FilterID: 0, Filter: (*domain.Filter)(nil), ActionStatus: []domain.ReleaseActionStatus(nil)}, want: &domain.Release{ID: 0, FilterStatus: "PENDING", Rejections: []string{}, Indexer: "mock-feed", FilterName: "", Protocol: "torrent", Implementation: "RSS", Timestamp: now, GroupID: "", TorrentID: "", TorrentURL: "https://fake-feed.com/details.php?id=00000&hit=1", TorrentTmpFile: "", TorrentDataRawBytes: []uint8(nil), TorrentHash: "", TorrentName: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", Size: 0x0, Title: "Some Release Title", Category: "", Season: 0, Episode: 0, Year: 2022, Resolution: "720p", Source: "WEB", Codec: []string{"H.264"}, Container: "", HDR: []string(nil), Audio: []string(nil), AudioChannels: "", Group: "GROUP", Region: "", Language: nil, Proper: false, Repack: false, Website: "", Artists: "", Type: "", LogScore: 0, IsScene: false, Origin: "", Tags: []string{}, ReleaseTags: "", Freeleech: false, FreeleechPercent: 0, Bonus: []string(nil), Uploader: "", PreTime: "", Other: []string(nil), RawCookie: "", AdditionalSizeCheckRequired: false, FilterID: 0, Filter: (*domain.Filter)(nil), ActionStatus: []domain.ReleaseActionStatus(nil)},
}, },
{ {
name: "with_baseurl", name: "with_baseurl",
@ -93,7 +93,7 @@ func TestRSSJob_processItem(t *testing.T) {
Link: "https://fake-feed.com/details.php?id=00000&hit=1", Link: "https://fake-feed.com/details.php?id=00000&hit=1",
GUID: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", GUID: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP",
}}, }},
want: &domain.Release{ID: 0, FilterStatus: "PENDING", Rejections: []string{}, Indexer: "mock-feed", FilterName: "", Protocol: "torrent", Implementation: "RSS", Timestamp: now, GroupID: "", TorrentID: "", TorrentURL: "https://fake-feed.com/details.php?id=00000&hit=1", TorrentTmpFile: "", TorrentDataRawBytes: []uint8(nil), TorrentHash: "", TorrentName: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", Size: 0x0, Title: "Some Release Title", Category: "", Season: 0, Episode: 0, Year: 2022, Resolution: "720p", Source: "WEB", Codec: []string{"H.264"}, Container: "", HDR: []string(nil), Audio: []string(nil), AudioChannels: "", Group: "GROUP", Region: "", Language: "", Proper: false, Repack: false, Website: "", Artists: "", Type: "", LogScore: 0, IsScene: false, Origin: "", Tags: []string{}, ReleaseTags: "", Freeleech: false, FreeleechPercent: 0, Bonus: []string(nil), Uploader: "", PreTime: "", Other: []string(nil), RawCookie: "", AdditionalSizeCheckRequired: false, FilterID: 0, Filter: (*domain.Filter)(nil), ActionStatus: []domain.ReleaseActionStatus(nil)}, want: &domain.Release{ID: 0, FilterStatus: "PENDING", Rejections: []string{}, Indexer: "mock-feed", FilterName: "", Protocol: "torrent", Implementation: "RSS", Timestamp: now, GroupID: "", TorrentID: "", TorrentURL: "https://fake-feed.com/details.php?id=00000&hit=1", TorrentTmpFile: "", TorrentDataRawBytes: []uint8(nil), TorrentHash: "", TorrentName: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", Size: 0x0, Title: "Some Release Title", Category: "", Season: 0, Episode: 0, Year: 2022, Resolution: "720p", Source: "WEB", Codec: []string{"H.264"}, Container: "", HDR: []string(nil), Audio: []string(nil), AudioChannels: "", Group: "GROUP", Region: "", Language: nil, Proper: false, Repack: false, Website: "", Artists: "", Type: "", LogScore: 0, IsScene: false, Origin: "", Tags: []string{}, ReleaseTags: "", Freeleech: false, FreeleechPercent: 0, Bonus: []string(nil), Uploader: "", PreTime: "", Other: []string(nil), RawCookie: "", AdditionalSizeCheckRequired: false, FilterID: 0, Filter: (*domain.Filter)(nil), ActionStatus: []domain.ReleaseActionStatus(nil)},
}, },
{ {
name: "time_parse", name: "time_parse",
@ -123,7 +123,7 @@ func TestRSSJob_processItem(t *testing.T) {
GUID: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", GUID: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP",
//PublishedParsed: &nowMinusTime, //PublishedParsed: &nowMinusTime,
}}, }},
want: &domain.Release{ID: 0, FilterStatus: "PENDING", Rejections: []string{}, Indexer: "mock-feed", FilterName: "", Protocol: "torrent", Implementation: "RSS", Timestamp: now, GroupID: "", TorrentID: "", TorrentURL: "https://fake-feed.com/details.php?id=00000&hit=1", TorrentTmpFile: "", TorrentDataRawBytes: []uint8(nil), TorrentHash: "", TorrentName: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", Size: 0x0, Title: "Some Release Title", Category: "", Season: 0, Episode: 0, Year: 2022, Resolution: "720p", Source: "WEB", Codec: []string{"H.264"}, Container: "", HDR: []string(nil), Audio: []string(nil), AudioChannels: "", Group: "GROUP", Region: "", Language: "", Proper: false, Repack: false, Website: "", Artists: "", Type: "", LogScore: 0, IsScene: false, Origin: "", Tags: []string{}, ReleaseTags: "", Freeleech: false, FreeleechPercent: 0, Bonus: []string(nil), Uploader: "", PreTime: "", Other: []string(nil), RawCookie: "", AdditionalSizeCheckRequired: false, FilterID: 0, Filter: (*domain.Filter)(nil), ActionStatus: []domain.ReleaseActionStatus(nil)}, want: &domain.Release{ID: 0, FilterStatus: "PENDING", Rejections: []string{}, Indexer: "mock-feed", FilterName: "", Protocol: "torrent", Implementation: "RSS", Timestamp: now, GroupID: "", TorrentID: "", TorrentURL: "https://fake-feed.com/details.php?id=00000&hit=1", TorrentTmpFile: "", TorrentDataRawBytes: []uint8(nil), TorrentHash: "", TorrentName: "Some.Release.Title.2022.09.22.720p.WEB.h264-GROUP", Size: 0x0, Title: "Some Release Title", Category: "", Season: 0, Episode: 0, Year: 2022, Resolution: "720p", Source: "WEB", Codec: []string{"H.264"}, Container: "", HDR: []string(nil), Audio: []string(nil), AudioChannels: "", Group: "GROUP", Region: "", Language: nil, Proper: false, Repack: false, Website: "", Artists: "", Type: "", LogScore: 0, IsScene: false, Origin: "", Tags: []string{}, ReleaseTags: "", Freeleech: false, FreeleechPercent: 0, Bonus: []string(nil), Uploader: "", PreTime: "", Other: []string(nil), RawCookie: "", AdditionalSizeCheckRequired: false, FilterID: 0, Filter: (*domain.Filter)(nil), ActionStatus: []domain.ReleaseActionStatus(nil)},
}, },
{ {
name: "time_parse", name: "time_parse",

View file

@ -1,6 +1,7 @@
package torznab package torznab
import ( import (
"context"
"encoding/xml" "encoding/xml"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -232,7 +233,7 @@ func TestClient_GetCaps(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := NewClient(Config{Host: tt.fields.Host, ApiKey: tt.fields.ApiKey}) c := NewClient(Config{Host: tt.fields.Host, ApiKey: tt.fields.ApiKey})
got, err := c.FetchCaps() got, err := c.FetchCaps(context.TODO())
if tt.wantErr && assert.Error(t, err) { if tt.wantErr && assert.Error(t, err) {
assert.EqualErrorf(t, err, tt.expectedErr, "Error should be: %v, got: %v", tt.wantErr, err) assert.EqualErrorf(t, err, tt.expectedErr, "Error should be: %v, got: %v", tt.wantErr, err)
} }

View file

@ -154,6 +154,61 @@ export const originOptions = [
export const ORIGIN_OPTIONS = originOptions.map(v => ({ value: v, label: v, key: v })); export const ORIGIN_OPTIONS = originOptions.map(v => ({ value: v, label: v, key: v }));
export const languageOptions = [
"BALTIC",
"BRAZiLiAN",
"BULGARiAN",
"CHiNESE",
"CHS",
"CHT",
"CZECH",
"DANiSH",
"DUBBED",
"DKSUBS",
"DUTCH",
"ENGLiSH",
"ESTONiAN",
"FLEMiSH",
"FiNNiSH",
"FRENCH",
"GERMAN",
"GREEK",
"HAiTiAN",
"HARDSUB",
"Hardcoded",
"HEBREW",
"HebSub",
"HiNDi",
"HUNGARiAN",
"iCELANDiC",
"iTALiAN",
"JAPANESE",
"KOREAN",
"LATiN",
"MANDARiN",
"MULTi",
"MULTILANG",
"MULTiSUB",
"MULTiSUBS",
"NORDiC",
"NORWEGiAN",
"POLiSH",
"PORTUGUESE",
"ROMANiAN",
"RUSSiAN",
"SPANiSH",
"SUBBED",
"SUBFORCED",
"SUBPACK",
"SWEDiSH",
"SYNCED",
"TURKiSH",
"UKRAiNiAN",
"UNSUBBED"
];
export const LANGUAGE_OPTIONS = languageOptions.map(v => ({ value: v, label: v, key: v }));
export interface RadioFieldsetOption { export interface RadioFieldsetOption {
label: string; label: string;
description: string; description: string;

View file

@ -11,6 +11,7 @@ import {
downloadsPerUnitOptions, downloadsPerUnitOptions,
FORMATS_OPTIONS, FORMATS_OPTIONS,
HDR_OPTIONS, HDR_OPTIONS,
LANGUAGE_OPTIONS,
ORIGIN_OPTIONS, ORIGIN_OPTIONS,
OTHER_OPTIONS, OTHER_OPTIONS,
QUALITY_MUSIC_OPTIONS, QUALITY_MUSIC_OPTIONS,
@ -265,6 +266,8 @@ export default function FilterDetails() {
except_tags: filter.except_tags, except_tags: filter.except_tags,
match_uploaders: filter.match_uploaders, match_uploaders: filter.match_uploaders,
except_uploaders: filter.except_uploaders, except_uploaders: filter.except_uploaders,
match_language: filter.match_language || [],
except_language: filter.except_language || [],
freeleech: filter.freeleech, freeleech: filter.freeleech,
freeleech_percent: filter.freeleech_percent, freeleech_percent: filter.freeleech_percent,
formats: filter.formats || [], formats: filter.formats || [],
@ -516,6 +519,11 @@ export function Advanced({ values }: AdvancedProps) {
<TextField name="except_uploaders" label="Except uploaders" columns={6} placeholder="eg. anonymous" /> <TextField name="except_uploaders" label="Except uploaders" columns={6} placeholder="eg. anonymous" />
</CollapsableSection> </CollapsableSection>
<CollapsableSection defaultOpen={true} title="Language" subtitle="Match or ignore languages.">
<MultiSelect name="match_language" options={LANGUAGE_OPTIONS} label="Match Language" columns={6} creatable={true} />
<MultiSelect name="except_language" options={LANGUAGE_OPTIONS} label="Except Language" columns={6} creatable={true} />
</CollapsableSection>
<CollapsableSection defaultOpen={true} title="Origins" subtitle="Match Internals, scene, p2p etc. if announced."> <CollapsableSection defaultOpen={true} title="Origins" subtitle="Match Internals, scene, p2p etc. if announced.">
<MultiSelect name="origins" options={ORIGIN_OPTIONS} label="Match Origins" columns={6} creatable={true} /> <MultiSelect name="origins" options={ORIGIN_OPTIONS} label="Match Origins" columns={6} creatable={true} />
<MultiSelect name="except_origins" options={ORIGIN_OPTIONS} label="Except Origins" columns={6} creatable={true} /> <MultiSelect name="except_origins" options={ORIGIN_OPTIONS} label="Except Origins" columns={6} creatable={true} />

View file

@ -51,6 +51,8 @@ interface Filter {
except_categories: string; except_categories: string;
match_uploaders: string; match_uploaders: string;
except_uploaders: string; except_uploaders: string;
match_language: string[];
except_language: string[];
tags: string; tags: string;
except_tags: string; except_tags: string;
tags_any: string; tags_any: string;