diff --git a/internal/handlers/media.go b/internal/handlers/media.go
index f61b85ed..cf8606d2 100644
--- a/internal/handlers/media.go
+++ b/internal/handlers/media.go
@@ -1542,6 +1542,13 @@ func (mh *MediaHandler) UpdateMediaItem(c *echo.Context) error {
return c.JSON(http.StatusNotFound, map[string]string{"error": "media item not found"})
}
+ // Edit segregation (plan D10, task 1.8): podcast episodes are
+ // fetcher-owned (D9 — the sole writer); the generic edit box would
+ // clobber feed metadata that the next refresh overwrites anyway.
+ if existing.Source == "podcast_episode" {
+ return c.JSON(http.StatusBadRequest, map[string]string{"error": "podcast episodes are managed by their feed and cannot be edited here"})
+ }
+
coverPath := existing.CoverImagePath.String
coverUploaded := false
diff --git a/internal/services/metadata_writer.go b/internal/services/metadata_writer.go
index ede392d2..3fef039c 100644
--- a/internal/services/metadata_writer.go
+++ b/internal/services/metadata_writer.go
@@ -110,6 +110,14 @@ func (w *MetadataWriter) WriteMetadataToFile(ctx context.Context, itemID uuid.UU
return nil, fmt.Errorf("media item not found: %w", err)
}
+ // Edit segregation (plan D10/D9, task 1.8): podcast episodes are
+ // feed-managed; writing their metadata into the file would be
+ // clobbered by the next download/refresh and violates the
+ // fetcher's sole-writer rule.
+ if item.Source == "podcast_episode" {
+ return nil, fmt.Errorf("podcast episodes are managed by their feed; metadata write-to-file is not available")
+ }
+
absPath, err := w.libraries.ResolveMediaPath(ctx, item.LibraryID, item.FilePath)
if err != nil {
return nil, fmt.Errorf("cannot resolve library folder: %w", err)
diff --git a/templates/book_detail.templ b/templates/book_detail.templ
index 46628e6b..6743c0ff 100644
--- a/templates/book_detail.templ
+++ b/templates/book_detail.templ
@@ -176,7 +176,7 @@ templ BookDetail(user User, book handlers.MediaDetail, errorMessage string, tomb
{ book.NotesCount + book.HighlightsCount }
}
- if user.Role == "admin" {
+ if user.Role == "admin" && book.LibraryTypeName != "podcasts" {