feat(filters): external webhook retry on status codes (#1206)

* feat: external filter retry status codes

* chore: go mod tidy

* fix(database): migrations

---------

Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
Steven Kreitzer 2023-10-27 10:37:57 -05:00 committed by GitHub
parent 40a1a4c014
commit 2080136669
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 330 additions and 165 deletions

View file

@ -247,6 +247,10 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
"fe.webhook_data",
"fe.webhook_headers",
"fe.webhook_expect_status",
"fe.webhook_retry_status",
"fe.webhook_retry_attempts",
"fe.webhook_retry_delay_seconds",
"fe.webhook_retry_max_jitter_seconds",
).
From("filter f").
LeftJoin("filter_external fe ON f.id = fe.filter_id").
@ -276,8 +280,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
var delay, maxDownloads, logScore sql.NullInt32
// filter external
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData sql.NullString
var extId, extIndex, extWebhookStatus, extExecStatus sql.NullInt32
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString
var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus sql.NullInt32
var extEnabled sql.NullBool
if err := rows.Scan(
@ -354,6 +358,10 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
&extWebhookData,
&extWebhookHeaders,
&extWebhookStatus,
&extWebhookRetryStatus,
&extWebhookRetryAttempts,
&extWebhookDelaySeconds,
&extWebhookRetryJitterSeconds,
); err != nil {
return nil, errors.Wrap(err, "error scanning row")
}
@ -396,19 +404,23 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
if extId.Valid {
external := domain.FilterExternal{
ID: int(extId.Int32),
Name: extName.String,
Index: int(extIndex.Int32),
Type: domain.FilterExternalType(extType.String),
Enabled: extEnabled.Bool,
ExecCmd: extExecCmd.String,
ExecArgs: extExecArgs.String,
ExecExpectStatus: int(extExecStatus.Int32),
WebhookHost: extWebhookHost.String,
WebhookMethod: extWebhookMethod.String,
WebhookData: extWebhookData.String,
WebhookHeaders: extWebhookHeaders.String,
WebhookExpectStatus: int(extWebhookStatus.Int32),
ID: int(extId.Int32),
Name: extName.String,
Index: int(extIndex.Int32),
Type: domain.FilterExternalType(extType.String),
Enabled: extEnabled.Bool,
ExecCmd: extExecCmd.String,
ExecArgs: extExecArgs.String,
ExecExpectStatus: int(extExecStatus.Int32),
WebhookHost: extWebhookHost.String,
WebhookMethod: extWebhookMethod.String,
WebhookData: extWebhookData.String,
WebhookHeaders: extWebhookHeaders.String,
WebhookExpectStatus: int(extWebhookStatus.Int32),
WebhookRetryStatus: extWebhookRetryStatus.String,
WebhookRetryAttempts: int(extWebhookRetryAttempts.Int32),
WebhookRetryDelaySeconds: int(extWebhookDelaySeconds.Int32),
WebhookRetryMaxJitterSeconds: int(extWebhookRetryJitterSeconds.Int32),
}
externalMap[external.ID] = external
}
@ -502,6 +514,10 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
"fe.webhook_data",
"fe.webhook_headers",
"fe.webhook_expect_status",
"fe.webhook_retry_status",
"fe.webhook_retry_attempts",
"fe.webhook_retry_delay_seconds",
"fe.webhook_retry_max_jitter_seconds",
"fe.filter_id",
).
From("filter f").
@ -537,8 +553,8 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
var delay, maxDownloads, logScore sql.NullInt32
// filter external
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData sql.NullString
var extId, extIndex, extWebhookStatus, extExecStatus, extFilterId sql.NullInt32
var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString
var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus, extFilterId sql.NullInt32
var extEnabled sql.NullBool
if err := rows.Scan(
@ -615,6 +631,10 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
&extWebhookData,
&extWebhookHeaders,
&extWebhookStatus,
&extWebhookRetryStatus,
&extWebhookRetryAttempts,
&extWebhookDelaySeconds,
&extWebhookRetryJitterSeconds,
&extFilterId,
); err != nil {
return nil, errors.Wrap(err, "error scanning row")
@ -658,20 +678,24 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
if extId.Valid {
external := domain.FilterExternal{
ID: int(extId.Int32),
Name: extName.String,
Index: int(extIndex.Int32),
Type: domain.FilterExternalType(extType.String),
Enabled: extEnabled.Bool,
ExecCmd: extExecCmd.String,
ExecArgs: extExecArgs.String,
ExecExpectStatus: int(extExecStatus.Int32),
WebhookHost: extWebhookHost.String,
WebhookMethod: extWebhookMethod.String,
WebhookData: extWebhookData.String,
WebhookHeaders: extWebhookHeaders.String,
WebhookExpectStatus: int(extWebhookStatus.Int32),
FilterId: int(extFilterId.Int32),
ID: int(extId.Int32),
Name: extName.String,
Index: int(extIndex.Int32),
Type: domain.FilterExternalType(extType.String),
Enabled: extEnabled.Bool,
ExecCmd: extExecCmd.String,
ExecArgs: extExecArgs.String,
ExecExpectStatus: int(extExecStatus.Int32),
WebhookHost: extWebhookHost.String,
WebhookMethod: extWebhookMethod.String,
WebhookData: extWebhookData.String,
WebhookHeaders: extWebhookHeaders.String,
WebhookExpectStatus: int(extWebhookStatus.Int32),
WebhookRetryStatus: extWebhookRetryStatus.String,
WebhookRetryAttempts: int(extWebhookRetryAttempts.Int32),
WebhookRetryDelaySeconds: int(extWebhookDelaySeconds.Int32),
WebhookRetryMaxJitterSeconds: int(extWebhookRetryJitterSeconds.Int32),
FilterId: int(extFilterId.Int32),
}
externalMap[external.FilterId] = append(externalMap[external.FilterId], external)
}
@ -709,6 +733,10 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
"fe.webhook_data",
"fe.webhook_headers",
"fe.webhook_expect_status",
"fe.webhook_retry_status",
"fe.webhook_retry_attempts",
"fe.webhook_retry_delay_seconds",
"fe.webhook_retry_max_jitter_seconds",
).
From("filter_external fe").
Where(sq.Eq{"fe.filter_id": filterId})
@ -732,8 +760,8 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
var external domain.FilterExternal
// filter external
var extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData sql.NullString
var extWebhookStatus, extExecStatus sql.NullInt32
var extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString
var extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus sql.NullInt32
if err := rows.Scan(
&external.ID,
@ -749,6 +777,10 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
&extWebhookData,
&extWebhookHeaders,
&extWebhookStatus,
&extWebhookRetryStatus,
&extWebhookRetryAttempts,
&extWebhookDelaySeconds,
&extWebhookRetryJitterSeconds,
); err != nil {
return nil, errors.Wrap(err, "error scanning row")
}
@ -762,6 +794,10 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int)
external.WebhookData = extWebhookData.String
external.WebhookHeaders = extWebhookHeaders.String
external.WebhookExpectStatus = int(extWebhookStatus.Int32)
external.WebhookRetryStatus = extWebhookRetryStatus.String
external.WebhookRetryAttempts = int(extWebhookRetryAttempts.Int32)
external.WebhookRetryDelaySeconds = int(extWebhookDelaySeconds.Int32)
external.WebhookRetryMaxJitterSeconds = int(extWebhookRetryJitterSeconds.Int32)
externalFilters = append(externalFilters, external)
}
@ -1182,6 +1218,18 @@ func (r *FilterRepo) UpdatePartial(ctx context.Context, filter domain.FilterUpda
if filter.ExternalWebhookExpectStatus != nil {
q = q.Set("external_webhook_expect_status", filter.ExternalWebhookExpectStatus)
}
if filter.ExternalWebhookRetryStatus != nil {
q = q.Set("external_webhook_retry_status", filter.ExternalWebhookRetryStatus)
}
if filter.ExternalWebhookRetryAttempts != nil {
q = q.Set("external_webhook_retry_attempts", filter.ExternalWebhookRetryAttempts)
}
if filter.ExternalWebhookRetryDelaySeconds != nil {
q = q.Set("external_webhook_retry_delay_seconds", filter.ExternalWebhookRetryDelaySeconds)
}
if filter.ExternalWebhookRetryMaxJitterSeconds != nil {
q = q.Set("external_webhook_retry_max_jitter_seconds", filter.ExternalWebhookRetryMaxJitterSeconds)
}
q = q.Where(sq.Eq{"id": filter.ID})
@ -1462,6 +1510,10 @@ func (r *FilterRepo) StoreFilterExternal(ctx context.Context, filterID int, exte
"webhook_data",
"webhook_headers",
"webhook_expect_status",
"webhook_retry_status",
"webhook_retry_attempts",
"webhook_retry_delay_seconds",
"webhook_retry_max_jitter_seconds",
"filter_id",
)
@ -1479,6 +1531,10 @@ func (r *FilterRepo) StoreFilterExternal(ctx context.Context, filterID int, exte
toNullString(external.WebhookData),
toNullString(external.WebhookHeaders),
toNullInt32(int32(external.WebhookExpectStatus)),
toNullString(external.WebhookRetryStatus),
toNullInt32(int32(external.WebhookRetryAttempts)),
toNullInt32(int32(external.WebhookRetryDelaySeconds)),
toNullInt32(int32(external.WebhookRetryMaxJitterSeconds)),
filterID,
)
}

View file

@ -133,21 +133,25 @@ CREATE TABLE filter
CREATE TABLE filter_external
(
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
idx INTEGER,
type TEXT,
enabled BOOLEAN,
exec_cmd TEXT,
exec_args TEXT,
exec_expect_status INTEGER,
webhook_host TEXT,
webhook_method TEXT,
webhook_data TEXT,
webhook_headers TEXT,
webhook_expect_status INTEGER,
filter_id INTEGER NOT NULL,
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
idx INTEGER,
type TEXT,
enabled BOOLEAN,
exec_cmd TEXT,
exec_args TEXT,
exec_expect_status INTEGER,
webhook_host TEXT,
webhook_method TEXT,
webhook_data TEXT,
webhook_headers TEXT,
webhook_expect_status INTEGER,
webhook_retry_status TEXT,
webhook_retry_attempts INTEGER,
webhook_retry_delay_seconds INTEGER,
webhook_retry_max_jitter_seconds INTEGER,
filter_id INTEGER NOT NULL,
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
);
CREATE TABLE filter_indexer
@ -797,5 +801,17 @@ CREATE INDEX feed_cache_feed_id_key_index
`,
`ALTER TABLE action
ADD COLUMN external_client_id INTEGER;
`,
`ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_status TEXT;
ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_attempts INTEGER;
ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_delay_seconds INTEGER;
ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_max_jitter_seconds INTEGER;
`,
}

View file

@ -133,21 +133,25 @@ CREATE TABLE filter
CREATE TABLE filter_external
(
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
idx INTEGER,
type TEXT,
enabled BOOLEAN,
exec_cmd TEXT,
exec_args TEXT,
exec_expect_status INTEGER,
webhook_host TEXT,
webhook_method TEXT,
webhook_data TEXT,
webhook_headers TEXT,
webhook_expect_status INTEGER,
filter_id INTEGER NOT NULL,
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
idx INTEGER,
type TEXT,
enabled BOOLEAN,
exec_cmd TEXT,
exec_args TEXT,
exec_expect_status INTEGER,
webhook_host TEXT,
webhook_method TEXT,
webhook_data TEXT,
webhook_headers TEXT,
webhook_expect_status INTEGER,
webhook_retry_status TEXT,
webhook_retry_attempts INTEGER,
webhook_retry_delay_seconds INTEGER,
webhook_retry_max_jitter_seconds INTEGER,
filter_id INTEGER NOT NULL,
FOREIGN KEY (filter_id) REFERENCES filter(id) ON DELETE CASCADE
);
CREATE TABLE filter_indexer
@ -1348,5 +1352,17 @@ CREATE INDEX feed_cache_feed_id_key_index
`,
`ALTER TABLE action
ADD COLUMN external_client_id INTEGER;
`,
`ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_status TEXT;
ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_attempts INTEGER;
ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_delay_seconds INTEGER;
ALTER TABLE filter_external
ADD COLUMN external_webhook_retry_max_jitter_seconds INTEGER;
`,
}

View file

@ -138,20 +138,24 @@ type Filter struct {
}
type FilterExternal struct {
ID int `json:"id"`
Name string `json:"name"`
Index int `json:"index"`
Type FilterExternalType `json:"type"`
Enabled bool `json:"enabled"`
ExecCmd string `json:"exec_cmd,omitempty"`
ExecArgs string `json:"exec_args,omitempty"`
ExecExpectStatus int `json:"exec_expect_status,omitempty"`
WebhookHost string `json:"webhook_host,omitempty"`
WebhookMethod string `json:"webhook_method,omitempty"`
WebhookData string `json:"webhook_data,omitempty"`
WebhookHeaders string `json:"webhook_headers,omitempty"`
WebhookExpectStatus int `json:"webhook_expect_status,omitempty"`
FilterId int `json:"-"`
ID int `json:"id"`
Name string `json:"name"`
Index int `json:"index"`
Type FilterExternalType `json:"type"`
Enabled bool `json:"enabled"`
ExecCmd string `json:"exec_cmd,omitempty"`
ExecArgs string `json:"exec_args,omitempty"`
ExecExpectStatus int `json:"exec_expect_status,omitempty"`
WebhookHost string `json:"webhook_host,omitempty"`
WebhookMethod string `json:"webhook_method,omitempty"`
WebhookData string `json:"webhook_data,omitempty"`
WebhookHeaders string `json:"webhook_headers,omitempty"`
WebhookExpectStatus int `json:"webhook_expect_status,omitempty"`
WebhookRetryStatus string `json:"webhook_retry_status,omitempty"`
WebhookRetryAttempts int `json:"webhook_retry_attempts,omitempty"`
WebhookRetryDelaySeconds int `json:"webhook_retry_delay_seconds,omitempty"`
WebhookRetryMaxJitterSeconds int `json:"webhook_retry_max_jitter_seconds,omitempty"`
FilterId int `json:"-"`
}
type FilterExternalType string
@ -162,79 +166,83 @@ const (
)
type FilterUpdate struct {
ID int `json:"id"`
Name *string `json:"name,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
MinSize *string `json:"min_size,omitempty"`
MaxSize *string `json:"max_size,omitempty"`
Delay *int `json:"delay,omitempty"`
Priority *int32 `json:"priority,omitempty"`
MaxDownloads *int `json:"max_downloads,omitempty"`
MaxDownloadsUnit *FilterMaxDownloadsUnit `json:"max_downloads_unit,omitempty"`
MatchReleases *string `json:"match_releases,omitempty"`
ExceptReleases *string `json:"except_releases,omitempty"`
UseRegex *bool `json:"use_regex,omitempty"`
MatchReleaseGroups *string `json:"match_release_groups,omitempty"`
ExceptReleaseGroups *string `json:"except_release_groups,omitempty"`
MatchReleaseTags *string `json:"match_release_tags,omitempty"`
ExceptReleaseTags *string `json:"except_release_tags,omitempty"`
UseRegexReleaseTags *bool `json:"use_regex_release_tags,omitempty"`
MatchDescription *string `json:"match_description,omitempty"`
ExceptDescription *string `json:"except_description,omitempty"`
UseRegexDescription *bool `json:"use_regex_description,omitempty"`
Scene *bool `json:"scene,omitempty"`
Origins *[]string `json:"origins,omitempty"`
ExceptOrigins *[]string `json:"except_origins,omitempty"`
Bonus *[]string `json:"bonus,omitempty"`
Freeleech *bool `json:"freeleech,omitempty"`
FreeleechPercent *string `json:"freeleech_percent,omitempty"`
SmartEpisode *bool `json:"smart_episode,omitempty"`
Shows *string `json:"shows,omitempty"`
Seasons *string `json:"seasons,omitempty"`
Episodes *string `json:"episodes,omitempty"`
Resolutions *[]string `json:"resolutions,omitempty"` // SD, 480i, 480p, 576p, 720p, 810p, 1080i, 1080p.
Codecs *[]string `json:"codecs,omitempty"` // XviD, DivX, x264, h.264 (or h264), mpeg2 (or mpeg-2), VC-1 (or VC1), WMV, Remux, h.264 Remux (or h264 Remux), VC-1 Remux (or VC1 Remux).
Sources *[]string `json:"sources,omitempty"` // DSR, PDTV, HDTV, HR.PDTV, HR.HDTV, DVDRip, DVDScr, BDr, BD5, BD9, BDRip, BRRip, DVDR, MDVDR, HDDVD, HDDVDRip, BluRay, WEB-DL, TVRip, CAM, R5, TELESYNC, TS, TELECINE, TC. TELESYNC and TS are synonyms (you don't need both). Same for TELECINE and TC
Containers *[]string `json:"containers,omitempty"`
MatchHDR *[]string `json:"match_hdr,omitempty"`
ExceptHDR *[]string `json:"except_hdr,omitempty"`
MatchOther *[]string `json:"match_other,omitempty"`
ExceptOther *[]string `json:"except_other,omitempty"`
Years *string `json:"years,omitempty"`
Artists *string `json:"artists,omitempty"`
Albums *string `json:"albums,omitempty"`
MatchReleaseTypes *[]string `json:"match_release_types,omitempty"` // Album,Single,EP
ExceptReleaseTypes *string `json:"except_release_types,omitempty"`
Formats *[]string `json:"formats,omitempty"` // MP3, FLAC, Ogg, AAC, AC3, DTS
Quality *[]string `json:"quality,omitempty"` // 192, 320, APS (VBR), V2 (VBR), V1 (VBR), APX (VBR), V0 (VBR), q8.x (VBR), Lossless, 24bit Lossless, Other
Media *[]string `json:"media,omitempty"` // CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, Other
PerfectFlac *bool `json:"perfect_flac,omitempty"`
Cue *bool `json:"cue,omitempty"`
Log *bool `json:"log,omitempty"`
LogScore *int `json:"log_score,omitempty"`
MatchCategories *string `json:"match_categories,omitempty"`
ExceptCategories *string `json:"except_categories,omitempty"`
MatchUploaders *string `json:"match_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"`
ExceptTags *string `json:"except_tags,omitempty"`
TagsAny *string `json:"tags_any,omitempty"`
ExceptTagsAny *string `json:"except_tags_any,omitempty"`
TagsMatchLogic *string `json:"tags_match_logic,omitempty"`
ExceptTagsMatchLogic *string `json:"except_tags_match_logic,omitempty"`
ExternalScriptEnabled *bool `json:"external_script_enabled,omitempty"`
ExternalScriptCmd *string `json:"external_script_cmd,omitempty"`
ExternalScriptArgs *string `json:"external_script_args,omitempty"`
ExternalScriptExpectStatus *int `json:"external_script_expect_status,omitempty"`
ExternalWebhookEnabled *bool `json:"external_webhook_enabled,omitempty"`
ExternalWebhookHost *string `json:"external_webhook_host,omitempty"`
ExternalWebhookData *string `json:"external_webhook_data,omitempty"`
ExternalWebhookExpectStatus *int `json:"external_webhook_expect_status,omitempty"`
Actions []*Action `json:"actions,omitempty"`
External []FilterExternal `json:"external,omitempty"`
Indexers []Indexer `json:"indexers,omitempty"`
ID int `json:"id"`
Name *string `json:"name,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
MinSize *string `json:"min_size,omitempty"`
MaxSize *string `json:"max_size,omitempty"`
Delay *int `json:"delay,omitempty"`
Priority *int32 `json:"priority,omitempty"`
MaxDownloads *int `json:"max_downloads,omitempty"`
MaxDownloadsUnit *FilterMaxDownloadsUnit `json:"max_downloads_unit,omitempty"`
MatchReleases *string `json:"match_releases,omitempty"`
ExceptReleases *string `json:"except_releases,omitempty"`
UseRegex *bool `json:"use_regex,omitempty"`
MatchReleaseGroups *string `json:"match_release_groups,omitempty"`
ExceptReleaseGroups *string `json:"except_release_groups,omitempty"`
MatchReleaseTags *string `json:"match_release_tags,omitempty"`
ExceptReleaseTags *string `json:"except_release_tags,omitempty"`
UseRegexReleaseTags *bool `json:"use_regex_release_tags,omitempty"`
MatchDescription *string `json:"match_description,omitempty"`
ExceptDescription *string `json:"except_description,omitempty"`
UseRegexDescription *bool `json:"use_regex_description,omitempty"`
Scene *bool `json:"scene,omitempty"`
Origins *[]string `json:"origins,omitempty"`
ExceptOrigins *[]string `json:"except_origins,omitempty"`
Bonus *[]string `json:"bonus,omitempty"`
Freeleech *bool `json:"freeleech,omitempty"`
FreeleechPercent *string `json:"freeleech_percent,omitempty"`
SmartEpisode *bool `json:"smart_episode,omitempty"`
Shows *string `json:"shows,omitempty"`
Seasons *string `json:"seasons,omitempty"`
Episodes *string `json:"episodes,omitempty"`
Resolutions *[]string `json:"resolutions,omitempty"` // SD, 480i, 480p, 576p, 720p, 810p, 1080i, 1080p.
Codecs *[]string `json:"codecs,omitempty"` // XviD, DivX, x264, h.264 (or h264), mpeg2 (or mpeg-2), VC-1 (or VC1), WMV, Remux, h.264 Remux (or h264 Remux), VC-1 Remux (or VC1 Remux).
Sources *[]string `json:"sources,omitempty"` // DSR, PDTV, HDTV, HR.PDTV, HR.HDTV, DVDRip, DVDScr, BDr, BD5, BD9, BDRip, BRRip, DVDR, MDVDR, HDDVD, HDDVDRip, BluRay, WEB-DL, TVRip, CAM, R5, TELESYNC, TS, TELECINE, TC. TELESYNC and TS are synonyms (you don't need both). Same for TELECINE and TC
Containers *[]string `json:"containers,omitempty"`
MatchHDR *[]string `json:"match_hdr,omitempty"`
ExceptHDR *[]string `json:"except_hdr,omitempty"`
MatchOther *[]string `json:"match_other,omitempty"`
ExceptOther *[]string `json:"except_other,omitempty"`
Years *string `json:"years,omitempty"`
Artists *string `json:"artists,omitempty"`
Albums *string `json:"albums,omitempty"`
MatchReleaseTypes *[]string `json:"match_release_types,omitempty"` // Album,Single,EP
ExceptReleaseTypes *string `json:"except_release_types,omitempty"`
Formats *[]string `json:"formats,omitempty"` // MP3, FLAC, Ogg, AAC, AC3, DTS
Quality *[]string `json:"quality,omitempty"` // 192, 320, APS (VBR), V2 (VBR), V1 (VBR), APX (VBR), V0 (VBR), q8.x (VBR), Lossless, 24bit Lossless, Other
Media *[]string `json:"media,omitempty"` // CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, Other
PerfectFlac *bool `json:"perfect_flac,omitempty"`
Cue *bool `json:"cue,omitempty"`
Log *bool `json:"log,omitempty"`
LogScore *int `json:"log_score,omitempty"`
MatchCategories *string `json:"match_categories,omitempty"`
ExceptCategories *string `json:"except_categories,omitempty"`
MatchUploaders *string `json:"match_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"`
ExceptTags *string `json:"except_tags,omitempty"`
TagsAny *string `json:"tags_any,omitempty"`
ExceptTagsAny *string `json:"except_tags_any,omitempty"`
TagsMatchLogic *string `json:"tags_match_logic,omitempty"`
ExceptTagsMatchLogic *string `json:"except_tags_match_logic,omitempty"`
ExternalScriptEnabled *bool `json:"external_script_enabled,omitempty"`
ExternalScriptCmd *string `json:"external_script_cmd,omitempty"`
ExternalScriptArgs *string `json:"external_script_args,omitempty"`
ExternalScriptExpectStatus *int `json:"external_script_expect_status,omitempty"`
ExternalWebhookEnabled *bool `json:"external_webhook_enabled,omitempty"`
ExternalWebhookHost *string `json:"external_webhook_host,omitempty"`
ExternalWebhookData *string `json:"external_webhook_data,omitempty"`
ExternalWebhookExpectStatus *int `json:"external_webhook_expect_status,omitempty"`
ExternalWebhookRetryStatus *string `json:"external_webhook_retry_status,omitempty"`
ExternalWebhookRetryAttempts *int `json:"external_webhook_retry_attempts,omitempty"`
ExternalWebhookRetryDelaySeconds *int `json:"external_webhook_retry_delay_seconds,omitempty"`
ExternalWebhookRetryMaxJitterSeconds *int `json:"external_webhook_retry_max_jitter_seconds,omitempty"`
Actions []*Action `json:"actions,omitempty"`
External []FilterExternal `json:"external,omitempty"`
Indexers []Indexer `json:"indexers,omitempty"`
}
func (f Filter) CheckFilter(r *Release) ([]string, bool) {

View file

@ -13,14 +13,17 @@ import (
"os"
"os/exec"
"sort"
"strconv"
"strings"
"time"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/internal/indexer"
"github.com/autobrr/autobrr/internal/logger"
"github.com/autobrr/autobrr/internal/utils"
"github.com/autobrr/autobrr/pkg/errors"
"github.com/avast/retry-go/v4"
"github.com/dustin/go-humanize"
"github.com/mattn/go-shellwords"
"github.com/rs/zerolog"
@ -730,25 +733,53 @@ func (s *service) webhook(ctx context.Context, external domain.FilterExternal, r
}
}
var opts []retry.Option
if external.WebhookRetryAttempts > 0 {
option := retry.Attempts(uint(external.WebhookRetryAttempts))
opts = append(opts, option)
}
if external.WebhookRetryDelaySeconds > 0 {
option := retry.Delay(time.Duration(external.WebhookRetryDelaySeconds) * time.Second)
opts = append(opts, option)
}
if external.WebhookRetryMaxJitterSeconds > 0 {
option := retry.MaxJitter(time.Duration(external.WebhookRetryMaxJitterSeconds) * time.Second)
opts = append(opts, option)
}
start := time.Now()
res, err := client.Do(req)
if err != nil {
return 0, errors.Wrap(err, "could not make request for webhook")
}
statusCode, err := retry.DoWithData(
func() (int, error) {
res, err := client.Do(req)
if err != nil {
return 0, errors.Wrap(err, "could not make request for webhook")
}
defer res.Body.Close()
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return 0, errors.Wrap(err, "could not read request body")
}
body, err := io.ReadAll(res.Body)
if err != nil {
return 0, errors.Wrap(err, "could not read request body")
}
if len(body) > 0 {
s.log.Debug().Msgf("filter external webhook response status: %d body: %s", res.StatusCode, body)
}
if len(body) > 0 {
s.log.Debug().Msgf("filter external webhook response status: %d body: %s", res.StatusCode, body)
}
if external.WebhookRetryStatus != "" {
retryStatusCodes := strings.Split(strings.ReplaceAll(external.WebhookRetryStatus, " ", ""), ",")
if utils.StrSliceContains(retryStatusCodes, strconv.Itoa(res.StatusCode)) {
return 0, errors.New("retrying webhook request, got status code: %d", res.StatusCode)
}
}
return res.StatusCode, nil
},
opts...)
s.log.Debug().Msgf("successfully ran external webhook filter to: (%s) payload: (%s) finished in %s", external.WebhookHost, dataArgs, time.Since(start))
return res.StatusCode, nil
return statusCode, err
}