Add support for using freeleech tokens if available

This commit is contained in:
Daniel Mason 2025-07-11 10:12:43 +12:00
parent 74f777340e
commit 3656a68a27
Signed by: idanoo
GPG key ID: 387387CDBC02F132
28 changed files with 199 additions and 39 deletions

View file

@ -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"`
@ -543,6 +544,7 @@ func prepareFilterForExport(filter domain.Filter, externalFilters []domain.Filte
AnnounceTypes: filter.AnnounceTypes,
Freeleech: filter.Freeleech,
FreeleechPercent: filter.FreeleechPercent,
FreeleechToken: filter.FreeleechToken,
Shows: filter.Shows,
Seasons: filter.Seasons,
Episodes: filter.Episodes,

View file

@ -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",
@ -278,7 +279,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
// filter
var minSize, maxSize, maxDownloadsUnit, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, matchReleaseTags, exceptReleaseTags, matchDescription, exceptDescription, freeleechPercent, shows, seasons, episodes, years, months, days, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, matchRecordLabels, exceptRecordLabels, tags, exceptTags, tagsMatchLogic, exceptTagsMatchLogic sql.NullString
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
var useRegex, scene, freeleech, freeleechToken, hasLog, hasCue, perfectFlac sql.NullBool
var delay, maxDownloads, logScore sql.NullInt32
var releaseProfileDuplicateId sql.NullInt64
@ -307,6 +308,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
&scene,
&freeleech,
&freeleechPercent,
&freeleechToken,
&f.SmartEpisode,
&shows,
&seasons,
@ -375,7 +377,9 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
f.ExceptReleaseTags = exceptReleaseTags.String
f.MatchDescription = matchDescription.String
f.ExceptDescription = exceptDescription.String
f.Freeleech = freeleech.Bool
f.FreeleechPercent = freeleechPercent.String
f.FreeleechToken = freeleechToken.Bool
f.Shows = shows.String
f.Seasons = seasons.String
f.Episodes = episodes.String
@ -400,7 +404,6 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
f.ExceptTagsMatchLogic = exceptTagsMatchLogic.String
f.UseRegex = useRegex.Bool
f.Scene = scene.Bool
f.Freeleech = freeleech.Bool
f.ReleaseProfileDuplicateID = releaseProfileDuplicateId.Int64
return &f, nil
@ -438,6 +441,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",
@ -535,7 +539,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
var f domain.Filter
var minSize, maxSize, maxDownloadsUnit, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, matchReleaseTags, exceptReleaseTags, matchDescription, exceptDescription, freeleechPercent, shows, seasons, episodes, years, months, days, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, matchRecordLabels, exceptRecordLabels, tags, exceptTags, tagsMatchLogic, exceptTagsMatchLogic sql.NullString
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
var useRegex, scene, freeleech, freeleechToken, hasLog, hasCue, perfectFlac sql.NullBool
var delay, maxDownloads, logScore sql.NullInt32
var releaseProfileDuplicateID, rdpId sql.NullInt64
@ -567,6 +571,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
&scene,
&freeleech,
&freeleechPercent,
&freeleechToken,
&f.SmartEpisode,
&shows,
&seasons,
@ -680,6 +685,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
f.UseRegex = useRegex.Bool
f.Scene = scene.Bool
f.Freeleech = freeleech.Bool
f.FreeleechToken = freeleechToken.Bool
f.ReleaseProfileDuplicateID = releaseProfileDuplicateID.Int64
f.Rejections = []string{}
@ -832,6 +838,7 @@ func (r *FilterRepo) Store(ctx context.Context, filter *domain.Filter) error {
"scene",
"freeleech",
"freeleech_percent",
"freeleech_token",
"smart_episode",
"shows",
"seasons",
@ -901,6 +908,7 @@ func (r *FilterRepo) Store(ctx context.Context, filter *domain.Filter) error {
filter.Scene,
filter.Freeleech,
filter.FreeleechPercent,
filter.FreeleechToken,
filter.SmartEpisode,
filter.Shows,
filter.Seasons,
@ -988,6 +996,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 +1137,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)
}

View file

@ -39,6 +39,7 @@ func getMockFilter() *domain.Filter {
Bonus: nil,
Freeleech: false,
FreeleechPercent: "100%",
FreeleechToken: false,
SmartEpisode: false,
Shows: "Is It Wrong to Try to Pick Up Girls in a Dungeon?",
Seasons: "4",

View file

@ -1373,5 +1373,8 @@ CREATE INDEX release_hybrid_index
FROM irc_network
WHERE server = 'irc.rocket-hd.cc'
);
`,
`ALTER TABLE filter
ADD freeleech_token BOOLEAN DEFAULT FALSE AFTER freeleech_percent;
`,
}

View file

@ -742,6 +742,7 @@ func getMockFilterDuplicates() *domain.Filter {
Bonus: nil,
Freeleech: false,
FreeleechPercent: "100%",
FreeleechToken: false,
SmartEpisode: false,
Shows: "Is It Wrong to Try to Pick Up Girls in a Dungeon?",
Seasons: "4",

View file

@ -2018,5 +2018,101 @@ CREATE INDEX release_hybrid_index
FROM irc_network
WHERE server = 'irc.rocket-hd.cc'
);
`, ``,
`ALTER TABLE filter RENAME TO filter_add_col;
DROP INDEX filter_enabled_index;
DROP INDEX filter_priority_index;
CREATE TABLE filter
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
enabled BOOLEAN,
name TEXT NOT NULL,
min_size TEXT,
max_size TEXT,
delay INTEGER,
priority INTEGER DEFAULT 0 NOT NULL,
max_downloads INTEGER DEFAULT 0,
max_downloads_unit TEXT,
announce_types TEXT [] DEFAULT '{}',
match_releases TEXT,
except_releases TEXT,
use_regex BOOLEAN,
match_release_groups TEXT,
except_release_groups TEXT,
match_release_tags TEXT,
except_release_tags TEXT,
use_regex_release_tags BOOLEAN DEFAULT FALSE,
match_description TEXT,
except_description TEXT,
use_regex_description BOOLEAN DEFAULT FALSE,
scene BOOLEAN,
freeleech BOOLEAN,
freeleech_percent TEXT,
freeleech_token BOOLEAN DEFAULT FALSE,
smart_episode BOOLEAN DEFAULT FALSE,
shows TEXT,
seasons TEXT,
episodes TEXT,
resolutions TEXT [] DEFAULT '{}' NOT NULL,
codecs TEXT [] DEFAULT '{}' NOT NULL,
sources TEXT [] DEFAULT '{}' NOT NULL,
containers TEXT [] DEFAULT '{}' NOT NULL,
match_hdr TEXT [] DEFAULT '{}',
except_hdr TEXT [] DEFAULT '{}',
match_other TEXT [] DEFAULT '{}',
except_other TEXT [] DEFAULT '{}',
years TEXT,
months TEXT,
days TEXT,
artists TEXT,
albums TEXT,
release_types_match TEXT [] DEFAULT '{}',
release_types_ignore TEXT [] DEFAULT '{}',
formats TEXT [] DEFAULT '{}',
quality TEXT [] DEFAULT '{}',
media TEXT [] DEFAULT '{}',
log_score INTEGER,
has_log BOOLEAN,
has_cue BOOLEAN,
perfect_flac BOOLEAN,
match_categories TEXT,
except_categories TEXT,
match_uploaders TEXT,
except_uploaders TEXT,
match_record_labels TEXT,
except_record_labels TEXT,
match_language TEXT [] DEFAULT '{}',
except_language TEXT [] DEFAULT '{}',
tags TEXT,
except_tags TEXT,
tags_match_logic TEXT,
except_tags_match_logic TEXT,
origins TEXT [] DEFAULT '{}',
except_origins TEXT [] DEFAULT '{}',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
min_seeders INTEGER DEFAULT 0,
max_seeders INTEGER DEFAULT 0,
min_leechers INTEGER DEFAULT 0,
max_leechers INTEGER DEFAULT 0,
release_profile_duplicate_id INTEGER,
FOREIGN KEY (release_profile_duplicate_id) REFERENCES release_profile_duplicate(id) ON DELETE SET NULL
);
CREATE INDEX filter_enabled_index
ON filter (enabled);
CREATE INDEX filter_priority_index
ON filter (priority);
INSERT INTO filter SELECT id, enabled, name, min_size, max_size, delay, priority, max_downloads, max_downloads_unit, announce_types,
match_releases, except_releases, use_regex, match_release_groups, except_release_groups, match_release_tags, except_release_tags,
use_regex_release_tags, match_description, except_description, use_regex_description, scene, freeleech, freeleech_percent,
false,
smart_episode, shows, seasons, episodes, resolutions, codecs, sources, containers, match_hdr, except_hdr, match_other, except_other,
years, months, days, artists, albums, release_types_match, release_types_ignore, formats, quality, media, log_score, has_log, has_cue, perfect_flac, match_categories, except_categories, match_uploaders, except_uploaders, match_record_labels, except_record_labels, match_language, except_language, tags, except_tags, tags_match_logic, except_tags_match_logic, origins, except_origins, created_at, updated_at, min_seeders, max_seeders, min_leechers, max_leechers, release_profile_duplicate_id
FROM filter_add_col;
DROP TABLE filter_add_col;
`,
}

View file

@ -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"`

View file

@ -2087,6 +2087,7 @@ func TestFilter_CheckFilter1(t *testing.T) {
ExceptOrigins: tt.fields.ExceptOrigins,
Freeleech: tt.fields.Freeleech,
FreeleechPercent: tt.fields.FreeleechPercent,
FreeleechToken: tt.fields.FreeleechToken,
Shows: tt.fields.Shows,
Seasons: tt.fields.Seasons,
Episodes: tt.fields.Episodes,

View file

@ -47,6 +47,7 @@ type Macro struct {
FilterName string
Freeleech bool
FreeleechPercent int
FreeleechToken bool
Group string
GroupID string
HDR string

View file

@ -282,6 +282,7 @@ func TestRelease_DownloadTorrentFile(t *testing.T) {
ReleaseTags: tt.fields.ReleaseTags,
Freeleech: tt.fields.Freeleech,
FreeleechPercent: tt.fields.FreeleechPercent,
FreeleechToken: tt.fields.FreeleechToken,
Bonus: tt.fields.Bonus,
Uploader: tt.fields.Uploader,
PreTime: tt.fields.PreTime,

View file

@ -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

View file

@ -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

View file

@ -120,4 +120,4 @@ irc:
match:
infourl: "/torrents.php?torrentid={{ .torrentId }}"
torrenturl: "/torrents.php?action=download&id={{ .torrentId }}&torrent_pass={{ .torrent_pass }}"
torrenturl: "/torrents.php?action=download&id={{ .torrentId }}&torrent_pass={{ .torrent_pass }}{{ .freeleech_token }}"

View file

@ -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)
}
@ -41,7 +41,7 @@ func NewMockClient(apiKey string, opts ...OptFunc) IndexerApiClient {
return c
}
func (c *IndexerClient) GetTorrentByID(ctx context.Context, torrentID string) (*domain.TorrentBasic, error) {
func (c *IndexerClient) GetTorrentByID(ctx context.Context, torrentID string, freeleechToken bool) (*domain.TorrentBasic, error) {
if torrentID == "" {
return nil, errors.New("mock client: must have torrentID")
}

View file

@ -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")
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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)

View file

@ -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 {

View file

@ -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)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}

View file

@ -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 || [],

View file

@ -14,6 +14,7 @@ export const FILTER_FIELDS: Record<string, string> = {
"scene": "boolean",
"smart_episode": "boolean",
"freeleech": "boolean",
"freeleech_token": "boolean",
"perfect_flac": "boolean",
"download_duplicates": "boolean",
"cue": "boolean",

View file

@ -354,7 +354,7 @@ const Freeleech = () => {
return (
<CollapsibleSection
defaultOpen={values.freeleech || values.freeleech_percent !== undefined}
defaultOpen={values.freeleech || values.freeleech_token || values.freeleech_percent !== undefined}
title="Freeleech"
subtitle="Match based off freeleech (if announced)"
>
@ -401,6 +401,21 @@ const Freeleech = () => {
}
/>
</FilterHalfRow>
<FilterHalfRow>
<SwitchGroup
name="freeleech_token"
label="Freeleech Tokens"
className="py-0"
description="Use freeleech tokens if available."
tooltip={
<div>
<p>
Use freelech tokens for downloads if supported bt the tracker.
</p>
</div>
}
/>
</FilterHalfRow>
</CollapsibleSection>
);
}

View file

@ -32,6 +32,7 @@ interface Filter {
except_origins: string[];
freeleech: boolean;
freeleech_percent: string;
freeleech_token: boolean;
shows: string;
seasons: string;
episodes: string;