updated marketplace and extensions
This commit is contained in:
155
book/comix.js
Normal file
155
book/comix.js
Normal file
@@ -0,0 +1,155 @@
|
||||
class Comix {
|
||||
constructor() {
|
||||
this.baseUrl = "https://comix.to";
|
||||
this.apiUrl = "https://comix.to/api/v2";
|
||||
this.type = "book-board";
|
||||
this.version = "1.0";
|
||||
this.mediaType = "manga";
|
||||
}
|
||||
|
||||
get headers() {
|
||||
return {
|
||||
"Referer": `${this.baseUrl}/`,
|
||||
"User-Agent": "Mozilla/5.0"
|
||||
};
|
||||
}
|
||||
|
||||
async search(queryObj) {
|
||||
const q = (queryObj.query || "").trim().replace(/\s+/g, "+");
|
||||
|
||||
const url = new URL(`${this.apiUrl}/manga`);
|
||||
if (q) {
|
||||
url.searchParams.set("keyword", q);
|
||||
url.searchParams.set("order[relevance]", "desc");
|
||||
} else {
|
||||
url.searchParams.set("order[views_30d]", "desc");
|
||||
}
|
||||
url.searchParams.set("limit", "50");
|
||||
url.searchParams.set("page", "1");
|
||||
|
||||
const res = await fetch(url, { headers: this.headers });
|
||||
if (!res.ok) throw new Error(`Search failed: ${res.status}`);
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
return json.result.items.map(m => ({
|
||||
id: m.hash_id,
|
||||
title: m.title,
|
||||
image: m.poster?.large || null,
|
||||
rating: m.score ?? null,
|
||||
type: "book"
|
||||
}));
|
||||
}
|
||||
|
||||
async getMetadata(id) {
|
||||
const url = `${this.apiUrl}/manga/${id}?includes[]=genre&includes[]=author&includes[]=artist`;
|
||||
|
||||
const res = await fetch(url, { headers: this.headers });
|
||||
if (!res.ok) throw new Error(`Metadata failed: ${res.status}`);
|
||||
|
||||
const { result } = await res.json();
|
||||
|
||||
return {
|
||||
id: result.hash_id,
|
||||
title: result.title,
|
||||
format: "MANGA",
|
||||
score: result.score ?? 0,
|
||||
genres: result.genres?.map(g => g.name).join(", ") ?? "",
|
||||
status: result.status ?? "unknown",
|
||||
published: result.created_at ?? "",
|
||||
summary: result.description ?? "",
|
||||
chapters: result.chapters_count ?? 0,
|
||||
image: result.poster?.large || null
|
||||
};
|
||||
}
|
||||
|
||||
async getSlug(mangaId) {
|
||||
const res = await fetch(`${this.apiUrl}/manga/${mangaId}`, {
|
||||
headers: this.headers
|
||||
});
|
||||
if (!res.ok) return "";
|
||||
const { result } = await res.json();
|
||||
return result?.slug || "";
|
||||
}
|
||||
|
||||
async findChapters(mangaId) {
|
||||
const slug = await this.getSlug(mangaId);
|
||||
if (!slug) return [];
|
||||
|
||||
const baseUrl = `${this.apiUrl}/manga/${mangaId}/chapters?order[number]=desc&limit=100`;
|
||||
|
||||
const res = await fetch(baseUrl, { headers: this.headers });
|
||||
if (!res.ok) return [];
|
||||
|
||||
const first = await res.json();
|
||||
const totalPages = first.result.pagination?.last_page || 1;
|
||||
|
||||
let all = [...first.result.items];
|
||||
|
||||
for (let p = 2; p <= totalPages; p++) {
|
||||
const r = await fetch(`${baseUrl}&page=${p}`, { headers: this.headers });
|
||||
if (!r.ok) continue;
|
||||
const d = await r.json();
|
||||
if (d?.result?.items) all.push(...d.result.items);
|
||||
}
|
||||
|
||||
const map = new Map();
|
||||
|
||||
for (const ch of all) {
|
||||
if (ch.language !== "en") continue;
|
||||
|
||||
const key = ch.number;
|
||||
if (!map.has(key) || ch.is_official === 1) {
|
||||
map.set(key, ch);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(map.values())
|
||||
.sort((a, b) => a.number - b.number)
|
||||
.map((ch, i) => ({
|
||||
id: `${mangaId}|${slug}|${ch.chapter_id}|${ch.number}`,
|
||||
title: ch.name
|
||||
? `Chapter ${ch.number} — ${ch.name}`
|
||||
: `Chapter ${ch.number}`,
|
||||
number: Number(ch.number),
|
||||
releaseDate: ch.updated_at ?? null,
|
||||
index: i
|
||||
}));
|
||||
}
|
||||
|
||||
async findChapterPages(chapterId) {
|
||||
const parts = chapterId.split("|");
|
||||
console.log(parts)
|
||||
if (parts.length < 4) return [];
|
||||
|
||||
const [hashId, slug, chapterRealId, number] = parts;
|
||||
const readerUrl = `${this.baseUrl}/title/${hashId}-${slug}/${chapterRealId}-chapter-${number}`;
|
||||
|
||||
const res = await fetch(readerUrl, { headers: this.headers });
|
||||
if (!res.ok) return [];
|
||||
|
||||
const html = await res.text();
|
||||
|
||||
const regex = /["\\]*images["\\]*\s*:\s*(\[[^\]]*\])/s;
|
||||
const match = html.match(regex);
|
||||
if (!match?.[1]) return [];
|
||||
|
||||
let images;
|
||||
try {
|
||||
images = JSON.parse(match[1]);
|
||||
} catch {
|
||||
images = JSON.parse(match[1].replace(/\\"/g, '"'));
|
||||
}
|
||||
|
||||
return images.map((img, i) => ({
|
||||
url: img.url,
|
||||
index: i,
|
||||
headers: {
|
||||
Referer: readerUrl,
|
||||
"User-Agent": "Mozilla/5.0"
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Comix;
|
||||
Reference in New Issue
Block a user