utils/parse/download_photo_new_qa6.js
2026-09-22 12:11:28 +03:00

98 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(async () => {
const BASE = 'https://qa6.ru';
const PER_PAGE = 500; // меньше запросов к API
const PAGE_CONCURRENCY = 8; // параллельные страницы
const IMG_CONCURRENCY = 16; // параллельные фото
const ZIP_NAME = 'qa6-models.zip';
const loadScript = (src) =>
new Promise((res, rej) => {
const s = document.createElement('script');
s.src = src;
s.onload = res;
s.onerror = rej;
document.head.appendChild(s);
});
if (!window.JSZip) {
await loadScript('https://cdn.jsdelivr.net/npm/jszip@3.10.2/dist/jszip.min.js');
}
const pool = async (items, limit, fn) => {
const out = new Array(items.length);
let i = 0;
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
while (i < items.length) {
const idx = i++;
out[idx] = await fn(items[idx], idx);
}
});
await Promise.all(workers);
return out;
};
const firstUrl =
`${BASE}/api/new-car-kit/admin/1/all-models` +
`?per_page=${PER_PAGE}&page=1&sort_by=id_car_model&sort_dir=asc`;
const first = await fetch(firstUrl).then((r) => r.json());
const lastPage = first.last_page || 1;
console.log(`моделей: ${first.total}, страниц: ${lastPage}`);
const pages = [first.data || []];
const rest = Array.from({ length: lastPage - 1 }, (_, i) => i + 2);
const restData = await pool(rest, PAGE_CONCURRENCY, async (page) => {
const url =
`${BASE}/api/new-car-kit/admin/1/all-models` +
`?per_page=${PER_PAGE}&page=${page}&sort_by=id_car_model&sort_dir=asc`;
const json = await fetch(url).then((r) => r.json());
console.log(`API page ${page}/${lastPage}`);
return json.data || [];
});
pages.push(...restData);
const models = pages.flat().filter((m) => m.image_path && String(m.image_path).trim());
console.log(`с фото: ${models.length}`);
const zip = new JSZip();
const folder = zip.folder('models');
let ok = 0;
let fail = 0;
await pool(models, IMG_CONCURRENCY, async (m, idx) => {
const path = String(m.image_path).replace(/^\/+/, '');
const url = `${BASE}/storage/${path}`;
const ext = path.match(/\.[a-z0-9]+$/i)?.[0] || '.png';
const name =
`${String(m.mark_name || 'mark')}_${String(m.model_name || 'model')}_${m.car_id || idx}`
.replace(/[\\/:*?"<>|]+/g, '_')
.replace(/\s+/g, '_') + ext;
try {
const res = await fetch(url);
if (!res.ok) throw new Error(res.status);
const buf = await res.arrayBuffer();
folder.file(name, buf);
ok++;
} catch (e) {
fail++;
console.warn('fail', url, e);
}
if ((ok + fail) % 50 === 0) {
console.log(`фото ${ok + fail}/${models.length} (ok ${ok}, fail ${fail})`);
}
});
console.log(`качаю zip: ok ${ok}, fail ${fail}`);
const blob = await zip.generateAsync({ type: 'blob', compression: 'STORE' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = ZIP_NAME;
a.click();
URL.revokeObjectURL(a.href);
console.log('готово');
})();