diff --git a/cmd/autobrrctl/main.go b/cmd/autobrrctl/main.go index c6aa987..1c1b039 100644 --- a/cmd/autobrrctl/main.go +++ b/cmd/autobrrctl/main.go @@ -447,6 +447,7 @@ type FilterExport struct { // Media-specific fields Freeleech bool `json:"freeleech,omitempty"` FreeleechPercent string `json:"freeleech_percent,omitempty"` + FreeleechToken bool `json:"freeleech_token,omitempty"` Shows string `json:"shows,omitempty"` Seasons string `json:"seasons,omitempty"` Episodes string `json:"episodes,omitempty"` diff --git a/internal/database/filter.go b/internal/database/filter.go index 4fdda0d..61d71cf 100644 --- a/internal/database/filter.go +++ b/internal/database/filter.go @@ -210,6 +210,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter "f.scene", "f.freeleech", "f.freeleech_percent", + "f.freeleech_token", "f.smart_episode", "f.shows", "f.seasons", @@ -438,6 +439,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string "f.scene", "f.freeleech", "f.freeleech_percent", + "f.freeleech_token", "f.smart_episode", "f.shows", "f.seasons", @@ -832,6 +834,7 @@ func (r *FilterRepo) Store(ctx context.Context, filter *domain.Filter) error { "scene", "freeleech", "freeleech_percent", + "freeleech_token", "smart_episode", "shows", "seasons", @@ -988,6 +991,7 @@ func (r *FilterRepo) Update(ctx context.Context, filter *domain.Filter) error { Set("scene", filter.Scene). Set("freeleech", filter.Freeleech). Set("freeleech_percent", filter.FreeleechPercent). + Set("freeleech_token", filter.FreeleechToken). Set("smart_episode", filter.SmartEpisode). Set("shows", filter.Shows). Set("seasons", filter.Seasons). @@ -1128,6 +1132,9 @@ func (r *FilterRepo) UpdatePartial(ctx context.Context, filter domain.FilterUpda if filter.FreeleechPercent != nil { q = q.Set("freeleech_percent", filter.FreeleechPercent) } + if filter.FreeleechToken != nil { + q = q.Set("freeleech_token", filter.FreeleechToken) + } if filter.SmartEpisode != nil { q = q.Set("smart_episode", filter.SmartEpisode) } diff --git a/internal/database/sqlite_migrate.go b/internal/database/sqlite_migrate.go index 1ea827b..7c48974 100644 --- a/internal/database/sqlite_migrate.go +++ b/internal/database/sqlite_migrate.go @@ -2018,5 +2018,9 @@ CREATE INDEX release_hybrid_index FROM irc_network WHERE server = 'irc.rocket-hd.cc' ); +`, + `ALTER TABLE filters + ADD COLUMN freeleach_tokens BOOLEAN + AFTER freeleech_percent; `, } diff --git a/internal/domain/filter.go b/internal/domain/filter.go index af4e3cb..0a49040 100644 --- a/internal/domain/filter.go +++ b/internal/domain/filter.go @@ -114,6 +114,7 @@ type Filter struct { Bonus []string `json:"bonus,omitempty"` Freeleech bool `json:"freeleech,omitempty"` FreeleechPercent string `json:"freeleech_percent,omitempty"` + FreeleechToken bool `json:"freeleech_token,omitempty"` SmartEpisode bool `json:"smart_episode"` Shows string `json:"shows,omitempty"` Seasons string `json:"seasons,omitempty"` @@ -248,6 +249,7 @@ type FilterUpdate struct { Bonus *[]string `json:"bonus,omitempty"` Freeleech *bool `json:"freeleech,omitempty"` FreeleechPercent *string `json:"freeleech_percent,omitempty"` + FreeleechToken *bool `json:"freeleech_token,omitempty"` SmartEpisode *bool `json:"smart_episode,omitempty"` Shows *string `json:"shows,omitempty"` Seasons *string `json:"seasons,omitempty"` diff --git a/internal/filter/service.go b/internal/filter/service.go index b7de765..8e0498d 100644 --- a/internal/filter/service.go +++ b/internal/filter/service.go @@ -558,7 +558,7 @@ func (s *service) AdditionalSizeCheck(ctx context.Context, f *domain.Filter, rel if (release.Size == 0 && release.AdditionalSizeCheckRequired) || (release.Uploader == "" && release.AdditionalUploaderCheckRequired) || (release.RecordLabel == "" && release.AdditionalRecordLabelCheckRequired) { l.Trace().Msgf("(%s) preparing to check size via api", f.Name) - torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID) + torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID, f.FreeleechToken) if err != nil || torrentInfo == nil { l.Error().Err(err).Msgf("(%s) could not get torrent info from api: '%s' from: %s", f.Name, release.TorrentID, release.Indexer.Identifier) return false, err @@ -643,7 +643,7 @@ func (s *service) AdditionalUploaderCheck(ctx context.Context, f *domain.Filter, case "redacted", "ops", "mock": l.Trace().Msgf("(%s) preparing to check via api", f.Name) - torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID) + torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID, f.FreeleechToken) if err != nil || torrentInfo == nil { l.Error().Err(err).Msgf("(%s) could not get torrent info from api: '%s' from: %s", f.Name, release.TorrentID, release.Indexer.Identifier) return false, err @@ -722,7 +722,7 @@ func (s *service) AdditionalRecordLabelCheck(ctx context.Context, f *domain.Filt case "redacted", "ops", "mock": l.Trace().Msgf("(%s) preparing to check via api", f.Name) - torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID) + torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID, f.FreeleechToken) if err != nil || torrentInfo == nil { l.Error().Err(err).Msgf("(%s) could not get torrent info from api: '%s' from: %s", f.Name, release.TorrentID, release.Indexer.Identifier) return false, err diff --git a/internal/indexer/api.go b/internal/indexer/api.go index 956d3c1..e3a07cd 100644 --- a/internal/indexer/api.go +++ b/internal/indexer/api.go @@ -20,13 +20,13 @@ import ( type APIService interface { TestConnection(ctx context.Context, req domain.IndexerTestApiRequest) (bool, error) - GetTorrentByID(ctx context.Context, indexer string, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, indexer string, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) AddClient(indexer string, settings map[string]string) error RemoveClient(indexer string) error } type apiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } @@ -42,7 +42,7 @@ func NewAPIService(log logger.Logger) APIService { } } -func (s *apiService) GetTorrentByID(ctx context.Context, indexer string, torrentID string) (*domain.TorrentBasic, error) { +func (s *apiService) GetTorrentByID(ctx context.Context, indexer string, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) { client, err := s.getApiClient(indexer) if err != nil { s.log.Error().Stack().Err(err).Msgf("could not get api client for: %s", indexer) @@ -51,7 +51,7 @@ func (s *apiService) GetTorrentByID(ctx context.Context, indexer string, torrent s.log.Trace().Str("method", "GetTorrentByID").Msgf("%s fetching torrent from api...", indexer) - torrent, err := client.GetTorrentByID(ctx, torrentID) + torrent, err := client.GetTorrentByID(ctx, torrentID, freeleechToken) if err != nil { s.log.Error().Stack().Err(err).Msgf("could not get torrent: %s from: %s", torrentID, indexer) return nil, err diff --git a/internal/mock/indexer_api.go b/internal/mock/indexer_api.go index 7f83d55..43e650f 100644 --- a/internal/mock/indexer_api.go +++ b/internal/mock/indexer_api.go @@ -11,7 +11,7 @@ import ( ) type IndexerApiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } diff --git a/pkg/btn/btn.go b/pkg/btn/btn.go index f6b5bcd..16d41e3 100644 --- a/pkg/btn/btn.go +++ b/pkg/btn/btn.go @@ -32,12 +32,12 @@ func (c *Client) TestAPI(ctx context.Context) (bool, error) { return false, nil } -func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) { +func (c *Client) GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) { if torrentID == "" { return nil, errors.New("btn client: must have torrentID") } - res, err := c.rpcClient.CallCtx(ctx, "getTorrentById", [2]string{c.APIKey, torrentID}) + res, err := c.rpcClient.CallCtx(ctx, "getTorrentById", [3]interface{}{c.APIKey, torrentID, freeleechToken}) if err != nil { return nil, errors.Wrap(err, "call getTorrentById failed") } diff --git a/pkg/btn/client.go b/pkg/btn/client.go index 289ee0e..92f92ac 100644 --- a/pkg/btn/client.go +++ b/pkg/btn/client.go @@ -20,7 +20,7 @@ import ( const DefaultURL = "https://api.broadcasthe.net/" type ApiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } diff --git a/pkg/ggn/ggn.go b/pkg/ggn/ggn.go index f486105..5d8b8d5 100644 --- a/pkg/ggn/ggn.go +++ b/pkg/ggn/ggn.go @@ -27,7 +27,7 @@ var ErrForbidden = errors.New("forbidden") var ErrTooManyRequests = errors.New("too many requests: rate-limit reached") type ApiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freelechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } @@ -243,7 +243,7 @@ func (c *Client) getJSON(ctx context.Context, params url.Values, data any) error return nil } -func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) { +func (c *Client) GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) { if torrentID == "" { return nil, errors.New("ggn client: must have torrentID") } @@ -253,6 +253,9 @@ func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain. params := url.Values{} params.Add("request", "torrent") params.Add("id", torrentID) + if freeleechToken { + params.Add("usetoken", "1") + } err := c.getJSON(ctx, params, &response) if err != nil { diff --git a/pkg/ggn/ggn_test.go b/pkg/ggn/ggn_test.go index a4bbddd..ec6a7b8 100644 --- a/pkg/ggn/ggn_test.go +++ b/pkg/ggn/ggn_test.go @@ -86,7 +86,7 @@ func Test_client_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: key, }, - args: args{torrentID: "422368"}, + args: args{torrentID: "422368", freeleechToken: false}, want: &domain.TorrentBasic{ Id: "422368", InfoHash: "78DA2811E6732012B8224198D4DC2FD49A5E950F", @@ -100,7 +100,7 @@ func Test_client_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: key, }, - args: args{torrentID: "100002"}, + args: args{torrentID: "100002", freeleechToken: true}, want: nil, wantErr: false, }, @@ -109,7 +109,7 @@ func Test_client_GetTorrentByID(t *testing.T) { t.Run(tt.name, func(t *testing.T) { c := NewClient(tt.fields.APIKey, WithUrl(ts.URL)) - got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID) + got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID, tt.args.freeleechToken) if tt.wantErr && assert.Error(t, err) { t.Logf("got err: %v", err) assert.Equal(t, tt.wantErr, err) diff --git a/pkg/ops/ops.go b/pkg/ops/ops.go index 4eb9306..9584ffa 100644 --- a/pkg/ops/ops.go +++ b/pkg/ops/ops.go @@ -23,7 +23,7 @@ import ( const DefaultURL = "https://orpheus.network/ajax.php" type ApiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } @@ -226,7 +226,7 @@ func (c *Client) getJSON(ctx context.Context, params url.Values, data any) error return nil } -func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) { +func (c *Client) GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) { if torrentID == "" { return nil, errors.New("orpheus client: must have torrentID") } @@ -236,6 +236,9 @@ func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain. params := url.Values{} params.Add("action", "torrent") params.Add("id", torrentID) + if freeleechToken { + params.Add("usetoken", "1") + } err := c.getJSON(ctx, params, &response) if err != nil { diff --git a/pkg/ops/ops_test.go b/pkg/ops/ops_test.go index 5901e5d..15570bd 100644 --- a/pkg/ops/ops_test.go +++ b/pkg/ops/ops_test.go @@ -70,7 +70,7 @@ func TestOrpheusClient_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: key, }, - args: args{torrentID: "2156788"}, + args: args{torrentID: "2156788", freeleechToken: false}, want: &domain.TorrentBasic{ Id: "2156788", InfoHash: "", @@ -86,7 +86,7 @@ func TestOrpheusClient_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: key, }, - args: args{torrentID: "100002"}, + args: args{torrentID: "100002", freeleechToken: false}, want: nil, wantErr: "could not get torrent by id: 100002: status code: 400 status: failure error: bad id parameter", }, @@ -96,16 +96,26 @@ func TestOrpheusClient_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: "", }, - args: args{torrentID: "100002"}, + args: args{torrentID: "100002", freeleechToken: false}, want: nil, wantErr: "could not get torrent by id: 100002: orpheus client missing API key!", }, + { + name: "get_by_id_1_freeleech_token", + fields: fields{ + Url: ts.URL, + APIKey: "", + }, + args: args{torrentID: "100002", freeleechToken: true}, + want: nil, + wantErr: "could not get torrent by id: 1. freeleech token not supported or no tokens available", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := NewClient(tt.fields.APIKey, WithUrl(ts.URL)) - got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID) + got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID, tt.args.freeleechToken) if tt.wantErr != "" && assert.Error(t, err) { assert.EqualErrorf(t, err, tt.wantErr, "Error should be: %v, got: %v", tt.wantErr, err) } diff --git a/pkg/ptp/ptp.go b/pkg/ptp/ptp.go index 63d2bdd..1d5c123 100644 --- a/pkg/ptp/ptp.go +++ b/pkg/ptp/ptp.go @@ -26,7 +26,7 @@ var ErrForbidden = errors.New("forbidden") var ErrTooManyRequests = errors.New("too many requests: rate-limit reached") type ApiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } @@ -180,7 +180,7 @@ func (c *Client) getJSON(ctx context.Context, params url.Values, data any) error return nil } -func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) { +func (c *Client) GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) { if torrentID == "" { return nil, errors.New("ptp client: must have torrentID") } @@ -189,6 +189,9 @@ func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain. params := url.Values{} params.Add("torrentid", torrentID) + if freeleechToken { + params.Add("usetoken", "1") + } err := c.getJSON(ctx, params, &response) if err != nil { diff --git a/pkg/ptp/ptp_test.go b/pkg/ptp/ptp_test.go index 7c28f52..d48fa63 100644 --- a/pkg/ptp/ptp_test.go +++ b/pkg/ptp/ptp_test.go @@ -72,7 +72,7 @@ func TestPTPClient_GetTorrentByID(t *testing.T) { APIUser: user, APIKey: key, }, - args: args{torrentID: "1"}, + args: args{torrentID: "1", freeleechToken: false}, want: &domain.TorrentBasic{ Id: "1", InfoHash: "F57AA86DFB03F87FCC7636E310D35918442EAE5C", @@ -87,7 +87,7 @@ func TestPTPClient_GetTorrentByID(t *testing.T) { APIUser: user, APIKey: key, }, - args: args{torrentID: "100002"}, + args: args{torrentID: "100002", freeleechToken: false}, want: nil, wantErr: true, }, @@ -96,7 +96,7 @@ func TestPTPClient_GetTorrentByID(t *testing.T) { t.Run(tt.name, func(t *testing.T) { c := NewClient(tt.fields.APIUser, tt.fields.APIKey, WithUrl(ts.URL)) - got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID) + got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID, tt.args.freeleechToken) if tt.wantErr { assert.Error(t, err) } else { diff --git a/pkg/red/red.go b/pkg/red/red.go index acbc487..28f8e1b 100644 --- a/pkg/red/red.go +++ b/pkg/red/red.go @@ -23,7 +23,7 @@ import ( const DefaultURL = "https://redacted.sh/ajax.php" type ApiClient interface { - GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) + GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) TestAPI(ctx context.Context) (bool, error) } @@ -214,7 +214,7 @@ func (c *Client) getJSON(ctx context.Context, params url.Values, data any) error return nil } -func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) { +func (c *Client) GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) { if torrentID == "" { return nil, errors.New("red client: must have torrentID") } @@ -224,6 +224,9 @@ func (c *Client) GetTorrentByID(ctx context.Context, torrentID string) (*domain. params := url.Values{} params.Add("action", "torrent") params.Add("id", torrentID) + if freeleechToken { + params.Add("usetoken", "1") + } err := c.getJSON(ctx, params, &response) if err != nil { diff --git a/pkg/red/red_test.go b/pkg/red/red_test.go index 836e9a4..0d1386c 100644 --- a/pkg/red/red_test.go +++ b/pkg/red/red_test.go @@ -70,7 +70,7 @@ func TestREDClient_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: key, }, - args: args{torrentID: "29991962"}, + args: args{torrentID: "29991962", freeleechToken: false}, want: &domain.TorrentBasic{ Id: "29991962", InfoHash: "B2BABD3A361EAFC6C4E9142C422DF7DDF5D7E163", @@ -86,7 +86,7 @@ func TestREDClient_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: key, }, - args: args{torrentID: "100002"}, + args: args{torrentID: "100002", freeleechToken: false}, want: nil, wantErr: "could not get torrent by id: 100002: status code: 400 status: failure error: bad id parameter", }, @@ -96,7 +96,7 @@ func TestREDClient_GetTorrentByID(t *testing.T) { Url: ts.URL, APIKey: "", }, - args: args{torrentID: "100002"}, + args: args{torrentID: "100002", freeleechToken: false}, want: nil, wantErr: "could not get torrent by id: 100002: RED client missing API key!", }, @@ -105,7 +105,7 @@ func TestREDClient_GetTorrentByID(t *testing.T) { t.Run(tt.name, func(t *testing.T) { c := NewClient(tt.fields.APIKey, WithUrl(ts.URL)) - got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID) + got, err := c.GetTorrentByID(context.Background(), tt.args.torrentID, tt.args.freeleechToken) if tt.wantErr != "" && assert.Error(t, err) { assert.EqualErrorf(t, err, tt.wantErr, "Error should be: %v, got: %v", tt.wantErr, err) } diff --git a/web/src/screens/filters/Details.tsx b/web/src/screens/filters/Details.tsx index 2a41361..3b057f7 100644 --- a/web/src/screens/filters/Details.tsx +++ b/web/src/screens/filters/Details.tsx @@ -437,6 +437,7 @@ export const FilterDetails = () => { except_language: filter.except_language || [], freeleech: filter.freeleech, freeleech_percent: filter.freeleech_percent, + freeleech_token: filter.freeleech_token, formats: filter.formats || [], quality: filter.quality || [], media: filter.media || [], diff --git a/web/src/screens/filters/_const.ts b/web/src/screens/filters/_const.ts index 1b6d9a6..a0be3a0 100644 --- a/web/src/screens/filters/_const.ts +++ b/web/src/screens/filters/_const.ts @@ -14,6 +14,7 @@ export const FILTER_FIELDS: Record = { "scene": "boolean", "smart_episode": "boolean", "freeleech": "boolean", + "freeleech_token": "boolean", "perfect_flac": "boolean", "download_duplicates": "boolean", "cue": "boolean", diff --git a/web/src/screens/filters/sections/Advanced.tsx b/web/src/screens/filters/sections/Advanced.tsx index 8b79985..df20250 100644 --- a/web/src/screens/filters/sections/Advanced.tsx +++ b/web/src/screens/filters/sections/Advanced.tsx @@ -354,7 +354,7 @@ const Freeleech = () => { return ( @@ -401,6 +401,21 @@ const Freeleech = () => { } /> + + +

+ Use freelech tokens for downloads if supported bt the tracker. +

+ + } + /> +
); } diff --git a/web/src/types/Filter.d.ts b/web/src/types/Filter.d.ts index 4b3dbed..0ef5360 100644 --- a/web/src/types/Filter.d.ts +++ b/web/src/types/Filter.d.ts @@ -32,6 +32,7 @@ interface Filter { except_origins: string[]; freeleech: boolean; freeleech_percent: string; + freeleech_token: boolean; shows: string; seasons: string; episodes: string;