0.2.63 update

This commit is contained in:
ponzischeme89
2026-08-14 11:47:32 +12:00
parent 9a8ecbdceb
commit 06b6490ac9
41 changed files with 921 additions and 179 deletions
+87 -7
View File
@@ -48,7 +48,7 @@ type requestLookupResponse struct {
}
func (s *Server) requestAllowed(r *http.Request, sess store.Session) bool {
if s.store == nil || (s.sonarr == nil && s.radarr == nil) {
if s.store == nil || (!s.sonarrEnabled(r.Context()) && !s.radarrEnabled(r.Context())) {
return false
}
policy, err := s.store.RequestPolicy(r.Context())
@@ -73,7 +73,7 @@ func (s *Server) handleRequestLookup(w http.ResponseWriter, r *http.Request, ses
var movieCandidates []requestCandidate
var seriesCandidates []requestCandidate
var wg sync.WaitGroup
if s.radarr != nil {
if s.radarrEnabled(r.Context()) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -98,7 +98,7 @@ func (s *Server) handleRequestLookup(w http.ResponseWriter, r *http.Request, ses
}
}()
}
if s.sonarr != nil {
if s.sonarrEnabled(r.Context()) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -235,7 +235,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request, sess stor
switch req.MediaType {
case "movie":
if s.radarr == nil {
if !s.radarrEnabled(r.Context()) {
s.logMediaRequest(r.Context(), req, "failed", errors.New("movie requests are not configured"))
writeError(w, http.StatusServiceUnavailable, "movie requests are not configured")
return
@@ -265,13 +265,22 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request, sess stor
return
}
req.Title = movie.Title
added, err := s.radarr.AddRequested(r.Context(), movie)
requestOptions, rootFolder, profileName, err := s.radarrRequestOptions(r.Context())
if err != nil {
s.logRadarrRequest(r.Context(), sess, req, movie, 0, "failed", "", 0, "", false, err)
s.publishRadarrRequestConfigurationProblem(r.Context(), err)
writeError(w, http.StatusServiceUnavailable, "movie requests are unavailable: "+err.Error())
return
}
added, err := s.radarr.AddRequested(r.Context(), movie, rootFolder, requestOptions)
if err != nil {
s.logRadarrRequest(r.Context(), sess, req, movie, 0, "failed", profileName, requestOptions.QualityProfileID, rootFolder, requestOptions.SearchImmediately, err)
s.logMediaRequest(r.Context(), req, "failed", err)
s.writeRequestUpstreamError(r.Context(), w, err, "could not request that movie")
return
}
req.Title = added.Title
s.logRadarrRequest(r.Context(), sess, req, added, added.ID, "successful", profileName, requestOptions.QualityProfileID, rootFolder, requestOptions.SearchImmediately, nil)
s.recordMediaRequest(r.Context(), sess, req, added.Year,
radarrCoverURL(added.Images, "poster"))
s.logMediaRequest(r.Context(), req, "successful", nil)
@@ -279,7 +288,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request, sess stor
return
}
case "series":
if s.sonarr == nil {
if !s.sonarrEnabled(r.Context()) {
s.logMediaRequest(r.Context(), req, "failed", errors.New("series requests are not configured"))
writeError(w, http.StatusServiceUnavailable, "series requests are not configured")
return
@@ -392,7 +401,7 @@ func (s *Server) writeRequestUpstreamError(
// sonarrRequestOptions validates every component before a POST can reach Sonarr. A missing
// configured profile is an error, not permission to fall back to Sonarr's "Any" profile.
func (s *Server) sonarrRequestOptions(ctx context.Context) (sonarr.RequestOptions, string, string, error) {
if s.sonarr == nil {
if !s.sonarrEnabled(ctx) {
return sonarr.RequestOptions{}, "", "", errors.New("Sonarr integration is unavailable")
}
policy, err := s.store.SonarrRequestPolicy(ctx)
@@ -465,3 +474,74 @@ func (s *Server) publishSonarrRequestConfigurationProblem(ctx context.Context, e
Link: "/admin/integrations", Metadata: adminevents.Meta(map[string]any{"error": err.Error()}),
})
}
func (s *Server) radarrRequestOptions(ctx context.Context) (radarr.RequestOptions, string, string, error) {
if !s.radarrEnabled(ctx) {
return radarr.RequestOptions{}, "", "", errors.New("Radarr integration is unavailable")
}
policy, err := s.store.RadarrRequestPolicy(ctx)
if err != nil {
return radarr.RequestOptions{}, "", "", errors.New("could not read the Radarr request policy")
}
roots, err := s.radarr.RootFolders(ctx)
if err != nil {
return radarr.RequestOptions{}, "", "", errors.New("could not validate Radarr root folders")
}
if len(roots) == 0 || strings.TrimSpace(roots[0].Path) == "" {
return radarr.RequestOptions{}, "", "", errors.New("Radarr has no valid root folder")
}
profiles, err := s.radarr.QualityProfiles(ctx)
if err != nil {
return radarr.RequestOptions{}, "", "", errors.New("could not validate Radarr quality profiles")
}
profileID := policy.QualityProfileID
if profileID == 0 {
for _, profile := range profiles {
if is720pProfile(profile.Name) {
profileID = profile.ID
break
}
}
if profileID == 0 {
return radarr.RequestOptions{}, "", "", errors.New("no request quality profile is configured and Radarr has no 720p profile")
}
policy.QualityProfileID = profileID
if err := s.store.SetRadarrRequestPolicy(ctx, policy); err != nil {
return radarr.RequestOptions{}, "", "", errors.New("could not save the default Radarr request quality profile")
}
}
for _, profile := range profiles {
if profile.ID == profileID {
return radarr.RequestOptions{QualityProfileID: profile.ID, SearchImmediately: policy.SearchImmediately}, roots[0].Path, profile.Name, nil
}
}
return radarr.RequestOptions{}, "", "", errors.New("the configured Radarr request quality profile no longer exists")
}
func (s *Server) logRadarrRequest(
ctx context.Context, sess store.Session, req requestPayload, movie radarr.Movie, movieID int,
outcome, profileName string, profileID int, rootFolder string, searchImmediately bool, err error,
) {
fields := []any{
"user", clientLogValue(sess.Username), "user_id", sess.EmbyUserID,
"title", clientLogValue(movie.Title), "tmdb_id", movie.TMDBID,
"radarr_movie_id", movieID, "quality_profile", profileName,
"quality_profile_id", profileID, "monitoring_strategy", "movie",
"root_folder", rootFolder, "search_immediately", searchImmediately, "outcome", outcome,
}
if err != nil {
fields = append(fields, "radarr_result", err.Error())
s.loggerFor(ctx).Warn("Radarr movie request", fields...)
return
}
fields = append(fields, "radarr_result", "created")
s.loggerFor(ctx).Info("Radarr movie request", fields...)
}
func (s *Server) publishRadarrRequestConfigurationProblem(ctx context.Context, err error) {
s.publishAdmin(ctx, adminevents.Event{
Type: "radarr.request_configuration", Severity: adminevents.SeverityError,
Title: "Radarr movie requests need attention", Summary: err.Error(), Actor: "memby-server",
Link: "/admin/integrations", Metadata: adminevents.Meta(map[string]any{"error": err.Error()}),
})
}