Skip to content

fix: auto-blacklist streams that return 451 Infringing Torrent#1367

Open
thescottthompson wants to merge 1 commit intorivenmedia:mainfrom
thescottthompson:fix/blacklist-infringing-torrents
Open

fix: auto-blacklist streams that return 451 Infringing Torrent#1367
thescottthompson wants to merge 1 commit intorivenmedia:mainfrom
thescottthompson:fix/blacklist-infringing-torrents

Conversation

@thescottthompson
Copy link
Copy Markdown

@thescottthompson thescottthompson commented Feb 6, 2026

Summary

  • When Real-Debrid returns HTTP 451 for a torrent (flagged as infringing), the stream is now immediately blacklisted
  • Previously, these streams would continue to be retried indefinitely, wasting API calls and contributing to circuit breaker trips
  • This is a permanent failure - once a torrent is flagged as infringing by the debrid service, it will never succeed

Changes

  1. models.py: Added InfringingTorrentException class to represent 451 errors with the infohash
  2. realdebrid.py: Detect [451] in error messages and raise InfringingTorrentException
  3. init.py: Catch InfringingTorrentException and immediately blacklist the stream

Test plan

  • Verify syntax with python -m py_compile on all modified files
  • Test with a known infringing torrent hash to confirm blacklisting occurs
  • Confirm circuit breaker no longer trips due to repeated 451 errors

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of infringing torrents in the downloader service. When an infringing torrent is detected, it's now immediately blacklisted and the search continues with other services, rather than failing and retrying unnecessarily.

When Real-Debrid returns HTTP 451 for a torrent (flagged as infringing),
the stream should be immediately blacklisted since it will never succeed
on this debrid service. Previously, these streams would continue to be
retried, wasting API calls and contributing to circuit breaker trips.

Changes:
- Add InfringingTorrentException in models.py
- Raise InfringingTorrentException on 451 errors in realdebrid.py
- Handle InfringingTorrentException in __init__.py to blacklist immediately

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 6, 2026

Walkthrough

A new exception type for infringing torrents is introduced to the downloader service. RealDebrid returns a 451 error code when torrents are infringing; this is now caught and converted to an InfringingTorrentException, which is then handled at the downloader level to blacklist the stream and continue processing.

Changes

Cohort / File(s) Summary
Infringing Torrent Exception Model
src/program/services/downloaders/models.py
New exception class InfringingTorrentException(Exception) added to signal infringing torrent detection, storing the infohash for blacklisting.
RealDebrid Integration
src/program/services/downloaders/realdebrid.py
RealDebrid service now detects 451 error responses and raises InfringingTorrentException instead of returning None.
Exception Handling in Downloader
src/program/services/downloaders/__init__.py
Downloader flow imports and catches InfringingTorrentException, blacklisting the stream and logging a warning before continuing to the next service.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A torrent's caught in legal snare,
Four-fifty-one floats through the air—
We blacklist quick, no retry game,
And move along without the shame!
hops away triumphantly

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: auto-blacklisting streams that return HTTP 451 errors, which is the primary objective of this pull request.
Description check ✅ Passed The description provides a clear summary, details the three file changes, and includes a test plan. However, it does not follow the required template structure with the 'Resolves' issue number or explicit checklist items marked.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/program/services/downloaders/realdebrid.py (1)

279-294: ⚠️ Potential issue | 🟡 Minor

Use exception chaining: raise InfringingTorrentException(infohash) from e.

Ruff B904 correctly flags this. Raising inside an except block without from makes it ambiguous whether this is a deliberate conversion or an accidental error during handling. Chaining with from e preserves the original RealDebridError context for debugging.

Proposed fix
             # 451 = Infringing torrent - raise special exception for immediate blacklisting
             # This is a permanent failure, the torrent will never work on this debrid service
             if "[451]" in error_msg:
-                raise InfringingTorrentException(infohash)
+                raise InfringingTorrentException(infohash) from e
🤖 Fix all issues with AI agents
In `@src/program/services/downloaders/__init__.py`:
- Around line 187-195: The InfringingTorrentException handler in the inner loop
should stop trying other services for the same stream and remove the unused
exception binding: replace the unused "except InfringingTorrentException as e:"
with "except InfringingTorrentException:" and change the trailing "continue"
after calling item.blacklist_stream(stream) to "break" so the loop over
available_services exits for that blacklisted stream (affecting the
InfringingTorrentException handler that logs using stream.infohash and
service.key and sets stream_failed_on_all_services).

Comment on lines +187 to +195
except InfringingTorrentException as e:
# 451 Infringing Torrent - immediately blacklist, do not retry
# This is a permanent failure from the debrid service
logger.warning(
f"Stream {stream.infohash} flagged as infringing by {service.key}, blacklisting immediately"
)
item.blacklist_stream(stream)
stream_failed_on_all_services = False # Already handled via blacklist
continue
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

continue should be break — no point trying other services for a blacklisted stream.

After blacklisting at line 193, the inner for service in available_services loop still continues to the next service. Since the stream is already blacklisted (removed from item.streams and moved to blacklisted_streams), attempting it on remaining services is wasteful. Use break to move on to the next stream.

Also, the as e binding is unused (Ruff F841).

Proposed fix
-                    except InfringingTorrentException as e:
+                    except InfringingTorrentException:
                         # 451 Infringing Torrent - immediately blacklist, do not retry
                         # This is a permanent failure from the debrid service
                         logger.warning(
                             f"Stream {stream.infohash} flagged as infringing by {service.key}, blacklisting immediately"
                         )
                         item.blacklist_stream(stream)
                         stream_failed_on_all_services = False  # Already handled via blacklist
-                        continue
+                        break
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except InfringingTorrentException as e:
# 451 Infringing Torrent - immediately blacklist, do not retry
# This is a permanent failure from the debrid service
logger.warning(
f"Stream {stream.infohash} flagged as infringing by {service.key}, blacklisting immediately"
)
item.blacklist_stream(stream)
stream_failed_on_all_services = False # Already handled via blacklist
continue
except InfringingTorrentException:
# 451 Infringing Torrent - immediately blacklist, do not retry
# This is a permanent failure from the debrid service
logger.warning(
f"Stream {stream.infohash} flagged as infringing by {service.key}, blacklisting immediately"
)
item.blacklist_stream(stream)
stream_failed_on_all_services = False # Already handled via blacklist
break
🧰 Tools
🪛 Ruff (0.14.14)

[error] 187-187: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

🤖 Prompt for AI Agents
In `@src/program/services/downloaders/__init__.py` around lines 187 - 195, The
InfringingTorrentException handler in the inner loop should stop trying other
services for the same stream and remove the unused exception binding: replace
the unused "except InfringingTorrentException as e:" with "except
InfringingTorrentException:" and change the trailing "continue" after calling
item.blacklist_stream(stream) to "break" so the loop over available_services
exits for that blacklisted stream (affecting the InfringingTorrentException
handler that logs using stream.infohash and service.key and sets
stream_failed_on_all_services).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant