// === NYPL Augustów Book Downloader - High Resolution === // הריצו בקונסול של הדפדפן באתר NYPL (בעמוד הספר) // הסקריפט מוריד טיילים ברזולוציה גבוהה ומרכיב PDF (async function() { // --- הגדרות --- const FIRST_IMAGE_ID = 5921178; const TOTAL_PAGES = 560; const TILE_SIZE = 512; // בחר רזולוציה: 1=מלאה(4134x5788), 2=גבוהה(2067x2894), 4=בינונית(1034x1447) const SCALE_FACTOR = 2; const NATIVE_W = 4134; const NATIVE_H = 5788; const BATCH_SIZE = 10; // כמה טיילים להוריד במקביל const PAGE_DELAY_MS = 100; // השהיה בין עמודים // --- סוף הגדרות --- const OUTPUT_W = Math.ceil(NATIVE_W / SCALE_FACTOR); const OUTPUT_H = Math.ceil(NATIVE_H / SCALE_FACTOR); const REGION_SIZE = TILE_SIZE * SCALE_FACTOR; const TILES_X = Math.ceil(NATIVE_W / REGION_SIZE); const TILES_Y = Math.ceil(NATIVE_H / REGION_SIZE); const TILES_PER_PAGE = TILES_X * TILES_Y; console.log(`📐 רזולוציה: ${OUTPUT_W}x${OUTPUT_H}, ${TILES_PER_PAGE} טיילים/עמוד, ${TILES_PER_PAGE * TOTAL_PAGES} טיילים סה"כ`); // טעינת jsPDF console.log('📦 טוען jsPDF...'); const jspdfUrl = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js'; const resp = await fetch(jspdfUrl); if (!resp.ok) throw new Error(`Failed to fetch jsPDF: HTTP ${resp.status}`); const code = await resp.text(); if (code.startsWith('<')) throw new Error('Got HTML instead of JS'); try { const blob = new Blob([code], { type: 'application/javascript' }); const blobUrl = URL.createObjectURL(blob); await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = blobUrl; script.onload = () => { URL.revokeObjectURL(blobUrl); resolve(); }; script.onerror = () => { URL.revokeObjectURL(blobUrl); reject(); }; document.head.appendChild(script); }); } catch(e) { console.log('⚠️ Blob URL blocked, using eval...'); (0, eval)(code); } const { jsPDF } = window.jspdf; console.log('✅ jsPDF loaded'); // סטטוס על המסך const statusDiv = document.createElement('div'); statusDiv.style.cssText = 'position:fixed;top:10px;right:10px;z-index:999999;background:#222;color:#0f0;padding:15px 25px;border-radius:8px;font-family:monospace;font-size:14px;direction:ltr;box-shadow:0 4px 12px rgba(0,0,0,0.5);min-width:400px;'; document.body.appendChild(statusDiv); function updateStatus(text, pct) { const bar = pct !== undefined ? `
` : ''; statusDiv.innerHTML = `
${text}
${bar}`; } // הורדת טייל בודד async function fetchTile(imageId, regionX, regionY, retries = 3) { const rw = Math.min(REGION_SIZE, NATIVE_W - regionX); const rh = Math.min(REGION_SIZE, NATIVE_H - regionY); const outW = Math.ceil(rw / SCALE_FACTOR); const outH = Math.ceil(rh / SCALE_FACTOR); const url = `https://iiif.nypl.org/iiif/3/${imageId}/${regionX},${regionY},${rw},${rh}/${outW},${outH}/0/default.jpg`; for (let attempt = 1; attempt <= retries; attempt++) { try { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const blob = await response.blob(); return await createImageBitmap(blob); } catch (err) { if (attempt === retries) throw err; await new Promise(r => setTimeout(r, 500 * attempt)); } } } // הרכבת עמוד שלם מטיילים async function buildPage(imageId) { const canvas = document.createElement('canvas'); canvas.width = OUTPUT_W; canvas.height = OUTPUT_H; const ctx = canvas.getContext('2d'); // הורדת טיילים בקבוצות const tiles = []; for (let ty = 0; ty < TILES_Y; ty++) { for (let tx = 0; tx < TILES_X; tx++) { tiles.push({ tx, ty, regionX: tx * REGION_SIZE, regionY: ty * REGION_SIZE }); } } for (let i = 0; i < tiles.length; i += BATCH_SIZE) { const batch = tiles.slice(i, i + BATCH_SIZE); const bitmaps = await Promise.all( batch.map(t => fetchTile(imageId, t.regionX, t.regionY).catch(() => null)) ); bitmaps.forEach((bmp, idx) => { if (!bmp) return; const t = batch[idx]; const dx = Math.ceil(t.regionX / SCALE_FACTOR); const dy = Math.ceil(t.regionY / SCALE_FACTOR); ctx.drawImage(bmp, dx, dy); bmp.close(); }); } return canvas.toDataURL('image/jpeg', 0.85); } // יצירת PDF console.log(`📥 מתחיל הורדה של ${TOTAL_PAGES} עמודים...`); const pageW_mm = (OUTPUT_W * 25.4) / 150; const pageH_mm = (OUTPUT_H * 25.4) / 150; const pdf = new jsPDF({ orientation: pageW_mm > pageH_mm ? 'l' : 'p', unit: 'mm', format: [pageW_mm, pageH_mm], compress: true }); let failedPages = []; for (let p = 0; p < TOTAL_PAGES; p++) { const imageId = FIRST_IMAGE_ID + p; const pct = ((p / TOTAL_PAGES) * 100).toFixed(1); updateStatus(`📥 עמוד ${p + 1}/${TOTAL_PAGES} (${pct}%) - מוריד ${TILES_PER_PAGE} טיילים...`, pct); try { const dataURL = await buildPage(imageId); if (p > 0) { pdf.addPage([pageW_mm, pageH_mm], pageW_mm > pageH_mm ? 'l' : 'p'); } pdf.addImage(dataURL, 'JPEG', 0, 0, pageW_mm, pageH_mm); } catch (err) { console.error(`❌ עמוד ${p + 1} נכשל: ${err.message}`); failedPages.push(p + 1); if (p > 0) { pdf.addPage([pageW_mm, pageH_mm], pageW_mm > pageH_mm ? 'l' : 'p'); } } // נותן לדפדפן לנשום if (p % 5 === 0) { await new Promise(r => setTimeout(r, PAGE_DELAY_MS)); } } if (failedPages.length > 0) { console.warn(`⚠️ ${failedPages.length} עמודים נכשלו: ${failedPages.join(', ')}`); } updateStatus('💾 שומר PDF... (זה לוקח זמן עם 560 עמודים)', 100); console.log('💾 שומר PDF...'); pdf.save('Augustow_NYPL_HighRes.pdf'); updateStatus(`✅ הסתיים! ${TOTAL_PAGES - failedPages.length}/${TOTAL_PAGES} עמודים נשמרו`, 100); console.log('✅ הסתיים בהצלחה!'); setTimeout(() => statusDiv.remove(), 15000); })();