You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
5.4 KiB
173 lines
5.4 KiB
import type { EmbeddedFile, ImageProcessor } from "./main";
|
|
import { GM_fetch } from "./requests";
|
|
import { settings } from "./stores";
|
|
|
|
export type Booru = {
|
|
domain: string;
|
|
endpoint: string;
|
|
quirks: tran;
|
|
};
|
|
|
|
export type BooruMatch = {
|
|
source?: string;
|
|
page?: string;
|
|
tags: string[];
|
|
full_url: string;
|
|
preview_url: string;
|
|
ext: string;
|
|
};
|
|
|
|
type tran = (a: any) => BooruMatch[];
|
|
|
|
const gelquirk: (s: string) => tran = prefix => (a =>
|
|
(a.post || a).map((e: any) => ({
|
|
ext: e.image.substr(e.image.indexOf('.') + 1),
|
|
full_url: e.file_url,
|
|
source: e.source,
|
|
page: `${prefix}${e.id}`,
|
|
preview_url: e.preview_url,
|
|
tags: e.tags.split(' ')
|
|
} as BooruMatch)) || []);
|
|
|
|
export const boorus: Booru[] = [
|
|
{
|
|
domain: 'gelbooru.com',
|
|
endpoint: '/index.php?page=dapi&s=post&q=index&json=1&tags=md5:',
|
|
quirks: gelquirk("https://gelbooru.com/index.php?page=post&s=view&id=")
|
|
},
|
|
{
|
|
domain: 'yande.re',
|
|
endpoint: '/post.json?tags=md5:',
|
|
quirks: a =>
|
|
a.map((e: any) => ({
|
|
source: e.source,
|
|
page: `https://yande.re/post/show/${e.id}`,
|
|
ext: e.file_ext,
|
|
full_url: e.file_url,
|
|
preview_url: e.preview_url,
|
|
tags: e.tags.split(' ')
|
|
} as BooruMatch))
|
|
},
|
|
{
|
|
domain: 'capi-v2.sankakucomplex.com',
|
|
endpoint: '/posts/keyset?tags=md5:',
|
|
quirks: a => a.data ?
|
|
a.data.map((e: any) => ({
|
|
source: e.source,
|
|
// api cannot differenciate between idol and chan?
|
|
page: `https://chan.sankakucomplex.com/post/show/${e.id}`,
|
|
ext: e.file_type.substr(e.file_type.indexOf('/') + 1),
|
|
full_url: e.file_url,
|
|
preview_url: e.preview_url,
|
|
tags: e.tags.map((e: any) => e.name_en)
|
|
} as BooruMatch)) : []
|
|
},
|
|
{
|
|
domain: 'api.rule34.xxx',
|
|
endpoint: '/index.php?page=dapi&s=post&q=index&json=1&tags=md5:',
|
|
// note: rule34 do not seem to give source in their API
|
|
quirks: gelquirk("https://rule34.xxx/index.php?page=post&s=view&id=")
|
|
},
|
|
{
|
|
domain: 'danbooru.donmai.us',
|
|
endpoint: '/posts.json?tags=md5:',
|
|
quirks: a =>
|
|
a.map((e: any) => ({
|
|
source: e.source,
|
|
page: `https://danbooru.donmai.us/posts/${e.id}`,
|
|
ext: e.file_ext,
|
|
full_url: e.file_url,
|
|
preview_url: e.preview_url,
|
|
tags: e.tag_string.split(' ')
|
|
} as BooruMatch))
|
|
},
|
|
{
|
|
domain: 'lolibooru.moe',
|
|
endpoint: '/post.json?tags=md5:',
|
|
quirks: a =>
|
|
a.map((e: any) => ({
|
|
source: e.source,
|
|
page: `https://lolibooru.moe/post/show/${e.id}`,
|
|
ext: e.file_url.substr(e.file_url.lastIndexOf('.') + 1),
|
|
full_url: e.file_url,
|
|
preview_url: e.preview_url,
|
|
tags: e.tags.split(' ')
|
|
} as BooruMatch))
|
|
}
|
|
];
|
|
|
|
let black = new Set<string>();
|
|
let sources = new Set<string>();
|
|
|
|
settings.subscribe(s => {
|
|
black = new Set(s.blacklist);
|
|
sources = new Set(s.sources);
|
|
});
|
|
|
|
const cache: any = {};
|
|
|
|
const findFileFrom = async (b: Booru, hex: string) => {
|
|
try {
|
|
if (b.domain in cache && hex in cache[b.domain])
|
|
return cache[b.domain][hex] as BooruMatch[];
|
|
const res = await GM_fetch(`https://${b.domain}${b.endpoint}${hex}`);
|
|
// might throw because some endpoint respond with invalid json when an error occurs
|
|
const pres = await res.json();
|
|
const tran = b.quirks(pres).filter(e => !e.tags.some(e => black.has(e)));
|
|
if (!(b.domain in cache))
|
|
cache[b.domain] = {};
|
|
cache[b.domain][hex] = tran;
|
|
return tran;
|
|
} catch {
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const extract = async (b: Buffer, fn?: string) => {
|
|
let result!: BooruMatch[];
|
|
for (const e of Object.values(boorus)) {
|
|
if (!sources.has(e.domain))
|
|
continue;
|
|
result = await findFileFrom(e, fn!.substring(0, 32));
|
|
if (result.length)
|
|
break;
|
|
}
|
|
let cachedFile: ArrayBuffer;
|
|
return {
|
|
source: result[0].source,
|
|
page: result[0].page,
|
|
filename: fn!.substring(0, 33) + result[0].ext,
|
|
thumbnail: (await (await GM_fetch(result[0].preview_url)).arrayBuffer()),
|
|
data: async (lsn) => {
|
|
if (!cachedFile)
|
|
cachedFile = (await (await GM_fetch(result[0].full_url, undefined, lsn)).arrayBuffer());
|
|
return cachedFile;
|
|
}
|
|
} as EmbeddedFile;
|
|
};
|
|
|
|
const has_embed = async (b: Buffer, fn?: string) => {
|
|
// It's not worth to bother skipping images with filenames that match their md5 because
|
|
// 4chan reencodes jpegs, which is well over half the files posted
|
|
|
|
// ok fine you autists
|
|
if (Buffer.from(fn!, 'hex').equals(b))
|
|
return false;
|
|
|
|
let result: BooruMatch[] | undefined = undefined;
|
|
for (const e of Object.values(boorus)) {
|
|
if (!sources.has(e.domain))
|
|
continue;
|
|
result = await findFileFrom(e, fn!.substring(0, 32));
|
|
if (result.length)
|
|
break;
|
|
}
|
|
return result && result.length != 0;
|
|
};
|
|
|
|
export default {
|
|
skip: true,
|
|
extract,
|
|
has_embed,
|
|
match: fn => !!fn.match(/^[0-9a-fA-F]{32}\.....?$/)
|
|
} as ImageProcessor;
|