Files
WaifuBoard-Extensions/book/mangadex.js

207 lines
6.9 KiB
JavaScript

class MangaDex {
constructor() {
this.baseUrl = "https://mangadex.org";
this.apiUrl = "https://api.mangadex.org";
this.type = "book-board";
this.mediaType = "manga";
this.version = "1.0"
}
getHeaders() {
return {
'User-Agent': 'MangaDex-Client-Adapter/1.0',
'Content-Type': 'application/json'
};
}
async search(queryObj) {
const query = queryObj.query?.trim() || "";
const limit = 25;
const offset = (1 - 1) * limit;
const url = `${this.apiUrl}/manga?title=${encodeURIComponent(query)}&limit=${limit}&offset=${offset}&includes[]=cover_art&contentRating[]=safe&contentRating[]=suggestive&availableTranslatedLanguage[]=en`;
try {
const response = await fetch(url, { headers: this.getHeaders() });
if (!response.ok) {
console.error(`MangaDex API Error: ${response.statusText}`);
return [];
}
const json = await response.json();
if (!json || !Array.isArray(json.data)) {
return [];
}
return json.data.map(manga => {
const attributes = manga.attributes;
const titleObject = attributes.title || {};
const title = titleObject.en || Object.values(titleObject)[0] || 'Unknown Title';
const coverRelationship = manga.relationships?.find(rel => rel.type === 'cover_art');
const coverFileName = coverRelationship?.attributes?.fileName;
const coverUrl = coverFileName
? `https://uploads.mangadex.org/covers/${manga.id}/${coverFileName}.256.jpg`
: '';
return {
id: manga.id,
image: coverUrl,
title: title,
rating: null,
type: 'book'
};
});
} catch (e) {
console.error("Error during MangaDex search:", e);
return [];
}
}
async getMetadata(id) {
try {
const res = await fetch(`https://api.mangadex.org/manga/${id}?includes[]=cover_art`);
if (!res.ok) throw new Error("MangaDex API error");
const json = await res.json();
const manga = json.data;
const attr = manga.attributes;
const title =
attr.title?.en ||
Object.values(attr.title || {})[0] ||
"";
const summary =
attr.description?.en ||
Object.values(attr.description || {})[0] ||
"";
const genres = manga.relationships
?.filter(r => r.type === "tag")
?.map(r =>
r.attributes?.name?.en ||
Object.values(r.attributes?.name || {})[0]
)
?.filter(Boolean) || [];
const coverRel = manga.relationships.find(r => r.type === "cover_art");
const coverFile = coverRel?.attributes?.fileName;
const image = coverFile
? `https://uploads.mangadex.org/covers/${id}/${coverFile}.512.jpg`
: "";
const score100 = 0;
const statusMap = {
ongoing: "Ongoing",
completed: "Completed",
hiatus: "Hiatus",
cancelled: "Cancelled"
};
return {
id,
title,
format: "Manga",
score: score100,
genres,
status: statusMap[attr.status] || "",
published: attr.year ? String(attr.year) : "???",
summary,
chapters: attr.lastChapter ? Number(attr.lastChapter) || 0 : 0,
image
};
} catch (e) {
console.error("MangaDex getMetadata error:", e);
return {
id,
title: "",
format: "Manga",
score: 0,
genres: [],
status: "",
published: "???",
summary: "",
chapters: 0,
image: ""
};
}
}
async findChapters(mangaId) {
if (!mangaId) return [];
const url = `${this.apiUrl}/manga/${mangaId}/feed?translatedLanguage[]=en&order[chapter]=asc&limit=500&includes[]=scanlation_group`;
try {
const response = await fetch(url, { headers: this.getHeaders() });
let chapters = [];
if (response.ok) {
const json = await response.json();
if (json && Array.isArray(json.data)) {
const allChapters = json.data
.filter(ch => ch.attributes.chapter && !ch.attributes.externalUrl)
.map((ch, index) => ({
id: ch.id,
title: ch.attributes.title || `Chapter ${ch.attributes.chapter}`,
number: ch.attributes.chapter,
index: index,
language: ch.attributes.translatedLanguage
}));
const seenChapters = new Set();
allChapters.forEach(ch => {
if (!seenChapters.has(ch.chapter)) {
seenChapters.add(ch.chapter);
chapters.push(ch);
}
});
chapters.sort((a, b) => parseFloat(a.chapter) - parseFloat(b.chapter));
}
}
return chapters;
} catch (e) {
console.error("Error finding MangaDex chapters:", e);
return { chapters: [], cover: null };
}
}
async findChapterPages(chapterId) {
if (!chapterId) return [];
const url = `${this.apiUrl}/at-home/server/${chapterId}`;
try {
const response = await fetch(url, { headers: this.getHeaders() });
if (!response.ok) throw new Error(`Failed to fetch pages: ${response.statusText}`);
const json = await response.json();
if (!json || !json.baseUrl || !json.chapter) return [];
const baseUrl = json.baseUrl;
const chapterHash = json.chapter.hash;
const imageFilenames = json.chapter.data;
return imageFilenames.map((filename, index) => ({
url: `${baseUrl}/data/${chapterHash}/${filename}`,
index: index,
headers: {
'Referer': `https://mangadex.org/chapter/${chapterId}`
}
}));
} catch (e) {
console.error("Error finding MangaDex pages:", e);
return [];
}
}
}
module.exports = MangaDex;