ui: UpgradeUI archive_dir + candidates picker + bump v1.9.3
Frontend Upgrade: - state.upArchiveDir + handler Sfoglia/Rimuovi per la seconda path-display - start_upgrade payload include archive_dir - bridge handler 'upgrade:candidates_needed' apre openUpgradePicker() con lista candidati (basename + bitrate + size + similarity + path) - 3 azioni utente: Usa selezionato / Cerca YouTube / Salta; ESC e click su backdrop = Salta. Chiusura implicita quando upgrade:done. - CSS minimale (.upgrade-picker-*) accodato dopo le sezioni esistenti. Version bump v1.9.2 -> v1.9.3.
This commit is contained in:
1 parent
f0a29f7ca7
commit
e10466d93b
3 files changed
+199
-1
No files matched your search
+1
-1
@@ -5,7 +5,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
VERSION = "v1.9.2"
|
VERSION = "v1.9.3"
|
||||||
|
|
||||||
|
|
||||||
APP_NAME = "MusicTools"
|
APP_NAME = "MusicTools"
|
||||||
|
|||||||
@@ -1708,3 +1708,70 @@ input[type="number"]::-webkit-inner-spin-button {
|
|||||||
.hint-inline { margin-left: 0; }
|
.hint-inline { margin-left: 0; }
|
||||||
.beatport-table .col-state { width: 110px; }
|
.beatport-table .col-state { width: 110px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===============================
|
||||||
|
UPGRADE — picker modal (match locale)
|
||||||
|
=============================== */
|
||||||
|
.modal-body.upgrade-picker-body {
|
||||||
|
/* Override del default .modal-body (mono + pre-wrap): qui è UI, non log */
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text);
|
||||||
|
white-space: normal;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.modal-sub {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-2);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.upgrade-picker-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
max-height: 380px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.upgrade-picker-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--r-md, 10px);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s ease, background 0.15s ease;
|
||||||
|
background: var(--bg-input, transparent);
|
||||||
|
}
|
||||||
|
.upgrade-picker-row:hover { border-color: var(--text-2); }
|
||||||
|
.upgrade-picker-row.selected {
|
||||||
|
border-color: var(--blue, #3b82f6);
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
}
|
||||||
|
.upgrade-picker-row input[type="radio"] {
|
||||||
|
margin-top: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.upgrade-picker-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.upgrade-picker-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.upgrade-picker-meta {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-2);
|
||||||
|
margin-top: 3px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.upgrade-picker-path {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-3);
|
||||||
|
margin-top: 2px;
|
||||||
|
word-break: break-all;
|
||||||
|
font-family: "SF Mono", Menlo, Consolas, monospace;
|
||||||
|
}
|
||||||
+131
@@ -12,6 +12,8 @@ const state = {
|
|||||||
loaded: null, // { kind: "urls"|"tracks", urls?, tracks?, count }
|
loaded: null, // { kind: "urls"|"tracks", urls?, tracks?, count }
|
||||||
dlOutputDir: "",
|
dlOutputDir: "",
|
||||||
upDir: "",
|
upDir: "",
|
||||||
|
upArchiveDir: "",
|
||||||
|
upPicker: { file: "", candidates: [], selectedIdx: -1 },
|
||||||
videoOutputDir: "",
|
videoOutputDir: "",
|
||||||
showSecret: false,
|
showSecret: false,
|
||||||
downloading: false,
|
downloading: false,
|
||||||
@@ -310,8 +312,13 @@ const bridgeHandlers = {
|
|||||||
state.upgrading = false;
|
state.upgrading = false;
|
||||||
$("#upgradeBtn").disabled = false;
|
$("#upgradeBtn").disabled = false;
|
||||||
$("#stopUpgradeBtn").disabled = true;
|
$("#stopUpgradeBtn").disabled = true;
|
||||||
|
// Chiudi eventuale modal picker rimasto aperto
|
||||||
|
const modal = $("#upgradePickerModal");
|
||||||
|
if (modal && !modal.hidden) modal.hidden = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"upgrade:candidates_needed": (p) => openUpgradePicker(p),
|
||||||
|
|
||||||
"video:progress": (p) => {
|
"video:progress": (p) => {
|
||||||
if (typeof p.overall === "number") {
|
if (typeof p.overall === "number") {
|
||||||
const pct = Math.round(p.overall * 100);
|
const pct = Math.round(p.overall * 100);
|
||||||
@@ -714,6 +721,26 @@ $("#upBrowseBtn").addEventListener("click", async () => {
|
|||||||
|
|
||||||
$("#upRecursive").addEventListener("change", refreshUpgradeScan);
|
$("#upRecursive").addEventListener("change", refreshUpgradeScan);
|
||||||
|
|
||||||
|
// --- Cartella archivio (opzionale) ---
|
||||||
|
$("#upArchiveBrowseBtn").addEventListener("click", async () => {
|
||||||
|
const path = await window.pywebview.api.browse_directory();
|
||||||
|
if (path) {
|
||||||
|
state.upArchiveDir = path;
|
||||||
|
const disp = $("#upArchivePathDisplay");
|
||||||
|
disp.textContent = path;
|
||||||
|
disp.classList.remove("empty");
|
||||||
|
$("#upArchiveClearBtn").hidden = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#upArchiveClearBtn").addEventListener("click", () => {
|
||||||
|
state.upArchiveDir = "";
|
||||||
|
const disp = $("#upArchivePathDisplay");
|
||||||
|
disp.textContent = "Nessuna cartella";
|
||||||
|
disp.classList.add("empty");
|
||||||
|
$("#upArchiveClearBtn").hidden = true;
|
||||||
|
});
|
||||||
|
|
||||||
async function refreshUpgradeScan() {
|
async function refreshUpgradeScan() {
|
||||||
if (!state.upDir) return;
|
if (!state.upDir) return;
|
||||||
const res = await window.pywebview.api.scan_audio_folder(state.upDir, $("#upRecursive").checked);
|
const res = await window.pywebview.api.scan_audio_folder(state.upDir, $("#upRecursive").checked);
|
||||||
@@ -748,6 +775,7 @@ $("#upgradeBtn").addEventListener("click", async () => {
|
|||||||
directory: state.upDir,
|
directory: state.upDir,
|
||||||
recursive: $("#upRecursive").checked,
|
recursive: $("#upRecursive").checked,
|
||||||
threshold: parseInt($("#upThreshold").value, 10) || 310,
|
threshold: parseInt($("#upThreshold").value, 10) || 310,
|
||||||
|
archive_dir: state.upArchiveDir || "",
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
if (!handleGateBlock(res)) toast(res.error || "Errore", "error");
|
if (!handleGateBlock(res)) toast(res.error || "Errore", "error");
|
||||||
@@ -760,6 +788,109 @@ $("#stopUpgradeBtn").addEventListener("click", async () => {
|
|||||||
$("#stopUpgradeBtn").disabled = true;
|
$("#stopUpgradeBtn").disabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Modal picker "Match locali multipli" ---
|
||||||
|
function _fmtBytes(n) {
|
||||||
|
if (!n || n < 0) return "—";
|
||||||
|
if (n < 1024) return n + " B";
|
||||||
|
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + " KB";
|
||||||
|
if (n < 1024 * 1024 * 1024) return (n / (1024 * 1024)).toFixed(1) + " MB";
|
||||||
|
return (n / (1024 * 1024 * 1024)).toFixed(2) + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
function _basename(p) {
|
||||||
|
if (!p) return "";
|
||||||
|
const parts = String(p).split(/[/\\]/);
|
||||||
|
return parts[parts.length - 1] || p;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _escapeHtml(s) {
|
||||||
|
return String(s ?? "").replace(/[&<>"']/g, (c) => ({
|
||||||
|
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
|
||||||
|
}[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function openUpgradePicker(payload) {
|
||||||
|
const modal = $("#upgradePickerModal");
|
||||||
|
if (!modal) return;
|
||||||
|
const file = (payload && payload.file) || "";
|
||||||
|
const candidates = (payload && payload.candidates) || [];
|
||||||
|
state.upPicker = { file, candidates, selectedIdx: -1 };
|
||||||
|
|
||||||
|
$("#upgradePickerFilename").textContent = "Brano: " + file;
|
||||||
|
const list = $("#upgradePickerList");
|
||||||
|
list.innerHTML = "";
|
||||||
|
candidates.forEach((c, i) => {
|
||||||
|
const row = document.createElement("label");
|
||||||
|
row.className = "upgrade-picker-row";
|
||||||
|
row.dataset.idx = String(i);
|
||||||
|
const simPct = Math.round((c.similarity || 0) * 100);
|
||||||
|
const kbps = c.bitrate ? `${c.bitrate}k` : "—";
|
||||||
|
row.innerHTML = `
|
||||||
|
<input type="radio" name="upgradePickerRadio" value="${i}" />
|
||||||
|
<div class="upgrade-picker-info">
|
||||||
|
<div class="upgrade-picker-title">${_escapeHtml(_basename(c.path))}</div>
|
||||||
|
<div class="upgrade-picker-meta">${_escapeHtml(kbps)} · ${_escapeHtml(_fmtBytes(c.size))} · similarity ${simPct}%</div>
|
||||||
|
<div class="upgrade-picker-path">${_escapeHtml(c.path || "")}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
row.addEventListener("click", () => {
|
||||||
|
state.upPicker.selectedIdx = i;
|
||||||
|
$("#upgradePickerUseBtn").disabled = false;
|
||||||
|
$$("#upgradePickerList .upgrade-picker-row").forEach((r) =>
|
||||||
|
r.classList.toggle("selected", r === row)
|
||||||
|
);
|
||||||
|
const radio = row.querySelector("input[type=radio]");
|
||||||
|
if (radio) radio.checked = true;
|
||||||
|
});
|
||||||
|
list.appendChild(row);
|
||||||
|
});
|
||||||
|
$("#upgradePickerUseBtn").disabled = true;
|
||||||
|
modal.hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeUpgradePicker() {
|
||||||
|
const modal = $("#upgradePickerModal");
|
||||||
|
if (modal) modal.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _sendUpgradeChoice(choice) {
|
||||||
|
try {
|
||||||
|
await window.pywebview.api.upgrade_resolve_candidates(choice);
|
||||||
|
} catch (_e) { /* backend può essere già terminato: ignora */ }
|
||||||
|
closeUpgradePicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#upgradePickerUseBtn")?.addEventListener("click", () => {
|
||||||
|
const idx = state.upPicker.selectedIdx;
|
||||||
|
if (idx < 0 || idx >= state.upPicker.candidates.length) return;
|
||||||
|
const path = state.upPicker.candidates[idx].path;
|
||||||
|
_sendUpgradeChoice({ action: "use_local", path });
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#upgradePickerYoutubeBtn")?.addEventListener("click", () => {
|
||||||
|
_sendUpgradeChoice({ action: "use_youtube" });
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#upgradePickerSkipBtn")?.addEventListener("click", () => {
|
||||||
|
_sendUpgradeChoice({ action: "skip" });
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#upgradePickerCloseBtn")?.addEventListener("click", () => {
|
||||||
|
_sendUpgradeChoice({ action: "skip" });
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#upgradePickerModal")?.addEventListener("click", (e) => {
|
||||||
|
// Click sul backdrop (non sulla card) → skip
|
||||||
|
if (e.target === e.currentTarget) _sendUpgradeChoice({ action: "skip" });
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
const modal = $("#upgradePickerModal");
|
||||||
|
if (modal && !modal.hidden) _sendUpgradeChoice({ action: "skip" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// RECORDER tab
|
// RECORDER tab
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
Reference in new issue
Block a user