Show metadata as read-only by default
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.4.1 - 2026-09-15
|
||||||
|
|
||||||
|
- Changed title details to show metadata as read-only text by default.
|
||||||
|
- Added an explicit `Edit` action that opens the metadata form only when needed.
|
||||||
|
|
||||||
## 1.4.0 - 2026-09-15
|
## 1.4.0 - 2026-09-15
|
||||||
|
|
||||||
- Added missing metadata fetching from AniList, with AniDB fallback when AniList has no usable match or is unavailable.
|
- Added missing metadata fetching from AniList, with AniDB fallback when AniList has no usable match or is unavailable.
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ If an older `data/metadata.json` file exists, the app imports it into SQLite on
|
|||||||
|
|
||||||
## Metadata Fields
|
## Metadata Fields
|
||||||
|
|
||||||
Use the app to edit:
|
Title details show metadata as normal read-only text by default. Use the `Edit` button to change:
|
||||||
|
|
||||||
- display title
|
- display title
|
||||||
- short description
|
- short description
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "h-player",
|
"name": "h-player",
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "A small private web app for browsing a folder-based video collection.",
|
"description": "A small private web app for browsing a folder-based video collection.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+61
-26
@@ -120,25 +120,26 @@ function renderDetails(title) {
|
|||||||
<p>${escapeHtml(title.folder)} · ${title.count} file${title.count === 1 ? "" : "s"} · ${bytes(title.size)}</p>
|
<p>${escapeHtml(title.folder)} · ${title.count} file${title.count === 1 ? "" : "s"} · ${bytes(title.size)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary__actions">
|
<div class="summary__actions">
|
||||||
|
<button id="editMetaBtn" type="button">Edit</button>
|
||||||
<button id="fetchMetadataBtn" type="button">Metadata</button>
|
<button id="fetchMetadataBtn" type="button">Metadata</button>
|
||||||
<button id="makeThumbBtn" type="button">Thumbnail</button>
|
<button id="makeThumbBtn" type="button">Thumbnail</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form id="metaForm" class="meta-form">
|
<div id="metaView" class="meta-view">
|
||||||
<label>
|
<section>
|
||||||
<span>Display title</span>
|
<span>Display title</span>
|
||||||
<input name="title" value="${escapeAttr(title.title)}">
|
<p>${escapeHtml(title.title)}</p>
|
||||||
</label>
|
</section>
|
||||||
<label>
|
<section>
|
||||||
<span>Thumbnail path or URL</span>
|
<span>Thumbnail</span>
|
||||||
<input name="thumbnail" value="${escapeAttr(title.thumbnail)}" placeholder="/thumbs/example.jpg">
|
<p>${title.thumbnail ? escapeHtml(title.thumbnail) : "No thumbnail set"}</p>
|
||||||
</label>
|
</section>
|
||||||
<label>
|
<section>
|
||||||
<span>Short description</span>
|
<span>Description</span>
|
||||||
<textarea name="description" rows="5" placeholder="Add a private note or synopsis">${escapeHtml(title.description)}</textarea>
|
<p>${title.description ? escapeHtml(title.description) : "No description yet"}</p>
|
||||||
</label>
|
</section>
|
||||||
<button class="primary" type="submit">Save metadata</button>
|
</div>
|
||||||
</form>
|
<div id="metaEditMount"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="seasons">
|
<div class="seasons">
|
||||||
@@ -146,18 +147,8 @@ function renderDetails(title) {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
document.querySelector("#metaForm").addEventListener("submit", async (event) => {
|
document.querySelector("#editMetaBtn").addEventListener("click", () => {
|
||||||
event.preventDefault();
|
renderMetadataForm(title);
|
||||||
const form = new FormData(event.currentTarget);
|
|
||||||
await api(`/api/titles/${encodeURIComponent(title.id)}`, {
|
|
||||||
method: "PATCH",
|
|
||||||
body: JSON.stringify({
|
|
||||||
title: form.get("title"),
|
|
||||||
thumbnail: form.get("thumbnail"),
|
|
||||||
description: form.get("description")
|
|
||||||
})
|
|
||||||
});
|
|
||||||
await loadLibrary();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector("#makeThumbBtn").addEventListener("click", async (event) => {
|
document.querySelector("#makeThumbBtn").addEventListener("click", async (event) => {
|
||||||
@@ -187,6 +178,50 @@ function renderDetails(title) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderMetadataForm(title) {
|
||||||
|
document.querySelector("#metaView").classList.add("hidden");
|
||||||
|
const mount = document.querySelector("#metaEditMount");
|
||||||
|
mount.innerHTML = `
|
||||||
|
<form id="metaForm" class="meta-form">
|
||||||
|
<label>
|
||||||
|
<span>Display title</span>
|
||||||
|
<input name="title" value="${escapeAttr(title.title)}">
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>Thumbnail path or URL</span>
|
||||||
|
<input name="thumbnail" value="${escapeAttr(title.thumbnail)}" placeholder="/thumbs/example.jpg">
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>Short description</span>
|
||||||
|
<textarea name="description" rows="5" placeholder="Add a private note or synopsis">${escapeHtml(title.description)}</textarea>
|
||||||
|
</label>
|
||||||
|
<div class="meta-form__actions">
|
||||||
|
<button class="primary" type="submit">Save metadata</button>
|
||||||
|
<button id="cancelMetaBtn" class="ghost" type="button">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.querySelector("#cancelMetaBtn").addEventListener("click", () => {
|
||||||
|
mount.innerHTML = "";
|
||||||
|
document.querySelector("#metaView").classList.remove("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector("#metaForm").addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const form = new FormData(event.currentTarget);
|
||||||
|
await api(`/api/titles/${encodeURIComponent(title.id)}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
body: JSON.stringify({
|
||||||
|
title: form.get("title"),
|
||||||
|
thumbnail: form.get("thumbnail"),
|
||||||
|
description: form.get("description")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
await loadLibrary();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function renderSeason(season) {
|
function renderSeason(season) {
|
||||||
return `
|
return `
|
||||||
<section class="season">
|
<section class="season">
|
||||||
|
|||||||
@@ -451,13 +451,49 @@ label {
|
|||||||
margin-top: 0.45rem;
|
margin-top: 0.45rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.meta-view {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
max-width: 780px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-view section {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-view span {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-view p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
.meta-form {
|
.meta-form {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.85rem;
|
gap: 0.85rem;
|
||||||
max-width: 780px;
|
max-width: 780px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta-form button {
|
.meta-form__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-form .primary {
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
background: linear-gradient(135deg, var(--accent), var(--accent-strong));
|
background: linear-gradient(135deg, var(--accent), var(--accent-strong));
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
|||||||
Reference in New Issue
Block a user