feat: renommage de fichiers + gestion des conflits dans le redimensionnement
- Route POST /rename : renomme un fichier CDN avec validation sécurité,
retourne JSON (name, path, browse_url)
- Route /resize : accepte param `conflict` (backup | overwrite | rename | skip)
backup → renomme l'existant en {stem}_bak_{timestamp}{ext} avant création
rename → auto-incrémente le nom de la copie ({stem}_1, _2…)
overwrite → écrase silencieusement
skip → ignore (signalé dans les erreurs)
- browse.html : bouton ✏️ par fichier, renommage inline avec Entrée/Échap
- preview_image.html : bouton ✏️ dans l'en-tête, champ inline + redirect
après validation ; radio segmenté pour la stratégie de conflit
- app.css : styles btn-rename, rename-inline, radio-chips segmentés
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c503f5e074
commit
14259c59f1
4 changed files with 268 additions and 16 deletions
81
app/app.py
81
app/app.py
|
|
@ -97,6 +97,20 @@ def _humansize(n: int) -> str:
|
||||||
return f"{n:.1f} To"
|
return f"{n:.1f} To"
|
||||||
|
|
||||||
|
|
||||||
|
def _backup_path(p: Path) -> Path:
|
||||||
|
ts = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
return p.parent / f"{p.stem}_bak_{ts}{p.suffix}"
|
||||||
|
|
||||||
|
|
||||||
|
def _auto_rename(p: Path) -> Path:
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
candidate = p.parent / f"{p.stem}_{i}{p.suffix}"
|
||||||
|
if not candidate.exists():
|
||||||
|
return candidate
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
def _folder_stats(path: Path) -> dict:
|
def _folder_stats(path: Path) -> dict:
|
||||||
files, size = 0, 0
|
files, size = 0, 0
|
||||||
for f in path.rglob("*"):
|
for f in path.rglob("*"):
|
||||||
|
|
@ -472,6 +486,51 @@ def delete_file():
|
||||||
return redirect(url_for("browse", subpath=parent) if parent != "." else url_for("browse"))
|
return redirect(url_for("browse", subpath=parent) if parent != "." else url_for("browse"))
|
||||||
|
|
||||||
|
|
||||||
|
# ── Renommage de fichiers ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
@app.route("/rename", methods=["POST"])
|
||||||
|
def rename_file():
|
||||||
|
redir = _require_admin()
|
||||||
|
if redir:
|
||||||
|
return redir
|
||||||
|
|
||||||
|
subpath = request.form.get("path", "").strip()
|
||||||
|
new_name = request.form.get("new_name", "").strip()
|
||||||
|
|
||||||
|
if not subpath or not new_name:
|
||||||
|
return jsonify({"error": "Paramètres manquants"}), 400
|
||||||
|
|
||||||
|
if "/" in new_name or "\\" in new_name or new_name in (".", ".."):
|
||||||
|
return jsonify({"error": "Nom invalide"}), 400
|
||||||
|
|
||||||
|
new_name = secure_filename(new_name)
|
||||||
|
if not new_name:
|
||||||
|
return jsonify({"error": "Nom invalide après nettoyage"}), 400
|
||||||
|
|
||||||
|
target = _safe_path(subpath)
|
||||||
|
if not target.is_file():
|
||||||
|
return jsonify({"error": "Fichier introuvable"}), 404
|
||||||
|
|
||||||
|
top = Path(subpath).parts[0]
|
||||||
|
if top in _HIDDEN or top.startswith("."):
|
||||||
|
return jsonify({"error": "Accès refusé"}), 403
|
||||||
|
|
||||||
|
dest = target.parent / new_name
|
||||||
|
if not dest.is_relative_to(ASSETS_ROOT):
|
||||||
|
return jsonify({"error": "Destination invalide"}), 400
|
||||||
|
|
||||||
|
if dest.exists():
|
||||||
|
return jsonify({"error": f"« {new_name} » existe déjà"}), 409
|
||||||
|
|
||||||
|
target.rename(dest)
|
||||||
|
new_path = str(dest.relative_to(ASSETS_ROOT))
|
||||||
|
return jsonify({
|
||||||
|
"name": new_name,
|
||||||
|
"path": new_path,
|
||||||
|
"browse_url": url_for("browse", subpath=new_path),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
# ── Upload de fichiers ────────────────────────────────────────────────
|
# ── Upload de fichiers ────────────────────────────────────────────────
|
||||||
|
|
||||||
@app.route("/upload", methods=["POST"])
|
@app.route("/upload", methods=["POST"])
|
||||||
|
|
@ -540,6 +599,10 @@ def resize_image():
|
||||||
if not sizes or not formats:
|
if not sizes or not formats:
|
||||||
return jsonify({"error": "Tailles ou formats invalides"}), 400
|
return jsonify({"error": "Tailles ou formats invalides"}), 400
|
||||||
|
|
||||||
|
conflict = request.form.get("conflict", "skip")
|
||||||
|
if conflict not in ("backup", "overwrite", "rename", "skip"):
|
||||||
|
conflict = "skip"
|
||||||
|
|
||||||
is_svg = ext == ".svg"
|
is_svg = ext == ".svg"
|
||||||
stem = target.stem
|
stem = target.stem
|
||||||
parent = target.parent
|
parent = target.parent
|
||||||
|
|
@ -550,6 +613,24 @@ def resize_image():
|
||||||
out_name = f"{stem}_{size}x{size}.{fmt}"
|
out_name = f"{stem}_{size}x{size}.{fmt}"
|
||||||
out_path = parent / out_name
|
out_path = parent / out_name
|
||||||
out_rel = str(out_path.relative_to(ASSETS_ROOT))
|
out_rel = str(out_path.relative_to(ASSETS_ROOT))
|
||||||
|
|
||||||
|
if out_path.exists():
|
||||||
|
if conflict == "skip":
|
||||||
|
errors.append({"name": out_name, "reason": "Fichier existant, ignoré"})
|
||||||
|
continue
|
||||||
|
elif conflict == "backup":
|
||||||
|
try:
|
||||||
|
bak = _backup_path(out_path)
|
||||||
|
out_path.rename(bak)
|
||||||
|
except Exception as exc:
|
||||||
|
errors.append({"name": out_name, "reason": f"Backup impossible : {exc}"})
|
||||||
|
continue
|
||||||
|
elif conflict == "rename":
|
||||||
|
out_path = _auto_rename(out_path)
|
||||||
|
out_name = out_path.name
|
||||||
|
out_rel = str(out_path.relative_to(ASSETS_ROOT))
|
||||||
|
# conflict == "overwrite" : on continue sans rien faire
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if fmt == "svg" and is_svg:
|
if fmt == "svg" and is_svg:
|
||||||
shutil.copy2(target, out_path)
|
shutil.copy2(target, out_path)
|
||||||
|
|
|
||||||
|
|
@ -135,16 +135,33 @@ main { max-width: 1100px; margin: 2rem auto; padding: 0 1.5rem 3rem; display: fl
|
||||||
.empty { text-align: center; color: var(--muted); padding: 2rem; font-style: italic; }
|
.empty { text-align: center; color: var(--muted); padding: 2rem; font-style: italic; }
|
||||||
|
|
||||||
|
|
||||||
/* ── Vues / suppression (browse) ─────────────────────────────────────── */
|
/* ── Vues / actions (browse) ─────────────────────────────────────────── */
|
||||||
.col-hits { width: 4.5rem; text-align: right; white-space: nowrap; }
|
.col-hits { width: 4.5rem; text-align: right; white-space: nowrap; }
|
||||||
.col-del { width: 2.4rem; text-align: center; }
|
.col-actions { width: 5.5rem; text-align: right; }
|
||||||
|
|
||||||
.hits-badge { display: inline-block; font-size: .75rem; border-radius: 10px; padding: .1rem .45rem; font-weight: 600; }
|
.row-actions { display: flex; justify-content: flex-end; align-items: center; gap: .1rem; }
|
||||||
|
|
||||||
|
.hits-badge { display: inline-block; font-size: .75rem; border-radius: 10px; padding: .1rem .45rem; font-weight: 600; }
|
||||||
.hits-active { background: #dcfce7; color: #15803d; }
|
.hits-active { background: #dcfce7; color: #15803d; }
|
||||||
.hits-zero { background: #f3f4f6; color: #9ca3af; }
|
.hits-zero { background: #f3f4f6; color: #9ca3af; }
|
||||||
|
|
||||||
.btn-del { background: none; border: none; cursor: pointer; font-size: .95rem; opacity: .4; padding: .2rem; line-height: 1; transition: opacity .15s; }
|
.btn-del { background: none; border: none; cursor: pointer; font-size: .95rem; opacity: .4; padding: .2rem; line-height: 1; transition: opacity .15s; }
|
||||||
.btn-del:hover { opacity: 1; }
|
.btn-del:hover { opacity: 1; }
|
||||||
|
.btn-rename { background: none; border: none; cursor: pointer; font-size: .85rem; opacity: .35; padding: .2rem; line-height: 1; transition: opacity .15s; }
|
||||||
|
.btn-rename:hover { opacity: 1; }
|
||||||
|
|
||||||
|
/* ── Rename inline ───────────────────────────────────────────────────── */
|
||||||
|
.btn-icon { background: none; border: none; cursor: pointer; font-size: .95rem; opacity: .4; padding: .15rem; line-height: 1; transition: opacity .15s; }
|
||||||
|
.btn-icon:hover { opacity: 1; }
|
||||||
|
|
||||||
|
.rename-inline { display: flex; align-items: center; gap: .4rem; padding: .4rem 0; flex-wrap: wrap; }
|
||||||
|
.rename-input { border: 1px solid var(--blue); border-radius: 5px; padding: .3rem .6rem; font-size: .88rem; outline: none; min-width: 200px; }
|
||||||
|
.rename-input:focus { box-shadow: 0 0 0 2px rgba(26,107,191,.18); }
|
||||||
|
.btn-rename-ok { background: none; border: none; cursor: pointer; color: #15803d; font-size: 1rem; padding: .2rem .4rem; border-radius: 4px; }
|
||||||
|
.btn-rename-ok:hover { background: #dcfce7; }
|
||||||
|
.btn-rename-cancel { background: none; border: none; cursor: pointer; color: #b91c1c; font-size: 1rem; padding: .2rem .4rem; border-radius: 4px; }
|
||||||
|
.btn-rename-cancel:hover { background: #fef2f2; }
|
||||||
|
.rename-error { font-size: .8rem; color: #b91c1c; font-style: italic; }
|
||||||
|
|
||||||
/* ── Upload ───────────────────────────────────────────────────────── */
|
/* ── Upload ───────────────────────────────────────────────────────── */
|
||||||
.upload-card h2 { margin-bottom: .8rem; }
|
.upload-card h2 { margin-bottom: .8rem; }
|
||||||
|
|
@ -175,6 +192,11 @@ main { max-width: 1100px; margin: 2rem auto; padding: 0 1.5rem 3rem; display: fl
|
||||||
.chip--disabled { cursor: not-allowed; opacity: .45; }
|
.chip--disabled { cursor: not-allowed; opacity: .45; }
|
||||||
.chip--disabled span { background: #f3f4f6; color: #9ca3af; }
|
.chip--disabled span { background: #f3f4f6; color: #9ca3af; }
|
||||||
|
|
||||||
|
.resize-chips--radio { gap: 0; }
|
||||||
|
.resize-chips--radio .chip span { border-radius: 0; border-right-width: 0; }
|
||||||
|
.resize-chips--radio .chip:first-child span { border-radius: 20px 0 0 20px; }
|
||||||
|
.resize-chips--radio .chip:last-child span { border-radius: 0 20px 20px 0; border-right-width: 2px; }
|
||||||
|
|
||||||
.resize-actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
|
.resize-actions { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
|
||||||
.resize-hint { font-size: .82rem; color: var(--muted); font-style: italic; }
|
.resize-hint { font-size: .82rem; color: var(--muted); font-style: italic; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
<th class="col-size">Taille</th>
|
<th class="col-size">Taille</th>
|
||||||
<th class="col-date">Modifié le</th>
|
<th class="col-date">Modifié le</th>
|
||||||
{% if has_hits %}<th class="col-hits">Vues</th>{% endif %}
|
{% if has_hits %}<th class="col-hits">Vues</th>{% endif %}
|
||||||
<th class="col-del"></th>
|
<th class="col-actions"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -86,12 +86,17 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="col-del">
|
<td class="col-actions">
|
||||||
{% if not e.is_dir %}
|
{% if not e.is_dir %}
|
||||||
<form method="post" action="{{ url_for('delete_file') }}">
|
<div class="row-actions">
|
||||||
<input type="hidden" name="path" value="{{ e.path }}">
|
<button type="button" class="btn-rename"
|
||||||
<button type="submit" class="btn-del" title="Supprimer du CDN">🗑</button>
|
data-path="{{ e.path }}" data-name="{{ e.name }}"
|
||||||
</form>
|
title="Renommer">✏️</button>
|
||||||
|
<form method="post" action="{{ url_for('delete_file') }}" style="display:contents">
|
||||||
|
<input type="hidden" name="path" value="{{ e.path }}">
|
||||||
|
<button type="submit" class="btn-del" title="Supprimer du CDN">🗑</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -129,4 +134,69 @@
|
||||||
</script>
|
</script>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
function escHtml(s) {
|
||||||
|
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doRename(path, newName, nameCell, origHtml) {
|
||||||
|
if (!newName || newName === path.split('/').pop()) {
|
||||||
|
nameCell.innerHTML = origHtml;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('path', path);
|
||||||
|
fd.append('new_name', newName);
|
||||||
|
try {
|
||||||
|
const resp = await fetch('/rename', { method: 'POST', body: fd });
|
||||||
|
const data = await resp.json();
|
||||||
|
if (data.error) {
|
||||||
|
const inp = nameCell.querySelector('.rename-input');
|
||||||
|
if (inp) { inp.style.borderColor = '#b91c1c'; inp.title = data.error; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
location.reload();
|
||||||
|
} catch (_) {
|
||||||
|
nameCell.innerHTML = origHtml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
const btn = e.target.closest('.btn-rename');
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
const row = btn.closest('tr');
|
||||||
|
const nameCell = row.querySelector('.col-name');
|
||||||
|
const path = btn.dataset.path;
|
||||||
|
const name = btn.dataset.name;
|
||||||
|
const origHtml = nameCell.innerHTML;
|
||||||
|
|
||||||
|
const thumb = nameCell.querySelector('.thumb-sm');
|
||||||
|
const thumbHtml = thumb ? thumb.outerHTML : '';
|
||||||
|
|
||||||
|
nameCell.innerHTML =
|
||||||
|
thumbHtml +
|
||||||
|
'<input type="text" class="rename-input" value="' + escHtml(name) + '">' +
|
||||||
|
'<button class="btn-rename-ok" title="Valider">✓</button>' +
|
||||||
|
'<button class="btn-rename-cancel" title="Annuler">✕</button>' +
|
||||||
|
'<span class="rename-error"></span>';
|
||||||
|
|
||||||
|
const inp = nameCell.querySelector('.rename-input');
|
||||||
|
inp.focus(); inp.select();
|
||||||
|
|
||||||
|
nameCell.querySelector('.btn-rename-cancel').addEventListener('click', () => {
|
||||||
|
nameCell.innerHTML = origHtml;
|
||||||
|
});
|
||||||
|
nameCell.querySelector('.btn-rename-ok').addEventListener('click', () => {
|
||||||
|
doRename(path, inp.value.trim(), nameCell, origHtml);
|
||||||
|
});
|
||||||
|
inp.addEventListener('keydown', ev => {
|
||||||
|
if (ev.key === 'Enter') doRename(path, inp.value.trim(), nameCell, origHtml);
|
||||||
|
if (ev.key === 'Escape') nameCell.innerHTML = origHtml;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,16 @@
|
||||||
<div class="preview-header">
|
<div class="preview-header">
|
||||||
<a href="{{ url_for('browse', subpath=parent_path) if parent_path else url_for('browse') }}"
|
<a href="{{ url_for('browse', subpath=parent_path) if parent_path else url_for('browse') }}"
|
||||||
class="back-link">← Retour</a>
|
class="back-link">← Retour</a>
|
||||||
<h1>{{ filename }}</h1>
|
<h1 id="preview-title">{{ filename }}</h1>
|
||||||
|
<button type="button" id="rename-toggle" class="btn-icon" title="Renommer ce fichier">✏️</button>
|
||||||
{% include '_preview_nav.html' %}
|
{% include '_preview_nav.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
<div id="rename-inline" class="rename-inline" style="display:none">
|
||||||
|
<input type="text" id="rename-input" class="rename-input" value="{{ filename }}">
|
||||||
|
<button type="button" id="rename-save" class="btn-rename-ok" title="Valider">✓</button>
|
||||||
|
<button type="button" id="rename-cancel" class="btn-rename-cancel" title="Annuler">✕</button>
|
||||||
|
<span id="rename-error" class="rename-error"></span>
|
||||||
|
</div>
|
||||||
<div class="preview-meta">
|
<div class="preview-meta">
|
||||||
<span>Taille : <strong>{{ filesize }}</strong></span>
|
<span>Taille : <strong>{{ filesize }}</strong></span>
|
||||||
<span>Modifié : <strong>{{ mtime.strftime('%d/%m/%Y %H:%M') }}</strong></span>
|
<span>Modifié : <strong>{{ mtime.strftime('%d/%m/%Y %H:%M') }}</strong></span>
|
||||||
|
|
@ -57,6 +64,28 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="resize-group">
|
||||||
|
<div class="resize-group-label">Si le fichier existe déjà</div>
|
||||||
|
<div class="resize-chips resize-chips--radio">
|
||||||
|
<label class="chip">
|
||||||
|
<input type="radio" name="conflict" value="backup" checked>
|
||||||
|
<span>Backup</span>
|
||||||
|
</label>
|
||||||
|
<label class="chip">
|
||||||
|
<input type="radio" name="conflict" value="overwrite">
|
||||||
|
<span>Écraser</span>
|
||||||
|
</label>
|
||||||
|
<label class="chip">
|
||||||
|
<input type="radio" name="conflict" value="rename">
|
||||||
|
<span>Renommer la copie</span>
|
||||||
|
</label>
|
||||||
|
<label class="chip">
|
||||||
|
<input type="radio" name="conflict" value="skip">
|
||||||
|
<span>Ignorer</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="resize-actions">
|
<div class="resize-actions">
|
||||||
<button id="resize-btn" class="btn btn-primary" disabled>Générer les copies</button>
|
<button id="resize-btn" class="btn btn-primary" disabled>Générer les copies</button>
|
||||||
<span class="resize-hint">Les fichiers sont créés dans le même dossier</span>
|
<span class="resize-hint">Les fichiers sont créés dans le même dossier</span>
|
||||||
|
|
@ -69,12 +98,60 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
/* ── Rename ── */
|
||||||
|
const RENAME_URL = {{ url_for('rename_file') | tojson }};
|
||||||
|
const FILE_PATH = {{ subpath | tojson }};
|
||||||
|
|
||||||
|
const renameToggle = document.getElementById('rename-toggle');
|
||||||
|
const renameInline = document.getElementById('rename-inline');
|
||||||
|
const renameInput = document.getElementById('rename-input');
|
||||||
|
const renameSave = document.getElementById('rename-save');
|
||||||
|
const renameCancel = document.getElementById('rename-cancel');
|
||||||
|
const renameError = document.getElementById('rename-error');
|
||||||
|
|
||||||
|
renameToggle.addEventListener('click', () => {
|
||||||
|
const open = renameInline.style.display !== 'none';
|
||||||
|
renameInline.style.display = open ? 'none' : 'flex';
|
||||||
|
if (!open) { renameInput.focus(); renameInput.select(); }
|
||||||
|
renameError.textContent = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
renameCancel.addEventListener('click', () => {
|
||||||
|
renameInline.style.display = 'none';
|
||||||
|
renameError.textContent = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
async function doRename() {
|
||||||
|
const newName = renameInput.value.trim();
|
||||||
|
if (!newName) return;
|
||||||
|
renameSave.disabled = true;
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('path', FILE_PATH);
|
||||||
|
fd.append('new_name', newName);
|
||||||
|
try {
|
||||||
|
const resp = await fetch(RENAME_URL, { method: 'POST', body: fd });
|
||||||
|
const data = await resp.json();
|
||||||
|
if (data.error) { renameError.textContent = data.error; return; }
|
||||||
|
window.location.href = data.browse_url;
|
||||||
|
} catch (_) {
|
||||||
|
renameError.textContent = 'Erreur réseau.';
|
||||||
|
} finally {
|
||||||
|
renameSave.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renameSave.addEventListener('click', doRename);
|
||||||
|
renameInput.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'Enter') doRename();
|
||||||
|
if (e.key === 'Escape') renameCancel.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Resize ── */
|
||||||
const szCbs = document.querySelectorAll('.resize-sz');
|
const szCbs = document.querySelectorAll('.resize-sz');
|
||||||
const fmtCbs = document.querySelectorAll('.resize-fmt');
|
const fmtCbs = document.querySelectorAll('.resize-fmt');
|
||||||
const btn = document.getElementById('resize-btn');
|
const btn = document.getElementById('resize-btn');
|
||||||
const result = document.getElementById('resize-result');
|
const result = document.getElementById('resize-result');
|
||||||
const PATH = {{ subpath | tojson }};
|
const RESIZE_URL = {{ url_for('resize_image') | tojson }};
|
||||||
const URL_RESIZE = {{ url_for('resize_image') | tojson }};
|
|
||||||
|
|
||||||
function canSubmit() {
|
function canSubmit() {
|
||||||
return Array.from(szCbs).some(c => c.checked)
|
return Array.from(szCbs).some(c => c.checked)
|
||||||
|
|
@ -85,8 +162,10 @@
|
||||||
}));
|
}));
|
||||||
|
|
||||||
btn.addEventListener('click', async () => {
|
btn.addEventListener('click', async () => {
|
||||||
|
const conflict = document.querySelector('input[name="conflict"]:checked')?.value || 'skip';
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('path', PATH);
|
fd.append('path', FILE_PATH);
|
||||||
|
fd.append('conflict', conflict);
|
||||||
szCbs.forEach(c => { if (c.checked) fd.append('sizes', c.value); });
|
szCbs.forEach(c => { if (c.checked) fd.append('sizes', c.value); });
|
||||||
fmtCbs.forEach(c => { if (c.checked) fd.append('formats', c.value); });
|
fmtCbs.forEach(c => { if (c.checked) fd.append('formats', c.value); });
|
||||||
|
|
||||||
|
|
@ -95,7 +174,7 @@
|
||||||
result.style.display = 'none';
|
result.style.display = 'none';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(URL_RESIZE, { method: 'POST', body: fd });
|
const resp = await fetch(RESIZE_URL, { method: 'POST', body: fd });
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue