162 lines
5.3 KiB
JavaScript
162 lines
5.3 KiB
JavaScript
const languages = new Set(["en", "fr", "es", "it", "pl", "tr", "ja", "zh", "sv"]);
|
|
const contentTypes = new Set(["sfw", "nsfw"]);
|
|
const urlBase = "https://api.comick.fun/chapter/";
|
|
const genreBase = "https://api.comick.io/genre/";
|
|
const cache = new Map();
|
|
const genreCache = new Map();
|
|
|
|
export default {
|
|
async fetch(request) {
|
|
const url = new URL(request.url);
|
|
const path = url.pathname;
|
|
|
|
if (path === "/info") {
|
|
return getInfoResponse();
|
|
}
|
|
|
|
try {
|
|
const language = findLanguage(url);
|
|
const contentType = findContentType(url);
|
|
const cacheKey = `${language}-${contentType}`;
|
|
|
|
let data;
|
|
if (cache.has(cacheKey)) {
|
|
data = cache.get(cacheKey);
|
|
} else {
|
|
const apiUrl = `${urlBase}?lang=${language}&page=1&order=new&accept_erotic_content=${contentType === 'nsfw'}`;
|
|
data = await fetchData(apiUrl);
|
|
cache.set(cacheKey, data); // Cache the response
|
|
}
|
|
|
|
// Fetch genres if not already cached
|
|
if (!genreCache.size) {
|
|
const genres = await fetchData(genreBase);
|
|
const genreMap = genres.reduce((acc, genre) => {
|
|
acc[genre.id] = genre.name;
|
|
return acc;
|
|
}, {});
|
|
genreCache.set('allGenres', genreMap);
|
|
}
|
|
|
|
const rssData = generateRSS(data, language, contentType, url);
|
|
return new Response(rssData, {
|
|
headers: {
|
|
"Content-Type": "application/rss+xml",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Error processing request:", error);
|
|
return new Response("Internal Server Error", {
|
|
status: 500,
|
|
headers: {
|
|
"Content-Type": "text/plain",
|
|
},
|
|
});
|
|
}
|
|
},
|
|
};
|
|
|
|
function getInfoResponse() {
|
|
return new Response(`
|
|
<html>
|
|
<head>
|
|
<title>Worker Info</title>
|
|
</head>
|
|
<body>
|
|
<h1>Welcome to Manga-RSS!</h1>
|
|
<p>Unfortunately, the GitHub repo was taken down and the python script stopped working, but after rewriting the project in JavaScript, I was able to bring it back. To use this new version with even more supported languages, you just append /[language] to the base url and either /sfw or /nsfw for the content rating.</p>
|
|
</body>
|
|
</html>`, {
|
|
headers: {
|
|
"Content-Type": "text/html",
|
|
},
|
|
});
|
|
}
|
|
|
|
async function fetchData(apiUrl) {
|
|
const init = {
|
|
headers: {
|
|
"content-type": "application/json;charset=UTF-8",
|
|
"User-Agent": "Mozilla/5.0",
|
|
},
|
|
};
|
|
|
|
try {
|
|
const responseFromApi = await fetch(apiUrl, init);
|
|
if (!responseFromApi.ok) {
|
|
throw new Error(`Failed to fetch data from API (${responseFromApi.status} ${responseFromApi.statusText})`);
|
|
}
|
|
|
|
const contentType = (responseFromApi.headers.get("content-type") || "").toLowerCase();
|
|
return contentType.includes("application/json") ? responseFromApi.json() : responseFromApi.text();
|
|
} catch (error) {
|
|
console.error("Error fetching data:", error);
|
|
throw new Error("Failed to fetch data from the API");
|
|
}
|
|
}
|
|
|
|
function findLanguage(url) {
|
|
for (const lang of languages) {
|
|
if (url.pathname.includes(`/${lang}`)) {
|
|
return lang;
|
|
}
|
|
}
|
|
return "en";
|
|
}
|
|
|
|
function findContentType(url) {
|
|
for (const type of contentTypes) {
|
|
if (url.pathname.includes(`/${type}`)) {
|
|
return type;
|
|
}
|
|
}
|
|
return "sfw";
|
|
}
|
|
|
|
function encodeHTML(text) {
|
|
return text.replace(/</g, '').replace(/>/g, '');
|
|
}
|
|
|
|
function getGenreNames(genreIds) {
|
|
const allGenres = genreCache.get('allGenres');
|
|
return genreIds.map(id => `<li>${encodeHTML(allGenres[id] || 'Unknown Genre')}</li>`).join('');
|
|
}
|
|
|
|
function generateRSS(data, language, contentType, url) {
|
|
const rssHeader = `<?xml version="1.0" encoding="UTF-8" ?>
|
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
<channel>
|
|
<title>ComicK - ${language} ${contentType.toUpperCase()} RSS Feed</title>
|
|
<link>https://github.com/ld3z/manga-rss</link>
|
|
<description>A simple RSS feed for ComicK!</description>
|
|
<atom:link href="${url.origin + url.pathname}" rel="self" type="application/rss+xml"/>`;
|
|
|
|
const rssItems = data.map(comicData => {
|
|
const comic = comicData.md_comics;
|
|
|
|
const title = `<![CDATA[${encodeHTML(`${comic.title} - Chapter ${comicData.chap}`)}]]>`;
|
|
|
|
// Embed the image in the description as an <img> tag
|
|
const coverImageURL = comic.md_covers[0] ? `https://meo.comick.pictures/${comic.md_covers[0].b2key}` : '';
|
|
const description = coverImageURL ? `<![CDATA[Chapter ${comicData.chap} of ${comic.title} is now available on ComicK!<br><strong>Genres:</strong><ul>${getGenreNames(comic.genres)}</ul><br><img src="${coverImageURL}" alt="Cover Image" width="200" height="300" />]]>` :
|
|
`<![CDATA[Chapter ${comicData.chap} of ${comic.title} is now available on ComicK!<br><strong>Genres:</strong><ul>${getGenreNames(comic.genres)}</ul>]]>`;
|
|
|
|
// Extract and format the 'updated_at' field from the API response
|
|
const pubDate = comicData.updated_at ? new Date(comicData.updated_at).toUTCString() : new Date().toUTCString();
|
|
|
|
return `
|
|
<item>
|
|
<guid>https://comick.io/comic/${comic.slug}</guid>
|
|
<title>${title}</title>
|
|
<link>https://comick.io/comic/${comic.slug}</link>
|
|
<description>${description}</description>
|
|
<pubDate>${pubDate}</pubDate>
|
|
</item>`;
|
|
}).join('');
|
|
|
|
const rssFooter = `
|
|
</channel>
|
|
</rss>`;
|
|
|
|
return `${rssHeader}${rssItems}${rssFooter}`;
|
|
}
|