// === 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);
})();