feat & Bugfix: layout en rename (year)

This commit is contained in:
kodi
2026-03-08 07:41:24 +01:00
parent 6bf753e3b7
commit 06c144d2fc
9 changed files with 181 additions and 20 deletions
+78 -5
View File
@@ -6,7 +6,9 @@
selectedSeries: null,
episodes: [],
roots: [],
folders: [],
discoveredFiles: [],
currentSubpath: "",
};
const el = {
@@ -22,10 +24,12 @@
clearSelectedEpisodesBtn: document.getElementById("clearSelectedEpisodesBtn"),
selectedEpisodesList: document.getElementById("selectedEpisodesList"),
rootsSelect: document.getElementById("rootsSelect"),
foldersSelect: document.getElementById("foldersSelect"),
refreshRootsBtn: document.getElementById("refreshRootsBtn"),
subpathInput: document.getElementById("subpathInput"),
loadFoldersBtn: document.getElementById("loadFoldersBtn"),
loadFilesBtn: document.getElementById("loadFilesBtn"),
recursiveInput: document.getElementById("recursiveInput"),
discoverBtn: document.getElementById("discoverBtn"),
discoveredFilesList: document.getElementById("discoveredFilesList"),
refreshSelectedFilesBtn: document.getElementById("refreshSelectedFilesBtn"),
clearSelectedFilesBtn: document.getElementById("clearSelectedFilesBtn"),
@@ -89,6 +93,11 @@
opt.textContent = `${root.id}: ${root.path}`;
el.rootsSelect.appendChild(opt);
}
if (state.roots.length > 0) {
state.currentSubpath = "";
el.subpathInput.value = "";
await loadFolders();
}
out("Roots loaded", data);
}
@@ -192,10 +201,40 @@
return data;
}
async function loadFolders() {
const rootId = el.rootsSelect.value;
if (!rootId) throw new Error("No root selected");
const subpath = (el.subpathInput.value || "").trim();
state.currentSubpath = subpath;
const data = await api(
`/api/files/folders?root_id=${encodeURIComponent(rootId)}&subpath=${encodeURIComponent(subpath)}&limit=500`
);
state.folders = data.items || [];
el.foldersSelect.innerHTML = "";
const placeholder = document.createElement("option");
placeholder.value = "";
placeholder.textContent = state.folders.length ? "Choose folder..." : "(No folders)";
el.foldersSelect.appendChild(placeholder);
state.folders.forEach((folder) => {
const opt = document.createElement("option");
opt.value = folder.subpath;
opt.textContent = folder.subpath || folder.name;
el.foldersSelect.appendChild(opt);
});
out("Folders loaded", data);
}
async function discoverFiles() {
const rootId = el.rootsSelect.value;
if (!rootId) throw new Error("No root selected");
const subpath = encodeURIComponent((el.subpathInput.value || "").trim());
const selectedFolder = (el.foldersSelect.value || "").trim();
if (!selectedFolder) throw new Error("Choose a folder first");
el.subpathInput.value = selectedFolder;
state.currentSubpath = selectedFolder;
const subpath = encodeURIComponent(selectedFolder);
const recursive = el.recursiveInput.checked ? "true" : "false";
const data = await api(`/api/files/discover?root_id=${encodeURIComponent(rootId)}&subpath=${subpath}&recursive=${recursive}&limit=200`);
state.discoveredFiles = data.items || [];
@@ -269,8 +308,28 @@
const data = await api(q("/api/session/rename-execute") + "&confirm=true", {
method: "POST",
});
out("Rename execute", data);
await loadSelectedFiles();
if (data.executed) {
out("Rename execute: success", data);
// Keep UI in sync with renamed destinations using existing selected-files endpoints.
const renamedFiles = (data.items || [])
.filter((item) => item.status === "renamed")
.map((item) => ({
path: item.destination_path,
name: item.proposed_filename,
}));
await api(q("/api/session/selected-files"), { method: "DELETE" });
if (renamedFiles.length > 0) {
await api(q("/api/session/selected-files"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items: renamedFiles }),
});
}
await loadSelectedFiles();
return;
}
out("Rename execute: preflight failed", data);
}
async function withHandler(fn, btn) {
@@ -296,7 +355,20 @@
}, el.clearSelectedEpisodesBtn)
);
el.refreshRootsBtn.addEventListener("click", () => withHandler(loadRoots, el.refreshRootsBtn));
el.discoverBtn.addEventListener("click", () => withHandler(discoverFiles, el.discoverBtn));
el.rootsSelect.addEventListener("change", () => withHandler(async () => {
state.currentSubpath = "";
el.subpathInput.value = "";
await loadFolders();
el.discoveredFilesList.innerHTML = "";
}, el.rootsSelect));
el.loadFoldersBtn.addEventListener("click", () => withHandler(loadFolders, el.loadFoldersBtn));
el.loadFilesBtn.addEventListener("click", () => withHandler(discoverFiles, el.loadFilesBtn));
el.foldersSelect.addEventListener("change", () => {
const chosen = (el.foldersSelect.value || "").trim();
if (chosen) {
el.subpathInput.value = chosen;
}
});
el.refreshSelectedFilesBtn.addEventListener("click", () => withHandler(loadSelectedFiles, el.refreshSelectedFilesBtn));
el.clearSelectedFilesBtn.addEventListener("click", () =>
withHandler(async () => {
@@ -312,6 +384,7 @@
async function init() {
el.sessionMeta.textContent = `session_id: ${state.sessionId}`;
el.recursiveInput.checked = true;
bindEvents();
await loadRoots();
await loadSelectedEpisodes();