diff --git a/src/components/LanguageSelector.astro b/src/components/LanguageSelector.astro index 7b376c0..7f4ba68 100644 --- a/src/components/LanguageSelector.astro +++ b/src/components/LanguageSelector.astro @@ -1,50 +1,34 @@ --- -import { availableLanguages, availableContentTypes, getLanguageName } from '../lib/api'; +import { availableLanguages, languageMap } from '../lib/api'; interface Props { currentLang: string; currentType: string; + contentType: string; } -const { currentLang, currentType } = Astro.props; +const { currentLang, currentType, contentType } = Astro.props; --- -
- - {availableLanguages.map(lang => ( - - ))} + { + availableLanguages.map((lang) => ( + + )) + } - - -
- - \ No newline at end of file +
+ + + +
+ \ No newline at end of file diff --git a/src/components/RSSLink.astro b/src/components/RSSLink.astro index 1adabe2..4a32ab2 100644 --- a/src/components/RSSLink.astro +++ b/src/components/RSSLink.astro @@ -1,31 +1,31 @@ --- interface Props { lang: string; + contentType: string; type: string; } -const { lang, type } = Astro.props; +const { lang, contentType, type } = Astro.props; --- - - - - - + + + RSS Feed \ No newline at end of file diff --git a/src/components/TypeFilter.astro b/src/components/TypeFilter.astro index 0519ecb..8ea63d3 100644 --- a/src/components/TypeFilter.astro +++ b/src/components/TypeFilter.astro @@ -1 +1,31 @@ - \ No newline at end of file +--- +interface Props { + currentType: string; + lang: string; + contentType: string; +} + +const { currentType, lang, contentType } = Astro.props; + +const types = [ + { id: 'all', label: 'All' }, + { id: 'manga', label: 'Manga' }, + { id: 'manhwa', label: 'Manhwa' }, + { id: 'manhua', label: 'Manhua' }, +]; +--- + +
+ {types.map(type => ( + + {type.label} + + ))} +
\ No newline at end of file diff --git a/src/lib/api.ts b/src/lib/api.ts index 2c66bfe..131f8ec 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,4 +1,4 @@ -import type { Comic, Genre } from './types'; +import type { Comic, Genre, ComicType } from './types'; const languageMap = { "en": "English", @@ -16,6 +16,7 @@ const languageMap = { const languages = new Set(Object.keys(languageMap)); const contentTypes = new Set(["sfw", "nsfw"]); +const comicTypes = new Set(['manga', 'manhwa', 'manhua', 'all']); const urlBase = "https://api.comick.fun/chapter"; let genreCache: Genre[] = []; @@ -67,9 +68,18 @@ export function getGenreNames(genreIds: number[], genres: Genre[]): string[] { .filter(name => name !== ''); } -export async function fetchComics(language: string = 'en', contentType: string = 'sfw', page: number = 1): Promise { +export async function fetchComics( + language: string = 'en', + contentType: string = 'sfw', + comicType: ComicType = 'all', + page: number = 1 +): Promise { try { - const apiUrl = `${urlBase}?lang=${language}&page=${page}&order=new&accept_erotic_content=${contentType === 'nsfw'}`; + const typeParam = comicType === 'all' ? '' : `&type=${comicType}`; + const apiUrl = `${urlBase}?lang=${language}&page=${page}&order=new&accept_erotic_content=${contentType === 'nsfw'}${typeParam}`; + + console.log('Fetching comics from:', apiUrl); + const response = await fetchWithRetry(apiUrl); const data = await response.json(); @@ -93,12 +103,17 @@ export function isValidContentType(type: string): boolean { return contentTypes.has(type); } +export function isValidComicType(type: string): boolean { + return comicTypes.has(type); +} + export function getLanguageName(code: string): string { return languageMap[code as keyof typeof languageMap] || code.toUpperCase(); } export const availableLanguages = Array.from(languages); export const availableContentTypes = Array.from(contentTypes); +export const availableComicTypes = Array.from(comicTypes); export { languageMap }; interface ComicDetail { diff --git a/src/lib/types.ts b/src/lib/types.ts index 2efb35d..69b3f03 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -10,6 +10,7 @@ export interface Comic { b2key: string; }[]; content_rating: string; + comic_type: string; }; } @@ -21,4 +22,6 @@ export interface Genre { export interface FeedMapping { slugs: string[]; lang: string; -} \ No newline at end of file +} + +export type ComicType = 'manga' | 'manhwa' | 'manhua' | 'all'; \ No newline at end of file diff --git a/src/pages/[lang]/[contentType]/[type].astro b/src/pages/[lang]/[contentType]/[type].astro index 0519ecb..0eca550 100644 --- a/src/pages/[lang]/[contentType]/[type].astro +++ b/src/pages/[lang]/[contentType]/[type].astro @@ -1 +1,117 @@ - \ No newline at end of file +--- +import Layout from '../../../layouts/Layout.astro'; +import ComicCard from '../../../components/ComicCard.astro'; +import LoadingCard from '../../../components/LoadingCard.astro'; +import RSSLink from '../../../components/RSSLink.astro'; +import TypeFilter from '../../../components/TypeFilter.astro'; +import LanguageSelector from '../../../components/LanguageSelector.astro'; +import { fetchComics, fetchGenres, isValidLanguage, isValidContentType, isValidComicType, getLanguageName } from '../../../lib/api'; +import type { Comic, Genre } from '../../../lib/types'; + +export const prerender = false; + +const { lang, contentType, type } = Astro.params; + +if (!isValidLanguage(lang!) || !isValidContentType(contentType!) || !isValidComicType(type!)) { + return Astro.redirect('/en/sfw/all'); +} + +let comics: Comic[] = []; +let genres: Genre[] = []; +let error: string | null = null; + +try { + [comics, genres] = await Promise.all([ + fetchComics(lang, contentType, type as any), + fetchGenres(), + ]); +} catch (e) { + error = e instanceof Error ? e.message : 'Failed to fetch comics'; +} + +const title = `ComicK - ${getLanguageName(lang!)} ${contentType?.toUpperCase()} ${type?.toUpperCase()}`; +--- + + +
+
+

Latest Updates

+ +
+ + + + {error && ( +
+

Error: {error}

+ +
+ )} + +
+ {comics.length > 0 ? ( + comics.map(comic => ( + + )) + ) : !error ? ( + Array(10).fill(null).map(() => ( + + )) + ) : ( +
+

No comics found

+
+ )} +
+
+
\ No newline at end of file diff --git a/src/pages/[lang]/[type].astro b/src/pages/[lang]/[type].astro index 45b5112..876fd49 100644 --- a/src/pages/[lang]/[type].astro +++ b/src/pages/[lang]/[type].astro @@ -7,6 +7,11 @@ import RSSLink from '../../components/RSSLink.astro'; import { fetchComics, isValidLanguage, isValidContentType, fetchGenres, getLanguageName } from '../../lib/api'; import type { Comic } from '../../lib/types'; +export const prerender = false; + +// Redirect old URLs to new structure +return Astro.redirect(`/${Astro.params.lang}/${Astro.params.type}/all`); + export async function getStaticPaths() { const languages = ['en', 'fr', 'es', 'it', 'pl', 'tr', 'ja', 'zh', 'sv']; const types = ['sfw', 'nsfw']; diff --git a/src/pages/index.astro b/src/pages/index.astro index 02082cd..f6fc030 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -11,5 +11,5 @@ export const prerender = false; - + \ No newline at end of file diff --git a/src/pages/rss/[lang]/[contentType]/[type].xml.ts b/src/pages/rss/[lang]/[contentType]/[type].xml.ts new file mode 100644 index 0000000..c7bfd55 --- /dev/null +++ b/src/pages/rss/[lang]/[contentType]/[type].xml.ts @@ -0,0 +1,53 @@ +import rss from "@astrojs/rss"; +import { + fetchComics, + isValidLanguage, + isValidContentType, + isValidComicType, + fetchGenres, + getGenreNames, +} from "../../../../lib/api"; +import type { APIRoute } from "astro"; +import type { ComicType } from "../../../../lib/types"; + +export const GET: APIRoute = async ({ params, request }) => { + const { lang = "en", contentType = "sfw", type = "all" } = params; + + if (!isValidLanguage(lang) || !isValidContentType(contentType) || !isValidComicType(type)) { + return new Response("Invalid parameters", { status: 400 }); + } + + const [comics, genres] = await Promise.all([ + fetchComics(lang, contentType, type as ComicType), + fetchGenres(), + ]); + + const siteURL = new URL(request.url).origin; + + return rss({ + title: `ComicK - ${lang.toUpperCase()} ${contentType.toUpperCase()} ${type.toUpperCase()}`, + description: `A simple RSS feed for ComicK!`, + site: "https://github.com/ld3z/manga-rss", + items: comics.map((comic) => { + const genreNames = getGenreNames(comic.md_comics.genres, genres); + const genresHtml = genreNames.length > 0 + ? `

Genres: ${genreNames.join(', ')}

` + : ''; + + return { + title: `${comic.md_comics.title} - Chapter ${comic.chap}`, + link: `https://comick.io/comic/${comic.md_comics.slug}`, + pubDate: new Date(comic.updated_at), + description: `Chapter ${comic.chap} of ${comic.md_comics.title} is now available on ComicK! + ${genresHtml} + ${ + comic.md_comics.md_covers[0] + ? `Cover` + : "" + } + `, + }; + }), + }); +}; \ No newline at end of file