UNPKG

gphotos-scraper

Version:

A tool to extract public url and metadata from shared album

63 lines (50 loc) 1.66 kB
import { AlbumNotFoundException } from "../exceptions"; import type { Album } from "../interfaces"; import { getAlbum } from "./getAlbum"; import { getPhotos } from "./getPhotos"; export const extractAlbum = async (albumUrl: string): Promise<Album> => { const gAlbum = await getAlbum(albumUrl); if (!gAlbum) throw new AlbumNotFoundException(); const { items, key, id, title, cover, createdAt, updatedAt } = gAlbum; // Optimized O(1) Lookup Map const itemsMap = new Map(items.map((item) => [item.id, item.url])); const album: Album = { id, title, url: albumUrl, photos: [], createdAt, updatedAt, }; const { default: pLimit } = await import("p-limit"); const MAX_CONCURRENT_REQUESTS = 5; const limit = pLimit(MAX_CONCURRENT_REQUESTS); const chunkSize = 500; // Optimal batch size for Google Photos RPC const tasks = []; for (let i = 0; i < items.length; i += chunkSize) { tasks.push( limit(async () => { const chunk = items.slice(i, i + chunkSize); const ids = chunk.map((el) => el.id); try { const photosInfo = await getPhotos(key, ids); return photosInfo.map((el) => { const url = itemsMap.get(el.id) || ""; return { ...el, url }; }); } catch (err) { console.error(`Failed to fetch chunk starting at index ${i}`, err); return []; // Fail gracefully for this chunk } }), ); } const results = await Promise.all(tasks); album.photos.push(...results.flat()); // Sorting descending by createdAt album.photos.sort((a, b) => b.createdAt - a.createdAt); if (cover) { album.cover = album.photos.find((photo) => photo.url === cover.url); } return album; };