aded + '%'; // --- محاسبه زمان و سرعت --- const currentTime = Date.now(); const elapsedTime = (currentTime - startTime) / 1000; // in seconds speed = loaded / elapsedTime; // bytes per second // Avoid division by zero if speed is 0 const remaining = speed > 0 ? (totalBytes - loaded) / speed : Infinity; let display = "نامشخص"; if (isFinite(remaining)) { if (remaining > 60) { display = Math.ceil(remaining / 60) + " دقیقه"; } else { display = Math.ceil(remaining) + " ثانیه"; } } timeLeftEl.textContent = "حدوداً " + display + " باقی مانده"; } else if (totalBytes > 0) { timeLeftEl.textContent = "در حال محاسبه..."; } else { timeLeftEl.textContent = "زمان باقی مانده نامشخص"; } // --------------------------- } const blob = new Blob(chunks); const zip = await JSZip.loadAsync(blob); let images = []; zip.forEach((relativePath, file) => { // Adjusted regex to be more robust for common image extensions if (/\.(jpe?g|png|webp|gif|bmp)$/i.test(relativePath)) { images.push({ name: relativePath, file }); } }); // --- مرتب‌سازی با استفاده از تابع naturalSort --- images.sort(naturalSort); // --------------------------------------------- loader.style.display = 'none'; // لود یکی‌یکی تا ترتیب بهم نخوره for (let i = 0; i < images.length; i++) { const blob = await images[i].file.async('blob'); const src = URL.createObjectURL(blob); const img = document.createElement('img'); img.src = src; img.className = 'page'; viewer.appendChild(img); // Use a small delay to allow rendering and apply fade-in effect setTimeout(() => img.classList.add('fade-in'), i * 10); // Small delay per image } } catch (err) { console.error('Error loading ZIP:', err); loader.innerHTML = '

خطا در بارگذاری ZIP. لطفاً دوباره تلاش کنید.

'; } } // 🚀 لینک ZIP شما با CORS proxy const ZIP_URL = "https://cors.isteed.cc/" + 'https://hamhrtaha.mtplusteam.ir/SoloLevelingCh160.zip'; loadZipImages(ZIP_URL);