feat(filters): RED and OPS fetch uploader from API (#1348)

* feat: added uploader when fetching torrent details on RED indexer

* revert

* tests

* refactor(filters): size and uploader api checks

* refactor(filters): fix test

* refactor(filters): add mutex to rejections

---------

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
Alexander Chernov 2024-12-17 19:39:09 +00:00 committed by GitHub
parent 04c4bd482f
commit acef4ac624
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 251 additions and 88 deletions

View file

@ -42,6 +42,7 @@ type Service interface {
ToggleEnabled(ctx context.Context, filterID int, enabled bool) error
Delete(ctx context.Context, filterID int) error
AdditionalSizeCheck(ctx context.Context, f *domain.Filter, release *domain.Release) (bool, error)
AdditionalUploaderCheck(ctx context.Context, f *domain.Filter, release *domain.Release) (bool, error)
CheckSmartEpisodeCanDownload(ctx context.Context, params *domain.SmartEpisodeParams) (bool, error)
GetDownloadsByFilterId(ctx context.Context, filterID int) (*domain.FilterDownloads, error)
}
@ -429,8 +430,7 @@ func (s *service) CheckFilter(ctx context.Context, f *domain.Filter, release *do
l.Debug().Msgf("found and matched filter: %s", f.Name)
// If size constraints are set in a filter and the indexer did not
// announce the size, we need to do an additional out of band size
// check.
// announce the size, we need to do an additional out of band size check.
if release.AdditionalSizeCheckRequired {
l.Debug().Msgf("(%s) additional size check required", f.Name)
@ -446,6 +446,22 @@ func (s *service) CheckFilter(ctx context.Context, f *domain.Filter, release *do
}
}
// check uploader if the indexer supports check via api
if release.AdditionalUploaderCheckRequired {
l.Debug().Msgf("(%s) additional uploader check required", f.Name)
ok, err := s.AdditionalUploaderCheck(ctx, f, release)
if err != nil {
l.Error().Err(err).Msgf("(%s) additional uploader check error", f.Name)
return false, err
}
if !ok {
l.Trace().Msgf("(%s) additional uploader check not matching what filter wanted", f.Name)
return false, nil
}
}
// run external filters
if f.External != nil {
externalOk, err := s.RunExternalFilters(ctx, f, f.External, release)
@ -467,8 +483,8 @@ func (s *service) CheckFilter(ctx context.Context, f *domain.Filter, release *do
return false, nil
}
// AdditionalSizeCheck performs additional out of band checks to determine the
// size of a torrent. Some indexers do not announce torrent size, so it is
// AdditionalSizeCheck performs additional out-of-band checks to determine the
// values of a torrent. Some indexers do not announce torrent size, so it is
// necessary to determine the size of the torrent in some other way. Some
// indexers have an API implemented to fetch this data. For those which don't,
// it is necessary to download the torrent file and parse it to make the size
@ -484,12 +500,12 @@ func (s *service) AdditionalSizeCheck(ctx context.Context, f *domain.Filter, rel
// do additional size check against indexer api or torrent for size
l := s.log.With().Str("method", "AdditionalSizeCheck").Logger()
l.Debug().Msgf("(%s) additional size check required", f.Name)
l.Debug().Msgf("(%s) additional api size check required", f.Name)
switch release.Indexer.Identifier {
case "ptp", "btn", "ggn", "redacted", "ops", "mock":
if release.Size == 0 {
l.Trace().Msgf("(%s) preparing to check via api", f.Name)
case "btn", "ggn", "redacted", "ops", "mock":
if (release.Size == 0 && release.AdditionalSizeCheckRequired) || (release.Uploader == "" && release.AdditionalUploaderCheckRequired) {
l.Trace().Msgf("(%s) preparing to check size via api", f.Name)
torrentInfo, err := s.apiService.GetTorrentByID(ctx, release.Indexer.Identifier, release.TorrentID)
if err != nil || torrentInfo == nil {
@ -499,16 +515,25 @@ func (s *service) AdditionalSizeCheck(ctx context.Context, f *domain.Filter, rel
l.Debug().Msgf("(%s) got torrent info from api: %+v", f.Name, torrentInfo)
release.Size = torrentInfo.ReleaseSizeBytes()
torrentSize := torrentInfo.ReleaseSizeBytes()
if release.Size == 0 && torrentSize > 0 {
release.Size = torrentSize
}
if release.Uploader == "" {
release.Uploader = torrentInfo.Uploader
}
}
default:
l.Trace().Msgf("(%s) preparing to download torrent metafile", f.Name)
if release.Size == 0 && release.AdditionalSizeCheckRequired {
l.Trace().Msgf("(%s) preparing to download torrent metafile", f.Name)
// if indexer doesn't have api, download torrent and add to tmpPath
if err := s.downloadSvc.DownloadRelease(ctx, release); err != nil {
l.Error().Err(err).Msgf("(%s) could not download torrent file with id: '%s' from: %s", f.Name, release.TorrentID, release.Indexer.Identifier)
return false, errors.Wrap(err, "could not download torrent file for release: %s", release.TorrentName)
// if indexer doesn't have api, download torrent and add to tmpPath
if err := s.downloadSvc.DownloadRelease(ctx, release); err != nil {
l.Error().Err(err).Msgf("(%s) could not download torrent file with id: '%s' from: %s", f.Name, release.TorrentID, release.Indexer.Identifier)
return false, errors.Wrap(err, "could not download torrent file for release: %s", release.TorrentName)
}
}
}
@ -518,6 +543,9 @@ func (s *service) AdditionalSizeCheck(ctx context.Context, f *domain.Filter, rel
return false, err
}
// reset AdditionalSizeCheckRequired to not re-trigger check
release.AdditionalSizeCheckRequired = false
if !sizeOk {
l.Debug().Msgf("(%s) filter did not match after additional size check, trying next", f.Name)
return false, err
@ -526,6 +554,82 @@ func (s *service) AdditionalSizeCheck(ctx context.Context, f *domain.Filter, rel
return true, nil
}
func (s *service) AdditionalUploaderCheck(ctx context.Context, f *domain.Filter, release *domain.Release) (bool, error) {
var err error
defer func() {
// try recover panic if anything went wrong with API or size checks
errors.RecoverPanic(recover(), &err)
}()
// do additional check against indexer api
l := s.log.With().Str("method", "AdditionalUploaderCheck").Logger()
// if uploader was fetched before during size check we check it and return early
if release.Uploader != "" {
uploaderOk, err := f.CheckUploader(release.Uploader)
if err != nil {
l.Error().Err(err).Msgf("(%s) error comparing release and uploaders", f.Name)
return false, err
}
// reset AdditionalUploaderCheckRequired to not re-trigger check
release.AdditionalUploaderCheckRequired = false
if !uploaderOk {
l.Debug().Msgf("(%s) filter did not match after additional uploaders check, trying next", f.Name)
return false, err
}
return true, nil
}
l.Debug().Msgf("(%s) additional api size check required", f.Name)
switch release.Indexer.Identifier {
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)
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
}
l.Debug().Msgf("(%s) got torrent info from api: %+v", f.Name, torrentInfo)
torrentSize := torrentInfo.ReleaseSizeBytes()
if release.Size == 0 && torrentSize > 0 {
release.Size = torrentSize
// reset AdditionalSizeCheckRequired to not re-trigger check
release.AdditionalSizeCheckRequired = false
}
if release.Uploader == "" {
release.Uploader = torrentInfo.Uploader
}
default:
return false, errors.New("additional uploader check not supported for this indexer: %s", release.Indexer.Identifier)
}
uploaderOk, err := f.CheckUploader(release.Uploader)
if err != nil {
l.Error().Err(err).Msgf("(%s) error comparing release and uploaders", f.Name)
return false, err
}
// reset AdditionalUploaderCheckRequired to not re-trigger check
release.AdditionalUploaderCheckRequired = false
if !uploaderOk {
l.Debug().Msgf("(%s) filter did not match after additional uploaders check, trying next", f.Name)
return false, err
}
return true, nil
}
func (s *service) CheckSmartEpisodeCanDownload(ctx context.Context, params *domain.SmartEpisodeParams) (bool, error) {
return s.releaseRepo.CheckSmartEpisodeCanDownload(ctx, params)
}