This code defines a set of functions and configurations related...

August 23, 2025 at 06:20 PM

function completeImage(img: IImage): IImage { img.author = (img as any).owner; if ((!img.file_url || img.file_url.length < 5) && img.preview_url) { img.file_url = img.preview_url .replace("/thumbnails/", "/images/") .replace("/thumbnail_", "/"); } return img; } export const source: ISource = { name: "Gelbooru (0.2)", modifiers: ["rating:general", "rating:safe", "rating:questionable", "rating:explicit", "user:", "fav:", "fastfav:", "md5:", "source:", "id:", "width:", "height:", "score:", "mpixels:", "filesize:", "date:", "gentags:", "arttags:", "chartags:", "copytags:", "approver:", "parent:", "sub:", "order:id", "order:id_desc", "order:score", "order:score_asc", "order:mpixels", "order:mpixels_asc", "order:filesize", "order:landscape", "order:portrait", "order:favcount", "order:rank", "parent:none", "unlocked:rating", "sort:updated", "sort:id", "sort:score", "sort:rating", "sort:user", "sort:height", "sort:width", "sort:parent", "sort:source", "sort:updated"], tagFormat: { case: "lower", wordSeparator: "_", }, searchFormat: { and: " ", }, auth: { url: { type: "url", fields: [ { id: "userId", key: "user_id", }, { id: "apiKey", key: "api_key", }, ], }, session: { type: "post", url: "/index.php?page=account&s=login&code=00", fields: [ { id: "pseudo", key: "user", }, { id: "password", key: "pass", type: "password", }, ], check: { type: "cookie", key: "user_id", }, }, }, apis: { xml: { name: "XML", auth: [], maxLimit: 100, search: { url: (query: ISearchQuery, opts: IUrlOptions): string | IError => { const page: number = query.page - 1; const search: string = query.search.replace(/(^| )order:/gi, "$1sort:"); const fav = search.match(/(?:^| )fav:(\d+)(?:$| )/); if (fav) { return { error: "XML API cannot search favorites" }; } return "/index.php?page=dapi&s=post&q=index&limit=" + opts.limit + "&pid=" + page + "&tags=" + encodeURIComponent(search); }, parse: (src: string): IParsedSearch | IError => { const parsed = Grabber.parseXML(src); // Handle error messages if ("response" in parsed && parsed["response"]["@attributes"]["success"] === "false") { return { error: parsed["response"]["@attributes"]["reason"] }; } const data = Grabber.makeArray(parsed.posts.post); const images: IImage[] = []; for (const image of data) { if (image && "id" in image) { images.push(completeImage(Grabber.typedXML(image))); } else if (image && "@attributes" in image) { images.push(completeImage(image["@attributes"])); } } return { images, imageCount: parsed.posts["@attributes"]["count"], }; }, }, }, html: { name: "Regex", auth: [], forcedLimit: 42, search: { url: (query: ISearchQuery, opts: IUrlOptions, previous: IPreviousSearch | undefined): string | IError => { try { const baseUrl = opts.baseUrl.replace("//api.", "//"); const search: string = query.search.replace(/(^| )order:/gi, "$1sort:"); const fav = search.match(/(?:^| )fav:(\d+)(?:$| )/); if (fav) { const pagePart = Grabber.pageUrl(query.page, previous, 20000, "&pid={page}", "&pid={page}", " id:<{min}&p=1", (p: number) => (p - 1) * 50); return baseUrl + "/index.php?page=favorites&s=view&id=" + fav[1] + pagePart; } else { const pagePart = Grabber.pageUrl(query.page, previous, 20000, "&pid={page}", "&pid={page}", " id:<{min}&p=1", (p: number) => (p - 1) * 42); return baseUrl + "/index.php?page=post&s=list&tags=" + encodeURIComponent(search) + pagePart; } } catch (e: any) { return { error: e.message }; } }, parse: (src: string): IParsedSearch | IError => { if (src.indexOf("Unable to search this deep") !== -1) { return { error: "Page too far" }; } const pageCountRaw = Grabber.regexMatch('<a href="[^"]+pid=(?<page>\\d+)[^"]*"[^>]*>[^<]+</a>\\s*(?:<b>(?<last>\\d+)</b>\\s*)?(?:</div>|<br ?/>)', src); const pageCount = pageCountRaw && (pageCountRaw["last"] || pageCountRaw["page"]); const images = Grabber.regexToImages('<span[^>]*(?: id="?\\w(?<id>\\d+)"?)?>\\s*<a[^>]*(?: id="?\\w(?<id_2>\\d+)"?)[^>]*>\\s*<img [^>]*(?:src|data-original)="(?<preview_url>[^"]+/thumbnail_(?<md5>[^.]+)\\.[^"]+)" [^>]*title="\\s*(?<tags>[^"]+)"[^>]*/?>\\s*</a>|<img\\s+class="preview"\\s+src="(?<preview_url_2>[^"]+/thumbnail_(?<md5_2>[^.]+)\\.[^"]+)" [^>]*title="\\s*(?<tags_2>[^"]+)"[^>]*/?>', src); const images = Grabber.regexToImages('(?:<span[^>]*(?: id="?\\w(?<id>\\d+)"?)?>\\s*)?<a[^>]*(?: id="?\\w(?<id_2>\\d+)"?)[^>]*>\\s*<img [^>]*(?:src|data-original)="(?<preview_url>[^"]+/thumbnail_(?<md5>[^.]+)\\.[^"]+)" [^>]*title="\\s*(?<tags>[^"]+)"[^>]*/?>\\s*</a>|<img\\s+class="preview"\\s+src="(?<preview_url_2>[^"]+/thumbnail_(?<md5_2>[^.]+)\\.[^"]+)" [^>]*title="\\s*(?<tags_2>[^"]+)"[^>]*/?>', src); for (const img of images) { const json = src.match(new RegExp("posts\\[" + img.id + "\\]\\s*=\\s*({.+?})"))?.[1]; if (json) { img.rating = json.match(/'rating'\s*:\s*'([^']+)'/)?.[1].toLowerCase(); img.score = json.match(/'score'\s*:\s*(\d+)/)?.[1]; img.author = json.match(/'user'\s*:\s*'([^']+)'/)?.[1]; } } return { images: images.map(completeImage), tags: Grabber.regexToTags('<li class="tag-type-(?<type>[^"]+)">(?:[^<]*<a[^>]*>[^<]*</a>)*[^<]*<a[^>]*>(?<name>[^<]*)</a>[^<]*<span[^>]*>(?<count>\\d+)</span>[^<]*</li>', src), pageCount: pageCount && parseInt(pageCount, 10) / 42 + 1, }; }, }, details: { url: (id: string, md5: string, opts: IUrlDetailsOptions): string => { const baseUrl = opts.baseUrl.replace("//api.", "//"); return baseUrl + "/index.php?page=post&s=view&id=" + id; }, parse: (src: string): IParsedDetails => { return { tags: Grabber.regexToTags('<li class="tag-type-(?<type>[^"]+)">(?:[^<]*(?:<span[^>]*>[^<]*)?<a[^>]*>[^<]*</a>(?:[^<]*</span>)?)*[^<]*<a[^>]*>(?<name>[^<]*)</a>[^<]*<span[^>]*>(?<count>\\d+)</span>[^<]*</li>', src), imageUrl: Grabber.regexToConst("url", '<img[^>]+src="([^"]+)"[^>]+onclick="Note\\.toggle\\(\\);"[^>]*/>', src), createdAt: src.match(/>Posted:\s*([^<]+)</i)?.[1], source: src.match(/>Source:\s*<a href="([^"]+)"/i)?.[1], }; }, }, endpoints: { pool_list: { name: "Pools", input: {}, url: (query: Record<never, string>, opts: IUrlOptions): string => { const pid = (opts.page - 1) * 25; return "/index.php?page=pool&s=list&pid=" + String(pid); }, parse: (src: string): IParsedSearch => { const html = Grabber.parseHTML(src); const images: IImage[] = []; const rows = html.find("table tr"); for (const row of rows) { const parts = row.find("td"); const link = parts[1].find("a")[0]; const id = link.attr("href").match(/id=(\d+)/)[1]; images.push({ id, name: link.innerText(), type: "gallery", gallery_count: parts[2].innerText().match(/(\d+)\s+Images/)[1], details_endpoint: { endpoint: "pool_details", input: { id }, }, }) } return { images }; }, }, pool_details: { input: { id: { type: "input", }, }, url: (query: Record<"id", number>): string => { return "/index.php?page=pool&s=show&id=" + String(query.id); }, parse: (src: string): IParsedGallery => { const images = Grabber.regexToImages('<span[^>]*(?: id="?\\w(?<id>\\d+)"?)?>\\s*<a[^>]*(?: id="?\\w(?<id_2>\\d+)"?)[^>]*>\\s*<img [^>]*(?:src|data-original)="(?<preview_url>[^"]+/thumbnail_(?<md5>[^.]+)\\.[^"]+)" [^>]*title="\\s*(?<tags>[^"]+)"[^>]*/?>\\s*</a>|<img\\s+class="preview"\\s+src="(?<preview_url_2>[^"]+/thumbnail_(?<md5_2>[^.]+)\\.[^"]+)" [^>]*title="\\s*(?<tags_2>[^"]+)"[^>]*/?>', src); return { images: images.map(completeImage), }; }, }, }, tagTypes: false, tags: { url: (query: ITagsQuery, opts: IUrlOptions): string => { const baseUrl = opts.baseUrl.replace("//api.", "//"); const sorts = { count: "desc", date: "asc", name: "asc" }; const orderBys = { count: "index_count", date: "updated", name: "tag" }; const page: number = (query.page - 1) * 50; return baseUrl + "/index.php?page=tags&s=list&pid=" + page + "&sort=" + sorts[query.order] + "&order_by=" + orderBys[query.order]; }, parse: (src: string): IParsedTags => { return { tags: Grabber.regexToTags('(?:<td>(?<count_2>\\d+)</td>\\s*)?<td><span class="tag-type-(?<type>[^"]+)"><a[^>]+>(?<name>.+?)</a></span>(?:\\s*<span class="tag-count">(?<count>\\d+)</span>)?</td>', src), }; }, }, check: { url: (): string => { return "/"; }, parse: (src: string): boolean => { return src.search(/Running Gelbooru(?: Beta)? 0\.2/) !== -1 || src.search(/Running <a[^>]*>Gelbooru<\/a>(?: Beta)? 0\.2/) !== -1; }, }, }, }, };

This code defines a set of functions and configurations related to interacting with an imageboard using a specific API setup (Gelbooru in this case) while adhering to any set constraints or data processing requirements.

Breakdown:

1. function completeImage(img: IImage): IImage

  • This function processes or "completes" an image object (img) of type IImage to potentially fill missing or incomplete data fields.
  • What it does:
    • Maps the image's author field by assigning it from the owner property, if available (img.author = (img as any).owner;).
    • Ensures the file_url is valid:
      • If file_url is missing or its length is less than 5 characters and preview_url exists:
        • Updates file_url by transforming preview_url using string replacements to switch paths/formats (replace("/thumbnails/", "/images/") and replace("/thumbnail_", "/")).
    • Returns the updated img object.

2. export const source

  • This defines the entire configuration schema for a specific imageboard source ("Gelbooru (0.2)").
  • What it does:
    • Metadata:
      • Defines the source name (Gelbooru) and supported search modifiers (modifiers such as types of ratings, user IDs, tags, and ordering options).
      • Specifies tag formatting rules (e.g., all tags are lowercase, words are separated by underscores _).
    • Authentication:
      • Supports two authentication methods:
        1. Via URL parameters (url object).
        2. Session-based login where credentials (user and password) are posted to a login endpoint.
      • Checks for a successful session using a specific cookie key ("user_id").
    • API Definitions:
      • Configures how the app interacts with Gelbooru's APIs, supporting both XML-based and HTML-parsed responses.
      • Defines API endpoints for:
        • Searching images (search), including URL construction and result parsing into a standardized format.
        • Fetching additional details about specific items (e.g., tags, image URLs, created date).
        • Pool-related actions (e.g., list pools, fetch pool details).
        • Retrieving available tags with sorting and filtering options.
      • Enforces constraints like maximum limits (e.g., maxLimit = 100 for XML queries).
    • Search URL Parsing:
      • Defines how URLs are constructed based on user input, including translating Gelbooru-specific search formats (order:sort:).
      • Handles special cases like searching user favorites (fav:) or tagging invalid inputs (e.g., deep page searches or unsupported favorite APIs).
    • Data Parsing:
      • Interprets XML or HTML responses coming from the server to extract needed data (e.g., image details or error reasons).
      • Processes structured or regex-parsed image data and enriches it via the completeImage function.
      • Extracts additional metadata such as tags and pagination details.

3. completeImage() use throughout:

  • The completeImage function is called every time individual image data is processed (e.g., from API responses or pool listings). It ensures consistency in image attributes (author, file_url, etc.), translating or filling gaps where possible.

4. General Functionality:

  • The code provides a complete modular implementation of an integration with the Gelbooru imageboard API, focusing on:
    • Querying and searching for images or tags.
    • Retrieving details about specific entries.
    • Supporting both XML and HTML response formats.
    • Handling authentication for both user-based and key-based methods.
    • Processing image data into a consistent format, using helper functions like completeImage.
In summary:

This code is part of a larger framework for interacting with Gelbooru, particularly for fetching and processing image data, handling search queries, and authenticating users. It ensures that data returned from Gelbooru is properly parsed, enriched, and matches expected standards or formats.

Generate your own explanations
Download our vscode extension
Read other generated explanations

Built by @thebuilderjr
Sponsored by beam analytics
Read our terms and privacy policy
Forked from openai-quickstart-node