diff --git a/templates/book_detail_modals.templ b/templates/book_detail_modals.templ
index 1e4242e..7ccb6fe 100644
--- a/templates/book_detail_modals.templ
+++ b/templates/book_detail_modals.templ
@@ -586,22 +586,41 @@ templ MetadataEditorModal(book handlers.MediaDetail) {
class="input opacity-60" />
+
+
+
+
-
+
+
+
+
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, "Save")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/web/src/book-detail.ts b/web/src/book-detail.ts
index aae5cb1..f845a73 100644
--- a/web/src/book-detail.ts
+++ b/web/src/book-detail.ts
@@ -96,6 +96,7 @@ interface MetadataEditorState {
coverAction: string;
saving: boolean;
rescanning: boolean;
+ resetting: boolean;
userRating: number;
ratingHover: number;
ratingSaving: boolean;
@@ -109,6 +110,7 @@ interface MetadataEditorState {
removeCover(): void;
saveMetadata(): Promise;
rescanBook(): Promise;
+ resetMetadata(): Promise;
starFill(i: number): string;
ratingText(): string;
setRating(value: number): Promise;
@@ -142,6 +144,7 @@ Alpine.data("bookDetail", () => {
coverAction: "keep",
saving: false,
rescanning: false,
+ resetting: false,
userRating: 0,
ratingHover: 0,
ratingSaving: false,
@@ -550,6 +553,41 @@ Alpine.data("bookDetail", () => {
}
},
+ async resetMetadata() {
+ if (this.resetting) return;
+ if (
+ !window.confirm(
+ "Reset all metadata to scanned defaults? Manual edits and custom covers will be discarded.",
+ )
+ ) {
+ return;
+ }
+ this.resetting = true;
+ const mediaId = getMediaId();
+ try {
+ const resp = await fetch(
+ `/api/media-items/${mediaId}/rescan?reset_overrides=true`,
+ {
+ method: "POST",
+ headers: { Authorization: getAuthHeader() },
+ },
+ );
+ if (!resp.ok) {
+ const err = await resp.json().catch(() => ({}));
+ throw new Error(err.error || "Failed to reset metadata");
+ }
+ showToast("Metadata reset to scanned defaults", "success");
+ setTimeout(() => window.location.reload(), 500);
+ } catch (e) {
+ showToast(
+ e instanceof Error ? e.message : "Failed to reset metadata",
+ "error",
+ );
+ } finally {
+ this.resetting = false;
+ }
+ },
+
async searchEditorTags() {
const libraryId = document.body.getAttribute("data-library-id") || "";
if (!this.tagSearch || this.tagSearch.length < 2 || !libraryId) {