Skip to content

feat: add Sportarr integration (all triggers and payloads) - #1270

Open
Sportarr wants to merge 2 commits into
Notifiarr:mainfrom
Sportarr:sportarr-support
Open

feat: add Sportarr integration (all triggers and payloads)#1270
Sportarr wants to merge 2 commits into
Notifiarr:mainfrom
Sportarr:sportarr-support

Conversation

@Sportarr

@Sportarr Sportarr commented Aug 5, 2026

Copy link
Copy Markdown

Adds Sportarr as a first-class app type, following the same pattern this repo already uses for Lidarr/Radarr/Readarr/Prowlarr: it rides the existing golift.io/starr/sonarr client under its own Sportarr identity, no new dependency.

Every Sonarr-parallel surface is mirrored: the full relay API (44 routes), backup file lists, database corruption checks, TRaSH profile/format sync (new /trigger/sportarrsync endpoint), dashboard stats, stuck/downloading queue triggers, import list and notification connections, client-info/website payloads, the config template, and the full frontend (Starr Apps tab, Integrations status card, locale strings, swagger docs).

MDBList sync was left out on purpose. Sportarr's metadata comes through TheSportsDB via sportarr.net, and its TVDB id is a frozen internal alias, not a real one, so MDBList sync would just be noise, not a real gap.

Tested: go build/vet/gofmt/test clean, golangci-lint clean (linux/freebsd), codespell clean, svelte-check/vite build clean. Verified live against a real Sportarr instance using the actual golift.io/starr client, and by booting this binary itself and driving requests through its own relay end to end, including import lists and notification connections. Requires the next Sportarr release after v4.1.0.

I maintain Sportarr and will keep the integration updated.

Adds Sportarr (https://github.com/Sportarr/Sportarr), a sports event
manager exposing a Sonarr-v3-compatible API, as a first-class app type
riding the existing starr Sonarr client — the same pattern this repo
already uses for Lidarr/Radarr/Readarr/Prowlarr. This is the client-side
half of what davidnewhall asked for on Unpackerr PR Notifiarr#647: "the connect
with all the triggers and payloads."

Every Sonarr-parallel surface is mirrored under its own Sportarr
identity: the full relay API (44 routes), backup file lists, database
corruption checks, TRaSH profile/format sync (new /trigger/sportarrsync
endpoint), dashboard stats, stuck/downloading queue triggers,
client-info/website payloads, the TOML config template, and the full
frontend (Starr Apps tab, Integrations status card, locale strings,
swagger docs, generated TypeScript types).

Changes:
- pkg/apps/sportarr.go: full route mirror of sonarr.go under its own
  SportarrApp identity (a local starr.App constant, since golift.io/starr
  is external and can't be extended)
- pkg/checkapp, pkg/client, pkg/services, pkg/configfile: connection
  test, ping, GUI integrations panel, service checks, tray menu, and
  config template wiring
- pkg/triggers/{backups,cfsync,dashboard,starrqueue}: per-app trigger
  wiring, including a new cfsync/sportarr.go and dashboard/sportarr.go
  mirroring their sonarr.go counterparts
- pkg/triggers/handler.go: HTTP trigger dispatch for corrupt/backup/sync
  by name, plus the new sportarrsync endpoint
- pkg/website/clientinfo: instance counts, configs, version/status
  checks reported to notifiarr.com and the local GUI
- frontend: Sportarr tab in Starr Apps settings, Integrations status
  card, en.json fully translated with the other 11 locales carrying a
  title fallback (English covers the rest via svelte-i18n's per-key
  fallback), regenerated swagger docs and notifiarrConfig.ts, new logo
  asset
- examples/notifiarr.conf.example, README.md, examples/MANUAL.md:
  mention Sportarr alongside the other Starr apps

Verified: go build/vet/gofmt/test clean; golangci-lint clean on all
three CI GOOS targets (linux/darwin/freebsd); codespell clean;
svelte-check 0 errors; vite build succeeds with all 12 locale bundles.
Two real bugs were found and fixed during this verification pass: every
API handler was reading the wrong context value (getSonarr instead of
getSportarr, silently compiling since both symbols exist in the same
package, but would panic at runtime on first real use), and every
swagger @router annotation still pointed at /sonarr/ instead of
/sportarr/, silently colliding routes in the generated docs.

HELD: not pushed, no PR opened, no comment posted anywhere. Pending
owner review.
Comment thread README.md
- Trigger scripts and commands from Discord.
- Media requests from Discord.
- Sync TRaSH profiles to Radarr & Sonarr.
- Sync TRaSH profiles to Radarr, Sonarr & Sportarr.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Trash supports this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah, it's a real thing. We pull the same custom formats and quality profiles straight from the TRaSH-Guides repo, same pattern Recyclarr and a bunch of other tools already use. Wasn't aware that it needed approval, but I'm happy to reach out to the TRaSH team directly if it does.

Only difference from Sonarr/Radarr is we built the sync straight into Sportarr itself instead of leaving it to a separate tool like Recyclarr. Wanted users to get TRaSH quality profiles without having to stand up a second service just for that.

}
}

// minimalSportarrRecord creates a copy of the QueueRecord with only fields needed for stuck item detection.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is @austinwbest going to add stuck item detection for this app?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

True. I am happy to answer anything Austin runs into. It mirrors the Sonarr version pretty closely, so hopefully it's a quick look.

@davidnewhall

davidnewhall commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Feels rather optimistic. We'll discuss and figure out where to go. Thanks for the contribution.

@Sportarr

Sportarr commented Aug 6, 2026

Copy link
Copy Markdown
Author

Feels rather optimistic. We'll discuss and figure out where to go. Thanks for the contribution.

I know it's a lot to review at once! Happy to trim it down or split it into smaller PRs if that's easier too. Thank you to you and the team for all the support thus far. It truly is refreshing in this kind of space.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds Sportarr as a first-class Starr integration using the Sonarr-compatible client.

Changes:

  • Adds Sportarr configuration, relay APIs, health checks, backups, queues, dashboards, and TRaSH sync.
  • Adds frontend configuration/status views, localization, and API documentation.
  • Extends website payloads and trigger routing for Sportarr.

Reviewed changes

Copilot reviewed 53 out of 55 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Documents Sportarr TRaSH sync.
pkg/website/website_routes.go Lists Sportarr website sync support.
pkg/website/clientinfo/handlers.go Adds Sportarr connection status.
pkg/website/clientinfo/clientinfo.go Extends website configuration models.
pkg/website/clientinfo/appinfo.go Reports Sportarr instances.
pkg/triggers/starrqueue/stuckitems.go Adds stuck Sportarr queues.
pkg/triggers/starrqueue/sportarr.go Implements Sportarr queue caching.
pkg/triggers/starrqueue/setup.go Registers Sportarr queue triggers.
pkg/triggers/starrqueue/downloads.go Adds downloading Sportarr items.
pkg/triggers/handler.go Adds Sportarr trigger routing.
pkg/triggers/dashboard/sportarr.go Implements Sportarr dashboard state.
pkg/triggers/dashboard/dashboard.go Includes Sportarr dashboard output.
pkg/triggers/cfsync/sportarr.go Implements Sportarr TRaSH synchronization.
pkg/triggers/cfsync/handler.go Adds Sportarr aggregate sync endpoint.
pkg/triggers/cfsync/common.go Registers Sportarr sync actions.
pkg/triggers/backups/corruption.go Adds Sportarr corruption checks.
pkg/triggers/backups/config.go Defines Sportarr backup triggers.
pkg/triggers/backups/backups.go Adds Sportarr backup lists.
pkg/services/apps.go Adds Sportarr service monitoring.
pkg/configfile/template.go Adds Sportarr configuration template.
pkg/client/tunnel.go Accounts for Sportarr tunnel capacity.
pkg/client/tray.go Adds Sportarr tray actions.
pkg/client/tray_commands.go Displays Sportarr configuration.
pkg/client/init.go Logs Sportarr startup state.
pkg/client/handlers.go Adds Sportarr ping support.
pkg/client/handlers_gui.go Adds Sportarr GUI integration data.
pkg/checkapp/starr.go Implements Sportarr connection checks.
pkg/checkapp/checkapp.go Routes Sportarr instance checks.
pkg/checkapp/checkall.go Includes Sportarr in bulk checks.
pkg/apps/sportarr.go Implements Sportarr relay APIs and client setup.
pkg/apps/setup.go Registers Sportarr configuration and handlers.
pkg/apps/api.go Resolves Sportarr API instances.
frontend/src/pages/starrApps/page.svelte.ts Defines the Sportarr configuration tab.
frontend/src/pages/starrApps/Index.svelte Renders and saves Sportarr instances.
frontend/src/pages/integrations/Starr.svelte Supports Sportarr status details.
frontend/src/pages/integrations/Index.svelte Renders Sportarr integration cards.
frontend/src/pages/integrations/data.ts Adds Sportarr display metadata.
frontend/src/includes/locale/zh_Hant.json Adds Sportarr locale title.
frontend/src/includes/locale/sv.json Adds Sportarr locale title.
frontend/src/includes/locale/pt.json Adds Sportarr locale title.
frontend/src/includes/locale/pl.json Adds Sportarr locale title.
frontend/src/includes/locale/nl.json Adds Sportarr locale title.
frontend/src/includes/locale/it.json Adds Sportarr locale title.
frontend/src/includes/locale/hu.json Adds Sportarr locale title.
frontend/src/includes/locale/fr.json Adds Sportarr locale title.
frontend/src/includes/locale/fi.json Adds Sportarr locale title.
frontend/src/includes/locale/es.json Adds Sportarr locale title.
frontend/src/includes/locale/en.json Adds complete Sportarr UI text.
frontend/src/includes/locale/de.json Adds Sportarr locale title.
frontend/src/api/notifiarrConfig.ts Extends frontend API types.
frontend/public/ui_swagger.json Extends generated UI schemas.
examples/notifiarr.conf.example Adds example Sportarr configuration.
examples/MANUAL.md Documents Sportarr communication support.
Suppressed comments (5)

pkg/apps/sportarr.go:1021

  • This Swagger route omits /update, while the handler is registered at /sportarr/{instance}/update. The generated client documentation currently directs update requests to an unregistered endpoint.
// @Router			/sportarr/{instance} [put]

pkg/apps/sportarr.go:678

  • The delete-all handler is registered at /releaseProfiles/all (plural), but this annotation advertises a different path. Requests copied from the generated docs will not reach the handler.
// @Router			/sportarr/{instance}/releaseProfile/all [delete]

pkg/apps/sportarr.go:1290

  • The GET route is registered as /qualitydefinitions (plural), while this annotation generates the singular path used only by the PUT handler. The documented GET therefore does not exist.
// @Router			/sportarr/{instance}/qualitydefinition [get]

pkg/apps/sportarr.go:1336

  • This handler is registered at singular /notification, but the generated Swagger route is plural. Following the API documentation currently produces a 404.
// @Router			/sportarr/{instance}/notifications [get]

pkg/apps/sportarr.go:1458

  • The actual route accepts DELETE, whereas this annotation publishes it as POST. Generated clients will use a method the router rejects.
// @Router			/sportarr/{instance}/delete/{episodeFileID} [post]

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +259 to +260
case "sportarr":
if instance <= len(c.Apps.Sportarr) && c.Apps.Sportarr[idx].Enabled() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've never seen this panic. I think there is always an instance 0. Will do some more digging...

Comment thread pkg/apps/sportarr.go Outdated
return apiError(http.StatusBadRequest, "decoding payload", err)
}

moveFiles := mux.Vars(req)["moveFiles"] == strconv.FormatBool(true)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this will work if you add .Query() to the mux route.

Comment thread pkg/apps/sportarr.go
return apiError(http.StatusBadRequest, "decoding payload", err)
}

output, err := getSportarr(req).UpdateCustomFormatContext(req.Context(), &cusform)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah this is a weird in Sonarr too. @austinwbest Do you send the cfid in the payload already? We could just remove it from the url.

Comment thread pkg/apps/sportarr.go Outdated
// @Success 201 {object} apps.ApiResponse{message=[]sonarr.ReleaseProfile} "all profiles"
// @Failure 500 {object} apps.ApiResponse{message=string} "instance error"
// @Failure 404 {object} string "bad token or api key"
// @Router /sportarr/{instance}/releaseProfile [get]
Comment thread pkg/triggers/handler.go Outdated
// @Summary Sync TRaSH Sportarr data
// @Tags Triggers,TRaSH
// @Produce json
// @Param instance path bool false "Triggers sync on this instance if provided, otherwise all instances"
…nds check

Addresses the review findings.

The update handler read moveFiles from mux.Vars, which only holds path
variables, so the flag was always false and callers could never move
files. It now reads the query string the same way the lidarr and radarr
handlers do.

Six swagger annotations documented routes that are not registered, so the
generated docs described endpoints that 404: releaseProfiles and
qualitydefinitions were documented singular, notification was documented
plural, deleting an episode file was documented as POST rather than
DELETE, and updating a series was documented at the bare instance path
instead of /update. The annotations and the generated definitions now
match the registered routes. The sportarrsync trigger documented its
instance parameter as a boolean, but the handler parses it with Atoi.

Instances are 1-indexed and the route pattern accepts any digits, so a
request for instance 0 reached a slice at index -1 and panicked. The
length checks did not catch it because zero is below them. A single guard
now rejects anything under 1 before the switch.

Note for the maintainers: sonarr.go carries the same query-param bug and
the same six documentation mismatches, and the radarrsync and sonarrsync
triggers document the same boolean parameter. The bounds guard covers
every app, since they all share this function. I left the sonarr files
alone to keep this pull request to its own integration, and I am happy to
fix them here or separately, whichever you prefer.
@Sportarr

Copy link
Copy Markdown
Author

Fixed above in a89e903.

Instance 0 does panic. Confirmed with zero, one and two instances configured. Added a single guard before the switch since every app shares that path.

moveFiles now comes off the query string, matching lidarr and radarr.

Six swagger routes didn't match their registrations, two beyond what Copilot listed. Delete episode file was POST instead of DELETE, and update series sat on the bare instance path instead of /update. Fixed and regenerated.

Left cfid for you, happy to match whatever you decide for Sonarr.

sonarr.go has the same moveFiles bug and the same six mismatches if you want them picked up.

@davidnewhall

Copy link
Copy Markdown
Contributor

I'd just hold off on additional changes for now. You're going to keep getting conflicts as I massage away other problems.

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.

3 participants