Esmaill1 commited on
Commit ·
08a45b9
1
Parent(s): 2faaae6
Optimize caching and further reduce asset sizes for better UI responsiveness
Browse files- web/server.py +8 -8
- web/web_storage/index.html +7 -13
web/server.py
CHANGED
|
@@ -119,15 +119,15 @@ async def upload_image(file: UploadFile = File(...)):
|
|
| 119 |
# Get original dimensions after transposition for the web cropper
|
| 120 |
width, height = img.size
|
| 121 |
|
| 122 |
-
# Create a faster, smaller thumbnail for the UI (
|
| 123 |
-
img.thumbnail((
|
| 124 |
thumb_path = UPLOAD_DIR / f"{file_id}_thumb.jpg"
|
| 125 |
if img.mode in ("RGBA", "LA"):
|
| 126 |
bg = Image.new("RGB", img.size, (255, 255, 255))
|
| 127 |
bg.paste(img, mask=img.split()[-1])
|
| 128 |
-
bg.save(thumb_path, "JPEG", quality=
|
| 129 |
else:
|
| 130 |
-
img.convert("RGB").save(thumb_path, "JPEG", quality=
|
| 131 |
return {
|
| 132 |
"id": file_id,
|
| 133 |
"filename": file.filename,
|
|
@@ -225,15 +225,15 @@ async def process_image(
|
|
| 225 |
result_path = RESULT_DIR / f"{file_id}_layout.jpg"
|
| 226 |
final_layout.save(result_path, "JPEG", quality=95, dpi=(300, 300))
|
| 227 |
|
| 228 |
-
# 5. Generate a lightweight WEB PREVIEW (max
|
| 229 |
preview_path = RESULT_DIR / f"{file_id}_preview.jpg"
|
| 230 |
pw, ph = final_layout.size
|
| 231 |
-
p_scale =
|
| 232 |
if p_scale < 1.0:
|
| 233 |
preview_img = final_layout.resize((int(pw * p_scale), int(ph * p_scale)), Image.BILINEAR)
|
| 234 |
-
preview_img.save(preview_path, "JPEG", quality=
|
| 235 |
else:
|
| 236 |
-
final_layout.save(preview_path, "JPEG", quality=
|
| 237 |
|
| 238 |
if temp_crop.exists(): temp_crop.unlink()
|
| 239 |
|
|
|
|
| 119 |
# Get original dimensions after transposition for the web cropper
|
| 120 |
width, height = img.size
|
| 121 |
|
| 122 |
+
# Create a faster, smaller thumbnail for the UI (200x200 is plenty for the 72px grid)
|
| 123 |
+
img.thumbnail((200, 200), Image.BILINEAR)
|
| 124 |
thumb_path = UPLOAD_DIR / f"{file_id}_thumb.jpg"
|
| 125 |
if img.mode in ("RGBA", "LA"):
|
| 126 |
bg = Image.new("RGB", img.size, (255, 255, 255))
|
| 127 |
bg.paste(img, mask=img.split()[-1])
|
| 128 |
+
bg.save(thumb_path, "JPEG", quality=60)
|
| 129 |
else:
|
| 130 |
+
img.convert("RGB").save(thumb_path, "JPEG", quality=60)
|
| 131 |
return {
|
| 132 |
"id": file_id,
|
| 133 |
"filename": file.filename,
|
|
|
|
| 225 |
result_path = RESULT_DIR / f"{file_id}_layout.jpg"
|
| 226 |
final_layout.save(result_path, "JPEG", quality=95, dpi=(300, 300))
|
| 227 |
|
| 228 |
+
# 5. Generate a lightweight WEB PREVIEW (max 900px width) for the UI
|
| 229 |
preview_path = RESULT_DIR / f"{file_id}_preview.jpg"
|
| 230 |
pw, ph = final_layout.size
|
| 231 |
+
p_scale = 900 / pw if pw > 900 else 1.0
|
| 232 |
if p_scale < 1.0:
|
| 233 |
preview_img = final_layout.resize((int(pw * p_scale), int(ph * p_scale)), Image.BILINEAR)
|
| 234 |
+
preview_img.save(preview_path, "JPEG", quality=70)
|
| 235 |
else:
|
| 236 |
+
final_layout.save(preview_path, "JPEG", quality=70)
|
| 237 |
|
| 238 |
if temp_crop.exists(): temp_crop.unlink()
|
| 239 |
|
web/web_storage/index.html
CHANGED
|
@@ -793,7 +793,7 @@
|
|
| 793 |
for (let file of files) {
|
| 794 |
try {
|
| 795 |
const data = await uploadFileWithProgress(file);
|
| 796 |
-
imageData.push({ ...data, name: "", id_num: "", result_url: null, custom_crop: null, steps: null, status: 'waiting' });
|
| 797 |
renderQueue();
|
| 798 |
if (currentIndex === -1) selectImage(imageData.length - 1);
|
| 799 |
} catch (e) {
|
|
@@ -840,7 +840,6 @@
|
|
| 840 |
return;
|
| 841 |
}
|
| 842 |
|
| 843 |
-
const t = new Date().getTime();
|
| 844 |
const html = imageData.map((img, idx) => {
|
| 845 |
let statusIcon = '';
|
| 846 |
if (img.status === 'waiting') statusIcon = '<i class="fa-regular fa-clock text-slate-500"></i>';
|
|
@@ -850,7 +849,7 @@
|
|
| 850 |
|
| 851 |
return `
|
| 852 |
<div onclick="selectImage(${idx})" class="queue-slide ${currentIndex === idx ? 'active' : ''}">
|
| 853 |
-
<img src="${img.preview_url ? img.preview_url + '?
|
| 854 |
<div class="slide-status">${statusIcon}</div>
|
| 855 |
<button onclick="deleteImage(event, ${idx})" class="slide-delete" title="حذف"><i class="fa-solid fa-xmark"></i></button>
|
| 856 |
<div class="slide-name">${img.filename}</div>
|
|
@@ -895,16 +894,10 @@
|
|
| 895 |
reprocessBtn.disabled = !data.result_url;
|
| 896 |
|
| 897 |
const previewSkeleton = document.getElementById('preview-skeleton');
|
| 898 |
-
const url = data.preview_url ? data.preview_url + '?
|
| 899 |
-
data.result_url ? data.result_url + '?
|
| 900 |
data.thumb_url;
|
| 901 |
|
| 902 |
-
// Reset the image completely before loading the new one
|
| 903 |
-
mainPreview.classList.add('hidden');
|
| 904 |
-
mainPreview.classList.remove('opacity-100');
|
| 905 |
-
mainPreview.classList.add('opacity-0');
|
| 906 |
-
mainPreview.removeAttribute('src');
|
| 907 |
-
|
| 908 |
if (!data.result_url) {
|
| 909 |
previewSkeleton.classList.remove('hidden');
|
| 910 |
} else {
|
|
@@ -912,7 +905,7 @@
|
|
| 912 |
}
|
| 913 |
previewPlaceholder.classList.add('hidden');
|
| 914 |
|
| 915 |
-
//
|
| 916 |
const tempImg = new Image();
|
| 917 |
tempImg.onload = () => {
|
| 918 |
mainPreview.src = tempImg.src;
|
|
@@ -952,7 +945,7 @@
|
|
| 952 |
mainPreview.src = data.thumb_url;
|
| 953 |
label.textContent = 'النتيجة';
|
| 954 |
} else {
|
| 955 |
-
mainPreview.src = (data.preview_url || data.result_url) + '?
|
| 956 |
label.textContent = 'الأصلية';
|
| 957 |
}
|
| 958 |
mainPreview.onload = () => {
|
|
@@ -1045,6 +1038,7 @@
|
|
| 1045 |
if (result.error) throw new Error(result.error);
|
| 1046 |
imageData[idx].result_url = result.result_url;
|
| 1047 |
imageData[idx].preview_url = result.preview_url;
|
|
|
|
| 1048 |
imageData[idx].status = 'done';
|
| 1049 |
imageData[idx].steps = {
|
| 1050 |
rmbg: document.getElementById('step-rmbg').checked,
|
|
|
|
| 793 |
for (let file of files) {
|
| 794 |
try {
|
| 795 |
const data = await uploadFileWithProgress(file);
|
| 796 |
+
imageData.push({ ...data, name: "", id_num: "", result_url: null, preview_url: null, version: 1, custom_crop: null, steps: null, status: 'waiting' });
|
| 797 |
renderQueue();
|
| 798 |
if (currentIndex === -1) selectImage(imageData.length - 1);
|
| 799 |
} catch (e) {
|
|
|
|
| 840 |
return;
|
| 841 |
}
|
| 842 |
|
|
|
|
| 843 |
const html = imageData.map((img, idx) => {
|
| 844 |
let statusIcon = '';
|
| 845 |
if (img.status === 'waiting') statusIcon = '<i class="fa-regular fa-clock text-slate-500"></i>';
|
|
|
|
| 849 |
|
| 850 |
return `
|
| 851 |
<div onclick="selectImage(${idx})" class="queue-slide ${currentIndex === idx ? 'active' : ''}">
|
| 852 |
+
<img src="${img.preview_url ? img.preview_url + '?v=' + img.version : img.thumb_url}" alt="">
|
| 853 |
<div class="slide-status">${statusIcon}</div>
|
| 854 |
<button onclick="deleteImage(event, ${idx})" class="slide-delete" title="حذف"><i class="fa-solid fa-xmark"></i></button>
|
| 855 |
<div class="slide-name">${img.filename}</div>
|
|
|
|
| 894 |
reprocessBtn.disabled = !data.result_url;
|
| 895 |
|
| 896 |
const previewSkeleton = document.getElementById('preview-skeleton');
|
| 897 |
+
const url = data.preview_url ? data.preview_url + '?v=' + data.version :
|
| 898 |
+
data.result_url ? data.result_url + '?v=' + data.version :
|
| 899 |
data.thumb_url;
|
| 900 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 901 |
if (!data.result_url) {
|
| 902 |
previewSkeleton.classList.remove('hidden');
|
| 903 |
} else {
|
|
|
|
| 905 |
}
|
| 906 |
previewPlaceholder.classList.add('hidden');
|
| 907 |
|
| 908 |
+
// Load new image
|
| 909 |
const tempImg = new Image();
|
| 910 |
tempImg.onload = () => {
|
| 911 |
mainPreview.src = tempImg.src;
|
|
|
|
| 945 |
mainPreview.src = data.thumb_url;
|
| 946 |
label.textContent = 'النتيجة';
|
| 947 |
} else {
|
| 948 |
+
mainPreview.src = (data.preview_url || data.result_url) + '?v=' + data.version;
|
| 949 |
label.textContent = 'الأصلية';
|
| 950 |
}
|
| 951 |
mainPreview.onload = () => {
|
|
|
|
| 1038 |
if (result.error) throw new Error(result.error);
|
| 1039 |
imageData[idx].result_url = result.result_url;
|
| 1040 |
imageData[idx].preview_url = result.preview_url;
|
| 1041 |
+
imageData[idx].version++;
|
| 1042 |
imageData[idx].status = 'done';
|
| 1043 |
imageData[idx].steps = {
|
| 1044 |
rmbg: document.getElementById('step-rmbg').checked,
|