UNPKG

booru

Version:

Search (and do other things) on a bunch of different boorus!

305 lines 10.2 kB
"use strict"; /** * @packageDocumentation * @module Structures */ Object.defineProperty(exports, "__esModule", { value: true }); const node_util_1 = require("node:util"); const common = (0, node_util_1.deprecate)(() => { }, 'Common is now deprecated, just access the properties directly'); /** * Tries to figure out what the image url should be * * @param {string} url why * @param {*} data boorus * @param {Booru} booru so hard */ function parseImageUrl(url, data, booru) { let outUrl = url; // If the image's file_url is *still* undefined or the source is empty or it's deleted // Thanks danbooru *grumble grumble* if (!outUrl || outUrl.trim() === '' || data.is_deleted) { return null; } if (outUrl.startsWith('/data')) { outUrl = `https://danbooru.donmai.us${outUrl}`; } if (outUrl.startsWith('/cached')) { outUrl = `https://danbooru.donmai.us${outUrl}`; } if (outUrl.startsWith('/_images')) { outUrl = `https://dollbooru.org${outUrl}`; } if (outUrl.startsWith('//derpicdn.net')) { outUrl = `https:${data.image}`; } if (outUrl.startsWith('/_thumbs')) { outUrl = `https://rule34.paheal.net${outUrl}`; } // Why??? if (!data.file_url && data.directory !== undefined) { // Danbooru-based boorus sometimes sort their files into directories // There's 2 directories, one named after the first 2 characters of the hash // and one named after the next 2 characters of the hash // Sometimes we get it in the API response as `data.directory`, sometimes it's null // for some ungodly reason // I despise the danbooru api honestly const directory = data.directory ?? `${data.hash.substr(0, 2)}/${data.hash.substr(2, 2)}`; outUrl = `//${booru.domain}/images/${directory}/${data.image}`; } if (!outUrl.startsWith('http')) { outUrl = `https:${outUrl}`; } if (booru.domain === 'xbooru.com' && outUrl.startsWith('https://api-cdn.rule34.xxx/')) { outUrl = outUrl.replace('https://api-cdn.rule34.xxx/', 'https://xbooru.com/'); } return encodeURI(outUrl); } /** * Takes and transforms tags from the booru's api into a common format * (which is an array of strings) * @param {any} data The data from the booru * @returns {string[]} The tags as a string array, and not just a string or an object */ function getTags(data) { let tags = []; if (Array.isArray(data.tags)) { tags = data.tags; } else if (data.tags?.general) { tags = Object.values(data.tags).flat(); } else if (typeof data.tags === 'string') { tags = fromTagString(data.tags); } else if (typeof data.tag_string === 'string') { tags = fromTagString(data.tag_string); } return tags.filter((v) => v !== ''); } /** * Parses a string of tags into an array of tags, doing some sanitization * * @example * fromTagString('tag1 tag2 tag3') => ['tag1', 'tag2', 'tag3'] * fromTagString('tag1 tag,2 tag3 ') => ['tag1', 'tag2', 'tag3'] * @param tags The tags as a string * @returns The string, parsed into an array of tags */ function fromTagString(tags) { return tags.split(' ').map((v) => v.replace(/,/g, '')); } /** * An image from a booru with a few common props * * @example * ``` * Post { * fileUrl: 'https://aaaa.com/image.jpg', * id: '124125', * tags: ['cat', 'cute'], * score: 5, * source: 'https://giraffeduck.com/aaaa.png', * rating: 's' * } * ``` */ class Post { /** The {@link Booru} it came from */ booru; /** The direct link to the file */ fileUrl; /** The height of the file */ height; /** The width of the file */ width; /** The url to the medium-sized image (if available) */ sampleUrl; /** The height of the medium-sized image (if available) */ sampleHeight; /** The width of the medium-sized image (if available) */ sampleWidth; /** The url to the smallest image (if available) */ previewUrl; /** The height of the smallest image (if available) */ previewHeight; /** The width of the smallest image (if available) */ previewWidth; /** The id of this post */ id; /** If this post is available (ie. not deleted, not banned, has file url) */ available; /** The tags of this post */ tags; /** The score of this post */ score; /** The source of this post, if available */ source; /** * The rating of the image, as just the first letter * (s/q/e/u) => safe/questionable/explicit/unrated */ rating; /** The Date this post was created at */ createdAt; /** All the data given by the booru @private */ data; /** * Create an image from a booru * * @param {Object} data The raw data from the Booru * @param {Booru} booru The booru that created the image */ constructor(data, booru) { // Damn wild mix of boorus this.data = data; this.booru = booru; // Again, thanks danbooru const deletedOrBanned = data.is_deleted || data.is_banned; this.fileUrl = parseImageUrl(data.file_url ?? data.image ?? (deletedOrBanned ? data.source : undefined) ?? data.file?.url ?? data.representations?.full, data, booru); this.available = !deletedOrBanned && this.fileUrl !== null; this.height = Number.parseInt(data.height ?? data.image_height ?? data.file?.height, 10); this.width = Number.parseInt(data.width ?? data.image_width ?? data.file?.width, 10); this.sampleUrl = parseImageUrl(data.sample_url ?? data.large_file_url ?? data.representations?.large ?? data.sample?.url, data, booru); this.sampleHeight = Number.parseInt(data.sample_height ?? data.sample?.height, 10); this.sampleWidth = Number.parseInt(data.sample_width ?? data.sample?.width, 10); this.previewUrl = parseImageUrl(data.preview_url ?? data.preview_file_url ?? data.representations?.small ?? data.preview?.url, data, booru); this.previewHeight = Number.parseInt(data.preview_height ?? data.preview?.height, 10); this.previewWidth = Number.parseInt(data.preview_width ?? data.preview?.width, 10); this.id = data.id ? data.id.toString() : 'No ID available'; this.tags = getTags(data); if (data.score && data.score.total !== undefined) { this.score = data.score.total; } else { this.score = data.score ? Number.parseInt(data.score, 10) : data.score; } this.source = data.source ?? data.sources ?? data.source_url; this.rating = data.rating ?? /(safe|suggestive|questionable|explicit)/i.exec(data.tags) ?? 'u'; if (Array.isArray(this.rating)) { this.rating = this.rating[0]; } // Thanks derpibooru if (this.rating === 'suggestive') { this.rating = 'q'; } this.rating = this.rating.charAt(0); this.createdAt = null; // eslint-disable-next-line if (typeof data.created_at === 'object') { this.createdAt = new Date(data.created_at.s * 1000 + data.created_at.n / 1000000000); } else if (typeof data.created_at === 'number') { this.createdAt = new Date(data.created_at * 1000); } else if (typeof data.created_at === 'string') { this.createdAt = new Date(data.created_at); } else if (typeof data.change === 'number') { this.createdAt = new Date(data.change * 1000); } else { this.createdAt = new Date(data.created_at ?? data.date); } } /** * The direct link to the file * <p>It's prefered to use `.fileUrl` instead because camelCase */ get file_url() { return this.fileUrl; } /** * The url to the medium-sized image (if available) * <p>It's prefered to use `.sampleUrl` instead because camelCase */ get sample_url() { return this.sampleUrl; } /** * The height of the medium-sized image (if available) * <p>It's prefered to use `.sampleHeight` instead because camelCase */ get sample_height() { return this.sampleHeight; } /** * The width of the medium-sized image (if available) * <p>It's prefered to use `.sampleWidth` instead because camelCase */ get sample_width() { return this.sampleWidth; } /** * The url to the smallest image (if available) * <p>It's prefered to use `.previewUrl` instead because camelCase */ get preview_url() { return this.previewUrl; } /** * The height of the smallest image (if available) * <p>It's prefered to use `.previewHeight` instead because camelCase */ get preview_height() { return this.previewHeight; } /** * The width of the smallest image (if available) * <p>It's prefered to use `.previewWidth` instead because camelCase */ get preview_width() { return this.previewWidth; } /** * Get the post view (url to the post) of this image * * @type {String} * @example * ``` * const e9 = Booru('e9') * const imgs = e9.search(['cat', 'dog']) * * // Log the post url of the first image * console.log(imgs[0].postView) * ``` */ get postView() { return this.booru.postView(this.id); } /** * Get some common props on the image * * @deprecated All common props are now attached directly to the image * @type {Object} * * @example * ``` * image.id * // deprecated, use this instead * image.id * * // To access the post's raw data from the booru, do * image._data.id * ``` */ get common() { common(); return this; } } exports.default = Post; //# sourceMappingURL=Post.js.map