autobrr/internal/domain/list.go
Fabricio Silva b724429b97
feat(lists): add anilist support (#1949)
* fix(lists): clear selected list

* chore(web): improve onChange set values for select_wide

* feat(web): add anilist lists

* feat(web): Filter is required on ListForm

* fix(web): ListForm reset url when change type

* feat(lists): add anilist support

* feat(lists): filter duplicates for anilist

* feat(anilist): handle special characters

* fix(lists): better title matching
fix(lists): add alternatives to apostrophe replacement

* test(title): add some anime cases

* feat(anilist): replace unicodes with regex

* feat(lists): move additional anilist processing to autobrr instead of brr api

* feat(lists): clean Unicode Block “Latin Extended-A” chars

---------

Co-authored-by: martylukyy <35452459+martylukyy@users.noreply.github.com>
2025-01-31 19:17:23 +01:00

137 lines
4 KiB
Go

// Copyright (c) 2021 - 2025, Ludvig Lundgren and the autobrr contributors.
// SPDX-License-Identifier: GPL-2.0-or-later
package domain
import (
"context"
"net/http"
"net/url"
"strings"
"time"
"github.com/autobrr/autobrr/pkg/errors"
)
type ListRepo interface {
List(ctx context.Context) ([]*List, error)
FindByID(ctx context.Context, listID int64) (*List, error)
Store(ctx context.Context, listID *List) error
Update(ctx context.Context, listID *List) error
UpdateLastRefresh(ctx context.Context, list *List) error
ToggleEnabled(ctx context.Context, listID int64, enabled bool) error
Delete(ctx context.Context, listID int64) error
GetListFilters(ctx context.Context, listID int64) ([]ListFilter, error)
}
type ListType string
const (
ListTypeRadarr ListType = "RADARR"
ListTypeSonarr ListType = "SONARR"
ListTypeLidarr ListType = "LIDARR"
ListTypeReadarr ListType = "READARR"
ListTypeWhisparr ListType = "WHISPARR"
ListTypeMDBList ListType = "MDBLIST"
ListTypeMetacritic ListType = "METACRITIC"
ListTypePlaintext ListType = "PLAINTEXT"
ListTypeTrakt ListType = "TRAKT"
ListTypeSteam ListType = "STEAM"
ListTypeAniList ListType = "ANILIST"
)
type ListRefreshStatus string
const (
ListRefreshStatusSuccess ListRefreshStatus = "SUCCESS"
ListRefreshStatusError ListRefreshStatus = "ERROR"
)
type List struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type ListType `json:"type"`
Enabled bool `json:"enabled"`
ClientID int `json:"client_id"`
URL string `json:"url"`
Headers []string `json:"headers"`
APIKey string `json:"api_key"`
Filters []ListFilter `json:"filters"`
MatchRelease bool `json:"match_release"`
TagsInclude []string `json:"tags_included"`
TagsExclude []string `json:"tags_excluded"`
IncludeUnmonitored bool `json:"include_unmonitored"`
IncludeAlternateTitles bool `json:"include_alternate_titles"`
LastRefreshTime time.Time `json:"last_refresh_time"`
LastRefreshData string `json:"last_refresh_error"`
LastRefreshStatus ListRefreshStatus `json:"last_refresh_status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (l *List) Validate() error {
if l.Name == "" {
return errors.New("name is required")
}
if l.Type == "" {
return errors.New("type is required")
}
if !l.ListTypeArr() && !l.ListTypeList() {
return errors.New("invalid list type: %s", l.Type)
}
if l.ListTypeArr() && l.ClientID == 0 {
return errors.New("arr client id is required")
}
if l.ListTypeList() {
if l.URL == "" {
return errors.New("list url is required")
}
_, err := url.Parse(l.URL)
if err != nil {
return errors.Wrap(err, "could not parse list url: %s", l.URL)
}
}
if len(l.Filters) == 0 {
return errors.New("at least one filter is required")
}
return nil
}
func (l *List) ListTypeArr() bool {
return l.Type == ListTypeRadarr || l.Type == ListTypeSonarr || l.Type == ListTypeLidarr || l.Type == ListTypeReadarr || l.Type == ListTypeWhisparr
}
func (l *List) ListTypeList() bool {
return l.Type == ListTypeMDBList || l.Type == ListTypeMetacritic || l.Type == ListTypePlaintext || l.Type == ListTypeTrakt || l.Type == ListTypeSteam || l.Type == ListTypeAniList
}
func (l *List) ShouldProcessItem(monitored bool) bool {
if l.IncludeUnmonitored {
return true
}
return monitored
}
// SetRequestHeaders set headers from list on the request
func (l *List) SetRequestHeaders(req *http.Request) {
for _, header := range l.Headers {
parts := strings.Split(header, "=")
if len(parts) != 2 {
continue
}
req.Header.Set(parts[0], parts[1])
}
}
type ListFilter struct {
ID int `json:"id"`
Name string `json:"name"`
}