feat: add Sportarr integration (all triggers and payloads) - #1270
feat: add Sportarr integration (all triggers and payloads)#1270Sportarr wants to merge 2 commits into
Conversation
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.
| - Trigger scripts and commands from Discord. | ||
| - Media requests from Discord. | ||
| - Sync TRaSH profiles to Radarr & Sonarr. | ||
| - Sync TRaSH profiles to Radarr, Sonarr & Sportarr. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Is @austinwbest going to add stuck item detection for this app?
There was a problem hiding this comment.
True. I am happy to answer anything Austin runs into. It mirrors the Sonarr version pretty closely, so hopefully it's a quick look.
|
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. |
There was a problem hiding this comment.
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.
| case "sportarr": | ||
| if instance <= len(c.Apps.Sportarr) && c.Apps.Sportarr[idx].Enabled() { |
There was a problem hiding this comment.
I've never seen this panic. I think there is always an instance 0. Will do some more digging...
| return apiError(http.StatusBadRequest, "decoding payload", err) | ||
| } | ||
|
|
||
| moveFiles := mux.Vars(req)["moveFiles"] == strconv.FormatBool(true) |
There was a problem hiding this comment.
I think this will work if you add .Query() to the mux route.
| return apiError(http.StatusBadRequest, "decoding payload", err) | ||
| } | ||
|
|
||
| output, err := getSportarr(req).UpdateCustomFormatContext(req.Context(), &cusform) |
There was a problem hiding this comment.
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.
| // @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] |
| // @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.
|
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. |
|
I'd just hold off on additional changes for now. You're going to keep getting conflicts as I massage away other problems. |
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/sonarrclient under its ownSportarridentity, 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/sportarrsyncendpoint), 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/testclean,golangci-lintclean (linux/freebsd),codespellclean,svelte-check/vite buildclean. Verified live against a real Sportarr instance using the actualgolift.io/starrclient, 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.