Skip to content

Multi-instance Sonarr/Radarr integration mapped to path pairs#426

Merged
nitrobass24 merged 6 commits into
developfrom
feat/multi-arr-instances
Apr 28, 2026
Merged

Multi-instance Sonarr/Radarr integration mapped to path pairs#426
nitrobass24 merged 6 commits into
developfrom
feat/multi-arr-instances

Conversation

@nitrobass24
Copy link
Copy Markdown
Owner

@nitrobass24 nitrobass24 commented Apr 28, 2026

Summary

  • Replaces the single-Sonarr / single-Radarr integration with a list of named *arr instances stored in integrations.json.
  • Each path pair gains arr_target_ids and a chip-row UI for attaching/detaching configured instances.
  • ArrNotifier now routes completed downloads via pair.arr_target_ids instead of broadcasting to a global pair of services.
  • Legacy [Integrations] section in settings.cfg is migrated on startup and dropped on the next persist; behavior is preserved by attaching the migrated instances to every existing path pair.

Closes #425.

Scope

Backend

  • common/integrations_config.py (new): ArrInstance + IntegrationsConfig Persist with CRUD + legacy migration.
  • common/path_pairs_config.py: PathPair gains arr_target_ids; new detach_arr_target(id) helper.
  • common/config.py: drop the flat Integrations inner class; remove its sensitive-property entries.
  • controller/arr_notifier.py: per-pair routing, no orphan-file fallback, kind dispatch (DownloadedEpisodesScan / DownloadedMoviesScan).
  • web/handler/integrations.py: REST CRUD + per-instance test endpoint, redacts api_key and respects the redacted sentinel on PUT.
  • web/handler/path_pairs.py: validates and persists arr_target_ids on create/update.
  • seedsync.py: loads integrations.json; on first boot, reads the old [Integrations] section directly from settings.cfg, builds instances, attaches them to every existing pair, and writes JSON.

Frontend

  • New app-integrations standalone component (list + form + per-instance Test Connection).
  • Path-pair card grows a Notify: chip row with picker for available instances.
  • IntegrationsService rewritten as full CRUD with instances$ BehaviorSubject; static Sonarr/Radarr OPTIONS_CONTEXT_* removed.

Migration behavior

  • Existing config with sonarr_url / radarr_url set: one named instance per service is created and attached to every existing path pair, so today's notifications keep firing.
  • Config without those values: integrations.json starts empty; nothing fires until the user adds an instance and attaches it.
  • Files with no pair_id (orphans, outside any pair): skipped — there's no way to know which *arr owns them in a multi-instance world.

Test plan

  • cd src/python && PYTHONPATH=. python3 -m pytest tests/unittests/test_common tests/unittests/test_controller/test_arr_notifier.py tests/integration/test_web/test_handler/test_integrations.py tests/integration/test_web/test_handler/test_path_pairs.py — 166 passed.
  • cd src/angular && npx ng lint — clean.
  • cd src/angular && npx ng test — 315 passed.
  • cd src/angular && npx ng build — succeeds.
  • Smoke-test in Docker with a config that has the legacy [Integrations] section and a path pair: confirm integrations.json is created with the migrated instance attached, settings.cfg is rewritten without the section on next save, and the Settings page renders the new Integrations card.
  • In the UI: add a Sonarr instance, attach to a path pair, complete a download, confirm Sonarr's Activity → Queue shows the scan. Repeat for Radarr.
  • Delete an attached instance and confirm it disappears from any pair's chip row.
  • Test Connection per instance: success and failure paths.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Dedicated Integrations page to add/edit/delete multiple Sonarr/Radarr instances and run per-instance connection tests.
    • Path pairs can be linked to specific integration instances (attach/detach).
    • Integrations moved to dedicated persistent storage with automatic migration from legacy settings.
  • Bug Fixes

    • Removed dangling references when integrations are deleted; API keys are redacted in responses.

Closes #425.

The *arr integration now stores a list of named Sonarr/Radarr instances
in a new integrations.json. Each path pair declares which instances to
notify via arr_target_ids, so completion scans are routed to the *arrs
that actually own the content. Files outside any path pair are skipped.

The legacy [Integrations] section in settings.cfg is migrated on first
boot: each configured *arr becomes a named instance attached to every
existing path pair, preserving today's behavior. Config.from_dict drops
the section so the next persist rewrites settings.cfg without it.

Frontend: a new Integrations card lists configurable instances with
add/edit/delete/test actions. Each path pair card grows a chip row
("Notify: [Sonarr — TV ✕] [+ Add]") for attaching/detaching configured
instances; deletes detach automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR implements support for multiple Sonarr/Radarr instances with dedicated management. It introduces instance models, refactors the integrations service into a CRUD layer with per-instance endpoints, adds instance attachment to path pairs, and migrates legacy flat configs to instance-based lists.

Changes

Cohort / File(s) Summary
ARR Instance Models
src/angular/src/app/models/arr-instance.ts
Introduces new ArrKind union type, ARR_KINDS constant, and interfaces for ArrInstance, ArrInstanceCreate, and ArrInstanceUpdate to support instance-based configuration.
Angular Config Structure
src/angular/src/app/models/config.ts
Removes Integrations interface and integrations property from Config model, eliminating flat Sonarr/Radarr fields from the config type definition.
Path Pair Model
src/angular/src/app/models/path-pair.ts
Extends PathPair interface with new arr_target_ids: string[] field to associate instances with path pairs.
Integrations UI Component
src/angular/src/app/pages/settings/integrations.component.ts, integrations.component.html, integrations.component.scss
Introduces new standalone IntegrationsComponent with CRUD operations, form validation, connection testing per instance, and dual-confirmation delete. Provides reusable form template for instance configuration (kind, name, URL, API key, enabled toggle) and instance list display with edit/delete controls.
Settings Options
src/angular/src/app/pages/settings/options-list.ts
Removes OPTIONS_CONTEXT_INTEGRATIONS_SONARR and OPTIONS_CONTEXT_INTEGRATIONS_RADARR constants that previously defined inline integration form configurations.
Settings Page Refactoring
src/angular/src/app/pages/settings/settings-page.component.ts, settings-page.component.html, settings-page.component.spec.ts
Removes inline integrations UI and integration test handlers; delegates to new IntegrationsComponent. Removes integration-specific imports and exposed contexts.
Path Pairs Instance Attachment
src/angular/src/app/pages/settings/path-pairs.component.ts, path-pairs.component.html, path-pairs.component.scss, path-pairs.component.spec.ts
Extends path-pair form to include arr_target_ids field; adds UI for viewing/attaching/detaching instances via chips and dropdown picker; includes helper methods for computing attached vs. available instances.
Integrations Service
src/angular/src/app/services/settings/integrations.service.ts, integrations.service.spec.ts
Refactors from Sonarr/Radarr-only test methods into full CRUD service with observable state (instances$), supporting create/update/remove/test operations; updates connection test API to per-instance POST endpoint.
File Service Tests
src/angular/src/app/services/files/view-file.service.spec.ts
Updates mocked PathPair fixtures to include required arr_target_ids: [] field across all test cases.
Python Integrations Config
src/python/common/integrations_config.py, test_integrations_config.py
Introduces new ArrInstance class with UUID-based ID assignment and IntegrationsConfig persistence layer supporting thread-safe instance management, JSON serialization, and legacy migration from flat config fields.
Python Config & Context
src/python/common/config.py, src/python/common/context.py
Removes legacy Config.Integrations inner class and flat fields; adds integrations_config parameter to Context constructor; updates logging to display active integrations.
Python Path Pairs Config
src/python/common/path_pairs_config.py
Extends PathPair to include arr_target_ids: list[str] field; adds detach_arr_target method to update all pairs when an instance is removed.
ARR Notifier Refactoring
src/python/controller/arr_notifier.py, test_arr_notifier.py
Refactors scan dispatch from fixed Sonarr/Radarr to per-instance routing via pair.arr_target_ids; updates constructor to accept IntegrationsConfig instead of main config; skips orphan files without pair_id.
Integrations Handler
src/python/web/handler/integrations.py, test_integrations.py
Converts from two fixed test endpoints to REST API with routes for listing, creating, updating, deleting, and testing instances; includes JSON validation, API key redaction, and instance detachment from path pairs on delete.
Path Pairs Handler
src/python/web/handler/path_pairs.py, test_path_pairs.py
Updates validation and handlers to extract and persist arr_target_ids parameter with type enforcement (list of strings).
Web App Builder
src/python/web/web_app_builder.py
Updates IntegrationsHandler instantiation to receive integrations_config instead of full config.
Seedsync Main
src/python/seedsync.py
Implements integrations.json persistence and legacy migration logic; adds integrations_config to Context; updates ArrNotifier injection.
Config & Settings Tests
src/python/common/__init__.py, src/python/tests/unittests/test_common/test_config.py
Exports new integrations types from common module; updates golden config string by removing [Integrations] section.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Multi-instance Sonarr/Radarr integration mapped to path pairs' clearly and concisely summarizes the main architectural change: replacing single Sonarr/Radarr with multiple named instances attached to path pairs.
Linked Issues check ✅ Passed The PR implementation comprehensively addresses all coding objectives from issue #425: multi-instance CRUD, per-pair attachment, instance-routed scan dispatch by kind, API redaction, and legacy migration.
Out of Scope Changes check ✅ Passed All changes directly support the two-tier instance/path-pair model and associated CRUD/routing/migration requirements; no unrelated modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 feat/multi-arr-instances

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: 5

🤖 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/angular/src/app/pages/settings/integrations.component.scss`:
- Around line 86-88: The Radarr badge (.kind-radarr) uses background-color
`#e6a532` with white text which fails WCAG contrast; update the .kind-radarr rule
to either use a darker background (e.g., a deeper amber/brown) or switch the
text color to a dark color to reach at least 4.5:1 contrast for small
text—modify the .kind-radarr selector in integrations.component.scss accordingly
and verify the new color combination meets WCAG AA contrast ratios.

In `@src/angular/src/app/pages/settings/path-pairs.component.ts`:
- Around line 187-203: Attach/detach currently calls PathPairsService.update()
and immediately closes the picker or silently ignores failures; change
onAttachInstance and onDetachInstance to handle the update Observable explicitly
by providing next and error handlers: perform the arrPickerPairId = null (close
picker) only in the next handler after a successful non-null response, and in
the error handler handle 409/conflict and other failures (e.g., show a
user-facing error toast, restore any optimistic changes or keep the picker open)
instead of silently subscribing; use the existing
takeUntilDestroyed(this.destroyRef) and reference the methods onAttachInstance,
onDetachInstance, PathPairsService.update and the arrPickerPairId field when
implementing these handlers.

In `@src/python/common/path_pairs_config.py`:
- Around line 30-39: The PathPair constructor currently assigns duplicates to
self.arr_target_ids; change the assignment in the initializer (the PathPair
__init__ that sets self.arr_target_ids) to deduplicate the incoming
arr_target_ids while preserving order (e.g., by converting to an ordered-unique
sequence) so duplicates are never stored and duplicate scan commands can't be
emitted.

In `@src/python/seedsync.py`:
- Around line 449-462: The migration updates PathPairsConfig in-memory (via
path_pairs_config.update_pair(pair)) but doesn't persist it, so if the process
dies after writing integrations (ic.to_file) the path_pairs.json changes are
lost; after the loop that sets pair.arr_target_ids and calls update_pair on each
pair, call the PathPairsConfig save API (e.g., path_pairs_config.persist() or
path_pairs_config.to_file(...)—use whatever save method exists on
PathPairsConfig) to write the updated pairs to disk before proceeding to
ic.to_file(file_path) and returning ic.
- Around line 125-131: The integrations migration is happening after defaults
may be written, which can delete the legacy [Integrations] section; move the
legacy integrations read/migration earlier so it runs before any config
rewrite/backfill. Specifically, call _load_integrations_config using
Seedsync.__FILE_INTEGRATIONS and integrations_path before invoking
_backfill_config_defaults (or any code path that calls Config.to_file), ensuring
the legacy section is preserved and migrated prior to any Config.to_file write.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a1abfe84-0e53-4554-a1a4-06b75df7aaf8

📥 Commits

Reviewing files that changed from the base of the PR and between 19e2665 and dc0b0d3.

📒 Files selected for processing (33)
  • src/angular/src/app/models/arr-instance.ts
  • src/angular/src/app/models/config.ts
  • src/angular/src/app/models/path-pair.ts
  • src/angular/src/app/pages/settings/integrations.component.html
  • src/angular/src/app/pages/settings/integrations.component.scss
  • src/angular/src/app/pages/settings/integrations.component.ts
  • src/angular/src/app/pages/settings/options-list.ts
  • src/angular/src/app/pages/settings/path-pairs.component.html
  • src/angular/src/app/pages/settings/path-pairs.component.scss
  • src/angular/src/app/pages/settings/path-pairs.component.spec.ts
  • src/angular/src/app/pages/settings/path-pairs.component.ts
  • src/angular/src/app/pages/settings/settings-page.component.html
  • src/angular/src/app/pages/settings/settings-page.component.spec.ts
  • src/angular/src/app/pages/settings/settings-page.component.ts
  • src/angular/src/app/services/files/view-file.service.spec.ts
  • src/angular/src/app/services/settings/config.service.spec.ts
  • src/angular/src/app/services/settings/integrations.service.spec.ts
  • src/angular/src/app/services/settings/integrations.service.ts
  • src/python/common/__init__.py
  • src/python/common/config.py
  • src/python/common/context.py
  • src/python/common/integrations_config.py
  • src/python/common/path_pairs_config.py
  • src/python/controller/arr_notifier.py
  • src/python/seedsync.py
  • src/python/tests/integration/test_web/test_handler/test_integrations.py
  • src/python/tests/integration/test_web/test_handler/test_path_pairs.py
  • src/python/tests/unittests/test_common/test_config.py
  • src/python/tests/unittests/test_common/test_integrations_config.py
  • src/python/tests/unittests/test_controller/test_arr_notifier.py
  • src/python/web/handler/integrations.py
  • src/python/web/handler/path_pairs.py
  • src/python/web/web_app_builder.py
💤 Files with no reviewable changes (4)
  • src/angular/src/app/services/settings/config.service.spec.ts
  • src/angular/src/app/pages/settings/options-list.ts
  • src/angular/src/app/models/config.ts
  • src/python/tests/unittests/test_common/test_config.py

Comment thread src/angular/src/app/pages/settings/integrations.component.scss Outdated
Comment thread src/angular/src/app/pages/settings/path-pairs.component.ts Outdated
Comment thread src/python/common/path_pairs_config.py
Comment thread src/python/seedsync.py
Comment thread src/python/seedsync.py
nitrobass24 and others added 2 commits April 27, 2026 21:12
- Reformat web_app_builder.py per ruff.
- Drop the helper PathPairDict alias and accept dict[str, Any] in PathPair.from_dict; use cast(list[Any]) to satisfy pyright on element narrowing.
- Inline the legacy-Integrations parser into __migrate_legacy_integrations so kwargs flow through with their declared types instead of being unpacked from a loose dict.
- Apply the same Any+cast pattern in the path_pairs handler validator and annotate its return tuple as list[str].
- Make the integrations card use h3.card-header so the "Integrations" heading still satisfies the settings-page E2E expectation, and scope the path-pairs page object selectors to .path-pairs so Playwright's strict mode no longer collides with the new sibling card.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- WCAG: switch the .kind-radarr badge to dark text on amber (#1a1a1a / #e6a532)
  for ~7.3:1 contrast — white-on-amber was ~2.6:1 and failed AA.

- Surface failures from path-pair attach/detach instead of swallowing them.
  Picker close now happens only on a non-null next, and error/null both set
  errorMessage so the user gets feedback. Conflicts on detach are surfaced too.

- Dedupe arr_target_ids in PathPair.__init__ via dict.fromkeys, so a malformed
  payload (or future regression) can't make ArrNotifier emit duplicate scans.

- Defer the legacy-Integrations rewrite until after migration:
  - _backfill_config_defaults is now applied in-memory only; the to_file write
    moves to AFTER _load_integrations_config, so the [Integrations] section in
    settings.cfg survives long enough to be migrated.
  - _load_integrations_config now persists path_pairs.json BEFORE
    integrations.json. A crash between the two writes is recoverable: the
    next boot has no integrations.json, re-runs the migration, and overwrites
    the attached IDs cleanly. The reverse order would leave instances with no
    pairs referencing them.

- Tests cover the dedup invariant on PathPair and the migration ordering
  (path_pairs.json written first, no migration when integrations.json exists,
  no migration when settings.cfg has no [Integrations] section).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nitrobass24 and others added 2 commits April 28, 2026 15:47
… gaps

Code fixes from PR #426 review:
- Corrupt integrations.json returns empty config instead of re-migrating (C2)
- IntegrationsService.refresh() preserves existing list on HTTP error (C3)
- onToggleEnabled() shows error and refreshes on failure (C4)
- ValueError catch in integrations handler maps to proper status codes (I3)
- arr_target_ids validated against IntegrationsConfig on create/update (I4)
- Migration logs warning when configparser.Error blocks legacy read (I5)
- migrate_from_legacy uses add_instance() instead of direct append (I6)
- _send_post log includes target URL for multi-instance disambiguation (M1)

Test additions:
- _backfill_config_defaults: 2 tests (T1)
- _load_path_pairs_config migration: 4 tests (T2)
- PathPairsConfig round-trip and malformed input: 2 tests (T4)
- Radarr handler parity (missing_api_key, bad_scheme, leak): 3 tests (T6)
- PathPairsComponent error paths (null create/update): 2 tests (T7)
- _send_post scheme guard: 1 test (T8)
- Unknown arr_target_ids validation: 2 tests (I4)
- Refresh error preserves list: 1 test updated (C3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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

🤖 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/angular/src/app/pages/settings/integrations.component.html`:
- Around line 39-40: The Sonarr/Radarr buttons currently lack explicit type
attributes which can cause unintended form submission; update both <button>
elements that call onStartAdd('sonarr') and onStartAdd('radarr') to include
type="button" so they behave as non-submit buttons (i.e., change the two buttons
that invoke the onStartAdd method to explicitly set type="button").
- Around line 53-54: The Save and Cancel buttons (the elements wired to
onSaveAdd() and onCancelAdd()) lack explicit types and may trigger form
submission if placed inside a form; update the two button elements that call
onSaveAdd and onCancelAdd to include type="button" so they act as non-submitting
action buttons and avoid unintended form submits.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 50e19963-4190-418c-9627-27fba5c1b24c

📥 Commits

Reviewing files that changed from the base of the PR and between dc0b0d3 and 9950f58.

📒 Files selected for processing (22)
  • src/angular/src/app/pages/settings/integrations.component.html
  • src/angular/src/app/pages/settings/integrations.component.scss
  • src/angular/src/app/pages/settings/integrations.component.ts
  • src/angular/src/app/pages/settings/path-pairs.component.spec.ts
  • src/angular/src/app/pages/settings/path-pairs.component.ts
  • src/angular/src/app/services/settings/integrations.service.spec.ts
  • src/angular/src/app/services/settings/integrations.service.ts
  • src/e2e-playwright/tests/pages/path-pairs.page.ts
  • src/e2e-playwright/tests/path-pairs.spec.ts
  • src/python/common/integrations_config.py
  • src/python/common/path_pairs_config.py
  • src/python/controller/arr_notifier.py
  • src/python/seedsync.py
  • src/python/tests/integration/test_web/test_handler/test_integrations.py
  • src/python/tests/integration/test_web/test_handler/test_path_pairs.py
  • src/python/tests/integration/test_web/test_web_app.py
  • src/python/tests/unittests/test_common/test_path_pairs_config.py
  • src/python/tests/unittests/test_controller/test_arr_notifier.py
  • src/python/tests/unittests/test_seedsync.py
  • src/python/web/handler/integrations.py
  • src/python/web/handler/path_pairs.py
  • src/python/web/web_app_builder.py

Comment on lines +39 to +40
<button class="btn btn-sm btn-add" (click)="onStartAdd('sonarr')">+ Sonarr</button>
<button class="btn btn-sm btn-add" (click)="onStartAdd('radarr')">+ Radarr</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add type="button" to prevent unintended form submission.

The "Add Sonarr/Radarr" buttons lack explicit type attributes. While not inside a <form>, adding type="button" is defensive and aligns with HTMLHint's recommendation.

🔧 Proposed fix
-        <button class="btn btn-sm btn-add" (click)="onStartAdd('sonarr')">+ Sonarr</button>
-        <button class="btn btn-sm btn-add" (click)="onStartAdd('radarr')">+ Radarr</button>
+        <button type="button" class="btn btn-sm btn-add" (click)="onStartAdd('sonarr')">+ Sonarr</button>
+        <button type="button" class="btn btn-sm btn-add" (click)="onStartAdd('radarr')">+ Radarr</button>
📝 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
<button class="btn btn-sm btn-add" (click)="onStartAdd('sonarr')">+ Sonarr</button>
<button class="btn btn-sm btn-add" (click)="onStartAdd('radarr')">+ Radarr</button>
<button type="button" class="btn btn-sm btn-add" (click)="onStartAdd('sonarr')">+ Sonarr</button>
<button type="button" class="btn btn-sm btn-add" (click)="onStartAdd('radarr')">+ Radarr</button>
🧰 Tools
🪛 HTMLHint (1.9.2)

[warning] 39-39: The type attribute must be present on elements.

(button-type-require)


[warning] 40-40: The type attribute must be present on

elements.

(button-type-require)

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

In `@src/angular/src/app/pages/settings/integrations.component.html` around lines
39 - 40, The Sonarr/Radarr buttons currently lack explicit type attributes which
can cause unintended form submission; update both <button> elements that call
onStartAdd('sonarr') and onStartAdd('radarr') to include type="button" so they
behave as non-submit buttons (i.e., change the two buttons that invoke the
onStartAdd method to explicitly set type="button").

Comment on lines +53 to +54
<button class="btn btn-sm btn-save" (click)="onSaveAdd()">Save</button>
<button class="btn btn-sm btn-cancel" (click)="onCancelAdd()">Cancel</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add type="button" to form action buttons.

These buttons should have explicit type="button" to prevent default form submission behavior if the template is ever wrapped in a form element.

🔧 Proposed fix
-        <button class="btn btn-sm btn-save" (click)="onSaveAdd()">Save</button>
-        <button class="btn btn-sm btn-cancel" (click)="onCancelAdd()">Cancel</button>
+        <button type="button" class="btn btn-sm btn-save" (click)="onSaveAdd()">Save</button>
+        <button type="button" class="btn btn-sm btn-cancel" (click)="onCancelAdd()">Cancel</button>
📝 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
<button class="btn btn-sm btn-save" (click)="onSaveAdd()">Save</button>
<button class="btn btn-sm btn-cancel" (click)="onCancelAdd()">Cancel</button>
<button type="button" class="btn btn-sm btn-save" (click)="onSaveAdd()">Save</button>
<button type="button" class="btn btn-sm btn-cancel" (click)="onCancelAdd()">Cancel</button>
🧰 Tools
🪛 HTMLHint (1.9.2)

[warning] 53-53: The type attribute must be present on elements.

(button-type-require)


[warning] 54-54: The type attribute must be present on

elements.

(button-type-require)

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

In `@src/angular/src/app/pages/settings/integrations.component.html` around lines
53 - 54, The Save and Cancel buttons (the elements wired to onSaveAdd() and
onCancelAdd()) lack explicit types and may trigger form submission if placed
inside a form; update the two button elements that call onSaveAdd and
onCancelAdd to include type="button" so they act as non-submitting action
buttons and avoid unintended form submits.

Prevents unintended form submission if the template structure
changes to include a <form> wrapper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nitrobass24 nitrobass24 merged commit a697133 into develop Apr 28, 2026
17 checks passed
@nitrobass24 nitrobass24 deleted the feat/multi-arr-instances branch April 28, 2026 21:59
nitrobass24 added a commit that referenced this pull request May 20, 2026
In IntegrationsHandler.__handle_delete the two on-disk writes ran in
the wrong order: integrations.json first, then path_pairs.json. A
crash between writes left integrations.json without the instance but
path_pairs.json still referencing it — a dangling arr_target_id that
the cross-validation in #426 rejects on next load.

Swap the order so path_pairs persists first. The worst-case crash
outcome is now an orphaned (no-longer-referenced) instance in
integrations.json, which is harmless — it survives load and gets
cleaned up the next time the user retries the delete.

Adds two integration tests: one spying both to_file calls to assert
the call order, one patching the second write to raise OSError and
verifying the persisted path_pairs.json has the detach applied.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

Support multiple Arr Instances.

1 participant