UNPKG

gphotos-scraper

Version:

A tool to extract public url and metadata from shared album

70 lines (57 loc) 1.62 kB
import { lookup } from "mime-types"; import type { GooglePhoto } from "../interfaces"; import { apiClient } from "./axiosClient"; const RPCID = "fDcn4b"; const BASE_URL = "https://photos.google.com/_/PhotosUi/data/batchexecute"; export const getPhotos = async ( key: string, photoIds: string[], ): Promise<GooglePhoto[]> => { const queryData = photoIds.map((photoId) => [ RPCID, JSON.stringify([photoId, null, key]), ]); const params = new URLSearchParams(); params.append("f.req", JSON.stringify([queryData])); const { data } = await apiClient.post(BASE_URL, params, { headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", }, }); // Security: Anti-hijacking cleanup const cleanData = data.replace(/^\)\]\}'/, ""); let list: any[]; try { list = JSON.parse(cleanData); } catch { return []; } // Optimized single-pass reduction return list.reduce((acc: GooglePhoto[], item: any[]) => { if (item?.[1] !== RPCID) return acc; try { const responseText = item?.[2]; if (!responseText) return acc; const jsonObj = JSON.parse(responseText); const photoData = jsonObj?.[0]; if (!photoData) return acc; const [id, description, filename, createdAt, , size, width, height] = photoData; const mimeType = lookup(filename as string) || "application/octet-stream"; acc.push({ id, url: "", // Will be populated in extractAlbum description, filename, createdAt, size, width, height, mimeType, }); } catch { // Silently ignore parsing failures for individual items } return acc; }, []); };