feat(audiobooks): wire Phase 1 close-out stores + mount routes
Wires RSSFeedStore into BuildABSHandler. Mounts the ten new
authenticated routes (stats x3 + author x1 + series x1 + continue x2
+ feeds-auth x3) under both /abs/api and /api inside bearerAuth.
Mounts the three public RSS routes (.xml + slug + /file/{ino}) in
the unauth public block. handleAuthorImage wired to CoverResolver.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
82bafb8375
commit
c735cd53b5
@@ -317,6 +317,11 @@ func (h *Handler) mountRoutes(r chi.Router) {
|
||||
r.Head(prefix+"/public/session/{sid}/track/{idx}", h.handlePublicTrack)
|
||||
}
|
||||
|
||||
// Public RSS feed routes — slug is the capability token, no auth.
|
||||
r.Get("/feed/{slug}.xml", h.handlePublicFeed)
|
||||
r.Get("/feed/{slug}", h.handlePublicFeed)
|
||||
r.Get("/feed/{slug}/file/{ino}", h.handlePublicFeedFile)
|
||||
|
||||
// Stage 3: playback session + file routes, registered under both the
|
||||
// legacy /abs/api prefix and the canonical /api prefix that the official
|
||||
// ABS mobile client builds against (no /abs prefix at server root).
|
||||
@@ -380,6 +385,17 @@ func (h *Handler) mountRoutes(r chi.Router) {
|
||||
r.Get(prefix+"/me/smart-collections/{id}/items", h.handleSmartCollectionItems)
|
||||
r.Patch(prefix+"/me/smart-collections/{id}", h.handleUpdateSmartCollection)
|
||||
r.Delete(prefix+"/me/smart-collections/{id}", h.handleDeleteSmartCollection)
|
||||
// Phase 1 close-out: listening stats / author+series / continue / RSS auth.
|
||||
r.Get(prefix+"/me/listening-stats", h.handleListeningStats)
|
||||
r.Get(prefix+"/me/listening-sessions", h.handleListeningSessions)
|
||||
r.Get(prefix+"/me/listening-sessions/{sid}", h.handleListeningSessionDetail)
|
||||
r.Get(prefix+"/authors/{id}", h.handleAuthorDetail)
|
||||
r.Get(prefix+"/series/{id}", h.handleSeriesDetail)
|
||||
r.Get(prefix+"/me/progress/{itemId}/remove-from-continue-listening", h.handleRemoveFromContinueListening)
|
||||
r.Get(prefix+"/me/progress/{itemId}/readd-to-continue-listening", h.handleReaddToContinueListening)
|
||||
r.Get(prefix+"/feeds", h.handleListRSSFeeds)
|
||||
r.Post(prefix+"/feeds/item/{itemId}/open", h.handleOpenItemFeed)
|
||||
r.Post(prefix+"/feeds/{id}/close", h.handleCloseFeed)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -243,12 +243,27 @@ func (h *Handler) handleItemCover(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
||||
|
||||
// handleAuthorImage — GET /abs/api/authors/{id}/image (unauthenticated)
|
||||
//
|
||||
// Author images are not yet stored in silo; return a clean 404 so the
|
||||
// ABS client renders the placeholder.
|
||||
func (h *Handler) handleAuthorImage(w http.ResponseWriter, _ *http.Request) {
|
||||
http.NotFound(w, nil)
|
||||
// handleAuthorImage — GET /authors/{id}/image. Public unauthenticated
|
||||
// route (mounted outside bearerAuth in handler.go). Uses MediaStore
|
||||
// to resolve the people row, then CoverResolver to mint a presigned
|
||||
// URL and 302-redirect to it.
|
||||
func (h *Handler) handleAuthorImage(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
author, err := h.deps.MediaStore.GetAuthorByID(r.Context(), id)
|
||||
if err != nil || author.PosterPath == "" {
|
||||
http.Error(w, "author image not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if h.deps.CoverResolver == nil {
|
||||
http.Error(w, "image resolver not configured", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
url := h.deps.CoverResolver(r.Context(), author.PosterPath, "")
|
||||
if url == "" {
|
||||
http.Error(w, "image resolution failed", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, url, http.StatusFound)
|
||||
}
|
||||
|
||||
// handleLibraryAuthors — GET /abs/api/libraries/{id}/authors
|
||||
|
||||
@@ -112,6 +112,11 @@ func (s *Service) BuildABSHandler(deps ABSHandlerDeps) *abs.Handler {
|
||||
smartCollectionStore = &ABSSmartCollectionStore{Pool: deps.Pool}
|
||||
}
|
||||
|
||||
var rssFeedStore abs.RSSFeedStore
|
||||
if deps.Pool != nil {
|
||||
rssFeedStore = &ABSRSSFeedStore{Pool: deps.Pool}
|
||||
}
|
||||
|
||||
var configProvider abs.ConfigProvider
|
||||
if deps.Settings != nil {
|
||||
configProvider = &ABSConfigProvider{Settings: deps.Settings}
|
||||
@@ -151,6 +156,7 @@ func (s *Service) BuildABSHandler(deps ABSHandlerDeps) *abs.Handler {
|
||||
CollectionStore: collectionStore,
|
||||
PlaylistStore: playlistStore,
|
||||
SmartCollectionStore: smartCollectionStore,
|
||||
RSSFeedStore: rssFeedStore,
|
||||
SocketIO: socketServer,
|
||||
CoverResolver: func(ctx context.Context, path, variant string) string {
|
||||
if deps.Detail == nil {
|
||||
|
||||
Reference in New Issue
Block a user