Skip to content

fix(HUNO): use the API's simplified upload method#1328

Open
wastaken7 wants to merge 5 commits intomasterfrom
huno-fix
Open

fix(HUNO): use the API's simplified upload method#1328
wastaken7 wants to merge 5 commits intomasterfrom
huno-fix

Conversation

@wastaken7
Copy link
Copy Markdown
Collaborator

@wastaken7 wastaken7 commented Apr 3, 2026

Summary by CodeRabbit

  • New Features

    • Added an announce URL configuration for HUNO and a debug-mode upload simulation.
  • Bug Fixes

    • Stricter validation of media attributes and tracker upload requirements.
    • Improved API error handling with clearer tracker status messages on failures.
    • Expanded approved hostnames for image assets.
  • Chores

    • Reworked HUNO upload and data-processing flow and adjusted tracker-specific result mapping.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

Thanks for taking the time to contribute to this project. Upload Assistant is currently in a complete rewrite, and no new development is being conducted on this python source at this time.

If you have come this far, please feel free to leave open, any pull requests regarding new sites being added to the source, as these can serve as the baseline for later conversion.

If your pull request relates to a critical bug, this will be addressed in this code base, and a new release published as needed.

If your pull request only addresses a quite minor bug, it is not likely to be addressed in this code base.

Details for the new code base will follow at a later date.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

Added announce_url config for HUNO and substantially refactored the HUNO tracker: new upload flow (file assembly, multipart POST via httpx, API-key requirement, debug mode), description file output to tmp, new mapping helpers, and HUNO-specific request parsing in tracker setup. (≤50 words)

Changes

Cohort / File(s) Summary
HUNO Configuration
data/example-config.py
Added announce_url key under the HUNO tracker with default https://hawke.uno/announce/<PASSKEY>.
HUNO Tracker Rewrite
src/trackers/HUNO.py
Major rewrite: new instance attributes (source_flag, requests_url, announce_url from config); expanded approved_image_hosts; removed many legacy field-generator helpers; get_description() now writes [HUNO]DESCRIPTION.txt to tmp and returns None; get_internal() returns raw int; added get_resolution_id(), get_type_id(), get_data(), get_files(); upload() now requires api_token (fails fast), supports debug fake upload, builds multipart payloads (torrent + description/mediainfo/bdinfo), posts via httpx, and records detailed success/failure messages in meta["tracker_status"].
Tracker Request Parsing
src/trackersetup.py
get_tracker_requests() branches for HUNO: uses tmdbId query param and maps results using each.get("attributes", each) and each.get("id") for HUNO while keeping prior mapping for other trackers.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant HUNO_Tracker as "HUNO Tracker"
    participant FileSystem as "Temp Filesystem"
    participant HUNO_API as "HUNO API"

    Client->>HUNO_Tracker: upload(meta)
    HUNO_Tracker->>HUNO_Tracker: validate api_token & announce_url
    alt missing api_token or announce_url
        HUNO_Tracker->>Client: set meta["skipping"]=tracker, return False
    else
        HUNO_Tracker->>HUNO_Tracker: build payload (get_data)
        HUNO_Tracker->>FileSystem: create/read torrent, description, mediainfo/bdinfo
        alt debug mode
            HUNO_Tracker->>HUNO_Tracker: construct fake response
            HUNO_Tracker->>Client: return True
        else
            HUNO_Tracker->>HUNO_API: POST multipart via httpx (files + api_token)
            HUNO_API->>HUNO_Tracker: JSON response (success/failure)
            HUNO_Tracker->>HUNO_Tracker: update meta["tracker_status"][tracker]["status_message"]
            HUNO_Tracker->>Client: return True/False
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Audionut

"I hopped through code with nimble paws,
Files and payloads sorted without pause.
Announce URL tucked, description penned,
Multipart torrents bundled to send.
HUNO hops forward — uploads to the end. 🐇"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring HUNO tracker to use the API's simplified upload method instead of the previous field-generator approach.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch huno-fix

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.

@wastaken7 wastaken7 changed the title fix(HUNO) fix(HUNO): use the API's simplified upload method Apr 3, 2026
Copy link
Copy Markdown

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/trackers/HUNO.py`:
- Around line 205-224: The file reads for building the files dict (calls around
self.common.create_torrent_for_upload and variables torrent_path, desc_path,
mediainfo_path, bdinfo_path that populate files["torrent"],
files["description"], files["mediainfo"/"bdinfo"]) occur before the try that
reports tracker status, so missing/corrupt files raise unhandled exceptions;
move the entire file-gathering block (creation of files dict and all
aiofiles.open reads) inside the existing try that begins later (the block that
handles tracker upload/response) so any read errors are caught and the tracker
status/reporting path executes, and ensure files remains a dict scoped to that
try and is cleaned up or logged on exception.
- Line 29: The code directly indexes
self.config["TRACKERS"][self.tracker]["announce_url"] which raises KeyError for
older configs; change the assignment in HUNO.__init__ (or where
self.announce_url is set) to safely fetch the value (e.g., use .get on the
TRACKERS dict or try/except) and default to None or an empty string when
missing, and ensure any later use of self.announce_url handles the None/empty
case so uploads don’t crash.

In `@src/trackersetup.py`:
- Line 589: The plain console.print(data) call is printing the full API response
every run; wrap it behind a debug/verbose check so full payloads are only shown
when debugging. Replace the direct console.print(data) in trackersetup.py with a
conditional that checks an existing debug flag or config (e.g., if debug or if
settings.DEBUG) or fall back to an environment-controlled boolean (e.g., if
os.getenv("DEBUG") == "1"), and only then call console.print(data); otherwise
remove or replace with a minimal log (e.g., logger.debug/console.log summary) to
avoid exposing full responses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 401f432c-76a9-4862-ba59-ef51a5389ecd

📥 Commits

Reviewing files that changed from the base of the PR and between a5a7693 and 61efbe1.

📒 Files selected for processing (3)
  • data/example-config.py
  • src/trackers/HUNO.py
  • src/trackersetup.py

Comment thread src/trackers/HUNO.py Outdated
Comment thread src/trackers/HUNO.py Outdated
Comment thread src/trackersetup.py Outdated
Copy link
Copy Markdown

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

♻️ Duplicate comments (1)
src/trackers/HUNO.py (1)

194-196: ⚠️ Potential issue | 🟠 Major

Move get_data() into guarded error flow to prevent unhandled upload crashes.

Line 195 executes await self.get_data(meta) before the try; since get_data() now does file I/O via get_description(), failures there bypass tracker-status error handling.

Proposed fix
 async def upload(self, meta: dict[str, Any], _: str = "") -> bool:
-        data = await self.get_data(meta)
-
         # Initialize tracker status
         meta.setdefault("tracker_status", {})
         meta["tracker_status"].setdefault(self.tracker, {})

         api_token = str(self.config["TRACKERS"][self.tracker].get("api_key", ""))
         if not api_token:
             console.print(f"[bold red]{self.tracker}: Missing API key in config.[/bold red]")
             meta["skipping"] = self.tracker
             return False

         url = f"{self.upload_url}?api_token={api_token}"

-        if meta.get("debug", False):
-            console.print(f"[cyan]{self.tracker} Request Data:")
-            console.print(data)
-            meta["tracker_status"][self.tracker]["status_message"] = "Debug mode enabled, not uploading."
-            await self.common.create_torrent_for_upload(meta, f"{self.tracker}_DEBUG", f"{self.tracker}_DEBUG", announce_url="https://fake.tracker")
-            return True
-
         try:
+            data = await self.get_data(meta)
+            if meta.get("debug", False):
+                console.print(f"[cyan]{self.tracker} Request Data:")
+                console.print(data)
+                meta["tracker_status"][self.tracker]["status_message"] = "Debug mode enabled, not uploading."
+                await self.common.create_torrent_for_upload(meta, f"{self.tracker}_DEBUG", f"{self.tracker}_DEBUG", announce_url="https://fake.tracker")
+                return True
+
             files: dict[str, tuple[str, bytes, str]] = {}
             await self.common.create_torrent_for_upload(meta, self.tracker, self.source_flag, announce_url=self.announce_url)
             torrent_path = f"{meta['base_dir']}/tmp/{meta['uuid']}/[{self.tracker}].torrent"
             async with aiofiles.open(torrent_path, "rb") as f:
                 files["torrent"] = (f"{meta['clean_name']}.torrent", await f.read(), "application/x-bittorrent")

Also applies to: 216-263

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/trackers/HUNO.py` around lines 194 - 196, The call to await
self.get_data(meta) happens before the try/except, so any I/O errors from
get_description()/get_data() escape the tracker's status handling; move the
get_data call inside the existing try block (and wrap it with the same exception
handling used for tracker status updates) so that failures are caught and
processed by the tracker error flow—apply the same change to the other
upload-related calls in the same file referenced around the 216-263 region (any
other await self.get_data(...) or calls that trigger get_description()) so they
are executed inside try blocks that update tracker status/error handling (refer
to the upload method and the other upload-related functions in HUNO.py).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/trackers/HUNO.py`:
- Around line 194-196: The call to await self.get_data(meta) happens before the
try/except, so any I/O errors from get_description()/get_data() escape the
tracker's status handling; move the get_data call inside the existing try block
(and wrap it with the same exception handling used for tracker status updates)
so that failures are caught and processed by the tracker error flow—apply the
same change to the other upload-related calls in the same file referenced around
the 216-263 region (any other await self.get_data(...) or calls that trigger
get_description()) so they are executed inside try blocks that update tracker
status/error handling (refer to the upload method and the other upload-related
functions in HUNO.py).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb47b827-edb2-4ca9-954c-a037056882f8

📥 Commits

Reviewing files that changed from the base of the PR and between 61efbe1 and 9bd662c.

📒 Files selected for processing (2)
  • src/trackers/HUNO.py
  • src/trackersetup.py

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
src/trackers/HUNO.py (1)

67-70: Consider using .get() for valid_mi_settings access.

Based on the context from src/prep.py, meta['valid_mi_settings'] is set unconditionally to True before the ENCODE validation, but if that code path isn't reached (e.g., different prep flow), direct dictionary access could raise KeyError.

Proposed defensive fix
-        if not meta["valid_mi_settings"]:
+        if not meta.get("valid_mi_settings", True):
             console.print(f"{self.tracker}: [bold red]No encoding settings in mediainfo, skipping upload.[/bold red]")
             return False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/trackers/HUNO.py` around lines 67 - 70, The access to
meta["valid_mi_settings"] can raise KeyError if that key isn’t set; change the
check in the HUNO tracker (the block that prints "{self.tracker}: No encoding
settings..." and returns False) to use meta.get("valid_mi_settings", False) so
missing keys are treated as invalid and the same console.print + return False
path is executed; update any other direct meta["valid_mi_settings"] usages in
the same function to use .get(...) for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/trackers/HUNO.py`:
- Around line 78-110: The encoding_settings variable is used outside the
Video-track branch causing a possible NameError and the CRF/bitrate checks run
for non-video tracks; fix by moving the entire CRF/bitrate validation logic
inside the if track.get("@type") == "Video": block (so encoding_settings is
assigned and used in the same scope), and after handling a Video track
(processing crf_match or bitrate) break out of the for tracks loop or return
immediately so you don't re-run validation for subsequent non-video tracks;
reference the variables/functions: tracks, track, encoding_settings, crf_match,
bit_rate, and meta to locate and scope the changes.

---

Nitpick comments:
In `@src/trackers/HUNO.py`:
- Around line 67-70: The access to meta["valid_mi_settings"] can raise KeyError
if that key isn’t set; change the check in the HUNO tracker (the block that
prints "{self.tracker}: No encoding settings..." and returns False) to use
meta.get("valid_mi_settings", False) so missing keys are treated as invalid and
the same console.print + return False path is executed; update any other direct
meta["valid_mi_settings"] usages in the same function to use .get(...) for
consistency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4bbf5974-523c-4f0d-bdb1-0405fc553c15

📥 Commits

Reviewing files that changed from the base of the PR and between 9bd662c and 55c2bb4.

📒 Files selected for processing (1)
  • src/trackers/HUNO.py

Comment thread src/trackers/HUNO.py Outdated
Comment on lines +78 to +110
if not meta["is_disc"] and meta["type"] in ["ENCODE", "DVDRIP", "HDTV"] and ("x265" in meta.get("video_encode", "") or "HEVC" in meta.get("video_codec", "")):
tracks = meta.get("mediainfo", {}).get("media", {}).get("track", [])
for track in tracks:
if track.get('@type') == "Video":
encoding_settings = track.get('Encoded_Library_Settings', {})

if encoding_settings:
crf_match = re.search(r'crf[ =:]+([\d.]+)', encoding_settings, re.IGNORECASE)
if crf_match:
if meta.get('debug', False):
console.print(f"Found CRF value: {crf_match.group(1)}")
crf_value = float(crf_match.group(1))
if crf_value > 22:
if not meta['unattended']:
console.print(f"CRF value too high: {crf_value} for HUNO")
return False
else:
if meta.get('debug', False):
console.print("No CRF value found in encoding settings.")
bit_rate = track.get('BitRate')
if bit_rate and "Animation" not in meta.get('genre', ""):
try:
bit_rate_num = int(bit_rate)
except (ValueError, TypeError):
bit_rate_num = None

if bit_rate_num is not None:
bit_rate_kbps = bit_rate_num / 1000

if bit_rate_kbps < 3000:
if not meta.get('unattended', False):
console.print(f"Video bitrate too low: {bit_rate_kbps:.0f} kbps for HUNO")
return False
if track.get("@type") == "Video":
encoding_settings = track.get("Encoded_Library_Settings", {})

if encoding_settings:
crf_match = re.search(r"crf[ =:]+([\d.]+)", encoding_settings, re.IGNORECASE)
if crf_match:
if meta.get("debug", False):
console.print(f"Found CRF value: {crf_match.group(1)}")
crf_value = float(crf_match.group(1))
if crf_value > 22:
if not meta["unattended"]:
console.print(f"CRF value too high: {crf_value} for HUNO")
return False
else:
if meta.get("debug", False):
console.print("No CRF value found in encoding settings.")
bit_rate = track.get("BitRate")
if bit_rate and "Animation" not in meta.get("genre", ""):
try:
bit_rate_num = int(bit_rate)
except (ValueError, TypeError):
bit_rate_num = None

if bit_rate_num is not None:
bit_rate_kbps = bit_rate_num / 1000

if bit_rate_kbps < 3000:
if not meta.get("unattended", False):
console.print(f"Video bitrate too low: {bit_rate_kbps:.0f} kbps for HUNO")
return False
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Variable encoding_settings may be referenced before assignment.

The if encoding_settings: block at line 84 is outside the if track.get("@type") == "Video": block, but encoding_settings is only assigned inside that condition. If the first track in tracks is not a Video track, this raises NameError. Additionally, the CRF/bitrate validation runs for every track iteration after finding a Video track, which is likely unintended.

Proposed fix to scope encoding_settings check correctly
         if not meta["is_disc"] and meta["type"] in ["ENCODE", "DVDRIP", "HDTV"] and ("x265" in meta.get("video_encode", "") or "HEVC" in meta.get("video_codec", "")):
             tracks = meta.get("mediainfo", {}).get("media", {}).get("track", [])
             for track in tracks:
                 if track.get("@type") == "Video":
                     encoding_settings = track.get("Encoded_Library_Settings", {})
-
-                if encoding_settings:
-                    crf_match = re.search(r"crf[ =:]+([\d.]+)", encoding_settings, re.IGNORECASE)
-                    if crf_match:
-                        if meta.get("debug", False):
-                            console.print(f"Found CRF value: {crf_match.group(1)}")
-                        crf_value = float(crf_match.group(1))
-                        if crf_value > 22:
-                            if not meta["unattended"]:
-                                console.print(f"CRF value too high: {crf_value} for HUNO")
-                            return False
-                    else:
-                        if meta.get("debug", False):
-                            console.print("No CRF value found in encoding settings.")
-                        bit_rate = track.get("BitRate")
-                        if bit_rate and "Animation" not in meta.get("genre", ""):
-                            try:
-                                bit_rate_num = int(bit_rate)
-                            except (ValueError, TypeError):
-                                bit_rate_num = None
-
-                            if bit_rate_num is not None:
-                                bit_rate_kbps = bit_rate_num / 1000
-
-                                if bit_rate_kbps < 3000:
-                                    if not meta.get("unattended", False):
-                                        console.print(f"Video bitrate too low: {bit_rate_kbps:.0f} kbps for HUNO")
-                                    return False
+                    if encoding_settings:
+                        crf_match = re.search(r"crf[ =:]+([\d.]+)", encoding_settings, re.IGNORECASE)
+                        if crf_match:
+                            if meta.get("debug", False):
+                                console.print(f"Found CRF value: {crf_match.group(1)}")
+                            crf_value = float(crf_match.group(1))
+                            if crf_value > 22:
+                                if not meta["unattended"]:
+                                    console.print(f"CRF value too high: {crf_value} for HUNO")
+                                return False
+                        else:
+                            if meta.get("debug", False):
+                                console.print("No CRF value found in encoding settings.")
+                            bit_rate = track.get("BitRate")
+                            if bit_rate and "Animation" not in meta.get("genre", ""):
+                                try:
+                                    bit_rate_num = int(bit_rate)
+                                except (ValueError, TypeError):
+                                    bit_rate_num = None
+
+                                if bit_rate_num is not None:
+                                    bit_rate_kbps = bit_rate_num / 1000
+
+                                    if bit_rate_kbps < 3000:
+                                        if not meta.get("unattended", False):
+                                            console.print(f"Video bitrate too low: {bit_rate_kbps:.0f} kbps for HUNO")
+                                        return False
+                    break  # Found Video track, no need to continue
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/trackers/HUNO.py` around lines 78 - 110, The encoding_settings variable
is used outside the Video-track branch causing a possible NameError and the
CRF/bitrate checks run for non-video tracks; fix by moving the entire
CRF/bitrate validation logic inside the if track.get("@type") == "Video": block
(so encoding_settings is assigned and used in the same scope), and after
handling a Video track (processing crf_match or bitrate) break out of the for
tracks loop or return immediately so you don't re-run validation for subsequent
non-video tracks; reference the variables/functions: tracks, track,
encoding_settings, crf_match, bit_rate, and meta to locate and scope the
changes.

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
src/trackers/HUNO.py (1)

182-182: Parameter type shadows Python builtin.

Ruff flags this (A002). Consider renaming to type_str or source_type for clarity.

Suggested fix
-    async def get_type_id(self, meta: dict[str, Any], type: str = "", reverse: bool = False, mapping_only: bool = False) -> dict[str, str]:
+    async def get_type_id(self, meta: dict[str, Any], type_str: str = "", reverse: bool = False, mapping_only: bool = False) -> dict[str, str]:
         type_id = {
             ...
         }
         ...
-        elif type:
-            return {"type_id": type_id.get(type, "0")}
+        elif type_str:
+            return {"type_id": type_id.get(type_str, "0")}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/trackers/HUNO.py` at line 182, The parameter name "type" in the async
function get_type_id(...) shadows the Python builtin and triggers lint A002;
rename that parameter (e.g., to type_str or source_type) across the function
signature and all its internal uses in get_type_id (and update any external
callers in this diff) to preserve behavior and avoid the shadowing warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/trackers/HUNO.py`:
- Line 182: The parameter name "type" in the async function get_type_id(...)
shadows the Python builtin and triggers lint A002; rename that parameter (e.g.,
to type_str or source_type) across the function signature and all its internal
uses in get_type_id (and update any external callers in this diff) to preserve
behavior and avoid the shadowing warning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb66db14-a6ce-4000-96fd-a08ee836f4ba

📥 Commits

Reviewing files that changed from the base of the PR and between 55c2bb4 and 1d5c675.

📒 Files selected for processing (1)
  • src/trackers/HUNO.py

@bencollinz
Copy link
Copy Markdown

bencollinz commented Apr 4, 2026

I tested this, and while it works, it returns a display error and puts the upload in the modq

this PR may address that #1322

This has been working without error/issue for the last day and a half. not sure if they changed something on their end.

And it's back to not working again. They must've changed something again.

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.

2 participants