This commit is contained in:
babayaga 2025-11-25 11:07:35 +01:00
parent e37cd8be67
commit 0d79173e5d
29 changed files with 3206 additions and 320 deletions

View File

@ -3,8 +3,14 @@ import { LocalResult } from './map_types.js';
export declare class HtmlToTextTransformer extends MappingDocumentTransformer {
static lc_name(): string;
constructor(options?: {});
_transformDocument(document: any): Promise<Document<any>>;
_transformDocument(document: Document): Promise<Document<{
[x: string]: any;
}>>;
}
export declare const cheerioLoader: (url: any) => Promise<unknown>;
export declare const puppeteerLoader: (url: any, headless: any, location: LocalResult) => Promise<unknown>;
export declare const findEMail: (question: string, url: string, opts: any, location: LocalResult) => Promise<false | any[]>;
export declare const cheerioLoader: (url: string) => Promise<unknown>;
export declare const puppeteerLoader: (url: string, headless: boolean, location: LocalResult) => Promise<unknown>;
export declare const findEMail: (question: string, url: string, opts: {
headless?: boolean;
searchFrom?: string;
[key: string]: any;
}, location: LocalResult) => Promise<false | string[]>;

File diff suppressed because one or more lines are too long

132
packages/search/dist-in/lib/google.d.ts vendored Normal file
View File

@ -0,0 +1,132 @@
import { LocationSiteMeta } from "./map_types.js";
export declare enum GoogleSearchUrls {
EN = "https://www.google.com/",
DE = "https://www.google.de/",
FR = "https://www.google.fr/",
ES = "https://www.google.es/",
IT = "https://www.google.it/",
PT = "https://www.google.pt/",
RU = "https://www.google.ru/",
JP = "https://www.google.co.jp/",
ZH = "https://www.google.com.hk/",
UK = "https://www.google.co.uk/"
}
export interface GpsCoordinates {
latitude: number;
longitude: number;
}
export interface OperatingHours {
thursday: string;
friday: string;
saturday: string;
sunday: string;
monday: string;
tuesday: string;
wednesday: string;
}
export interface Geo {
latitude: number;
lookupSource: string;
longitude: number;
localityLanguageRequested: string;
continent: string;
continentCode: string;
countryName: string;
countryCode: string;
principalSubdivision: string;
principalSubdivisionCode: string;
city: string;
locality: string;
postcode: string;
plusCode: string;
localityInfo: LocalityInfo;
}
export interface LocalityInfo {
administrative: Administrative[];
informative: Informative[];
}
export interface Administrative {
name: string;
description: string;
isoName?: string;
order: number;
adminLevel: number;
isoCode?: string;
wikidataId: string;
geonameId: number;
}
export interface Informative {
name: string;
description?: string;
isoName?: string;
order: number;
isoCode?: string;
wikidataId?: string;
geonameId?: number;
}
export interface GoogleMapResult {
position: number;
title: string;
place_id: string;
data_id: string;
data_cid: string;
reviews_link: string;
photos_link: string;
gps_coordinates: GpsCoordinates;
place_id_search: string;
provider_id: string;
rating: number;
reviews: number;
type: string;
types: string[];
address: string;
open_state: string;
hours: string;
operating_hours: OperatingHours;
website: string;
thumbnail: string;
geo: Geo;
}
export interface GoogleMapResultEx extends GoogleMapResult {
email?: string;
}
export interface GoogleMapPhotos {
search_metadata: SearchMetadata;
search_parameters: SearchParameters;
categories: Category[];
photos: Photo[];
serpapi_pagination: SerpapiPagination;
}
export interface SearchMetadata {
id: string;
status: string;
json_endpoint: string;
created_at: string;
processed_at: string;
google_maps_photos_url: string;
raw_html_file: string;
prettify_html_file: string;
total_time_taken: number;
}
export interface SearchParameters {
engine: string;
data_id: string;
hl: string;
}
export interface Category {
title: string;
id: string;
}
export interface Photo {
thumbnail: string;
image: string;
photo_meta_serpapi_link: string;
}
export interface SerpapiPagination {
next: string;
next_page_token: string;
}
export interface GoogleMapResultInternal extends GoogleMapResultEx {
google_media?: GoogleMapPhotos;
meta?: LocationSiteMeta;
}

View File

@ -0,0 +1,14 @@
export var GoogleSearchUrls;
(function (GoogleSearchUrls) {
GoogleSearchUrls["EN"] = "https://www.google.com/";
GoogleSearchUrls["DE"] = "https://www.google.de/";
GoogleSearchUrls["FR"] = "https://www.google.fr/";
GoogleSearchUrls["ES"] = "https://www.google.es/";
GoogleSearchUrls["IT"] = "https://www.google.it/";
GoogleSearchUrls["PT"] = "https://www.google.pt/";
GoogleSearchUrls["RU"] = "https://www.google.ru/";
GoogleSearchUrls["JP"] = "https://www.google.co.jp/";
GoogleSearchUrls["ZH"] = "https://www.google.com.hk/";
GoogleSearchUrls["UK"] = "https://www.google.co.uk/";
})(GoogleSearchUrls || (GoogleSearchUrls = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ29vZ2xlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpYi9nb29nbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsTUFBTSxDQUFOLElBQVksZ0JBV1g7QUFYRCxXQUFZLGdCQUFnQjtJQUMxQixrREFBOEIsQ0FBQTtJQUM5QixpREFBNkIsQ0FBQTtJQUM3QixpREFBNkIsQ0FBQTtJQUM3QixpREFBNkIsQ0FBQTtJQUM3QixpREFBNkIsQ0FBQTtJQUM3QixpREFBNkIsQ0FBQTtJQUM3QixpREFBNkIsQ0FBQTtJQUM3QixvREFBZ0MsQ0FBQTtJQUNoQyxxREFBaUMsQ0FBQTtJQUNqQyxvREFBZ0MsQ0FBQTtBQUNsQyxDQUFDLEVBWFcsZ0JBQWdCLEtBQWhCLGdCQUFnQixRQVczQiJ9

View File

@ -0,0 +1,13 @@
export type ParsedURL = {
scheme: string;
host?: string;
path?: string;
query?: Record<string, string>;
fragment?: string;
};
export declare const escapeFirstUrlSegment: (url: string) => string;
export declare const handleFs: (path: string) => Promise<string | object>;
export declare const schemeHandlers: Record<string, (arg1: string, arg2?: URLSearchParams) => Promise<string | object>>;
export declare const parseCustomUrl: (url: string) => Promise<string | object>;
export declare const resolvePath: (str: string, query: any, category: any, opts: any) => string;
export declare const cleanObjectStrings: (obj: any) => any;

View File

@ -0,0 +1,89 @@
import * as path from 'path';
import { URL } from 'url';
import { isFile, resolve } from '@polymech/commons';
import { sync as read } from '@polymech/fs/read';
import { sync as exists } from '@polymech/fs/exists';
export const escapeFirstUrlSegment = (url) => {
const schemeEndIndex = url.indexOf('://') + 3;
const restOfUrl = url.slice(schemeEndIndex);
const questionMarkIndex = restOfUrl.indexOf('?');
if (questionMarkIndex !== -1) {
const firstSegment = restOfUrl.slice(0, questionMarkIndex);
const escapedFirstSegment = encodeURIComponent(firstSegment);
return url.slice(0, schemeEndIndex) + escapedFirstSegment + restOfUrl.slice(questionMarkIndex);
}
else {
const escapedFirstSegment = encodeURIComponent(restOfUrl);
return url.slice(0, schemeEndIndex) + escapedFirstSegment;
}
};
export const handleFs = async (path) => {
return read(path);
};
export const schemeHandlers = {
// 'osr-ai': handleOsrAi,
'fs': handleFs,
'default': handleFs
};
export const parseCustomUrl = async (url) => {
if (!url.includes('://')) {
const _path = path.resolve(resolve(url));
if (exists(_path) && isFile(_path)) {
return read(_path, 'json');
}
}
const parsedUrl = new URL(escapeFirstUrlSegment(url));
let scheme = parsedUrl.protocol.replace(':', '') || 'default';
const handler = schemeHandlers[scheme];
let result = null;
if (handler) {
if (scheme === 'osr-ai') {
result = await handler(parsedUrl.hostname, parsedUrl.searchParams);
}
else {
result = await handler(parsedUrl.pathname);
}
}
return result || url;
};
export const resolvePath = (str, query, category, opts) => {
return path.resolve(resolve(str, false, {
QUERY: query,
FROM: opts.searchFrom ? opts.searchFrom.split(',').map((s) => s.trim()).join('/') : 'barcelona, spain',
ENGINE: opts.engine,
DOMAIN: opts.google_domain,
LANG: opts.language,
COUNTRY: opts.country,
AREA: opts.area,
CATEGORY: category || 'unknown',
...opts.variables || {}
}));
};
const cleanString = (str) => {
if (typeof str !== 'string') {
return str;
}
return str
.replace(/\u202F/g, ' ') // narrow no-break space
.replace(/\u2013/g, '-') // en dash
.replace(/\u2019/g, "'"); // right single quotation mark
};
export const cleanObjectStrings = (obj) => {
if (!obj) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(item => cleanObjectStrings(item));
}
if (typeof obj === 'object') {
return Object.entries(obj).reduce((acc, [key, value]) => {
acc[key] = cleanObjectStrings(value);
return acc;
}, {});
}
if (typeof obj === 'string') {
return cleanString(obj);
}
return obj;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ29vZ2xlbWFwcy11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvZ29vZ2xlbWFwcy11dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQTtBQUM1QixPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sS0FBSyxDQUFBO0FBQ3pCLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDbkQsT0FBTyxFQUFFLElBQUksSUFBSSxJQUFJLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNoRCxPQUFPLEVBQUUsSUFBSSxJQUFJLE1BQU0sRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBVXBELE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLENBQUMsR0FBVyxFQUFVLEVBQUU7SUFDekQsTUFBTSxjQUFjLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDOUMsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUM1QyxNQUFNLGlCQUFpQixHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFakQsSUFBSSxpQkFBaUIsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNCLE1BQU0sWUFBWSxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFDM0QsTUFBTSxtQkFBbUIsR0FBRyxrQkFBa0IsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUM3RCxPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLGNBQWMsQ0FBQyxHQUFHLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNuRyxDQUFDO1NBQU0sQ0FBQztRQUNKLE1BQU0sbUJBQW1CLEdBQUcsa0JBQWtCLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDMUQsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxjQUFjLENBQUMsR0FBRyxtQkFBbUIsQ0FBQztJQUM5RCxDQUFDO0FBQ0wsQ0FBQyxDQUFBO0FBR0QsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEtBQUssRUFBRSxJQUFZLEVBQTRCLEVBQUU7SUFDckUsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUE7QUFDckIsQ0FBQyxDQUFBO0FBRUQsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUF1RjtJQUM5Ryw0QkFBNEI7SUFDeEIsSUFBSSxFQUFFLFFBQVE7SUFDZCxTQUFTLEVBQUUsUUFBUTtDQUN0QixDQUFBO0FBQ0QsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQTRCLEVBQUU7SUFDMUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN2QixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQ3hDLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2pDLE9BQU8sSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQTtRQUM5QixDQUFDO0lBQ0wsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLElBQUksR0FBRyxDQUFDLHFCQUFxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFDckQsSUFBSSxNQUFNLEdBQUcsU0FBUyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLFNBQVMsQ0FBQTtJQUM3RCxNQUFNLE9BQU8sR0FBRyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDdEMsSUFBSSxNQUFNLEdBQW9CLElBQUksQ0FBQTtJQUNsQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1FBQ1YsSUFBSSxNQUFNLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDdEIsTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFBO1FBQ3RFLENBQUM7YUFBTSxDQUFDO1lBQ0osTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQTtRQUM5QyxDQUFDO0lBQ0wsQ0FBQztJQUNELE9BQU8sTUFBTSxJQUFJLEdBQUcsQ0FBQTtBQUN4QixDQUFDLENBQUE7QUFDTCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxHQUFXLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxJQUFTLEVBQUUsRUFBRTtJQUNuRSxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQ2xDO1FBQ0ksS0FBSyxFQUFFLEtBQUs7UUFDWixJQUFJLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLGtCQUFrQjtRQUN0RyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07UUFDbkIsTUFBTSxFQUFFLElBQUksQ0FBQyxhQUFhO1FBQzFCLElBQUksRUFBRSxJQUFJLENBQUMsUUFBUTtRQUNuQixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87UUFDckIsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO1FBQ2YsUUFBUSxFQUFFLFFBQVEsSUFBSSxTQUFTO1FBQy9CLEdBQUcsSUFBSSxDQUFDLFNBQVMsSUFBSSxFQUFFO0tBQzFCLENBQUMsQ0FBQyxDQUFBO0FBQ1gsQ0FBQyxDQUFBO0FBRUQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxHQUFXLEVBQVUsRUFBRTtJQUN4QyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzFCLE9BQU8sR0FBRyxDQUFBO0lBQ2QsQ0FBQztJQUNELE9BQU8sR0FBRztTQUNMLE9BQU8sQ0FBQyxTQUFTLEVBQUUsR0FBRyxDQUFDLENBQUMsd0JBQXdCO1NBQ2hELE9BQU8sQ0FBQyxTQUFTLEVBQUUsR0FBRyxDQUFDLENBQUMsVUFBVTtTQUNsQyxPQUFPLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFBLENBQUMsOEJBQThCO0FBQy9ELENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLENBQUMsR0FBUSxFQUFPLEVBQUU7SUFDaEQsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ1AsT0FBTyxHQUFHLENBQUE7SUFDZCxDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDckIsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtJQUNwRCxDQUFDO0lBQ0QsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUMxQixPQUFPLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUU7WUFDcEQsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFBO1lBQ3BDLE9BQU8sR0FBRyxDQUFBO1FBQ2QsQ0FBQyxFQUFFLEVBQVMsQ0FBQyxDQUFBO0lBQ2pCLENBQUM7SUFDRCxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzFCLE9BQU8sV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQzNCLENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQTtBQUNkLENBQUMsQ0FBQSJ9

View File

@ -0,0 +1,981 @@
import { z } from 'zod';
import * as CLI from 'yargs';
export declare enum ResolveFlags {
PHOTOS = "PHOTOS"
}
export declare const zodSchemaBase: () => z.ZodObject<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, z.ZodTypeAny, "passthrough">>;
export declare const zodSchema: () => z.ZodEffects<z.ZodObject<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, z.ZodTypeAny, "passthrough">>, z.objectOutputType<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
logLevel: z.ZodDefault<z.ZodString>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
}, z.ZodTypeAny, "passthrough">>;
export declare const zodSchemaEachExtras: () => z.ZodObject<{
logLevel: z.ZodDefault<z.ZodString>;
log: z.ZodOptional<z.ZodString>;
country: z.ZodString;
area: z.ZodString;
list: z.ZodString;
cwd: z.ZodDefault<z.ZodOptional<z.ZodString>>;
env: z.ZodDefault<z.ZodString>;
profile: z.ZodDefault<z.ZodString>;
migrate: z.ZodDefault<z.ZodBoolean>;
}, "strip", z.ZodTypeAny, {
logLevel?: string;
log?: string;
country?: string;
area?: string;
list?: string;
cwd?: string;
env?: string;
profile?: string;
migrate?: boolean;
}, {
logLevel?: string;
log?: string;
country?: string;
area?: string;
list?: string;
cwd?: string;
env?: string;
profile?: string;
migrate?: boolean;
}>;
export declare const zodSchemaEach: () => z.ZodEffects<z.ZodObject<{
api_key: z.ZodOptional<z.ZodString>;
blacklist: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
cache: z.ZodDefault<z.ZodBoolean>;
category: z.ZodDefault<z.ZodOptional<z.ZodString>>;
concurrency: z.ZodDefault<z.ZodNumber>;
dst: z.ZodOptional<z.ZodString>;
dump: z.ZodOptional<z.ZodString>;
engine: z.ZodDefault<z.ZodString>;
env_key: z.ZodDefault<z.ZodString>;
filterCity: z.ZodOptional<z.ZodString>;
filterCountry: z.ZodOptional<z.ZodString>;
filterContinent: z.ZodOptional<z.ZodString>;
filterType: z.ZodOptional<z.ZodString>;
findEMail: z.ZodDefault<z.ZodBoolean>;
geocode_key: z.ZodOptional<z.ZodString>;
google_domain: z.ZodDefault<z.ZodString>;
headless: z.ZodDefault<z.ZodBoolean>;
language: z.ZodDefault<z.ZodString>;
limit: z.ZodDefault<z.ZodNumber>;
meta: z.ZodDefault<z.ZodBoolean>;
searchCache: z.ZodDefault<z.ZodBoolean>;
query: z.ZodDefault<z.ZodString>;
resolve: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodNativeEnum<typeof ResolveFlags>, "many">>>;
searchCoord: z.ZodOptional<z.ZodString>;
searchFrom: z.ZodDefault<z.ZodOptional<z.ZodString>>;
source: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>]>>;
type: z.ZodDefault<z.ZodOptional<z.ZodString>>;
zoom: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
index: z.ZodDefault<z.ZodString>;
store: z.ZodDefault<z.ZodString>;
variables: z.ZodOptional<z.ZodAny>;
} & {
logLevel: z.ZodDefault<z.ZodString>;
log: z.ZodOptional<z.ZodString>;
country: z.ZodString;
area: z.ZodString;
list: z.ZodString;
cwd: z.ZodDefault<z.ZodOptional<z.ZodString>>;
env: z.ZodDefault<z.ZodString>;
profile: z.ZodDefault<z.ZodString>;
migrate: z.ZodDefault<z.ZodBoolean>;
}, "strip", z.ZodTypeAny, {
api_key?: string;
type?: string;
blacklist?: string[];
cache?: boolean;
category?: string;
concurrency?: number;
dst?: string;
dump?: string;
engine?: string;
env_key?: string;
filterCity?: string;
filterCountry?: string;
filterContinent?: string;
filterType?: string;
findEMail?: boolean;
geocode_key?: string;
google_domain?: string;
headless?: boolean;
language?: string;
limit?: number;
logLevel?: string;
meta?: boolean;
searchCache?: boolean;
query?: string;
resolve?: ResolveFlags.PHOTOS[];
searchCoord?: string;
searchFrom?: string;
source?: string | Record<string, string[]>;
zoom?: number;
index?: string;
store?: string;
variables?: any;
log?: string;
country?: string;
area?: string;
list?: string;
cwd?: string;
env?: string;
profile?: string;
migrate?: boolean;
}, {
api_key?: string;
type?: string;
blacklist?: string[];
cache?: boolean;
category?: string;
concurrency?: number;
dst?: string;
dump?: string;
engine?: string;
env_key?: string;
filterCity?: string;
filterCountry?: string;
filterContinent?: string;
filterType?: string;
findEMail?: boolean;
geocode_key?: string;
google_domain?: string;
headless?: boolean;
language?: string;
limit?: number;
logLevel?: string;
meta?: boolean;
searchCache?: boolean;
query?: string;
resolve?: ResolveFlags.PHOTOS[];
searchCoord?: string;
searchFrom?: string;
source?: string | Record<string, string[]>;
zoom?: number;
index?: string;
store?: string;
variables?: any;
log?: string;
country?: string;
area?: string;
list?: string;
cwd?: string;
env?: string;
profile?: string;
migrate?: boolean;
}>, {
api_key?: string;
type?: string;
blacklist?: string[];
cache?: boolean;
category?: string;
concurrency?: number;
dst?: string;
dump?: string;
engine?: string;
env_key?: string;
filterCity?: string;
filterCountry?: string;
filterContinent?: string;
filterType?: string;
findEMail?: boolean;
geocode_key?: string;
google_domain?: string;
headless?: boolean;
language?: string;
limit?: number;
logLevel?: string;
meta?: boolean;
searchCache?: boolean;
query?: string;
resolve?: ResolveFlags.PHOTOS[];
searchCoord?: string;
searchFrom?: string;
source?: string | Record<string, string[]>;
zoom?: number;
index?: string;
store?: string;
variables?: any;
log?: string;
country?: string;
area?: string;
list?: string;
cwd?: string;
env?: string;
profile?: string;
migrate?: boolean;
}, {
api_key?: string;
type?: string;
blacklist?: string[];
cache?: boolean;
category?: string;
concurrency?: number;
dst?: string;
dump?: string;
engine?: string;
env_key?: string;
filterCity?: string;
filterCountry?: string;
filterContinent?: string;
filterType?: string;
findEMail?: boolean;
geocode_key?: string;
google_domain?: string;
headless?: boolean;
language?: string;
limit?: number;
logLevel?: string;
meta?: boolean;
searchCache?: boolean;
query?: string;
resolve?: ResolveFlags.PHOTOS[];
searchCoord?: string;
searchFrom?: string;
source?: string | Record<string, string[]>;
zoom?: number;
index?: string;
store?: string;
variables?: any;
log?: string;
country?: string;
area?: string;
list?: string;
cwd?: string;
env?: string;
profile?: string;
migrate?: boolean;
}>;
export type IOptionsGoogleMaps = z.infer<ReturnType<typeof zodSchema>>;
export type IOptionsGoogleMapsInput = z.input<ReturnType<typeof zodSchema>>;
export type IOptionsGoogleMapsEach = z.infer<ReturnType<typeof zodSchemaEach>>;
export type IOptionsGoogleMapsEachInput = z.input<ReturnType<typeof zodSchemaEach>>;
export declare const yargsOptions: (yargs: CLI.Argv) => CLI.Argv;
export declare const yargsOptionsEach: (yargs: CLI.Argv) => CLI.Argv;
export declare const meta_schema: z.ZodObject<{
og: z.ZodObject<{
url: z.ZodString;
type: z.ZodString;
title: z.ZodString;
site_name: z.ZodString;
}, "strip", z.ZodTypeAny, {
type?: string;
url?: string;
title?: string;
site_name?: string;
}, {
type?: string;
url?: string;
title?: string;
site_name?: string;
}>;
meta: z.ZodObject<{
url: z.ZodString;
title: z.ZodString;
}, "strip", z.ZodTypeAny, {
url?: string;
title?: string;
}, {
url?: string;
title?: string;
}>;
links: z.ZodArray<z.ZodUnknown, "many">;
images: z.ZodArray<z.ZodObject<{
src: z.ZodString;
}, "strip", z.ZodTypeAny, {
src?: string;
}, {
src?: string;
}>, "many">;
allLinks: z.ZodArray<z.ZodString, "many">;
keywords: z.ZodArray<z.ZodUnknown, "many">;
instagram: z.ZodString;
structured: z.ZodArray<z.ZodObject<{
"@graph": z.ZodArray<z.ZodUnion<[z.ZodObject<{
"@id": z.ZodString;
url: z.ZodString;
name: z.ZodString;
"@type": z.ZodString;
isPartOf: z.ZodObject<{
"@id": z.ZodString;
}, "strip", z.ZodTypeAny, {
"@id"?: string;
}, {
"@id"?: string;
}>;
breadcrumb: z.ZodObject<{
"@id": z.ZodString;
}, "strip", z.ZodTypeAny, {
"@id"?: string;
}, {
"@id"?: string;
}>;
inLanguage: z.ZodString;
dateModified: z.ZodString;
datePublished: z.ZodString;
potentialAction: z.ZodArray<z.ZodObject<{
"@type": z.ZodString;
target: z.ZodArray<z.ZodString, "many">;
}, "strip", z.ZodTypeAny, {
"@type"?: string;
target?: string[];
}, {
"@type"?: string;
target?: string[];
}>, "many">;
}, "strip", z.ZodTypeAny, {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
isPartOf?: {
"@id"?: string;
};
breadcrumb?: {
"@id"?: string;
};
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: {
"@type"?: string;
target?: string[];
}[];
}, {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
isPartOf?: {
"@id"?: string;
};
breadcrumb?: {
"@id"?: string;
};
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: {
"@type"?: string;
target?: string[];
}[];
}>, z.ZodObject<{
"@id": z.ZodString;
"@type": z.ZodString;
itemListElement: z.ZodArray<z.ZodObject<{
name: z.ZodString;
"@type": z.ZodString;
position: z.ZodNumber;
}, "strip", z.ZodTypeAny, {
name?: string;
"@type"?: string;
position?: number;
}, {
name?: string;
"@type"?: string;
position?: number;
}>, "many">;
}, "strip", z.ZodTypeAny, {
"@id"?: string;
"@type"?: string;
itemListElement?: {
name?: string;
"@type"?: string;
position?: number;
}[];
}, {
"@id"?: string;
"@type"?: string;
itemListElement?: {
name?: string;
"@type"?: string;
position?: number;
}[];
}>, z.ZodObject<{
"@id": z.ZodString;
url: z.ZodString;
name: z.ZodString;
"@type": z.ZodString;
inLanguage: z.ZodString;
description: z.ZodString;
potentialAction: z.ZodArray<z.ZodObject<{
"@type": z.ZodString;
target: z.ZodObject<{
"@type": z.ZodString;
urlTemplate: z.ZodString;
}, "strip", z.ZodTypeAny, {
"@type"?: string;
urlTemplate?: string;
}, {
"@type"?: string;
urlTemplate?: string;
}>;
"query-input": z.ZodObject<{
"@type": z.ZodString;
valueName: z.ZodString;
valueRequired: z.ZodBoolean;
}, "strip", z.ZodTypeAny, {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
}, {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
}>;
}, "strip", z.ZodTypeAny, {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}, {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}>, "many">;
}, "strip", z.ZodTypeAny, {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
inLanguage?: string;
potentialAction?: {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}[];
description?: string;
}, {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
inLanguage?: string;
potentialAction?: {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}[];
description?: string;
}>]>, "many">;
"@context": z.ZodString;
}, "strip", z.ZodTypeAny, {
"@graph"?: ({
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
isPartOf?: {
"@id"?: string;
};
breadcrumb?: {
"@id"?: string;
};
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: {
"@type"?: string;
target?: string[];
}[];
} | {
"@id"?: string;
"@type"?: string;
itemListElement?: {
name?: string;
"@type"?: string;
position?: number;
}[];
} | {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
inLanguage?: string;
potentialAction?: {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}[];
description?: string;
})[];
"@context"?: string;
}, {
"@graph"?: ({
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
isPartOf?: {
"@id"?: string;
};
breadcrumb?: {
"@id"?: string;
};
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: {
"@type"?: string;
target?: string[];
}[];
} | {
"@id"?: string;
"@type"?: string;
itemListElement?: {
name?: string;
"@type"?: string;
position?: number;
}[];
} | {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
inLanguage?: string;
potentialAction?: {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}[];
description?: string;
})[];
"@context"?: string;
}>, "many">;
}, "strip", z.ZodTypeAny, {
meta?: {
url?: string;
title?: string;
};
og?: {
type?: string;
url?: string;
title?: string;
site_name?: string;
};
links?: unknown[];
images?: {
src?: string;
}[];
allLinks?: string[];
keywords?: unknown[];
instagram?: string;
structured?: {
"@graph"?: ({
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
isPartOf?: {
"@id"?: string;
};
breadcrumb?: {
"@id"?: string;
};
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: {
"@type"?: string;
target?: string[];
}[];
} | {
"@id"?: string;
"@type"?: string;
itemListElement?: {
name?: string;
"@type"?: string;
position?: number;
}[];
} | {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
inLanguage?: string;
potentialAction?: {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}[];
description?: string;
})[];
"@context"?: string;
}[];
}, {
meta?: {
url?: string;
title?: string;
};
og?: {
type?: string;
url?: string;
title?: string;
site_name?: string;
};
links?: unknown[];
images?: {
src?: string;
}[];
allLinks?: string[];
keywords?: unknown[];
instagram?: string;
structured?: {
"@graph"?: ({
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
isPartOf?: {
"@id"?: string;
};
breadcrumb?: {
"@id"?: string;
};
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: {
"@type"?: string;
target?: string[];
}[];
} | {
"@id"?: string;
"@type"?: string;
itemListElement?: {
name?: string;
"@type"?: string;
position?: number;
}[];
} | {
url?: string;
"@id"?: string;
name?: string;
"@type"?: string;
inLanguage?: string;
potentialAction?: {
"@type"?: string;
target?: {
"@type"?: string;
urlTemplate?: string;
};
"query-input"?: {
"@type"?: string;
valueName?: string;
valueRequired?: boolean;
};
}[];
description?: string;
})[];
"@context"?: string;
}[];
}>;

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
import { zodSchema, zodSchemaEach, yargsOptions, yargsOptionsEach, IOptionsGoogleMaps, IOptionsGoogleMapsEach } from './googlemaps-zod.js';
export { zodSchema, zodSchemaEach, yargsOptions, yargsOptionsEach, IOptionsGoogleMaps, IOptionsGoogleMapsEach };
import { zodSchema, zodSchemaEach, yargsOptions, yargsOptionsEach, IOptionsGoogleMaps, IOptionsGoogleMapsInput, IOptionsGoogleMapsEach, IOptionsGoogleMapsEachInput, ResolveFlags } from './googlemaps-zod.js';
export { zodSchema, zodSchemaEach, yargsOptions, yargsOptionsEach, IOptionsGoogleMaps, IOptionsGoogleMapsInput, IOptionsGoogleMapsEach, IOptionsGoogleMapsEachInput, ResolveFlags };
import type { GoogleParameters } from "serpapi";
import { IScaleserpSearch } from './types.js';
import { LocalResult } from './map_types.js';
import { GoogleMapResultInternal, type GoogleMapResult } from './google.js';
export declare enum SearchQueriesES {
INJECTION = "inyecci\u00F3n de plastico"
}
@ -20,8 +20,8 @@ export declare const defaultSearchParamsMapsES: (query: any, zoom: any, mixin?:
hl: string;
};
export declare const searchVendorSA: (query: string, location: string, key: string, opts: GoogleParameters) => Promise<import("serpapi").BaseResponse<GoogleParameters>>;
export declare const searchGoogleMap: (query: string, key: string, opts: IOptionsGoogleMaps) => Promise<LocalResult[]>;
export declare const searchGoogleMap: (query: string, key: string, opts: IOptionsGoogleMaps) => Promise<GoogleMapResult[]>;
export declare const parse: (argv: IOptionsGoogleMaps) => IOptionsGoogleMaps;
export declare const googleMaps: (opts: IOptionsGoogleMaps) => Promise<any[][]>;
export declare const googleMaps: (opts: IOptionsGoogleMapsInput) => Promise<GoogleMapResultInternal[]>;
export declare const migrate: (opts: IOptionsGoogleMapsEach) => Promise<void[]>;
export declare const each: (opts: IOptionsGoogleMapsEach) => Promise<any[]>;

File diff suppressed because one or more lines are too long

View File

@ -1,15 +1,15 @@
import { AxiosRequestConfig } from "axios";
import { Browser } from 'puppeteer';
import * as puppeteer from 'puppeteer';
import { LocalResult } from './map_types.js';
import { LocalResult, LocationSiteMeta } from './map_types.js';
export declare const STATS_SUFFIX = "_stats.json";
export declare const SESSION_EVENTS_SUFFIX = "_session.json";
export declare const TRACE_SUFFIX = "_trace.json";
export declare var scope: Scope;
export declare const extractEmail: (input: string) => string | null;
export declare const meta: (loc: LocalResult, options: any) => Promise<void>;
export declare const isValidUrl: (url: any) => boolean;
export declare const parse: (url: string, config: AxiosRequestConfig, options: any) => Promise<{}>;
export declare const meta: (loc: LocalResult, options: any) => Promise<LocationSiteMeta | void>;
export declare const isValidUrl: (url: string) => boolean;
export declare const parse: (url: string, config: AxiosRequestConfig | null, options: any) => Promise<LocationSiteMeta>;
export declare const getScope: (cliArgs?: any) => Scope;
export declare function capture_responses(scope: Scope, page: puppeteer.Page): Promise<void>;
export declare class Scope {
@ -27,4 +27,3 @@ export declare class Scope {
init(): Promise<void>;
}
export declare const body: (url: string) => Promise<unknown>;
export declare const contactUrl: (url: any) => Promise<void>;

File diff suppressed because one or more lines are too long

View File

@ -17,17 +17,42 @@ export interface GpsCoordinates {
longitude: number;
}
export interface OperatingHours {
viernes: string;
sábado: string;
domingo: string;
lunes: string;
martes: string;
miércoles: string;
jueves: string;
friday?: string;
saturday?: string;
sunday?: string;
monday?: string;
tuesday?: string;
wednesday?: string;
thursday?: string;
}
export type LocalResult = {
title?: string;
place_id?: string;
data_id?: string;
data_cid?: string;
reviews_link?: string;
photos_link?: string;
gps_coordinates?: GpsCoordinates;
place_id_search?: string;
provider_id?: string;
rating?: number;
reviews?: number;
price?: string;
type?: string;
types?: string[];
address?: string;
open_state?: string;
hours?: string;
operating_hours?: OperatingHours;
phone?: string;
website?: string;
description?: string;
service_options?: {
[key: string]: boolean;
};
thumbnail?: string;
email?: string;
[key: string]: any;
filterType?: string;
};
export interface SearchParameters {
engine: string;
@ -43,3 +68,90 @@ export interface SearchResult {
local_results: LocalResult[];
search_parameters: SearchParameters;
}
export interface LocationSiteMeta {
og?: Og;
meta?: Meta;
links?: string[];
images?: Image[];
allLinks?: string[];
keywords?: string[];
instagram?: string;
facebook?: string;
linkedin?: string;
youtube?: string;
twitter?: string;
structured?: Structured[];
}
export interface Og {
url?: string;
type?: string;
title?: string;
site_name?: string;
description?: string;
image?: string;
[key: string]: any;
}
export interface Meta {
url?: string;
title?: string;
description?: string;
image?: string;
type?: string;
site_name?: string;
keywords?: string[];
[key: string]: any;
}
export interface Image {
src: string;
}
export interface Structured {
"@graph"?: Graph[];
"@context"?: string;
"@type"?: string;
name?: string;
url?: string;
image?: string;
email?: string;
telephone?: string;
address?: string | object;
sameAs?: string[];
openingHours?: string;
legalName?: string;
[key: string]: any;
}
export interface Graph {
"@id": string;
url?: string;
name?: string;
"@type": string;
isPartOf?: IsPartOf;
breadcrumb?: Breadcrumb;
inLanguage?: string;
dateModified?: string;
datePublished?: string;
potentialAction?: PotentialAction[];
itemListElement?: ItemListElement[];
description?: string;
[key: string]: any;
}
export interface IsPartOf {
"@id": string;
}
export interface Breadcrumb {
"@id": string;
}
export interface PotentialAction {
"@type": string;
target: any;
"query-input"?: QueryInput;
}
export interface QueryInput {
"@type": string;
valueName: string;
valueRequired: boolean;
}
export interface ItemListElement {
name: string;
"@type": string;
position: number;
}

View File

@ -0,0 +1,3 @@
export declare enum ResolveFlags {
PHOTOS = "PHOTOS"
}

View File

@ -0,0 +1,5 @@
export var ResolveFlags;
(function (ResolveFlags) {
ResolveFlags["PHOTOS"] = "PHOTOS";
})(ResolveFlags || (ResolveFlags = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvcmVzb2x2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQU4sSUFBWSxZQUVYO0FBRkQsV0FBWSxZQUFZO0lBQ3BCLGlDQUFpQixDQUFBO0FBQ3JCLENBQUMsRUFGVyxZQUFZLEtBQVosWUFBWSxRQUV2QiJ9

View File

@ -4,12 +4,16 @@
"version": "0.2.0",
"typings": "index.d.ts",
"type": "module",
"exports": {
".": "./dist-in/index.js"
},
"publishConfig": {
"access": "public"
},
"bin": {
"polymech-search": "dist-in/main.js"
},
"main": "src/index.ts",
"dependencies": {
"@keyv/sqlite": "^4.0.1",
"@plastichub/osr-registry": "file:../registry",

View File

@ -24,7 +24,7 @@ export class HtmlToTextTransformer extends MappingDocumentTransformer {
value: options
})
}
async _transformDocument(document) {
async _transformDocument(document: Document) {
const extractedContent = htmlToText(document.pageContent, this['options']);
return new Document({
pageContent: extractedContent,
@ -33,7 +33,7 @@ export class HtmlToTextTransformer extends MappingDocumentTransformer {
}
}
export const cheerioLoader = async (url) => {
export const cheerioLoader = async (url: string) => {
const loader = new CheerioWebBaseLoader(url)
const docs = await loader.load()
const splitter = RecursiveCharacterTextSplitter.fromLanguage("html")
@ -44,7 +44,7 @@ export const cheerioLoader = async (url) => {
}
export const puppeteerLoader = async (url, headless, location: LocalResult) => {
export const puppeteerLoader = async (url: string, headless: boolean, location: LocalResult) => {
if (isValidUrl(url) === false || url.indexOf('mailto') !== -1) {
return []
}
@ -95,7 +95,7 @@ const extractEmailAddresses = (text: string): string[] => {
return emailAddresses
}
export const findEMail = async (question: string, url: string, opts: any, location: LocalResult) => {
export const findEMail = async (question: string, url: string, opts: { headless?: boolean, searchFrom?: string, [key: string]: any }, location: LocalResult) => {
// for some weird reason only the user knows :)
if (url.match(emailRegex) || url.match(mailtoRegex) || url.indexOf('mailto') !== -1) {
logger.warn('Email found in URL', url)
@ -106,8 +106,8 @@ export const findEMail = async (question: string, url: string, opts: any, locati
pageUrl = location.meta.links[0]
}
let docs = await puppeteerLoader(pageUrl, opts.headless, location) as any
let emails = []
docs.forEach((d) => {
let emails: string[] = []
docs.forEach((d: any) => {
if (d.pageContent && d.pageContent.indexOf('@') !== -1) {
const mails = extractEmailAddresses(d.pageContent)
if (mails) {

View File

@ -1,3 +1,5 @@
import { LocationSiteMeta } from "./map_types.js"
export enum GoogleSearchUrls {
EN = "https://www.google.com/",
DE = "https://www.google.de/",
@ -12,13 +14,12 @@ export enum GoogleSearchUrls {
}
export interface GpsCoordinates {
export interface GpsCoordinates {
latitude: number
longitude: number
}
}
export interface OperatingHours {
export interface OperatingHours {
thursday: string
friday: string
saturday: string
@ -26,9 +27,9 @@ export enum GoogleSearchUrls {
monday: string
tuesday: string
wednesday: string
}
}
export interface Geo {
export interface Geo {
latitude: number
lookupSource: string
longitude: number
@ -44,14 +45,14 @@ export enum GoogleSearchUrls {
postcode: string
plusCode: string
localityInfo: LocalityInfo
}
}
export interface LocalityInfo {
export interface LocalityInfo {
administrative: Administrative[]
informative: Informative[]
}
}
export interface Administrative {
export interface Administrative {
name: string
description: string
isoName?: string
@ -60,9 +61,9 @@ export enum GoogleSearchUrls {
isoCode?: string
wikidataId: string
geonameId: number
}
}
export interface Informative {
export interface Informative {
name: string
description?: string
isoName?: string
@ -70,9 +71,9 @@ export enum GoogleSearchUrls {
isoCode?: string
wikidataId?: string
geonameId?: number
}
}
export interface GoogleMapResult {
export interface GoogleMapResult {
position: number
title: string
place_id: string
@ -94,5 +95,67 @@ export enum GoogleSearchUrls {
website: string
thumbnail: string
geo: Geo
}
}
export interface GoogleMapResultEx extends GoogleMapResult {
email?: string
}
/////////////////////////////////////////////
//
// SerpApi - Media (GoogleMapResult::photos_link ->)
//
/////////////////////////////////////////////
export interface GoogleMapPhotos {
search_metadata: SearchMetadata
search_parameters: SearchParameters
categories: Category[]
photos: Photo[]
serpapi_pagination: SerpapiPagination
}
export interface SearchMetadata {
id: string
status: string
json_endpoint: string
created_at: string
processed_at: string
google_maps_photos_url: string
raw_html_file: string
prettify_html_file: string
total_time_taken: number
}
export interface SearchParameters {
engine: string
data_id: string
hl: string
}
export interface Category {
title: string
id: string
}
export interface Photo {
thumbnail: string
image: string
photo_meta_serpapi_link: string
}
export interface SerpapiPagination {
next: string
next_page_token: string
}
/////////////////////////////////////////////
//
// Internal Search Result Types
//
/////////////////////////////////////////////
export interface GoogleMapResultInternal extends GoogleMapResultEx {
google_media?: GoogleMapPhotos,
meta?: LocationSiteMeta
}

View File

@ -0,0 +1,103 @@
import * as path from 'path'
import { URL } from 'url'
import { isFile, resolve } from '@polymech/commons'
import { sync as read } from '@polymech/fs/read'
import { sync as exists } from '@polymech/fs/exists'
import { substitute } from '@polymech/commons'
export type ParsedURL = {
scheme: string
host?: string
path?: string
query?: Record<string, string>
fragment?: string
}
export const escapeFirstUrlSegment = (url: string): string => {
const schemeEndIndex = url.indexOf('://') + 3;
const restOfUrl = url.slice(schemeEndIndex);
const questionMarkIndex = restOfUrl.indexOf('?');
if (questionMarkIndex !== -1) {
const firstSegment = restOfUrl.slice(0, questionMarkIndex);
const escapedFirstSegment = encodeURIComponent(firstSegment);
return url.slice(0, schemeEndIndex) + escapedFirstSegment + restOfUrl.slice(questionMarkIndex);
} else {
const escapedFirstSegment = encodeURIComponent(restOfUrl);
return url.slice(0, schemeEndIndex) + escapedFirstSegment;
}
}
export const handleFs = async (path: string): Promise<string | object> => {
return read(path)
}
export const schemeHandlers: Record<string, (arg1: string, arg2?: URLSearchParams) => Promise<string | object>> = {
// 'osr-ai': handleOsrAi,
'fs': handleFs,
'default': handleFs
}
export const parseCustomUrl = async (url: string): Promise<string | object> => {
if (!url.includes('://')) {
const _path = path.resolve(resolve(url))
if (exists(_path) && isFile(_path)) {
return read(_path, 'json')
}
}
const parsedUrl = new URL(escapeFirstUrlSegment(url))
let scheme = parsedUrl.protocol.replace(':', '') || 'default'
const handler = schemeHandlers[scheme]
let result: string | object = null
if (handler) {
if (scheme === 'osr-ai') {
result = await handler(parsedUrl.hostname, parsedUrl.searchParams)
} else {
result = await handler(parsedUrl.pathname)
}
}
return result || url
}
export const resolvePath = (str: string, query, category, opts: any) => {
return path.resolve(resolve(str, false,
{
QUERY: query,
FROM: opts.searchFrom ? opts.searchFrom.split(',').map((s) => s.trim()).join('/') : 'barcelona, spain',
ENGINE: opts.engine,
DOMAIN: opts.google_domain,
LANG: opts.language,
COUNTRY: opts.country,
AREA: opts.area,
CATEGORY: category || 'unknown',
...opts.variables || {}
}))
}
const cleanString = (str: string): string => {
if (typeof str !== 'string') {
return str
}
return str
.replace(/\u202F/g, ' ') // narrow no-break space
.replace(/\u2013/g, '-') // en dash
.replace(/\u2019/g, "'") // right single quotation mark
}
export const cleanObjectStrings = (obj: any): any => {
if (!obj) {
return obj
}
if (Array.isArray(obj)) {
return obj.map(item => cleanObjectStrings(item))
}
if (typeof obj === 'object') {
return Object.entries(obj).reduce((acc, [key, value]) => {
acc[key] = cleanObjectStrings(value)
return acc
}, {} as any)
}
if (typeof obj === 'string') {
return cleanString(obj)
}
return obj
}

View File

@ -0,0 +1,190 @@
import { z } from 'zod'
import * as CLI from 'yargs'
import { toYargs } from '@polymech/commons'
export enum ResolveFlags {
PHOTOS = 'PHOTOS',
}
const o = {
query: "plastichub",
engine: "google_maps",
type: "search",
q: "plastichub",
ll: "@41.6911354,2.1652746,13z",
google_domain: "google.es",
hl: "en",
searchFrom: "barcelona, spain",
api_key: "517879d08bd8f13df9c4265c42aea8cfe960942f3a10e8774bbec11becbfb687",
geocode_key: "65bcf01943459613018206nmi9830a9",
openai: {
key: "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",
"key-p": "sk-x9O7hWAAeDCdX6HVyv49R2NV7JhFjGhUj7gG5szBoBT3BlbkFJfzB9Mo7j8Yl3xevSgeoSR-GXpftEevoS4ybwJrcWsA",
},
headless: false,
bigdata: {
key: "bdc_26a67478a1f1492faf5cec9c498da553",
},
}
// Base schema without transformation - allows merging
export const zodSchemaBase = () =>
z.object(
{
api_key: z.string().optional().describe('API Key'),
blacklist: z
.array(z.string())
.default(['bazar.preciousplastic.com'])
.describe('A list of hostnames to exclude from the results').optional(),
cache: z.boolean().default(false),
category: z.string().optional().default('category'),
concurrency: z.number().default(3).describe('The concurrency level for async operations'),
dst: z.string().optional().describe('${POLYMECH_ROOT}/campaign/maps/${FROM}/${CATEGORY}/${QUERY}-10.xls'),
dump: z.string().optional(),
engine: z.string().default('google_maps'),
env_key: z.string().default('OSR-CONFIG'),
filterCity: z.string().optional(),
filterCountry: z.string().optional(),
filterContinent: z.string().optional(),
filterType: z.string().optional(),
findEMail: z.boolean().default(false),
geocode_key: z.string().optional(),
google_domain: z.string().default('google.com'),
headless: z.boolean().default(true).describe('Headless mode'),
language: z.string().default('en'),
limit: z.number().default(5),
logLevel: z.string().default('info'),
meta: z.boolean().default(true),
searchCache: z.boolean().default(false).describe('Use search cache'),
query: z.string().default('plastichub'),
resolve: z.array(z.nativeEnum(ResolveFlags)).default([ResolveFlags.PHOTOS]).optional(),
searchCoord: z.string().optional(),
searchFrom: z.string().optional().default('barcelona, spain'),
source: z.union([z.string(), z.record(z.string(), z.array(z.string()))]).optional(),
type: z.string().optional().default('search'),
zoom: z.number().optional().default(13),
index: z.string().default('${OSR_ROOT}/osr-directory/meta/index.json').describe('Index file'),
store: z.string().default('${OSR_ROOT}/osr-directory/meta/index.db').describe('Index store'),
variables: z.any().optional(),
})
.passthrough()
// Schema with transformation applied
export const zodSchema = () =>
zodSchemaBase()
.transform((data) => {
if (!data.source) {
data.source = {
[data.category || 'unknown']: [data.query],
}
}
return data
})
export const zodSchemaEachExtras = () =>
z.object({
logLevel: z.string().default('info'),
log: z.string().optional(),
country: z.string().describe('The country to search in, variable ${COUNTRY}'),
area: z.string().describe('The city to search in, variable ${AREA}'),
list: z.string().describe('List of items to process, FILE|GLOB|AI-Query, provided as ${TOWN}'),
cwd: z.string().optional().default('./').describe('the current working directory to use, otherwise . is being assumed'),
env: z.string().default(''),
profile: z.string().default('${OSR_ROOT}/osr-templates/osrl/.osrl.json'),
migrate: z.boolean().default(false),
})
// Merge the base schemas first, then apply transformation
export const zodSchemaEach = () =>
zodSchemaBase()
.merge(zodSchemaEachExtras())
.transform((data) => {
if (!data.source) {
data.source = {
[data.category || 'unknown']: [data.query],
}
}
return data
})
.describe('IOptionsGoogleMapsEach')
export type IOptionsGoogleMaps = z.infer<ReturnType<typeof zodSchema>>
export type IOptionsGoogleMapsInput = z.input<ReturnType<typeof zodSchema>>
export type IOptionsGoogleMapsEach = z.infer<ReturnType<typeof zodSchemaEach>>
export type IOptionsGoogleMapsEachInput = z.input<ReturnType<typeof zodSchemaEach>>
export const yargsOptions = (yargs: CLI.Argv) => toYargs(yargs as any, zodSchema() as any)
export const yargsOptionsEach = (yargs: CLI.Argv) => toYargs(yargs as any, zodSchemaEach() as any)
export const meta_schema = z.object({
og: z.object({
url: z.string(),
type: z.string(),
title: z.string(),
site_name: z.string()
}),
meta: z.object({ url: z.string(), title: z.string() }),
links: z.array(z.unknown()),
images: z.array(z.object({ src: z.string() })),
allLinks: z.array(z.string()),
keywords: z.array(z.unknown()),
instagram: z.string(),
structured: z.array(
z.object({
"@graph": z.array(
z.union([
z.object({
"@id": z.string(),
url: z.string(),
name: z.string(),
"@type": z.string(),
isPartOf: z.object({ "@id": z.string() }),
breadcrumb: z.object({ "@id": z.string() }),
inLanguage: z.string(),
dateModified: z.string(),
datePublished: z.string(),
potentialAction: z.array(
z.object({ "@type": z.string(), target: z.array(z.string()) })
)
}),
z.object({
"@id": z.string(),
"@type": z.string(),
itemListElement: z.array(
z.object({
name: z.string(),
"@type": z.string(),
position: z.number()
})
)
}),
z.object({
"@id": z.string(),
url: z.string(),
name: z.string(),
"@type": z.string(),
inLanguage: z.string(),
description: z.string(),
potentialAction: z.array(
z.object({
"@type": z.string(),
target: z.object({
"@type": z.string(),
urlTemplate: z.string()
}),
"query-input": z.object({
"@type": z.string(),
valueName: z.string(),
valueRequired: z.boolean()
})
})
)
})
])
),
"@context": z.string()
})
)
})

View File

@ -3,7 +3,7 @@ import * as path from 'path'
import { CONFIG_DEFAULT, DEFAULT_ROOTS, IProfile, pathInfo, filesEx } from '@polymech/commons'
import { cleanObjectStrings } from './googlemaps-utils.js'
import {parse as parseProfile } from '@polymech/commons/profile'
import { parse as parseProfile } from '@polymech/commons/profile'
import { isFile, resolve, substitute } from '@polymech/commons'
@ -13,10 +13,13 @@ import {
yargsOptions,
yargsOptionsEach,
IOptionsGoogleMaps,
IOptionsGoogleMapsInput,
IOptionsGoogleMapsEach,
IOptionsGoogleMapsEachInput,
ResolveFlags,
} from './googlemaps-zod.js'
import { parseCustomUrl, resolvePath } from './googlemaps-utils.js'
export { zodSchema, zodSchemaEach, yargsOptions, yargsOptionsEach, IOptionsGoogleMaps, IOptionsGoogleMapsEach }
export { zodSchema, zodSchemaEach, yargsOptions, yargsOptionsEach, IOptionsGoogleMaps, IOptionsGoogleMapsInput, IOptionsGoogleMapsEach, IOptionsGoogleMapsEachInput, ResolveFlags }
import { clone } from '../options.js'
import type { GoogleMapsParameters, GoogleParameters } from "serpapi"
@ -38,8 +41,9 @@ import { reverse, REVERSE_DEFAULT } from './geo.js'
import { writeReport } from '../lib/report_map.js'
import { geocode_forward } from './geo.js'
import { LocalResult } from './map_types.js'
import { GoogleMapResultEx, GoogleMapResultInternal, type GoogleMapResult, type GoogleMapPhotos } from './google.js'
import { store as getStore } from '@polymech/registry'
import axios from 'axios'
const MODULE_NAME = 'osr-search'
const queryExtras = ''
@ -130,7 +134,7 @@ export const searchGoogleMap = async (
ll: opts.searchCoord
} as GoogleMapsParameters
let results: LocalResult[] = []
let results: GoogleMapResult[] = []
let pageIdx = 0
let index = opts.index ? (read(opts.index, 'json') as any) || {} : {}
const params: any = googleParams
@ -186,7 +190,7 @@ export const searchGoogleMap = async (
let idx = 0
//const cachedLoc = async (title: string) => getStored(title, opts.store, MODULE_NAME)
await enrichResults(results, index, opts)
await enrichResults(results as GoogleMapResult[], index, opts)
logger.debug(
`search ${query} with ${params.ll} / ${params.searchFrom} @ ${opts.zoom} | ${results.length} results before filters`,
@ -202,7 +206,7 @@ export const searchGoogleMap = async (
results = results.filter((r) => r.geo.continent.toLowerCase() === opts.filterContinent.toLowerCase())
}
if (opts.filterType) {
results = results.filter((r) => r.filterType === opts.filterType)
results = results.filter((r) => r.type === opts.filterType)
}
results = results.filter((r) => r.gps_coordinates)
@ -218,7 +222,7 @@ export const searchGoogleMap = async (
return results
}
const enrichResults = async (results: LocalResult[], index: any, opts: IOptionsGoogleMaps) => {
const enrichResults = async (results: GoogleMapResult[], index: any, opts: IOptionsGoogleMaps): Promise<GoogleMapResultInternal[]> => {
let idx = 0
await pMap(
results,
@ -269,7 +273,6 @@ const enrichResults = async (results: LocalResult[], index: any, opts: IOptionsG
}
if (entry.meta && entry.website && !entry.email) {
try {
logger.debug(`searching email for ${entry.website}`)
return findEMail(SEARCH_AI_PROMPTS.GET_EMAIL, entry.website, opts, entry)
} catch (e) {
logger.error(`Error retrieving EMail data ${entry.title}`)
@ -279,8 +282,34 @@ const enrichResults = async (results: LocalResult[], index: any, opts: IOptionsG
{ concurrency: 1 },
)
}
if (opts.resolve?.includes(ResolveFlags.PHOTOS)) {
await pMap(
results,
async (entry: GoogleMapResultInternal) => {
if (entry.google_media || !entry.data_id) {
return
}
if (index[entry.title] && index[entry.title].google_media) {
entry.google_media = index[entry.title].google_media
return
}
try {
if (!entry.photos_link) return
const url = `${entry.photos_link}&api_key=${opts.api_key}`
const response = await axios.get(url)
const photos: GoogleMapPhotos = response.data
if (photos) {
entry.google_media = photos
}
} catch (e) {
logger.error(`Error retrieving photo data for ${entry.title}`, e)
}
},
{ concurrency: opts.concurrency },
)
}
return results
return results as GoogleMapResultInternal[]
}
export const parse = (argv: IOptionsGoogleMaps): IOptionsGoogleMaps => {
@ -295,7 +324,7 @@ export const parse = (argv: IOptionsGoogleMaps): IOptionsGoogleMaps => {
logger.warn('No serpapi key found in config!')
return
}
const opts = {
let opts = {
query: argv.query as string,
...defaultSearchParamsMapsES(argv.query, argv.zoom),
...argv,
@ -305,9 +334,11 @@ export const parse = (argv: IOptionsGoogleMaps): IOptionsGoogleMaps => {
headless: argv.headless ? true : false,
bigdata: { key: config.bigdata.key }
} as any
opts = zodSchema().parse(opts)
/*
opts.source && isString(opts.source) && (opts.source = path.resolve(resolve(args.source, false)))
if (isString(opts.source)) {
if (opts.source && isString(opts.source)) {
if (exists(opts.source)) {
opts.source = read(opts.source, 'json')
} else {
@ -315,20 +346,17 @@ export const parse = (argv: IOptionsGoogleMaps): IOptionsGoogleMaps => {
return
}
}
*/
if (!opts.source && !opts.query) {
logger.warn(`Invalid source and query`)
return
}
if (opts.index) {
opts.index = path.resolve(resolve(args.index, false))
opts.index = path.resolve(resolve(opts.index, false))
}
if (opts.store) {
opts.store = path.resolve(resolve(args.store, false))
}
if (!opts.source) {
opts.source = {}
opts.source[opts.category] = [opts.query]
opts.store = path.resolve(resolve(opts.store, false))
}
if (!opts.api_key) {
logger.error('No Serpapi key found in config or options!')
@ -341,7 +369,7 @@ export const parse = (argv: IOptionsGoogleMaps): IOptionsGoogleMaps => {
return opts
}
export const googleMaps = async (opts: IOptionsGoogleMaps) => {
export const googleMaps = async (opts: IOptionsGoogleMapsInput): Promise<GoogleMapResultInternal[]> => {
opts = parse(opts)
if (!opts) {
logger.error('Invalid options', opts)
@ -364,9 +392,12 @@ export const googleMaps = async (opts: IOptionsGoogleMaps) => {
let ret: any[] = []
const search = async (query: string, category, opts: any) => {
opts = clone(opts)
if (opts.dst) {
opts.dst = resolvePath(path.join(opts.cwd || '', opts.dst || ''), query, category, opts)
logger.debug(`output destination --dst "${opts.dst}"`)
if (opts.cache !== false && exists(opts.dst + '.json')) {
}
if (opts.dst && opts.cache !== false && exists(opts.dst + '.json')) {
const cachedPath = opts.dst + '.json'
const cached = read(cachedPath, 'json') as any || []
logger.debug(`Searching ${opts.query} with ${opts.searchFrom} :: returning cached ${cached.length}`)
@ -374,41 +405,46 @@ export const googleMaps = async (opts: IOptionsGoogleMaps) => {
return cached
}
try {
logger.debug(`Searching ${opts.query} with ${opts.searchFrom}`)
const sr = await searchGoogleMap(query, opts.api_key, { ...opts })
if (sr && sr.length && opts.dst) {
logger.debug('Writing', opts.dst)
write(opts.dst + '.json', sr)
writeReport(sr, opts.dst, opts)
const parts = path.parse(opts.dst)
write(path.join(parts.dir, parts.name + '_options.json'), cleanOptions(opts))
// writeReport(sr, opts.dst.replace('.xlsx', '.md'), opts)
}
ret = [...ret, ...sr]
logger.debug(`Searching ${opts.query} with ${opts.searchFrom}`)
return ret
return sr
} catch (error) {
logger.error('Error searching GoogleMaps : ' + error.message, error, error.stack)
return []
}
}
const all = await pMap(Object.keys(opts.source), (k: any) => {
return pMap(opts.source[k], (t: any) => {
// @todos : retry, ...
const all: GoogleMapResultInternal[] = (await pMap(
Object.keys(opts.source),
(k: string) => {
return pMap(
opts.source[k],
(t: string) => {
return search(t, k, opts)
}, {
concurrency: 1
})
}, {
concurrency: 1
})
},
{
concurrency: 1,
},
)
},
{
concurrency: 1,
},
)).flat(2)
if (opts.dst) {
opts.dst = resolvePath(opts.dst, 'all', 'all', opts)
logger.debug(`final output destination --dst "${opts.dst}"`)
let existingResults: LocalResult[] = []
if (exists(opts.dst + '.json')) {
existingResults = (read(opts.dst + '.json', 'json') as LocalResult[]) || []
}
// Combine, deduplicate, clean, and process URLs in a single chain
const finalResults = Array.from(
[...existingResults, ...ret].reduce((map, obj) => {
@ -417,8 +453,7 @@ export const googleMaps = async (opts: IOptionsGoogleMaps) => {
}
return map
}, new Map<string, LocalResult>()).values(),
)
.map(cleanObjectStrings)
).map(cleanObjectStrings)
.map((r: any) => {
if (r.website && typeof r.website === 'string' && r.website.startsWith('/url?q=')) {
try {
@ -433,6 +468,7 @@ export const googleMaps = async (opts: IOptionsGoogleMaps) => {
return r
})
write(opts.dst + '.json', finalResults)
writeReport(finalResults, opts.dst, opts)
}
@ -446,7 +482,7 @@ export const googleMaps = async (opts: IOptionsGoogleMaps) => {
})
write(opts.index, index)
}
return all
return all as any
}
export const migrate = async (opts: IOptionsGoogleMapsEach) => {
@ -486,7 +522,7 @@ export const each = async (opts: IOptionsGoogleMapsEach) => {
COUNTRY: opts.country,
...DEFAULT_ROOTS
}, includes: [], env: {}
}, {env:opts.env})
}, { env: opts.env })
opts = parse(opts)
if (!opts) {

View File

@ -1,7 +1,6 @@
import https from 'https'
import axios, { AxiosRequestConfig } from "axios"
import { parse as HTML, HTMLElement } from "node-html-parser"
import * as cheerio from "cheerio"
import * as path from 'path'
import { URL } from 'url'
@ -10,8 +9,7 @@ import { Browser } from 'puppeteer'
import * as puppeteer from 'puppeteer'
import { logger } from '../index.js'
import { LocalResult } from './map_types.js'
import { parse as retrieveMeta } from './html.js'
import { LocalResult, LocationSiteMeta, Meta, Og, Image, Structured } from './map_types.js'
export const STATS_SUFFIX = '_stats.json'
export const SESSION_EVENTS_SUFFIX = '_session.json'
@ -37,7 +35,7 @@ export const extractEmail = (input: string): string | null => {
return match ? match[0] : null;
}
export const meta = async (loc: LocalResult, options: any) => {
export const meta = async (loc: LocalResult, options: any): Promise<LocationSiteMeta | void> => {
if (!loc.website) {
logger.warn(`No website to retrieve meta data : ${loc.title}`)
return
@ -46,7 +44,7 @@ export const meta = async (loc: LocalResult, options: any) => {
return
}
try {
const _meta: any = await retrieveMeta(loc.website, null, options) || {}
const _meta: LocationSiteMeta = await parse(loc.website, null, options) || {}
loc.meta = _meta
loc.instagram = _meta.instagram
loc.facebook = _meta.facebook
@ -54,6 +52,7 @@ export const meta = async (loc: LocalResult, options: any) => {
loc.linkedin = _meta.linkedin
loc.twitter = _meta.twitter
loc.email = (_meta.allLinks || []).map((l) => extractEmail(l)).filter((e) => e !== null)[0]
return _meta
} catch (error) {
logger.error('Error retrieving meta data : ' + loc.website, error.message)
if (error.status)
@ -61,7 +60,7 @@ export const meta = async (loc: LocalResult, options: any) => {
}
}
export const isValidUrl = (url) => {
export const isValidUrl = (url: string) => {
try {
new URL(url);
return true;
@ -70,24 +69,13 @@ export const isValidUrl = (url) => {
}
}
interface Meta {
title?: string;
description?: string,
image?: string
url?: string,
type?: string,
site_name?: string
keywords?: string[]
}
const readMetaTags = (el: HTMLElement, name: string) => {
var prop = el.getAttribute('name') || el.getAttribute('property');
return prop == name ? el.getAttribute('content') : null;
const readMetaTags = ($: cheerio.CheerioAPI, name: string) => {
return $(`meta[name="${name}"]`).attr('content') || $(`meta[property="${name}"]`).attr('content') || null;
};
export const parse = async (url: string, config: AxiosRequestConfig, options: any) => {
export const parse = async (url: string, config: AxiosRequestConfig | null, options: any): Promise<LocationSiteMeta> => {
if (!/(^http(s?):\/\/[^\s$.?#].[^\s]*)/i.test(url)) return {};
if (!/(^http(s?):\/\/[^\s$.?#].[^\s]*)/i.test(url)) return {} as LocationSiteMeta;
const { data } = await axios(url,
{
@ -101,52 +89,50 @@ export const parse = async (url: string, config: AxiosRequestConfig, options: an
timeout: 10000
})
const $ = HTML(data)
const og: Meta = {}
const $ = cheerio.load(data)
const og: Og = {}
const meta: Meta = {}
const images: any = []
const links: any = []
let allLinks: any = []
const images: Image[] = []
const links: string[] = []
let allLinks: string[] = []
const title = $.querySelector('title')
const title = $('title').text()
if (title)
meta.title = title.text;
meta.title = title;
const canonical = $.querySelector('link[rel=canonical]')
const canonical = $('link[rel=canonical]').attr('href')
if (canonical) {
meta.url = canonical.getAttribute('href')
meta.url = canonical
}
const metas = $.querySelectorAll('meta')
for (let i = 0; i < metas.length; i++) {
const el = metas[i];
// const prop = el.getAttribute('property') || el.getAttribute('name');
['title', 'description', 'image'].forEach(s => {
const val = readMetaTags(el, s);
const val = readMetaTags($, s);
if (val) meta[s] = val;
});
['og:title', 'og:description', 'og:image', 'og:url', 'og:site_name', 'og:type'].forEach(s => {
const val = readMetaTags(el, s);
const val = readMetaTags($, s);
if (val) og[s.split(':')[1]] = val;
})
}
$.querySelectorAll('img').forEach(el => {
let src = el.getAttribute('src')
$('img').each((i, el) => {
let src = $(el).attr('src')
if (src) {
try {
src = new URL(src, url).href;
images.push({ src });
} catch (e) {
// ignore invalid urls
}
}
})
const _$ = cheerio.load(data)
// Array to store JSON-LD data
const jsonLdArray: any[] = [];
const jsonLdArray: Structured[] = [];
// Select all <script> tags with type "application/ld+json"
_$('script[type="application/ld+json"]').each((_, element) => {
const jsonLdContent = _$(element).html();
$('script[type="application/ld+json"]').each((_, element) => {
const jsonLdContent = $(element).html();
if (jsonLdContent) {
try {
// Parse the JSON-LD content and push it to the array
@ -157,12 +143,14 @@ export const parse = async (url: string, config: AxiosRequestConfig, options: an
}
}
})
_$('a').each((index, element) => {
const href = _$(element).attr('href')
if (href && isValidUrl(href) && href.indexOf('contact') !== -1 && !links.includes(href)) {
$('a').each((index, element) => {
const href = $(element).attr('href')
if (href && isValidUrl(href)) {
if (href.indexOf('contact') !== -1 && !links.includes(href)) {
links.push(href)
}
isValidUrl(href) && allLinks.push(href)
allLinks.push(href)
}
})
allLinks = [...new Set(allLinks)]
const instagram = allLinks.find(link => link.includes('instagram.com'))
@ -170,13 +158,13 @@ export const parse = async (url: string, config: AxiosRequestConfig, options: an
const linkedin = allLinks.find(link => link.includes('linkedin.com'))
const youtube = allLinks.find(link => link.includes('youtube.com'))
const twitter = allLinks.find(link => link.includes('twitter.com'))
const ret = {
const ret: LocationSiteMeta = {
meta,
og,
images,
keywords:
_$('meta[property="og:keywords"]').attr("content") ||
_$('meta[name="keywords"]').attr("content") || [],
($('meta[property="og:keywords"]').attr("content") ||
$('meta[name="keywords"]').attr("content") || "").split(',').map(s => s.trim()).filter(s => s),
links,
allLinks,
instagram,
@ -357,7 +345,3 @@ export const body = async (url: string) => {
})
})
}
export const contactUrl = async (url) => {
}

View File

@ -1,5 +1,3 @@
import { IGeo } from '@polymech/commons/types'
export interface SearchMetadata {
id: string;
status: string;
@ -22,18 +20,41 @@ export interface GpsCoordinates {
}
export interface OperatingHours {
viernes: string;
sábado: string;
domingo: string;
lunes: string;
martes: string;
miércoles: string;
jueves: string;
friday?: string;
saturday?: string;
sunday?: string;
monday?: string;
tuesday?: string;
wednesday?: string;
thursday?: string;
}
export type LocalResult = {
title?: string;
place_id?: string;
data_id?: string;
data_cid?: string;
reviews_link?: string;
photos_link?: string;
gps_coordinates?: GpsCoordinates;
place_id_search?: string;
provider_id?: string;
rating?: number;
reviews?: number;
price?: string;
type?: string;
types?: string[];
address?: string;
open_state?: string;
hours?: string;
operating_hours?: OperatingHours;
phone?: string;
website?: string;
description?: string;
service_options?: { [key: string]: boolean };
thumbnail?: string;
email?: string;
[key: string]: any
filterType?: string
}
export interface SearchParameters {
@ -51,3 +72,107 @@ export interface SearchResult {
local_results: LocalResult[];
search_parameters: SearchParameters;
}
/////////////////////////////////////////////////
//
// loc.meta
//
/////////////////////////////////////////////////
export interface LocationSiteMeta {
og?: Og
meta?: Meta
links?: string[]
images?: Image[]
allLinks?: string[]
keywords?: string[]
instagram?: string
facebook?: string
linkedin?: string
youtube?: string
twitter?: string
structured?: Structured[]
}
export interface Og {
url?: string
type?: string
title?: string
site_name?: string
description?: string
image?: string
[key: string]: any
}
export interface Meta {
url?: string
title?: string
description?: string
image?: string
type?: string
site_name?: string
keywords?: string[]
[key: string]: any
}
export interface Image {
src: string
}
export interface Structured {
"@graph"?: Graph[]
"@context"?: string
"@type"?: string
name?: string
url?: string
image?: string
email?: string
telephone?: string
address?: string | object
sameAs?: string[]
openingHours?: string
legalName?: string
[key: string]: any
}
export interface Graph {
"@id": string
url?: string
name?: string
"@type": string
isPartOf?: IsPartOf
breadcrumb?: Breadcrumb
inLanguage?: string
dateModified?: string
datePublished?: string
potentialAction?: PotentialAction[]
itemListElement?: ItemListElement[]
description?: string
[key: string]: any
}
export interface IsPartOf {
"@id": string
}
export interface Breadcrumb {
"@id": string
}
export interface PotentialAction {
"@type": string
target: any
"query-input"?: QueryInput
}
export interface QueryInput {
"@type": string
valueName: string
valueRequired: boolean
}
export interface ItemListElement {
name: string
"@type": string
position: number
}

View File

@ -0,0 +1,163 @@
[
{
"position": 2,
"title": "Plastic Hub",
"place_id": "ChIJ_burz4DrpBIR7Tb0r_IWzQI",
"data_id": "0x12a4eb80cfabbbfd:0x2cd16f2aff436ed",
"data_cid": "201842789891454701",
"reviews_link": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_reviews&hl=en",
"photos_link": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_photos&hl=en",
"gps_coordinates": {
"latitude": 41.609375199999995,
"longitude": 2.1421068
},
"place_id_search": "https://serpapi.com/search.json?engine=google_maps&google_domain=google.com&hl=en&place_id=ChIJ_burz4DrpBIR7Tb0r_IWzQI",
"provider_id": "/g/11rxv42q_7",
"type": "Machine shop",
"types": [
"Machine shop"
],
"type_id": "machine_shop",
"type_ids": [
"machine_shop"
],
"address": "Carrer Can Peric, 11, 1B, 08181 Sentmenat, Barcelona, Spain",
"open_state": "Open · Closes 7 AM Sat",
"hours": "Open · Closes 7 AM Sat",
"operating_hours": {
"friday": "9 AM-12 AM",
"saturday": "12-7 AM",
"sunday": "Closed",
"monday": "9 AM-12 AM",
"tuesday": "12-7 AM, 9 AM-7 PM",
"wednesday": "9 AM-7 PM",
"thursday": "9 AM-7 PM"
},
"phone": "+34 691 95 22 87",
"website": "https://polymech.io/es",
"extensions": [
{
"accessibility": [
"Wheelchair accessible entrance"
]
},
{
"crowd": [
"LGBTQ+ friendly",
"Transgender safespace"
]
}
],
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOBYvdbdZTKoTP4rtiSnghEtwFVMtfO8-9gWS-S=w92-h92-k-no",
"serpapi_thumbnail": "https://serpapi.com/images/url/xe13Z3icuxmbUVJSUGylr5-TYayXnp-fnpNaWpxalJyfV5KaV6KXnJ-rX6Dv6GYYmFng7xRZlpKUEhXinR8SYFJUkhmcl57hWlLuFuZbkuZvoWuZHh6sG2xbbmmkmwHE2bp5-QCcDyFm",
"page": 0,
"geo": {
"latitude": 41.609375199999995,
"longitude": 2.1421068,
"localityLanguageRequested": "en",
"continent": "Europe",
"continentCode": "EU",
"countryName": "Spain",
"countryCode": "ES",
"principalSubdivision": "Catalunya",
"principalSubdivisionCode": "ES-CT",
"city": "Sentmenat",
"locality": "Sentmenat",
"postcode": "",
"plusCode": "8FH4J45R+QR",
"localityInfo": {
"administrative": [
{
"name": "Spain",
"description": "constitutional monarchy in Southwest Europe",
"isoName": "Spain",
"order": 2,
"adminLevel": 2,
"isoCode": "ES",
"wikidataId": "Q29",
"geonameId": 2510769
},
{
"name": "East",
"order": 5,
"adminLevel": 3
},
{
"name": "Catalunya",
"description": "nationality and autonomous community of Spain",
"isoName": "Catalunya",
"order": 7,
"adminLevel": 4,
"isoCode": "ES-CT",
"wikidataId": "Q5705",
"geonameId": 3336901
},
{
"name": "Provincia de Barcelona",
"description": "province in Catalonia, Spain",
"isoName": "Provincia de Barcelona",
"order": 9,
"adminLevel": 6,
"isoCode": "ES-B",
"wikidataId": "Q81949",
"geonameId": 3128759
},
{
"name": "Valles Occidental",
"description": "Catalan comarca (county) in the vegueria (region) of Barcelona",
"order": 11,
"adminLevel": 7,
"wikidataId": "Q13948"
},
{
"name": "Sentmenat",
"description": "municipality in the comarca of the Vallès Occidental in Catalonia, Spain",
"order": 12,
"adminLevel": 8,
"wikidataId": "Q13943",
"geonameId": 3109165
}
],
"informative": [
{
"name": "Europe",
"description": "continent in the Northern Hemisphere",
"isoName": "Europe",
"order": 1,
"isoCode": "EU",
"wikidataId": "Q46",
"geonameId": 6255148
},
{
"name": "Europe/Madrid",
"description": "time zone",
"order": 3
},
{
"name": "Iberian Peninsula",
"description": "peninsula located in the extreme southwest of Europe",
"order": 4,
"wikidataId": "Q12837",
"geonameId": 2267430
},
{
"name": "Catalan countries",
"description": "territories where Catalan is the native language",
"order": 6,
"wikidataId": "Q234963"
},
{
"name": "Catalan as own language in Catalonia",
"order": 8
},
{
"name": "Ambit metropolita de Barcelona",
"description": "region (vegueria) of Catalonia",
"order": 10,
"wikidataId": "Q249461"
}
]
}
}
}
]

View File

@ -0,0 +1,347 @@
[
{
"position": 1,
"title": "Plastics Hub",
"place_id": "ChIJabXHCeG3cEgRTYlNwRQIf6Y",
"data_id": "0x4870b7e109c7b569:0xa67f0814c14d894d",
"data_cid": "11997316817573742925",
"reviews_link": "https://serpapi.com/search.json?data_id=0x4870b7e109c7b569%3A0xa67f0814c14d894d&engine=google_maps_reviews&hl=en",
"photos_link": "https://serpapi.com/search.json?data_id=0x4870b7e109c7b569%3A0xa67f0814c14d894d&engine=google_maps_photos&hl=en",
"gps_coordinates": {
"latitude": 52.3961366,
"longitude": -1.7978661
},
"place_id_search": "https://serpapi.com/search.json?engine=google_maps&google_domain=google.com&hl=en&place_id=ChIJabXHCeG3cEgRTYlNwRQIf6Y",
"provider_id": "/g/11jwl8cc52",
"rating": 4,
"reviews": 316,
"type": "Building materials supplier",
"types": [
"Building materials supplier"
],
"type_id": "building_materials_supplier",
"type_ids": [
"building_materials_supplier"
],
"address": "Unit 15, Monkspath Business Park, Shirley, Solihull B90 4NY, United Kingdom",
"open_state": "Closed · Opens 8AM Mon",
"hours": "Closed · Opens 8AM Mon",
"operating_hours": {
"friday": "8AM4PM",
"saturday": "Closed",
"sunday": "Closed",
"monday": "8AM4PM",
"tuesday": "8AM4PM",
"wednesday": "8AM4PM",
"thursday": "8AM4PM"
},
"phone": "+44 121 716 2588",
"website": "/url?q=https://www.plasticshub.com/&opi=79508299&sa=U&ved=0ahUKEwikv-nOnoSRAxWgGlkFHWmBJq0Q61gIGSgR&usg=AOvVaw3zl0nqV2_UYXwnYro1ijXN",
"extensions": [
{
"service_options": [
"Onsite services"
]
},
{
"accessibility": [
"Wheelchair accessible entrance",
"Wheelchair accessible parking lot"
]
},
{
"payments": [
"Credit cards",
"Debit cards"
]
}
],
"service_options": {
"onsite_services": true
},
"user_review": "\"Absolutely fantastic experience using plastic hub.\"",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipMwWpEv9UmMgOu7tkaRhfIYjEixUvtx6NCtunZR=w93-h92-k-no",
"serpapi_thumbnail": "https://serpapi.com/images/url/c77q1HicuxmbUVJSUGylr5-TYayXnp-fnpNaWpxalJyfV5KaV6KXnJ-rX6Dv6GYYmFngWx5e4FpmGZrrm-5fal6SnRiUkeYZmeWaWRFaVlJh5udcUpoXFWRbbmmsm2FppJutm5cPAK6wIlA",
"page": 0,
"geo": {
"latitude": 52.3961366,
"longitude": -1.7978661,
"localityLanguageRequested": "en",
"continent": "Europe",
"continentCode": "EU",
"countryName": "United Kingdom of Great Britain and Northern Ireland (the)",
"countryCode": "GB",
"principalSubdivision": "England",
"principalSubdivisionCode": "GB-ENG",
"city": "Solihull",
"locality": "Solihull",
"postcode": "B90 4",
"plusCode": "9C4W96W2+FV",
"localityInfo": {
"administrative": [
{
"name": "United Kingdom of Great Britain and Northern Ireland (the)",
"description": "country in Western Europe",
"isoName": "United Kingdom of Great Britain and Northern Ireland (the)",
"order": 4,
"adminLevel": 2,
"isoCode": "GB",
"wikidataId": "Q145",
"geonameId": 2635167
},
{
"name": "England",
"description": "home nation of the United Kingdom",
"isoName": "England",
"order": 6,
"adminLevel": 4,
"isoCode": "GB-ENG",
"wikidataId": "Q21",
"geonameId": 6269131
},
{
"name": "West Midlands Combined Authority",
"description": "Combined authority",
"order": 10,
"adminLevel": 5,
"wikidataId": "Q21061625"
},
{
"name": "Solihull",
"description": "town in the West Midlands of England",
"order": 11,
"adminLevel": 8,
"wikidataId": "Q397343",
"geonameId": 2637546
},
{
"name": "Solihull",
"description": "metropolitan borough in the West Midlands, England",
"isoName": "Solihull",
"order": 12,
"adminLevel": 8,
"isoCode": "GB-SOL",
"wikidataId": "Q1925858",
"geonameId": 3333195
}
],
"informative": [
{
"name": "Europe",
"description": "continent in the Northern Hemisphere",
"isoName": "Europe",
"order": 1,
"isoCode": "EU",
"wikidataId": "Q46",
"geonameId": 6255148
},
{
"name": "British Isles",
"description": "group of islands in northwest Europe",
"order": 2,
"wikidataId": "Q38272",
"geonameId": 2654669
},
{
"name": "Europe/London",
"description": "time zone",
"order": 3
},
{
"name": "Great Britain",
"description": "island in the North Atlantic Ocean off the northwest coast of continental Europe",
"order": 5,
"wikidataId": "Q23666",
"geonameId": 2648147
},
{
"name": "West Midlands",
"description": "one of nine official regions of England",
"order": 7,
"wikidataId": "Q48038",
"geonameId": 11591953
},
{
"name": "West Midlands",
"description": "metropolitan county of England",
"order": 8,
"wikidataId": "Q23124",
"geonameId": 2634343
},
{
"name": "West Midlands Fire Service",
"description": "English regional fire and rescue service",
"order": 9,
"wikidataId": "Q7985942"
},
{
"name": "B90 4",
"description": "postal code",
"order": 13
}
]
}
}
},
{
"position": 2,
"title": "Plastic Hub",
"place_id": "ChIJ_burz4DrpBIR7Tb0r_IWzQI",
"data_id": "0x12a4eb80cfabbbfd:0x2cd16f2aff436ed",
"data_cid": "201842789891454701",
"reviews_link": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_reviews&hl=en",
"photos_link": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_photos&hl=en",
"gps_coordinates": {
"latitude": 41.609375199999995,
"longitude": 2.1421068
},
"place_id_search": "https://serpapi.com/search.json?engine=google_maps&google_domain=google.com&hl=en&place_id=ChIJ_burz4DrpBIR7Tb0r_IWzQI",
"provider_id": "/g/11rxv42q_7",
"type": "Machine shop",
"types": [
"Machine shop"
],
"type_id": "machine_shop",
"type_ids": [
"machine_shop"
],
"address": "Carrer Can Peric, 11, 1B, 08181 Sentmenat, Barcelona, Spain",
"open_state": "Open · Closes 7AM Sat",
"hours": "Open · Closes 7AM Sat",
"operating_hours": {
"friday": "9AM12AM",
"saturday": "127AM",
"sunday": "Closed",
"monday": "9AM12AM",
"tuesday": "127AM, 9AM7PM",
"wednesday": "9AM7PM",
"thursday": "9AM7PM"
},
"phone": "+34 691 95 22 87",
"website": "/url?q=https://polymech.io/es&opi=79508299&sa=U&ved=0ahUKEwikv-nOnoSRAxWgGlkFHWmBJq0Q61gINygQ&usg=AOvVaw1eXT3j7KlDqt_37xYZVivZ",
"extensions": [
{
"accessibility": [
"Wheelchair accessible entrance"
]
},
{
"crowd": [
"LGBTQ+ friendly",
"Transgender safespace"
]
}
],
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOBYvdbdZTKoTP4rtiSnghEtwFVMtfO8-9gWS-S=w92-h92-k-no",
"serpapi_thumbnail": "https://serpapi.com/images/url/xe13Z3icuxmbUVJSUGylr5-TYayXnp-fnpNaWpxalJyfV5KaV6KXnJ-rX6Dv6GYYmFng7xRZlpKUEhXinR8SYFJUkhmcl57hWlLuFuZbkuZvoWuZHh6sG2xbbmmkmwHE2bp5-QCcDyFm",
"page": 0,
"geo": {
"latitude": 41.609375199999995,
"longitude": 2.1421068,
"localityLanguageRequested": "en",
"continent": "Europe",
"continentCode": "EU",
"countryName": "Spain",
"countryCode": "ES",
"principalSubdivision": "Catalunya",
"principalSubdivisionCode": "ES-CT",
"city": "Sentmenat",
"locality": "Sentmenat",
"postcode": "",
"plusCode": "8FH4J45R+QR",
"localityInfo": {
"administrative": [
{
"name": "Spain",
"description": "constitutional monarchy in Southwest Europe",
"isoName": "Spain",
"order": 2,
"adminLevel": 2,
"isoCode": "ES",
"wikidataId": "Q29",
"geonameId": 2510769
},
{
"name": "East",
"order": 5,
"adminLevel": 3
},
{
"name": "Catalunya",
"description": "nationality and autonomous community of Spain",
"isoName": "Catalunya",
"order": 7,
"adminLevel": 4,
"isoCode": "ES-CT",
"wikidataId": "Q5705",
"geonameId": 3336901
},
{
"name": "Provincia de Barcelona",
"description": "province in Catalonia, Spain",
"isoName": "Provincia de Barcelona",
"order": 9,
"adminLevel": 6,
"isoCode": "ES-B",
"wikidataId": "Q81949",
"geonameId": 3128759
},
{
"name": "Valles Occidental",
"description": "Catalan comarca (county) in the vegueria (region) of Barcelona",
"order": 11,
"adminLevel": 7,
"wikidataId": "Q13948"
},
{
"name": "Sentmenat",
"description": "municipality in the comarca of the Vallès Occidental in Catalonia, Spain",
"order": 12,
"adminLevel": 8,
"wikidataId": "Q13943",
"geonameId": 3109165
}
],
"informative": [
{
"name": "Europe",
"description": "continent in the Northern Hemisphere",
"isoName": "Europe",
"order": 1,
"isoCode": "EU",
"wikidataId": "Q46",
"geonameId": 6255148
},
{
"name": "Europe/Madrid",
"description": "time zone",
"order": 3
},
{
"name": "Iberian Peninsula",
"description": "peninsula located in the extreme southwest of Europe",
"order": 4,
"wikidataId": "Q12837",
"geonameId": 2267430
},
{
"name": "Catalan countries",
"description": "territories where Catalan is the native language",
"order": 6,
"wikidataId": "Q234963"
},
{
"name": "Catalan as own language in Catalonia",
"order": 8
},
{
"name": "Ambit metropolita de Barcelona",
"description": "region (vegueria) of Catalonia",
"order": 10,
"wikidataId": "Q249461"
}
]
}
}
}
]

View File

@ -0,0 +1,34 @@
{
"query": "plastichub",
"engine": "google_maps",
"type": "search",
"q": "plastichub",
"ll": "@41.6911354,2.1652746,13z",
"google_domain": "google.com",
"hl": "en",
"api_key": "hidden",
"cache": false,
"category": "category",
"dst": "C:\\Users\\zx\\Desktop\\polymech\\polymech-mono\\packages\\search\\test\\campaign\\maps\\barcelona\\spain\\category\\plastichub-10.xls",
"env_key": "OSR-CONFIG",
"findEMail": false,
"geocode_key": "hidden",
"headless": true,
"language": "en",
"limit": 5,
"logLevel": "info",
"meta": true,
"searchCache": false,
"searchCoord": "@41.3825802,2.1770730,13z",
"searchFrom": "barcelona, spain",
"source": {
"category": [
"plastichub"
]
},
"zoom": 13,
"index": "C:\\Users\\zx\\Desktop\\osr\\osr-directory\\meta\\index.json",
"store": "C:\\Users\\zx\\Desktop\\osr\\osr-directory\\meta\\index.db",
"openai": "hidden",
"bigdata": "hidden"
}

View File

@ -0,0 +1,304 @@
{
"geo": {
"city": "Sentmenat",
"latitude": 41.609375199999995,
"locality": "Sentmenat",
"plusCode": "8FH4J45R+QR",
"postcode": "",
"continent": "Europe",
"longitude": 2.1421068,
"countryCode": "ES",
"countryName": "Spain",
"localityInfo": {
"informative": [
{
"name": "Europe",
"order": 1,
"isoCode": "EU",
"isoName": "Europe",
"geonameId": 6255148,
"wikidataId": "Q46",
"description": "continent in the Northern Hemisphere"
},
{
"name": "Europe/Madrid",
"order": 3,
"description": "time zone"
},
{
"name": "Iberian Peninsula",
"order": 4,
"geonameId": 2267430,
"wikidataId": "Q12837",
"description": "peninsula located in the extreme southwest of Europe"
},
{
"name": "Catalan countries",
"order": 6,
"wikidataId": "Q234963",
"description": "territories where Catalan is the native language"
},
{
"name": "Catalan as own language in Catalonia",
"order": 8
},
{
"name": "Ambit metropolita de Barcelona",
"order": 10,
"wikidataId": "Q249461",
"description": "region (vegueria) of Catalonia"
}
],
"administrative": [
{
"name": "Spain",
"order": 2,
"isoCode": "ES",
"isoName": "Spain",
"geonameId": 2510769,
"adminLevel": 2,
"wikidataId": "Q29",
"description": "constitutional monarchy in Southwest Europe"
},
{
"name": "East",
"order": 5,
"adminLevel": 3
},
{
"name": "Catalunya",
"order": 7,
"isoCode": "ES-CT",
"isoName": "Catalunya",
"geonameId": 3336901,
"adminLevel": 4,
"wikidataId": "Q5705",
"description": "nationality and autonomous community of Spain"
},
{
"name": "Provincia de Barcelona",
"order": 9,
"isoCode": "ES-B",
"isoName": "Provincia de Barcelona",
"geonameId": 3128759,
"adminLevel": 6,
"wikidataId": "Q81949",
"description": "province in Catalonia, Spain"
},
{
"name": "Valles Occidental",
"order": 11,
"adminLevel": 7,
"wikidataId": "Q13948",
"description": "Catalan comarca (county) in the vegueria (region) of Barcelona"
},
{
"name": "Sentmenat",
"order": 12,
"geonameId": 3109165,
"adminLevel": 8,
"wikidataId": "Q13943",
"description": "municipality in the comarca of the Vallès Occidental in Catalonia, Spain"
}
]
},
"continentCode": "EU",
"principalSubdivision": "Catalunya",
"principalSubdivisionCode": "ES-CT",
"localityLanguageRequested": "en"
},
"meta": {},
"page": 0,
"type": "Machine shop",
"hours": "Closed · Opens 9AM Mon",
"phone": "+34 691 95 22 87",
"title": "Plastic Hub",
"types": [
"Machine shop"
],
"address": "Carrer Can Peric, 11, 1B, 08181 Sentmenat, Barcelona, Spain",
"data_id": "0x12a4eb80cfabbbfd:0x2cd16f2aff436ed",
"type_id": "machine_shop",
"website": "https://polymech.io/es",
"data_cid": "201842789891454701",
"place_id": "ChIJ_burz4DrpBIR7Tb0r_IWzQI",
"position": 2,
"type_ids": [
"machine_shop"
],
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOBYvdbdZTKoTP4rtiSnghEtwFVMtfO8-9gWS-S=w92-h92-k-no",
"extensions": [
{
"accessibility": [
"Wheelchair accessible entrance"
]
},
{
"crowd": [
"LGBTQ+ friendly",
"Transgender safespace"
]
}
],
"open_state": "Closed · Opens 9AM Mon",
"photos_link": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_photos&hl=en",
"provider_id": "/g/11rxv42q_7",
"google_media": {
"photos": [
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOBYvdbdZTKoTP4rtiSnghEtwFVMtfO8-9gWS-S=w4000-h4000-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOBYvdbdZTKoTP4rtiSnghEtwFVMtfO8-9gWS-S=w203-h203-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOBYvdbdZTKoTP4rtiSnghEtwFVMtfO8-9gWS-S&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOGxIU6O0QqbLMqWw19uMXXecAYHYxOkH2Tpdiu=w6000-h4000-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOGxIU6O0QqbLMqWw19uMXXecAYHYxOkH2Tpdiu=w203-h135-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOGxIU6O0QqbLMqWw19uMXXecAYHYxOkH2Tpdiu&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipPh6Iv-vhwYwHuK52ditXEnSa-g4bspLCx8v11H=w6000-h4000-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipPh6Iv-vhwYwHuK52ditXEnSa-g4bspLCx8v11H=w203-h135-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipPh6Iv-vhwYwHuK52ditXEnSa-g4bspLCx8v11H&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipNDMwnNWhw0X8tZeMNwdcYfDFPwunNUSf_C8BXX=w1918-h1080-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipNDMwnNWhw0X8tZeMNwdcYfDFPwunNUSf_C8BXX=w203-h114-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipNDMwnNWhw0X8tZeMNwdcYfDFPwunNUSf_C8BXX&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOWsJcMBrfVBdQpXG6mHxOSyTLxIK8seM6cbPCZ=w1315-h740-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOWsJcMBrfVBdQpXG6mHxOSyTLxIK8seM6cbPCZ=w203-h114-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOWsJcMBrfVBdQpXG6mHxOSyTLxIK8seM6cbPCZ&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipMUhHwttDhH9vBwl3sDf-qe4e36c2qLC9fP3DJp=w3066-h2953-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipMUhHwttDhH9vBwl3sDf-qe4e36c2qLC9fP3DJp=w203-h195-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipMUhHwttDhH9vBwl3sDf-qe4e36c2qLC9fP3DJp&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipPi797NYjST6z0p6HyBeYcXougsrhH76KuIbbJ1=w1754-h2369-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipPi797NYjST6z0p6HyBeYcXougsrhH76KuIbbJ1=w203-h274-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipPi797NYjST6z0p6HyBeYcXougsrhH76KuIbbJ1&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipPojxfCrPbLPrYburkpvLiqNQd438QL6MJIFriQ=w1080-h1080-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipPojxfCrPbLPrYburkpvLiqNQd438QL6MJIFriQ=w203-h203-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipPojxfCrPbLPrYburkpvLiqNQd438QL6MJIFriQ&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOgQ4f-pnqIHMF4g1eZa7aJaiAZwbg5Gsdd59h9=w1676-h1676-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOgQ4f-pnqIHMF4g1eZa7aJaiAZwbg5Gsdd59h9=w203-h203-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOgQ4f-pnqIHMF4g1eZa7aJaiAZwbg5Gsdd59h9&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipNS_6N6KCGPBIfDiuSEuFx9gMqiwzyVYzV4p8iN=w1980-h1320-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipNS_6N6KCGPBIfDiuSEuFx9gMqiwzyVYzV4p8iN=w203-h135-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipNS_6N6KCGPBIfDiuSEuFx9gMqiwzyVYzV4p8iN&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipPaI1AakZx0gr4Liqh9m6WcJA9TyqRaUFo7e1rU=w1080-h1079-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipPaI1AakZx0gr4Liqh9m6WcJA9TyqRaUFo7e1rU=w203-h202-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipPaI1AakZx0gr4Liqh9m6WcJA9TyqRaUFo7e1rU&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipMuqKDNkGlwkvJoNon8qRqJ0hkFmcX9v8r34nO9=w2230-h2216-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipMuqKDNkGlwkvJoNon8qRqJ0hkFmcX9v8r34nO9=w203-h201-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipMuqKDNkGlwkvJoNon8qRqJ0hkFmcX9v8r34nO9&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipMXO5bDqn2Mrlhxoij4huVOjefpNon2m5poVYws=w2976-h1680-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipMXO5bDqn2Mrlhxoij4huVOjefpNon2m5poVYws=w203-h114-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipMXO5bDqn2Mrlhxoij4huVOjefpNon2m5poVYws&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipNelbtLodPWxZYzhDLOn9DHKnsmHoEfpZZPzSL0=w1677-h1676-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipNelbtLodPWxZYzhDLOn9DHKnsmHoEfpZZPzSL0=w203-h202-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipNelbtLodPWxZYzhDLOn9DHKnsmHoEfpZZPzSL0&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOlw6wXzEp9-5cMCXVTEiyZQFn19kDIyLxgfivq=w1320-h1980-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOlw6wXzEp9-5cMCXVTEiyZQFn19kDIyLxgfivq=w203-h304-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOlw6wXzEp9-5cMCXVTEiyZQFn19kDIyLxgfivq&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipMPeFgKsDj6HhESW-I0g2Q3nCq42jUPG0NkrS6C=w1980-h2640-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipMPeFgKsDj6HhESW-I0g2Q3nCq42jUPG0NkrS6C=w203-h270-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipMPeFgKsDj6HhESW-I0g2Q3nCq42jUPG0NkrS6C&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOMETnLT43RNTq2nJUw7eEr-0G1LyQL0BxV-Gy9=w3000-h4000-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOMETnLT43RNTq2nJUw7eEr-0G1LyQL0BxV-Gy9=w203-h270-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOMETnLT43RNTq2nJUw7eEr-0G1LyQL0BxV-Gy9&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipNgM_7ChYMZ22gY6qG4Lcw8BZ-3cAMc4uV9AIck=w2048-h1365-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipNgM_7ChYMZ22gY6qG4Lcw8BZ-3cAMc4uV9AIck=w203-h135-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipNgM_7ChYMZ22gY6qG4Lcw8BZ-3cAMc4uV9AIck&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOi1mkaUYcHxCwEjVX_LgUnu_HR9lCUzI7lDC6v=w749-h749-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOi1mkaUYcHxCwEjVX_LgUnu_HR9lCUzI7lDC6v=w203-h203-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOi1mkaUYcHxCwEjVX_LgUnu_HR9lCUzI7lDC6v&engine=google_maps_photo_meta"
},
{
"image": "https://lh3.googleusercontent.com/p/AF1QipOcNLwz4zUThMwZmWizqEmUWOQMKA9OFlyiy19W=w1320-h1980-k-no",
"thumbnail": "https://lh3.googleusercontent.com/p/AF1QipOcNLwz4zUThMwZmWizqEmUWOQMKA9OFlyiy19W=w203-h304-k-no",
"photo_meta_serpapi_link": "https://serpapi.com/search.json?data_id=AF1QipOcNLwz4zUThMwZmWizqEmUWOQMKA9OFlyiy19W&engine=google_maps_photo_meta"
}
],
"categories": [
{
"id": "CgIgAQ",
"title": "All"
},
{
"id": "CgIYEg",
"title": "Inside"
},
{
"id": "CgIgARICEAE",
"title": "By owner"
},
{
"id": "CgIgARICCAI",
"title": "Street View & 360°"
}
],
"search_metadata": {
"id": "6922fd178c60cafc34ff25d8",
"status": "Success",
"created_at": "2025-11-23 12:24:55 UTC",
"processed_at": "2025-11-23 12:24:55 UTC",
"json_endpoint": "https://serpapi.com/searches/04613d1aa4fa487f/6922fd178c60cafc34ff25d8.json",
"raw_html_file": "https://serpapi.com/searches/04613d1aa4fa487f/6922fd178c60cafc34ff25d8.html",
"total_time_taken": 0.2,
"prettify_html_file": "https://serpapi.com/searches/04613d1aa4fa487f/6922fd178c60cafc34ff25d8.prettify",
"google_maps_photos_url": "https://www.google.com/maps/rpc/photo/listentityphotos?hl=en&pb=!1e2!3m3!1s0x12a4eb80cfabbbfd:0x2cd16f2aff436ed!9e0!11s!5m58!2m2!1i203!2i100!3m2!2i20!5b1!7m50!1m3!1e1!2b0!3e3!1m3!1e2!2b1!3e2!1m3!1e2!2b0!3e3!1m3!1e3!2b0!3e3!1m3!1e8!2b0!3e3!1m3!1e3!2b1!3e2!1m3!1e10!2b0!3e3!1m3!1e10!2b1!3e2!1m3!1e9!2b1!3e2!1m3!1e10!2b0!3e3!1m3!1e10!2b1!3e2!1m3!1e10!2b0!3e4!2b1!4b1!9b0!6m3!1s!2z!7e81!16m4!1m1!1BCgIgAQ!2b1!4e1"
},
"search_parameters": {
"hl": "en",
"engine": "google_maps_photos",
"data_id": "0x12a4eb80cfabbbfd:0x2cd16f2aff436ed"
},
"serpapi_pagination": {
"next": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_photos&hl=en&next_page_token=EvgDKYQi49-NlUMIDwAAAAEAAAMAAAAACAAIAAAAIAAAAAAAJAAAAAAAAAAAgABAAAIAAAAAAACAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAABAAAAAAAAAEAAAAAAEQAAAAAAAAIAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAFAAAAAAAAAAAAAAAABAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAgAEAAAAAAACAAAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAACAAAECAAAAAAAAAAAAAAAAIAAAAAIAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAABIAAAAgCEAAAAAAAASACAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAGAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAIAAAAEAAAAAAAAAAABAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAGBRCVCmEIuPfjZVD6AEAACAAAAADAAAAAAkAAAD8AAEAABAoDAECAIAgAIAAAgAAWCCiAEQAAAAAKAgABABAAEAC0EBEUCAQBCAAAQQIAiYAEABCAAAAAA",
"next_page_token": "EvgDKYQi49-NlUMIDwAAAAEAAAMAAAAACAAIAAAAIAAAAAAAJAAAAAAAAAAAgABAAAIAAAAAAACAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAABAAAAAAAAAEAAAAAAEQAAAAAAAAIAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAFAAAAAAAAAAAAAAAABAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAgAEAAAAAAACAAAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAACAAAECAAAAAAAAAAAAAAAAIAAAAAIAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAABIAAAAgCEAAAAAAAASACAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAGAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAIAAAAEAAAAAAAAAAABAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAGBRCVCmEIuPfjZVD6AEAACAAAAADAAAAAAkAAAD8AAEAABAoDAECAIAgAIAAAgAAWCCiAEQAAAAAKAgABABAAEAC0EBEUCAQBCAAAQQIAiYAEABCAAAAAA"
}
},
"reviews_link": "https://serpapi.com/search.json?data_id=0x12a4eb80cfabbbfd%3A0x2cd16f2aff436ed&engine=google_maps_reviews&hl=en",
"gps_coordinates": {
"latitude": 41.609375199999995,
"longitude": 2.1421068
},
"operating_hours": {
"friday": "9AM12AM",
"monday": "9AM12AM",
"sunday": "Closed",
"tuesday": "127AM, 9AM7PM",
"saturday": "127AM",
"thursday": "9AM7PM",
"wednesday": "9AM7PM"
},
"place_id_search": "https://serpapi.com/search.json?engine=google_maps&google_domain=google.com&hl=en&place_id=ChIJ_burz4DrpBIR7Tb0r_IWzQI",
"serpapi_thumbnail": "https://serpapi.com/images/url/xe13Z3icuxmbUVJSUGylr5-TYayXnp-fnpNaWpxalJyfV5KaV6KXnJ-rX6Dv6GYYmFng7xRZlpKUEhXinR8SYFJUkhmcl57hWlLuFuZbkuZvoWuZHh6sG2xbbmmkmwHE2bp5-QCcDyFm"
}