feat(api): add POST /api/media-items/:id/rescan endpoint

Admin-only endpoint that re-extracts metadata and cover art for a single
book from the file on disk via MediaScanner.RescanMediaItem and returns
the updated media item. Normal library scans skip unchanged files, so
this gives a targeted way to backfill covers for previously imported
books. Includes a Bruno request alongside the existing media-items
collection.
This commit is contained in:
2026-09-11 23:08:20 -04:00
parent 0614795ca2
commit 298330a3a8
3 changed files with 72 additions and 0 deletions
+36
View File
@@ -1309,6 +1309,42 @@ func (mh *MediaHandler) UpdateMediaItem(c *echo.Context) error {
return c.JSON(http.StatusOK, item)
}
// RescanMediaItem handles POST /api/media-items/:id/rescan (admin only)
func (mh *MediaHandler) RescanMediaItem(c *echo.Context) error {
user := MustGetAuthenticatedUser(c)
if user.Role != "admin" {
return c.JSON(http.StatusForbidden, map[string]string{"error": "admin access required"})
}
mediaID := c.Param("id")
mediaUUID, err := uuid.Parse(mediaID)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid media item id"})
}
if _, err := mh.db.GetMediaItem(c.Request().Context(), pgtype.UUID{Bytes: mediaUUID, Valid: true}); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return c.JSON(http.StatusNotFound, map[string]string{"error": "media item not found"})
}
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
scanner := services.NewMediaScanner(mh.db)
defer scanner.Close()
if err := scanner.RescanMediaItem(c.Request().Context(), pgtype.UUID{Bytes: mediaUUID, Valid: true}); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
item, err := mh.db.GetMediaItem(c.Request().Context(), pgtype.UUID{Bytes: mediaUUID, Valid: true})
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusOK, item)
}
// DeleteMediaItem handles DELETE /api/media-items/:id (admin only)
func (mh *MediaHandler) DeleteMediaItem(c *echo.Context) error {
user := MustGetAuthenticatedUser(c)
+1
View File
@@ -60,6 +60,7 @@ func registerMediaRoutes(cfg *Config) {
// Admin-only media routes
admin.POST("/media-items", cfg.MediaHandler.CreateMediaItem)
admin.PUT("/media-items/:id", cfg.MediaHandler.UpdateMediaItem)
admin.POST("/media-items/:id/rescan", cfg.MediaHandler.RescanMediaItem)
admin.DELETE("/media-items/:id", cfg.MediaHandler.DeleteMediaItem)
// Shelf management (protected)