upload button eingefügt
This commit is contained in:
@@ -41,6 +41,8 @@ FILE_OBSERVER = None
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
app = Flask(__name__)
|
||||
MAX_UPLOAD_MB = int(os.environ.get("MAX_UPLOAD_MB", "2048"))
|
||||
app.config["MAX_CONTENT_LENGTH"] = MAX_UPLOAD_MB * 1024 * 1024
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1070,6 +1072,12 @@ h1 { margin:0 0 6px; }
|
||||
.filter-bar input, .filter-bar select { width:100%; margin-top:5px; padding:9px; background:#111; color:#eee; border:1px solid #555; border-radius:5px; font-size:15px; }
|
||||
.filter-bar .search { grid-column:auto; }
|
||||
.filter-info { color:#999; font-size:13px; margin-bottom:10px; }
|
||||
.upload-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:16px; }
|
||||
.upload-info { color:#999; font-size:13px; }
|
||||
.upload-progress { display:none; flex:1 1 240px; min-width:180px; height:18px; background:#292929; border-radius:9px; overflow:hidden; }
|
||||
.upload-progress.active { display:block; }
|
||||
.upload-progress-bar { width:0; height:100%; background:#1769aa; transition:width .15s ease; }
|
||||
|
||||
table { width:100%; border-collapse:collapse; }
|
||||
th,td { padding:10px 8px; border-bottom:1px solid #333; text-align:left; }
|
||||
th { color:#aaa; font-weight:normal; }
|
||||
@@ -1100,6 +1108,12 @@ th { color:#aaa; font-weight:normal; }
|
||||
|
||||
<div class="section">
|
||||
<h2>Aufnahmen bearbeiten</h2>
|
||||
<div class="upload-bar">
|
||||
<input type="file" id="uploadFile" accept="audio/mpeg,.mp3" hidden>
|
||||
<button class="button" type="button" onclick="document.getElementById('uploadFile').click()">⬆ Vortrag hochladen</button>
|
||||
<span class="upload-info" id="uploadInfo">MP3-Datei vom PC auswählen</span>
|
||||
<div class="upload-progress" id="uploadProgress"><div class="upload-progress-bar" id="uploadProgressBar"></div></div>
|
||||
</div>
|
||||
{% if recordings %}
|
||||
<table>
|
||||
<thead><tr><th>Dateiname</th><th>Größe</th><th>Dauer</th><th></th></tr></thead>
|
||||
@@ -1255,6 +1269,66 @@ async function refreshFileLists() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const uploadFile = document.getElementById('uploadFile');
|
||||
const uploadInfo = document.getElementById('uploadInfo');
|
||||
const uploadProgress = document.getElementById('uploadProgress');
|
||||
const uploadProgressBar = document.getElementById('uploadProgressBar');
|
||||
|
||||
uploadFile?.addEventListener('change', () => {
|
||||
const file = uploadFile.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (!file.name.toLowerCase().endsWith('.mp3')) {
|
||||
alert('Bitte eine MP3-Datei auswählen.');
|
||||
uploadFile.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
uploadProgress.classList.add('active');
|
||||
uploadProgressBar.style.width = '0%';
|
||||
uploadInfo.textContent = `Upload: ${file.name}`;
|
||||
|
||||
xhr.open('POST', '/api/upload');
|
||||
xhr.upload.addEventListener('progress', event => {
|
||||
if (!event.lengthComputable) return;
|
||||
const percent = Math.round(event.loaded / event.total * 100);
|
||||
uploadProgressBar.style.width = percent + '%';
|
||||
uploadInfo.textContent = `Upload: ${file.name} – ${percent} %`;
|
||||
});
|
||||
|
||||
xhr.addEventListener('load', () => {
|
||||
let result = {};
|
||||
try { result = JSON.parse(xhr.responseText); } catch (_) {}
|
||||
if (xhr.status >= 200 && xhr.status < 300 && result.success) {
|
||||
uploadProgressBar.style.width = '100%';
|
||||
uploadInfo.textContent = `${result.filename} wurde hochgeladen.`;
|
||||
setTimeout(() => location.reload(), 500);
|
||||
} else {
|
||||
uploadProgress.classList.remove('active');
|
||||
uploadInfo.textContent = 'Upload fehlgeschlagen';
|
||||
alert(result.error || `Upload fehlgeschlagen (HTTP ${xhr.status})`);
|
||||
}
|
||||
});
|
||||
|
||||
xhr.addEventListener('error', () => {
|
||||
uploadProgress.classList.remove('active');
|
||||
uploadInfo.textContent = 'Upload fehlgeschlagen';
|
||||
alert('Netzwerkfehler beim Upload.');
|
||||
});
|
||||
|
||||
xhr.addEventListener('abort', () => {
|
||||
uploadProgress.classList.remove('active');
|
||||
uploadInfo.textContent = 'Upload abgebrochen';
|
||||
});
|
||||
|
||||
xhr.send(formData);
|
||||
});
|
||||
|
||||
const events = new EventSource('/api/events');
|
||||
events.onmessage = () => refreshFileLists();
|
||||
events.onerror = () => { /* EventSource reconnects automatically. */ };
|
||||
@@ -1484,6 +1558,40 @@ def finished_file(filename):
|
||||
return send_from_directory(FINISHED_DIR, path.name, mimetype="audio/mpeg", as_attachment=False)
|
||||
|
||||
|
||||
@app.route("/api/upload", methods=["POST"])
|
||||
def api_upload():
|
||||
upload = request.files.get("file")
|
||||
if upload is None or not upload.filename:
|
||||
return jsonify({"success": False, "error": "Keine Datei ausgewählt"}), 400
|
||||
|
||||
original_name = Path(upload.filename).name
|
||||
if original_name != upload.filename:
|
||||
return jsonify({"success": False, "error": "Ungültiger Dateiname"}), 400
|
||||
|
||||
if not original_name.lower().endswith(".mp3"):
|
||||
return jsonify({"success": False, "error": "Bitte eine MP3-Datei auswählen"}), 400
|
||||
|
||||
filename = original_name
|
||||
target = RECORDINGS_DIR / filename
|
||||
if target.exists():
|
||||
return jsonify({"success": False, "error": f"Eine Datei mit dem Namen '{filename}' existiert bereits"}), 409
|
||||
|
||||
temp = RECORDINGS_DIR / f".{filename}.{uuid.uuid4().hex}.uploading"
|
||||
try:
|
||||
RECORDINGS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
upload.save(temp)
|
||||
os.replace(temp, target)
|
||||
print(f"[upload] Uploaded recording: {filename}", flush=True)
|
||||
return jsonify({"success": True, "filename": filename})
|
||||
except OSError as exc:
|
||||
try:
|
||||
temp.unlink(missing_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
print(f"[upload] upload failed for {filename}: {exc}", flush=True)
|
||||
return jsonify({"success": False, "error": "Upload konnte nicht gespeichert werden"}), 500
|
||||
|
||||
|
||||
@app.route("/api/file", methods=["POST"])
|
||||
def api_file():
|
||||
data = request.get_json(silent=True) or {}
|
||||
|
||||
Reference in New Issue
Block a user