Files
silo-server/internal/audiobooks/abs/author_series_handler.go
T
RXWatcherandClaude Opus 4.7 1b91791bf3 feat(audiobooks): author + series detail endpoints
GET /authors/{id} (people.id, kind=7 join over item_people) and
GET /series/{id} (case-insensitive series_name match, ordered by
series_index NULLS LAST). Both return entity + embedded books[].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:53:58 +02:00

81 lines
2.1 KiB
Go

package abs
import (
"errors"
"log/slog"
"net/http"
"net/url"
"github.com/go-chi/chi/v5"
)
func (h *Handler) handleAuthorDetail(w http.ResponseWriter, r *http.Request) {
a, ok := absAuthFrom(r)
if !ok || a.UserID == "" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
id := chi.URLParam(r, "id")
author, err := h.deps.MediaStore.GetAuthorByID(r.Context(), id)
if errors.Is(err, ErrNotFound) {
http.Error(w, "author not found", http.StatusNotFound)
return
}
if err != nil {
slog.Error("abs author detail failed", "err", err, "id", id)
http.Error(w, "author get failed", http.StatusInternalServerError)
return
}
writeJSON(w, http.StatusOK, authorToABS(author))
}
func (h *Handler) handleSeriesDetail(w http.ResponseWriter, r *http.Request) {
a, ok := absAuthFrom(r)
if !ok || a.UserID == "" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
idRaw := chi.URLParam(r, "id")
id, err := url.PathUnescape(idRaw)
if err != nil {
id = idRaw
}
series, err := h.deps.MediaStore.GetSeriesByName(r.Context(), id)
if errors.Is(err, ErrNotFound) {
http.Error(w, "series not found", http.StatusNotFound)
return
}
if err != nil {
slog.Error("abs series detail failed", "err", err, "id", id)
http.Error(w, "series get failed", http.StatusInternalServerError)
return
}
writeJSON(w, http.StatusOK, seriesToABS(series))
}
func authorToABS(a Author) map[string]any {
books := make([]map[string]any, 0, len(a.Books))
for _, b := range a.Books {
books = append(books, map[string]any{"id": b.ContentID, "media": map[string]any{"metadata": map[string]any{"title": b.Title}}})
}
return map[string]any{
"id": a.ID,
"name": a.Name,
"numBooks": len(a.Books),
"books": books,
}
}
func seriesToABS(s Series) map[string]any {
books := make([]map[string]any, 0, len(s.Books))
for _, b := range s.Books {
books = append(books, map[string]any{"id": b.ContentID, "media": map[string]any{"metadata": map[string]any{"title": b.Title}}})
}
return map[string]any{
"id": s.ID,
"name": s.Name,
"numBooks": len(s.Books),
"books": books,
}
}