Add worker.js

This commit is contained in:
Logan Descamps 2025-01-18 15:35:19 -07:00
commit 99f66230cd

162
worker.js Normal file
View file

@ -0,0 +1,162 @@
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}`;
}