diff --git a/sound_picture_app/Web_Server.cpp b/sound_picture_app/Web_Server.cpp
index 2cdb8b0..b7f9bf2 100644
--- a/sound_picture_app/Web_Server.cpp
+++ b/sound_picture_app/Web_Server.cpp
@@ -132,7 +132,7 @@ input[type=range]{width:100%;accent-color:var(--accent)}
@@ -344,19 +344,105 @@ function uploadFile(fileInput, endpoint, progressWrap, fill, txt, onDone) {
}
function uploadPhoto() {
- document.getElementById('nextStep1').disabled=true;
- uploadFile(
- document.getElementById('photoFile'),
- '/api/upload/photo',
- document.getElementById('progress1'),
- document.getElementById('fill1'),
- document.getElementById('txt1'),
- function(filename){
- if(filename){ uploadedPhoto=filename; goStep(2); }
- else showToast('Photo upload failed. Try again.', false);
- document.getElementById('nextStep1').disabled=false;
- }
- );
+ var fileInput = document.getElementById('photoFile');
+ var file = fileInput.files[0];
+ if (!file) return;
+
+ document.getElementById('nextStep1').disabled = true;
+ var progressWrap = document.getElementById('progress1');
+ var fill = document.getElementById('fill1');
+ var txt = document.getElementById('txt1');
+
+ progressWrap.style.display = 'block';
+ txt.textContent = 'Preparing photo...';
+ fill.style.width = '10%';
+
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ var img = new Image();
+ img.onload = function() {
+ var canvas = document.createElement('canvas');
+ canvas.width = 172;
+ canvas.height = 320;
+ var ctx = canvas.getContext('2d');
+
+ // Center crop cover math
+ var srcWidth = img.width;
+ var srcHeight = img.height;
+ var ratio = Math.max(172 / srcWidth, 320 / srcHeight);
+ var newWidth = srcWidth * ratio;
+ var newHeight = srcHeight * ratio;
+ var x = (172 - newWidth) / 2;
+ var y = (320 - newHeight) / 2;
+
+ ctx.fillStyle = '#000000';
+ ctx.fillRect(0, 0, 172, 320);
+ ctx.drawImage(img, x, y, newWidth, newHeight);
+
+ canvas.toBlob(function(blob) {
+ if (!blob) {
+ showToast('Photo preparation failed.', false);
+ document.getElementById('nextStep1').disabled = false;
+ progressWrap.style.display = 'none';
+ return;
+ }
+
+ var origName = file.name;
+ var lastDot = origName.lastIndexOf('.');
+ var baseName = lastDot >= 0 ? origName.substring(0, lastDot) : 'photo';
+ var newFilename = baseName + '.png';
+
+ var fd = new FormData();
+ fd.append('file', blob, newFilename);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/api/upload/photo');
+ xhr.upload.onprogress = function(progressEvt) {
+ if (progressEvt.lengthComputable) {
+ var pct = Math.round(progressEvt.loaded / progressEvt.total * 100);
+ fill.style.width = pct + '%';
+ txt.textContent = 'Uploading photo... ' + pct + '%';
+ }
+ };
+
+ xhr.onload = function() {
+ progressWrap.style.display = 'none';
+ try {
+ var res = JSON.parse(xhr.responseText);
+ if (res.ok && res.filename) {
+ uploadedPhoto = res.filename;
+ goStep(2);
+ } else {
+ showToast('Photo upload failed: ' + (res.error || 'unknown error'), false);
+ }
+ } catch(err) {
+ showToast('Photo upload failed.', false);
+ }
+ document.getElementById('nextStep1').disabled = false;
+ };
+
+ xhr.onerror = function() {
+ progressWrap.style.display = 'none';
+ showToast('Photo upload failed.', false);
+ document.getElementById('nextStep1').disabled = false;
+ };
+
+ xhr.send(fd);
+ }, 'image/png');
+ };
+ img.onerror = function() {
+ showToast('Invalid image file.', false);
+ document.getElementById('nextStep1').disabled = false;
+ progressWrap.style.display = 'none';
+ };
+ img.src = e.target.result;
+ };
+ reader.onerror = function() {
+ showToast('Failed to read file.', false);
+ document.getElementById('nextStep1').disabled = false;
+ progressWrap.style.display = 'none';
+ };
+ reader.readAsDataURL(file);
}
function uploadAudio() {