download-images.mjs 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import { mkdir, readFile, writeFile } from "node:fs/promises";
  2. import path from "node:path";
  3. const UA =
  4. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36";
  5. const entries = JSON.parse(await readFile(".scratch/images.json", "utf8"));
  6. // Map every tildacdn URL (including resize derivatives) to its original full-size URL.
  7. const canonical = new Map();
  8. for (const { url } of entries) {
  9. let orig = url.replace("thb.tildacdn.com", "static.tildacdn.com");
  10. orig = orig.replace(/\/-\/resize[^/]*\//, "/");
  11. orig = orig.replace(/\/20x\//, "/");
  12. canonical.set(url, orig);
  13. }
  14. const unique = [...new Set(canonical.values())];
  15. console.log(`${entries.length} refs -> ${unique.length} original images`);
  16. await mkdir("public/images", { recursive: true });
  17. const usedNames = new Set();
  18. const map = {};
  19. let ok = 0, fail = 0;
  20. for (const url of unique) {
  21. if (url.includes("/lib/icons/")) continue; // stock bullet icons, replaced by our own
  22. let name = decodeURIComponent(path.basename(new URL(url).pathname));
  23. if (usedNames.has(name)) {
  24. const ext = path.extname(name);
  25. name = name.slice(0, -ext.length) + "-" + Math.abs(hash(url)).toString(36) + ext;
  26. }
  27. usedNames.add(name);
  28. try {
  29. const res = await fetch(url, { headers: { "User-Agent": UA } });
  30. if (!res.ok) throw new Error("HTTP " + res.status);
  31. const buf = Buffer.from(await res.arrayBuffer());
  32. const min = url.endsWith(".svg") ? 50 : 1000;
  33. if (buf.length < min) throw new Error("too small");
  34. await writeFile(path.join("public/images", name), buf);
  35. map[url] = "/images/" + name;
  36. ok++;
  37. } catch (e) {
  38. fail++;
  39. console.log(`FAIL ${url}: ${e.message}`);
  40. }
  41. }
  42. // hash helper
  43. function hash(s) {
  44. let h = 0;
  45. for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
  46. return h;
  47. }
  48. await writeFile(".scratch/image-map.json", JSON.stringify(map, null, 2));
  49. console.log(`downloaded ${ok}, failed ${fail}`);