feat(trackers): add NordicBytes tracker support#1320
Conversation
Add NordicBytes (NB) to list of available trackers
|
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. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new NordicBytes (NB) UNIT3D tracker: new tracker class, example config entry, README update, and tracker registration. NB implements category mapping, upload validation rules, name/description builders, subtitle-language normalization, mod-queue flag handling, and optional interactive prompts. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Uploader
participant NBTracker as NB
participant TMDB as TmdbManager
participant User
Uploader->>NBTracker: submit meta + files
NBTracker->>NBTracker: _normalize_lang_set(meta['subtitle_languages'])
NBTracker->>NBTracker: get_category_id(meta)
NBTracker->>NBTracker: get_additional_checks(meta)
alt rejection or confirmation required
NBTracker->>User: _reject_or_confirm(reason)
User-->>NBTracker: confirm / reject
end
NBTracker->>TMDB: get_logo(title, preferred_languages) (if logo missing)
TMDB-->>NBTracker: logo URL
NBTracker->>Uploader: return validated meta, name, description, additional data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/trackers/NB.py (2)
32-33: Clean up boilerplate.The
banned_groupscontains an empty string which may cause unexpected behavior if code iterates over it expecting actual group names. Thepassstatement is also unnecessary.♻️ Proposed fix
- self.banned_groups = [""] - pass + self.banned_groups = []🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/trackers/NB.py` around lines 32 - 33, The field self.banned_groups currently contains an empty string and an unnecessary pass; update the class initialization in NB.py to use an empty list for banned_groups (self.banned_groups = []) and remove the redundant pass statement so callers don't accidentally iterate over a bogus "" entry and code is cleaner; locate the change around the class __init__ where self.banned_groups and pass are defined.
10-10: Unused import.
languages_manageris imported but not used in this file.♻️ Proposed fix
-from src.languages import languages_manager🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/trackers/NB.py` at line 10, Remove the unused import by deleting the "languages_manager" import statement (the line "from src.languages import languages_manager") in src/trackers/NB.py; if the tracker really needs language functionality instead of the import being unused, replace the bare import with the actual usage where needed (e.g., call languages_manager.some_method() in the relevant function or class), but otherwise delete the unused "languages_manager" import to eliminate the dead import.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@data/example-config.py`:
- Line 353: Add a missing "NB" tracker entry to the TRACKERS dictionary so
NordicBytes users can set credentials; insert a "NB": { "link_dir_name": "",
"api_key": "", "anon": False, "modq": False } block (matching the shape of other
tracker entries such as "MTV") to allow custom link folder names, API key,
anonymous flag, and modq toggle; ensure the new key is a top-level entry inside
TRACKERS and follows the same formatting/indentation style as other tracker
entries.
In `@src/trackers/NB.py`:
- Around line 1-4: The file is using Optional (reported at the use site around
line 56) but it is not imported; update the typing import statement in
src/trackers/NB.py (the line that currently imports Any and cast) to also import
Optional so references to Optional in functions/classes (e.g., where
Optional[...] is used) resolve at runtime and avoid NameError.
In `@src/trackersetup.py`:
- Line 55: The NB tracker class is imported but not registered: add an entry
mapping 'NB' to the NB class in the tracker_class_map (refer to the
tracker_class_map dictionary) so the code recognizes the tracker (e.g., add
'NB': NB, alongside other tracker registrations), and if NB uses UNIT3D API
uploads also include 'NB' in the api_trackers set (refer to the api_trackers
definition) to ensure it is treated as an API-based tracker.
---
Nitpick comments:
In `@src/trackers/NB.py`:
- Around line 32-33: The field self.banned_groups currently contains an empty
string and an unnecessary pass; update the class initialization in NB.py to use
an empty list for banned_groups (self.banned_groups = []) and remove the
redundant pass statement so callers don't accidentally iterate over a bogus ""
entry and code is cleaner; locate the change around the class __init__ where
self.banned_groups and pass are defined.
- Line 10: Remove the unused import by deleting the "languages_manager" import
statement (the line "from src.languages import languages_manager") in
src/trackers/NB.py; if the tracker really needs language functionality instead
of the import being unused, replace the bare import with the actual usage where
needed (e.g., call languages_manager.some_method() in the relevant function or
class), but otherwise delete the unused "languages_manager" import to eliminate
the dead import.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0342d6f0-0815-424f-8878-ba5f22e2644b
📒 Files selected for processing (4)
README.mddata/example-config.pysrc/trackers/NB.pysrc/trackersetup.py
|
Small issues has now been fixed :) |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/trackers/NB.py (1)
4-5:⚠️ Potential issue | 🔴 CriticalStill importing
Optionalas a module.
Optionalon Line 57 is a typing symbol, not a module. Unless there is a localOptional.py, this will fail to import; if you keep the override below, it needs to come fromtyping.🐛 Proposed fix
-from typing import Any, cast -import Optional +from typing import Any, Optional, castQuick verification: this should show
Optional[...]is used in the file and that the supported form istyping.Optional.#!/bin/bash sed -n '1,70p' src/trackers/NB.py python - <<'PY' from pathlib import Path import importlib.util text = Path("src/trackers/NB.py").read_text() print("uses Optional[...] =", "Optional[" in text) print("has `import Optional` =", any(line.strip() == "import Optional" for line in text.splitlines())) print("find_spec('Optional') =", importlib.util.find_spec("Optional")) from typing import Optional print("typing.Optional works =", Optional[str]) PY🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/trackers/NB.py` around lines 4 - 5, The file imports Optional as a module which will fail; update the typing imports in src/trackers/NB.py by removing the top-level "import Optional" and instead import Optional from typing (e.g. add Optional to the existing "from typing import Any, cast" so it becomes "from typing import Any, cast, Optional"); check any local override references in NB.py that expect typing.Optional and ensure they use the typing symbol (e.g., occurrences of Optional[...] remain valid).
🤖 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/NB.py`:
- Around line 90-96: The current check in NB.validate uses presence of any
hyphen in upload_name to infer the group separator, which allows titles like
"Spider-Man" to bypass validation; instead verify the actual "-GROUP" separator
by ensuring the upload_name ends with the exact tag suffix (use tag_value), e.g.
replace the hyphen test with a check like if tag_value and not
upload_name.endswith(f'-{tag_value}') and then call
self._reject_or_confirm(meta, f'{self.tracker} title appears to be missing the
group separator before tag (-GROUP).', allow_override=True) so the code confirms
the correct "-{tag}" suffix rather than any hyphen.
- Around line 54-66: This override of get_category_id in NB.py ignores the
method's parameters and returns a different shape than the UNIT3D contract;
either remove this override so the base UNIT3D.get_category_id is used, or
implement full parity by accepting and using the parameters (category, reverse,
mapping_only, meta) and returning the same dict shape and behavior as
UNIT3D.get_category_id; locate the get_category_id method in class NB and either
delete it to inherit UNIT3D behavior or update it to mirror UNIT3D's logic
(including respecting category argument, reverse handling, mapping_only
behavior, and returning the exact expected dict structure).
- Line 135: The current return in NB.py replaces the description call and loses
the original comparison=True behavior; update the return from
DescriptionBuilder(...).unit3d_edit_desc(meta) to preserve comparison by calling
the method with comparison=True (e.g., await DescriptionBuilder(self.tracker,
self.config).unit3d_edit_desc(meta, comparison=True)) while still applying the
meta['logo'] enrichment beforehand so UNIT3D.get_description() behavior for
comparison screenshots remains intact.
---
Duplicate comments:
In `@src/trackers/NB.py`:
- Around line 4-5: The file imports Optional as a module which will fail; update
the typing imports in src/trackers/NB.py by removing the top-level "import
Optional" and instead import Optional from typing (e.g. add Optional to the
existing "from typing import Any, cast" so it becomes "from typing import Any,
cast, Optional"); check any local override references in NB.py that expect
typing.Optional and ensure they use the typing symbol (e.g., occurrences of
Optional[...] remain valid).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/trackers/NB.py (3)
54-66:⚠️ Potential issue | 🟠 MajorEither inherit
UNIT3D.get_category_id()or mirror its full contract.Line 61 explicitly discards
category,reverse, andmapping_only, so this override only supports the forwardmeta['category']lookup. If NB uses the default movie/TV mapping, the safest fix is to remove this override.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/trackers/NB.py` around lines 54 - 66, The override of get_category_id in NB.py ignores the parameters category, reverse, and mapping_only and only looks up meta['category'], breaking the base UNIT3D.get_category_id contract; either remove this method so UNIT3D.get_category_id is used, or implement the full contract by handling the category, reverse, and mapping_only args (or delegating to super().get_category_id(meta, category=..., reverse=..., mapping_only=...)) and return the same dict shape; update the function get_category_id to respect those parameters rather than discarding them.
135-135:⚠️ Potential issue | 🟡 MinorPreserve the base
comparison=Truebehavior.
UNIT3D.get_description()enables comparison screenshots. Dropping that flag here changes the rendered description for releases that include them; either delegate back tosuper()after the logo enrichment or passcomparison=Trueexplicitly.📝 Minimal fix
- return {'description': await DescriptionBuilder(self.tracker, self.config).unit3d_edit_desc(meta)} + return {'description': await DescriptionBuilder(self.tracker, self.config).unit3d_edit_desc(meta, comparison=True)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/trackers/NB.py` at line 135, The current NB.get_description() call returns only DescriptionBuilder(...).unit3d_edit_desc(...) and drops the base behavior that enables comparison screenshots; update NB.get_description (or the method invoking DescriptionBuilder) to preserve comparison=True by either calling super().get_description(...) after enriching the logo or by passing comparison=True into the DescriptionBuilder/unit3d_edit_desc call so the returned dict matches the base implementation (i.e., ensure get_description returns the result of super().get_description(...) or includes comparison=True when constructing/returning the description).
90-96:⚠️ Potential issue | 🟠 MajorValidate the exact
-GROUPsuffix.Line 91 treats any hyphen in the title as proof that the group separator is present, so any release name that already contains
-can bypass this check. Compare against the normalized tag suffix instead.🐛 Suggested fix
tag_value = str(meta.get('tag', '')).strip() - if tag_value and '-' not in upload_name: + normalized_tag = tag_value if tag_value.startswith('-') else f'-{tag_value}' + if tag_value and not upload_name.endswith(normalized_tag): return self._reject_or_confirm( meta, f'{self.tracker} title appears to be missing the group separator before tag (-GROUP).', allow_override=True, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/trackers/NB.py` around lines 90 - 96, The check incorrectly treats any '-' in upload_name as the group separator; instead compute a normalized tag suffix and verify the upload name ends with that exact suffix. Replace the hyphen-existence test with something like: normalize tag_value (strip and lower), then if tag_value and not upload_name.lower().endswith(f'-{tag_value}'): call self._reject_or_confirm(meta, f'{self.tracker} title appears to be missing the group separator before tag (-{tag_value.upper()}).', allow_override=True). Use the same symbols tag_value, upload_name, meta and self._reject_or_confirm so the intent is clear.
🤖 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/NB.py`:
- Around line 117-118: The code currently reads meta.get('tmdb') into
tmdb_id_raw and converts it to tmdb_id, which can miss or mis-target the
canonical integer ID; change the lookup to prefer meta.get('tmdb_id') (falling
back to meta.get('tmdb') if needed), then parse that value into tmdb_id (using
the existing int/isdigit guard) so logo enrichment uses the canonical tmdb_id;
update the variables tmdb_id_raw and tmdb_id in the same block (in NB.py)
accordingly.
- Around line 21-27: The constructor NB.__init__ is overwriting the tracker key
by setting self.tracker = 'NordicBytes' — leave self.tracker as the
UNIT3D-provided code ('NB') and introduce a separate display attribute (e.g.,
self.display_name or self.tracker_display) for the human-readable "NordicBytes"
string; update any UI/logging usage to reference the new display attribute while
leaving DescriptionBuilder and inherited helpers to continue using self.tracker
('NB').
---
Duplicate comments:
In `@src/trackers/NB.py`:
- Around line 54-66: The override of get_category_id in NB.py ignores the
parameters category, reverse, and mapping_only and only looks up
meta['category'], breaking the base UNIT3D.get_category_id contract; either
remove this method so UNIT3D.get_category_id is used, or implement the full
contract by handling the category, reverse, and mapping_only args (or delegating
to super().get_category_id(meta, category=..., reverse=..., mapping_only=...))
and return the same dict shape; update the function get_category_id to respect
those parameters rather than discarding them.
- Line 135: The current NB.get_description() call returns only
DescriptionBuilder(...).unit3d_edit_desc(...) and drops the base behavior that
enables comparison screenshots; update NB.get_description (or the method
invoking DescriptionBuilder) to preserve comparison=True by either calling
super().get_description(...) after enriching the logo or by passing
comparison=True into the DescriptionBuilder/unit3d_edit_desc call so the
returned dict matches the base implementation (i.e., ensure get_description
returns the result of super().get_description(...) or includes comparison=True
when constructing/returning the description).
- Around line 90-96: The check incorrectly treats any '-' in upload_name as the
group separator; instead compute a normalized tag suffix and verify the upload
name ends with that exact suffix. Replace the hyphen-existence test with
something like: normalize tag_value (strip and lower), then if tag_value and not
upload_name.lower().endswith(f'-{tag_value}'): call
self._reject_or_confirm(meta, f'{self.tracker} title appears to be missing the
group separator before tag (-{tag_value.upper()}).', allow_override=True). Use
the same symbols tag_value, upload_name, meta and self._reject_or_confirm so the
intent is clear.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Fix for eg. "xx - yy" being "xx.-.yy" instead of "xx.yy"
Add NordicBytes
Summary by CodeRabbit
New Features
Documentation
Chores