Skip to content

feat(trackers): add NordicBytes tracker support#1320

Open
flah69 wants to merge 23 commits into
Audionut:masterfrom
flah69:tracker/nordicbytes
Open

feat(trackers): add NordicBytes tracker support#1320
flah69 wants to merge 23 commits into
Audionut:masterfrom
flah69:tracker/nordicbytes

Conversation

@flah69
Copy link
Copy Markdown

@flah69 flah69 commented Mar 24, 2026

Summary by CodeRabbit

  • New Features

    • Added support for NordicBytes (NB) tracker: category mapping, upload validations, interactive upload prompts, description/logo retrieval, and name formatting.
  • Documentation

    • Updated Supported Sites table to list NordicBytes and adjusted the MoreThanTV row.
  • Chores

    • Added NordicBytes (NB) to the example tracker configuration.

@github-actions
Copy link
Copy Markdown

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 Mar 24, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Documentation & Config
README.md, data/example-config.py
Updated Supported Sites table (added NordicBytes (NB), removed Nebulance alias from MoreThanTV); added NB to TRACKERS acronym list and provided TRACKERS["NB"] example block.
Tracker Implementation
src/trackers/NB.py
New NB class (subclass of UNIT3D) with category mapping, pre-upload checks (reject .PAD, enforce title charset and -GROUP, disallow hardcoded/auto-translated subs, block upscale/AI tokens), language normalization helper, name formatting (spaces→dots), description builder that fetches TMDB logo, mod-queue opt-in metadata, and interactive reject/confirm gating.
Tracker Registration
src/trackersetup.py
Imported NB and added 'NB'NB to tracker_class_map for tracker discovery/instantiation.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • Audionut

Poem

🐇 I hopped into NB with a curious nose,
I chased out PADs and watched title prose,
Dots for spaces, logos fetched on cue,
A gentle nudge when choices are due —
Hooray for NB, hopped and through! 🎉

🚥 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 'feat(trackers): add NordicBytes tracker support' accurately describes the main change: adding a new NordicBytes tracker implementation across multiple files.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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

🧹 Nitpick comments (2)
src/trackers/NB.py (2)

32-33: Clean up boilerplate.

The banned_groups contains an empty string which may cause unexpected behavior if code iterates over it expecting actual group names. The pass statement 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_manager is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 105a92f and 915bb28.

📒 Files selected for processing (4)
  • README.md
  • data/example-config.py
  • src/trackers/NB.py
  • src/trackersetup.py

Comment thread data/example-config.py
Comment thread src/trackers/NB.py Outdated
Comment thread src/trackersetup.py
@flah69
Copy link
Copy Markdown
Author

flah69 commented Mar 24, 2026

Small issues has now been fixed :)

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

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

4-5: ⚠️ Potential issue | 🔴 Critical

Still importing Optional as a module.

Optional on Line 57 is a typing symbol, not a module. Unless there is a local Optional.py, this will fail to import; if you keep the override below, it needs to come from typing.

🐛 Proposed fix
-from typing import Any, cast
-import Optional
+from typing import Any, Optional, cast

Quick verification: this should show Optional[...] is used in the file and that the supported form is typing.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).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc0e5add-3138-49f2-873e-1e31dd38c321

📥 Commits

Reviewing files that changed from the base of the PR and between 915bb28 and 15ae371.

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

Comment thread src/trackers/NB.py Outdated
Comment thread src/trackers/NB.py
Comment thread src/trackers/NB.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.

Actionable comments posted: 2

♻️ Duplicate comments (3)
src/trackers/NB.py (3)

54-66: ⚠️ Potential issue | 🟠 Major

Either inherit UNIT3D.get_category_id() or mirror its full contract.

Line 61 explicitly discards category, reverse, and mapping_only, so this override only supports the forward meta['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 | 🟡 Minor

Preserve the base comparison=True behavior.

UNIT3D.get_description() enables comparison screenshots. Dropping that flag here changes the rendered description for releases that include them; either delegate back to super() after the logo enrichment or pass comparison=True explicitly.

📝 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 | 🟠 Major

Validate the exact -GROUP suffix.

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9d161b2-6e59-47c4-9d8e-b6e16a5c855d

📥 Commits

Reviewing files that changed from the base of the PR and between 76db8af and daa9e58.

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

Comment thread src/trackers/NB.py
Comment thread src/trackers/NB.py Outdated
@flah69 flah69 changed the title Adding NordicBytes tracker feat(trackers): add NordicBytes tracker support Mar 26, 2026
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