feat: integrate xml-parser for API response handling and enhance data retrieval methods

This commit is contained in:
fzzinchemical
2025-03-28 16:14:49 +01:00
parent 0d62cbc8c7
commit f55b5579ad
4 changed files with 182 additions and 90 deletions

View File

@@ -1,96 +1,168 @@
import { assert } from "@std/assert/assert";
import * as xml_parser from "jsr:@melvdouc/xml-parser";
type APIResponse = Array<{
preview_url: string;
sample_url: string;
file_url: string;
directory: number;
hash: string;
width: number;
height: number;
id: number;
image: string;
change: number;
owner: string;
parent_id: number;
rating: string;
sample: boolean;
sample_height: number;
sample_width: number;
score: number;
tags: string;
source: string;
status: string;
has_notes: boolean;
comment_count: number;
}>;
type ImageResponse = {
preview_url: string;
sample_url: string;
file_url: string;
directory: number;
hash: string;
width: number;
height: number;
id: number;
image: string;
change: number;
owner: string;
parent_id: number;
rating: string;
sample: boolean;
sample_height: number;
sample_width: number;
score: number;
tags: string;
source: string;
status: string;
has_notes: boolean;
comment_count: number;
};
type CommentResponse = {
created_at: string;
post_id: number;
body: string;
creator: string;
id: number;
creator_id: number;
};
type TagResponse = {
type: number;
count: number;
name: string;
ambiguous: boolean;
id: number;
};
// Define the API URL
const apiUrl =
"https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&json=1";
const baseUrl = "https://api.rule34.xxx";
const postUrl = `${baseUrl}/index.php?page=dapi&s=post&q=index&json=1`;
const tagUrl = `${baseUrl}/index.php?page=dapi&s=tag&q=index`;
const commentsUrl = `${baseUrl}/index.php?page=dapi&s=comment&q=index`;
// Make a GET request
let baseResponse = await fetch(apiUrl, { headers: { "Accept": "application/json" } })
.then(async (response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return <APIResponse> await response.json();
});
const hyperlinkarray: string[] = [];
// ---------------------------------
// import * as stdHTML from "jsr:@std/html@1.0.2";
// Deno.serve({ port: 6969 }, async (request) => {
// const html = hyperlinkarray.map((x) => `<img src="${stdHTML.escape(x)}"/>`)
// .join("\n");
// return new Response(html, {
// headers: { "Content-Type": "text/html;charset=utf-8" },
// });
// });
// ---------------------------------
export async function refresh() {
await fetch(apiUrl, { headers: { "Accept": "application/json" } })
.then(async (response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
baseResponse = <APIResponse> await response.json();
for (const k of baseResponse) {
if (!hyperlinkarray.includes(k.file_url)) {
hyperlinkarray.push(k.file_url);
}
}
});
async function requestJSON<T>(URL: string) {
const response = await requestRaw(URL);
return <T> await response.json();
}
for (const k of baseResponse) {
hyperlinkarray.push(k.file_url);
async function requestRaw(URL: string) {
const response = await fetch(URL, {
headers: { "Accept": "application/json" },
});
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response;
}
export async function dropRule() {
if (hyperlinkarray.length === 0) {
await refresh();
return await dropRule();
}
return hyperlinkarray.pop()!;
//List
async function getPosts(n: number) {
return await requestJSON<ImageResponse[]>(`${postUrl}&limit=${n}`);
}
export async function dropRule5() {
let tmp = "";
for (let i = 0; i < 5; i++) {
tmp += await dropRule() + "\n";
}
return tmp;
async function getPostWithID(id: number) {
return await requestJSON<ImageResponse[]>(
`${postUrl}&id=${encodeURIComponent(id)}`,
);
}
async function getPID() {
return await requestJSON<ImageResponse[]>(`${postUrl}&pid`);
}
//test refresh here because local stack is not given to other functions
Deno.test("Test Image-Stack refresh", async() => {
await refresh()
const previousStack = [...hyperlinkarray]
await new Promise(r => setTimeout(r, 10000));
await refresh()
assert(previousStack.length <= hyperlinkarray.length, "Stack-size did not increase as expected!")
})
async function getTags(tags: string[]) {
const list = tags.join("+");
return await requestJSON<ImageResponse[]>(
`${postUrl}&tags=${encodeURIComponent(list)}`,
);
}
// Comments
async function getPostComments(postID: number) {
const response = await requestRaw(`${commentsUrl}&post_id=${postID}`);
return XMLtoGenericDatatypeParser(response, parseComment);
}
async function getTagByID(id: number) {
const response = await requestRaw(`${tagUrl}&id=${id}`);
return XMLtoGenericDatatypeParser(response, parseTag);
}
async function getTagList(n: number) {
const response = await requestRaw(`${tagUrl}&limit=${n}`);
return XMLtoGenericDatatypeParser(response, parseTag);
}
function parseTag(attributes: Record<string, string>) {
return Object.fromEntries(
Object.entries(attributes).map(([k, v]) => {
if (k === "ambiguous") {
return [k, v === "true"];
} else if (k === "name") {
return [k, v];
} else {
return [k, convertStringtoNumber(v)];
}
}),
);
}
function parseComment(attributes: Record<string, string>) {
return Object.fromEntries(
Object.entries(attributes).map(([k, v]) => {
if (k === "id" || k === "creator_id" || k === "post_id") {
return [k, convertStringtoNumber(v)];
} else {
return [k, v];
}
}),
);
}
function convertStringtoNumber(str: string) {
return parseFloat(str);
}
async function XMLtoGenericDatatypeParser<T>(
response: Response,
callback: (attributes: Record<string, string>) => T,
) {
const parsedResponse = xml_parser.parse(await response.text());
const stack: T[] = [];
for (const v of parsedResponse) {
if (v.kind === "REGULAR_TAG_NODE") {
for (const entry of v.children) {
if (entry.kind === "ORPHAN_TAG_NODE") {
if (entry.attributes !== undefined) {
stack.push(callback(entry.attributes));
}
}
}
}
}
return stack;
}
Deno.test("Post Comment", async () => {
const response = await getPostComments(1213);
console.debug(response);
});
Deno.test("Get Tag by ID", async () => {
const response = await getTagByID(1);
console.debug(response);
});
Deno.test("Get Tag List", async () => {
const response = await getTagList(6);
console.debug(response);
});

View File

@@ -1,8 +1,8 @@
import { assert } from "jsr:@std/assert";
import { dropRule, refresh } from "@root/plugins/rule34/api.ts";
// import { assert } from "jsr:@std/assert";
// import { dropRule, refresh } from "@root/plugins/rule34/api.ts";
Deno.test("Test Drop", async () => {
await refresh();
const link = await dropRule();
assert(link !== "", "Empty String was dropped!");
});
// Deno.test("Test Drop", async () => {
// await refresh();
// const link = await dropRule();
// assert(link !== "", "Empty String was dropped!");
// });