mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 00:39:13 +00:00
feat(filters): show enabled and disabled actions in list view (#1304)
* feat(filters): reflect enabled actions * dont store release unless enabled action found * store the release after the delay * add new parameter to FindByFilterID method
This commit is contained in:
parent
95cd053db5
commit
6e12654f6a
11 changed files with 59 additions and 45 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -38,6 +38,7 @@ bin/
|
|||
dist/
|
||||
.run/
|
||||
tmp/
|
||||
.golangci.yml
|
||||
|
||||
# Preserve files
|
||||
!.gitkeep
|
||||
|
|
|
@ -20,7 +20,7 @@ type Service interface {
|
|||
Store(ctx context.Context, action domain.Action) (*domain.Action, error)
|
||||
List(ctx context.Context) ([]domain.Action, error)
|
||||
Get(ctx context.Context, req *domain.GetActionRequest) (*domain.Action, error)
|
||||
FindByFilterID(ctx context.Context, filterID int) ([]*domain.Action, error)
|
||||
FindByFilterID(ctx context.Context, filterID int, active *bool) ([]*domain.Action, error)
|
||||
Delete(ctx context.Context, req *domain.DeleteActionRequest) error
|
||||
DeleteByFilterID(ctx context.Context, filterID int) error
|
||||
ToggleEnabled(actionID int) error
|
||||
|
@ -76,8 +76,8 @@ func (s *service) Get(ctx context.Context, req *domain.GetActionRequest) (*domai
|
|||
return a, nil
|
||||
}
|
||||
|
||||
func (s *service) FindByFilterID(ctx context.Context, filterID int) ([]*domain.Action, error) {
|
||||
return s.repo.FindByFilterID(ctx, filterID)
|
||||
func (s *service) FindByFilterID(ctx context.Context, filterID int, active *bool) ([]*domain.Action, error) {
|
||||
return s.repo.FindByFilterID(ctx, filterID, active)
|
||||
}
|
||||
|
||||
func (s *service) Delete(ctx context.Context, req *domain.DeleteActionRequest) error {
|
||||
|
|
|
@ -30,7 +30,7 @@ func NewActionRepo(log logger.Logger, db *DB, clientRepo domain.DownloadClientRe
|
|||
}
|
||||
}
|
||||
|
||||
func (r *ActionRepo) FindByFilterID(ctx context.Context, filterID int) ([]*domain.Action, error) {
|
||||
func (r *ActionRepo) FindByFilterID(ctx context.Context, filterID int, active *bool) ([]*domain.Action, error) {
|
||||
tx, err := r.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -38,7 +38,7 @@ func (r *ActionRepo) FindByFilterID(ctx context.Context, filterID int) ([]*domai
|
|||
|
||||
defer tx.Rollback()
|
||||
|
||||
actions, err := r.findByFilterID(ctx, tx, filterID)
|
||||
actions, err := r.findByFilterID(ctx, tx, filterID, active)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func (r *ActionRepo) FindByFilterID(ctx context.Context, filterID int) ([]*domai
|
|||
return actions, nil
|
||||
}
|
||||
|
||||
func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) ([]*domain.Action, error) {
|
||||
func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int, active *bool) ([]*domain.Action, error) {
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select(
|
||||
"id",
|
||||
|
@ -95,6 +95,10 @@ func (r *ActionRepo) findByFilterID(ctx context.Context, tx *Tx, filterID int) (
|
|||
From("action").
|
||||
Where(sq.Eq{"filter_id": filterID})
|
||||
|
||||
if active != nil {
|
||||
queryBuilder = queryBuilder.Where(sq.Eq{"enabled": *active})
|
||||
}
|
||||
|
||||
query, args, err := queryBuilder.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error building query")
|
||||
|
|
|
@ -214,7 +214,7 @@ func TestActionRepo_FindByFilterID(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Actual test for FindByFilterID
|
||||
actions, err := repo.FindByFilterID(context.Background(), createdFilters[0].ID)
|
||||
actions, err := repo.FindByFilterID(context.Background(), createdFilters[0].ID, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, actions)
|
||||
assert.Equal(t, 1, len(actions))
|
||||
|
@ -235,7 +235,7 @@ func TestActionRepo_FindByFilterID(t *testing.T) {
|
|||
assert.NotNil(t, createdFilters)
|
||||
|
||||
// Actual test for FindByFilterID
|
||||
actions, err := repo.FindByFilterID(context.Background(), createdFilters[0].ID)
|
||||
actions, err := repo.FindByFilterID(context.Background(), createdFilters[0].ID, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(actions))
|
||||
|
||||
|
@ -244,7 +244,7 @@ func TestActionRepo_FindByFilterID(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run(fmt.Sprintf("FindByFilterID_Succeeds_With_Invalid_FilterID [%s]", dbType), func(t *testing.T) {
|
||||
actions, err := repo.FindByFilterID(context.Background(), 9999) // 9999 is an invalid filter ID
|
||||
actions, err := repo.FindByFilterID(context.Background(), 9999, nil) // 9999 is an invalid filter ID
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, actions)
|
||||
assert.Equal(t, 0, len(actions))
|
||||
|
@ -254,7 +254,7 @@ func TestActionRepo_FindByFilterID(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
actions, err := repo.FindByFilterID(ctx, 1)
|
||||
actions, err := repo.FindByFilterID(ctx, 1, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, actions)
|
||||
})
|
||||
|
|
|
@ -57,6 +57,12 @@ func (r *FilterRepo) find(ctx context.Context, tx *Tx, params domain.FilterQuery
|
|||
From("action a").
|
||||
Where("a.filter_id = f.id")
|
||||
|
||||
actionEnabledCountQuery := r.db.squirrel.
|
||||
Select("COUNT(*)").
|
||||
From("action a").
|
||||
Where("a.filter_id = f.id").
|
||||
Where("a.enabled = '1'")
|
||||
|
||||
queryBuilder := r.db.squirrel.
|
||||
Select(
|
||||
"f.id",
|
||||
|
@ -68,6 +74,7 @@ func (r *FilterRepo) find(ctx context.Context, tx *Tx, params domain.FilterQuery
|
|||
).
|
||||
Distinct().
|
||||
Column(sq.Alias(actionCountQuery, "action_count")).
|
||||
Column(sq.Alias(actionEnabledCountQuery, "actions_enabled_count")).
|
||||
LeftJoin("filter_indexer fi ON f.id = fi.filter_id").
|
||||
LeftJoin("indexer i ON i.id = fi.indexer_id").
|
||||
From("filter f")
|
||||
|
@ -89,7 +96,6 @@ func (r *FilterRepo) find(ctx context.Context, tx *Tx, params domain.FilterQuery
|
|||
for _, v := range params.Filters.Indexers {
|
||||
filter = append(filter, sq.Eq{"i.identifier": v})
|
||||
}
|
||||
|
||||
queryBuilder = queryBuilder.Where(filter)
|
||||
}
|
||||
|
||||
|
@ -102,14 +108,13 @@ func (r *FilterRepo) find(ctx context.Context, tx *Tx, params domain.FilterQuery
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error executing query")
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
var filters []domain.Filter
|
||||
for rows.Next() {
|
||||
var f domain.Filter
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Priority, &f.CreatedAt, &f.UpdatedAt, &f.ActionsCount); err != nil {
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Priority, &f.CreatedAt, &f.UpdatedAt, &f.ActionsCount, &f.ActionsEnabledCount); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning row")
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
type ActionRepo interface {
|
||||
Store(ctx context.Context, action Action) (*Action, error)
|
||||
StoreFilterActions(ctx context.Context, filterID int64, actions []*Action) ([]*Action, error)
|
||||
FindByFilterID(ctx context.Context, filterID int) ([]*Action, error)
|
||||
FindByFilterID(ctx context.Context, filterID int, active *bool) ([]*Action, error)
|
||||
List(ctx context.Context) ([]Action, error)
|
||||
Get(ctx context.Context, req *GetActionRequest) (*Action, error)
|
||||
Delete(ctx context.Context, req *DeleteActionRequest) error
|
||||
|
|
|
@ -133,6 +133,7 @@ type Filter struct {
|
|||
ExceptDescription string `json:"except_description,omitempty"`
|
||||
UseRegexDescription bool `json:"use_regex_description,omitempty"`
|
||||
ActionsCount int `json:"actions_count"`
|
||||
ActionsEnabledCount int `json:"actions_enabled_count"`
|
||||
Actions []*Action `json:"actions,omitempty"`
|
||||
External []FilterExternal `json:"external,omitempty"`
|
||||
Indexers []Indexer `json:"indexers"`
|
||||
|
|
|
@ -118,7 +118,7 @@ func (s *service) FindByID(ctx context.Context, filterID int) (*domain.Filter, e
|
|||
return nil, err
|
||||
}
|
||||
|
||||
actions, err := s.actionRepo.FindByFilterID(ctx, filter.ID)
|
||||
actions, err := s.actionRepo.FindByFilterID(ctx, filter.ID, nil)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("could not find filter actions for filter id: %v", filter.ID)
|
||||
}
|
||||
|
@ -691,7 +691,7 @@ func (s *service) webhook(ctx context.Context, external domain.FilterExternal, r
|
|||
}
|
||||
|
||||
// add header to req
|
||||
req.Header.Add(http.CanonicalHeaderKey(h[0]), h[1])
|
||||
req.Header.Add(h[0], h[1]) // go already canonicalizes the provided header key.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -162,6 +162,27 @@ func (s *service) processFilters(ctx context.Context, filters []*domain.Filter,
|
|||
|
||||
l.Info().Msgf("Matched '%s' (%s) for %s", release.TorrentName, release.FilterName, release.Indexer)
|
||||
|
||||
// found matching filter, lets find the filter actions and attach
|
||||
active := true
|
||||
actions, err := s.actionSvc.FindByFilterID(ctx, f.ID, &active)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("release.Process: error finding actions for filter: %s", f.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
// if no actions, continue to next filter
|
||||
if len(actions) == 0 {
|
||||
s.log.Warn().Msgf("release.Process: no active actions found for filter '%s', trying next one..", f.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// sleep for the delay period specified in the filter before running actions
|
||||
delay := release.Filter.Delay
|
||||
if delay > 0 {
|
||||
l.Debug().Msgf("release.Process: delaying processing of '%s' (%s) for %s by %d seconds as specified in the filter", release.TorrentName, release.FilterName, release.Indexer, delay)
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
}
|
||||
|
||||
// save release here to only save those with rejections from actions instead of all releases
|
||||
if release.ID == 0 {
|
||||
release.FilterStatus = domain.ReleaseStatusFilterApproved
|
||||
|
@ -172,26 +193,6 @@ func (s *service) processFilters(ctx context.Context, filters []*domain.Filter,
|
|||
}
|
||||
}
|
||||
|
||||
// found matching filter, lets find the filter actions and attach
|
||||
actions, err := s.actionSvc.FindByFilterID(ctx, f.ID)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("release.Process: error finding actions for filter: %s", f.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
// if no actions, continue to next filter
|
||||
if len(actions) == 0 {
|
||||
s.log.Warn().Msgf("release.Process: no actions found for filter '%s', trying next one..", f.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// sleep for the delay period specified in the filter before running actions
|
||||
delay := release.Filter.Delay
|
||||
if delay > 0 {
|
||||
l.Debug().Msgf("release.Process: delaying processing of '%s' (%s) for %s by %d seconds as specified in the filter", release.TorrentName, release.FilterName, release.Indexer, delay)
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
}
|
||||
|
||||
var rejections []string
|
||||
|
||||
// run actions (watchFolder, test, exec, qBittorrent, Deluge, arr etc.)
|
||||
|
|
|
@ -645,19 +645,20 @@ function FilterListItem({ filter, values, idx }: FilterListItemProps) {
|
|||
to={`${filter.id.toString()}/actions`}
|
||||
className="flex items-center cursor-pointer hover:text-black dark:hover:text-gray-300"
|
||||
>
|
||||
<span className={classNames(!filter.actions_count ? "text-red-500 hover:text-red-400 dark:hover:text-red-400" : "")}>
|
||||
Actions: {filter.actions_count}
|
||||
<span className={filter.actions_count === 0 || filter.actions_enabled_count === 0 ? "text-red-500 hover:text-red-400 dark:hover:text-red-400" : ""}>
|
||||
Actions: {filter.actions_enabled_count}/{filter.actions_count}
|
||||
</span>
|
||||
{!filter.actions_count && (
|
||||
<span className="mr-2 ml-2 flex h-3 w-3 relative">
|
||||
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
{!filter.actions_count ? (
|
||||
<>{"You need to setup an action in the filter otherwise you will not get any snatches."}</>
|
||||
{filter.actions_count === 0 ? (
|
||||
<>
|
||||
{"No actions defined. Set up actions to enable snatching."}
|
||||
</>
|
||||
) : filter.actions_enabled_count === 0 ? (
|
||||
<>
|
||||
{"You need to enable at least one action in the filter otherwise you will not get any snatches."}
|
||||
</>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
|
1
web/src/types/Filter.d.ts
vendored
1
web/src/types/Filter.d.ts
vendored
|
@ -68,6 +68,7 @@ interface Filter {
|
|||
tags_match_logic: string;
|
||||
except_tags_match_logic: string;
|
||||
actions_count: number;
|
||||
actions_enabled_count: number;
|
||||
actions: Action[];
|
||||
indexers: Indexer[];
|
||||
external: ExternalFilter[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue