changed some files to ts

This commit is contained in:
2025-11-29 17:41:30 +01:00
parent 91526e1755
commit 655ab9c987
13 changed files with 624 additions and 168 deletions

View File

@@ -1,19 +1,25 @@
const animeService = require('./anime.service');
const { getExtension, getExtensionsList } = require('../shared/extensions');
import {FastifyReply, FastifyRequest} from 'fastify';
import * as animeService from './anime.service';
import { getExtension, getExtensionsList } from '../shared/extensions';
import {AnimeRequest, SearchRequest, ExtensionNameRequest, WatchStreamRequest, Anime} from '../types';
async function getAnime(req, reply) {
export async function getAnime(req: AnimeRequest, reply: FastifyReply) {
try {
const { id } = req.params;
const source = req.query.ext || 'anilist';
let anime;
let anime: Anime | { error: string };
if (source === 'anilist') {
anime = await animeService.getAnimeById(id);
} else {
const extensionName = source;
const ext = getExtension(extensionName);
const results = await animeService.searchAnimeInExtension(ext, extensionName, id.replaceAll("-", " "));
const results = await animeService.searchAnimeInExtension(
ext,
extensionName,
id.replaceAll("-", " ")
);
anime = results[0] || null;
}
@@ -23,7 +29,7 @@ async function getAnime(req, reply) {
}
}
async function getTrending(req, reply) {
export async function getTrending(req: FastifyRequest, reply: FastifyReply) {
try {
const results = await animeService.getTrendingAnime();
return { results };
@@ -32,7 +38,7 @@ async function getTrending(req, reply) {
}
}
async function getTopAiring(req, reply) {
export async function getTopAiring(req: FastifyRequest, reply: FastifyReply) {
try {
const results = await animeService.getTopAiringAnime();
return { results };
@@ -41,7 +47,7 @@ async function getTopAiring(req, reply) {
}
}
async function search(req, reply) {
export async function search(req: SearchRequest, reply: FastifyReply) {
try {
const query = req.query.q;
const results = await animeService.searchAnimeLocal(query);
@@ -58,11 +64,11 @@ async function search(req, reply) {
}
}
async function getExtensions(req, reply) {
export async function getExtensions(req: FastifyRequest, reply: FastifyReply) {
return { extensions: getExtensionsList() };
}
async function getExtensionSettings(req, reply) {
export async function getExtensionSettings(req: ExtensionNameRequest, reply: FastifyReply) {
const { name } = req.params;
const ext = getExtension(name);
@@ -77,19 +83,18 @@ async function getExtensionSettings(req, reply) {
return ext.getSettings();
}
async function getWatchStream(req, reply) {
export async function getWatchStream(req: WatchStreamRequest, reply: FastifyReply) {
try {
const { animeId, episode, server, category, ext } = req.query;
const extension = getExtension(ext);
if (!extension) return { error: "Extension not found" };
let anime;
let anime: Anime | { error: string };
if (!isNaN(Number(animeId))) {
anime = await animeService.getAnimeById(animeId);
if (anime.error) return { error: "Anime metadata not found" };
}
else {
if ('error' in anime) return { error: "Anime metadata not found" };
} else {
const results = await animeService.searchAnimeInExtension(
extension,
ext,
@@ -107,16 +112,7 @@ async function getWatchStream(req, reply) {
category
);
} catch (err) {
return { error: err.message };
const error = err as Error;
return { error: error.message };
}
}
module.exports = {
getAnime,
getTrending,
getTopAiring,
search,
getExtensions,
getExtensionSettings,
getWatchStream
};
}

View File

@@ -1,6 +1,7 @@
const controller = require('./anime.controller');
import { FastifyInstance } from 'fastify';
import * as controller from './anime.controller';
async function animeRoutes(fastify, options) {
async function animeRoutes(fastify: FastifyInstance) {
fastify.get('/anime/:id', controller.getAnime);
fastify.get('/trending', controller.getTrending);
fastify.get('/top-airing', controller.getTopAiring);
@@ -10,4 +11,4 @@ async function animeRoutes(fastify, options) {
fastify.get('/watch/stream', controller.getWatchStream);
}
module.exports = animeRoutes;
export default animeRoutes;

View File

@@ -1,7 +1,8 @@
const { queryOne, queryAll } = require('../shared/database');
const {getAllExtensions} = require("../shared/extensions");
import { queryOne, queryAll } from '../shared/database';
import { getAllExtensions } from '../shared/extensions';
import { Anime, Extension, StreamData } from '../types';
async function getAnimeById(id) {
export async function getAnimeById(id: string | number): Promise<Anime | { error: string }> {
const row = await queryOne("SELECT full_data FROM anime WHERE id = ?", [id]);
if (!row) {
@@ -11,17 +12,17 @@ async function getAnimeById(id) {
return JSON.parse(row.full_data);
}
async function getTrendingAnime() {
export async function getTrendingAnime(): Promise<Anime[]> {
const rows = await queryAll("SELECT full_data FROM trending ORDER BY rank ASC LIMIT 10");
return rows.map(r => JSON.parse(r.full_data));
return rows.map((r: { full_data: string; }) => JSON.parse(r.full_data));
}
async function getTopAiringAnime() {
export async function getTopAiringAnime(): Promise<Anime[]> {
const rows = await queryAll("SELECT full_data FROM top_airing ORDER BY rank ASC LIMIT 10");
return rows.map(r => JSON.parse(r.full_data));
return rows.map((r: { full_data: string; }) => JSON.parse(r.full_data));
}
async function searchAnimeLocal(query) {
export async function searchAnimeLocal(query: string): Promise<Anime[]> {
if (!query || query.length < 2) {
return [];
}
@@ -29,7 +30,7 @@ async function searchAnimeLocal(query) {
const sql = `SELECT full_data FROM anime WHERE full_data LIKE ? LIMIT 50`;
const rows = await queryAll(sql, [`%${query}%`]);
const results = rows.map(row => JSON.parse(row.full_data));
const results: Anime[] = rows.map((row: { full_data: string; }) => JSON.parse(row.full_data));
const cleanResults = results.filter(anime => {
const q = query.toLowerCase();
@@ -38,7 +39,7 @@ async function searchAnimeLocal(query) {
anime.title.romaji,
anime.title.native,
...(anime.synonyms || [])
].filter(Boolean).map(t => t.toLowerCase());
].filter(Boolean).map(t => t!.toLowerCase());
return titles.some(t => t.includes(q));
});
@@ -46,7 +47,13 @@ async function searchAnimeLocal(query) {
return cleanResults.slice(0, 10);
}
async function searchAnimeInExtension(ext, name, query) {
export async function searchAnimeInExtension(
ext: Extension | null,
name: string,
query: string
): Promise<Anime[]> {
if (!ext) return [];
if ((ext.type === 'anime-board') && ext.search) {
try {
console.log(`[${name}] Searching for book: ${query}`);
@@ -63,7 +70,7 @@ async function searchAnimeInExtension(ext, name, query) {
return matches.map(m => ({
id: m.id,
extensionName: name,
title: { romaji: m.title, english: m.title },
title: { romaji: m.title, english: m.title, native: null },
coverImage: { large: m.image || '' },
averageScore: m.rating || m.score || null,
format: 'ANIME',
@@ -75,9 +82,11 @@ async function searchAnimeInExtension(ext, name, query) {
console.error(`Extension search failed for ${name}:`, e);
}
}
return [];
}
async function searchAnimeExtensions(query) {
export async function searchAnimeExtensions(query: string): Promise<Anime[]> {
const extensions = getAllExtensions();
for (const [name, ext] of extensions) {
@@ -88,7 +97,13 @@ async function searchAnimeExtensions(query) {
return [];
}
async function getStreamData(extension, animeData, episode, server, category) {
export async function getStreamData(
extension: Extension,
animeData: Anime,
episode: string,
server?: string,
category?: string
): Promise<StreamData> {
const searchOptions = {
query: animeData.title.english || animeData.title.romaji,
dub: category === 'dub',
@@ -99,6 +114,10 @@ async function getStreamData(extension, animeData, episode, server, category) {
}
};
if (!extension.search || !extension.findEpisodes || !extension.findEpisodeServer) {
throw new Error("Extension doesn't support required methods");
}
const searchResults = await extension.search(searchOptions);
if (!searchResults || searchResults.length === 0) {
@@ -117,14 +136,4 @@ async function getStreamData(extension, animeData, episode, server, category) {
const streamData = await extension.findEpisodeServer(targetEp, serverName);
return streamData;
}
module.exports = {
getAnimeById,
getTrendingAnime,
getTopAiringAnime,
searchAnimeLocal,
searchAnimeExtensions,
searchAnimeInExtension,
getStreamData
};
}

View File

@@ -1,14 +1,16 @@
const booksService = require('./books.service');
const {getExtension} = require("../shared/extensions");
import {FastifyReply, FastifyRequest} from 'fastify';
import * as booksService from './books.service';
import { getExtension } from '../shared/extensions';
import { BookRequest, SearchRequest, ChapterRequest } from '../types';
async function getBook(req, reply) {
export async function getBook(req: BookRequest, reply: FastifyReply) {
try {
const { id } = req.params;
const source = req.query.ext || 'anilist';
let book;
if (source === 'anilist') {
book = await booksService.getBookById(id);
book = await booksService.getBookById(id);
} else {
const extensionName = source;
const ext = getExtension(extensionName);
@@ -20,11 +22,12 @@ async function getBook(req, reply) {
return book;
} catch (err) {
return { error: err.toString() };
const error = err as Error;
return { error: error.toString() };
}
}
async function getTrending(req, reply) {
export async function getTrending(req: FastifyRequest, reply: FastifyReply) {
try {
const results = await booksService.getTrendingBooks();
return { results };
@@ -33,7 +36,7 @@ async function getTrending(req, reply) {
}
}
async function getPopular(req, reply) {
export async function getPopular(req: FastifyRequest, reply: FastifyReply) {
try {
const results = await booksService.getPopularBooks();
return { results };
@@ -42,7 +45,7 @@ async function getPopular(req, reply) {
}
}
async function searchBooks(req, reply) {
export async function searchBooks(req: SearchRequest, reply: FastifyReply) {
try {
const query = req.query.q;
@@ -60,13 +63,14 @@ async function searchBooks(req, reply) {
const extResults = await booksService.searchBooksExtensions(query);
return { results: extResults };
} catch(e) {
console.error("Search Error:", e.message);
} catch (e) {
const error = e as Error;
console.error("Search Error:", error.message);
return { results: [] };
}
}
async function getChapters(req, reply) {
export async function getChapters(req: BookRequest, reply: FastifyReply) {
try {
const { id } = req.params;
return await booksService.getChaptersForBook(id);
@@ -75,7 +79,7 @@ async function getChapters(req, reply) {
}
}
async function getChapterContent(req, reply) {
export async function getChapterContent(req: ChapterRequest, reply: FastifyReply) {
try {
const { bookId, chapter, provider } = req.params;
@@ -87,17 +91,9 @@ async function getChapterContent(req, reply) {
return reply.send(content);
} catch (err) {
console.error("getChapterContent error:", err.message);
const error = err as Error;
console.error("getChapterContent error:", error.message);
return reply.code(500).send({ error: "Error loading chapter" });
}
}
module.exports = {
getBook,
getTrending,
getPopular,
searchBooks,
getChapters,
getChapterContent
};
}

View File

@@ -1,6 +1,7 @@
const controller = require('./books.controller');
import { FastifyInstance } from 'fastify';
import * as controller from './books.controller';
async function booksRoutes(fastify, options) {
async function booksRoutes(fastify: FastifyInstance) {
fastify.get('/book/:id', controller.getBook);
fastify.get('/books/trending', controller.getTrending);
fastify.get('/books/popular', controller.getPopular);
@@ -9,4 +10,4 @@ async function booksRoutes(fastify, options) {
fastify.get('/book/:bookId/:chapter/:provider', controller.getChapterContent);
}
module.exports = booksRoutes;
export default booksRoutes;

View File

@@ -1,7 +1,8 @@
const { queryOne, queryAll } = require('../shared/database');
const { getAllExtensions } = require('../shared/extensions');
import { queryOne, queryAll } from '../shared/database';
import { getAllExtensions } from '../shared/extensions';
import { Book, Extension, ChapterWithProvider, ChapterContent } from '../types';
async function getBookById(id) {
export async function getBookById(id: string | number): Promise<Book | { error: string }> {
const row = await queryOne("SELECT full_data FROM books WHERE id = ?", [id]);
if (row) {
@@ -31,31 +32,31 @@ async function getBookById(id) {
const response = await fetch('https://graphql.anilist.co', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify({ query, variables: { id: parseInt(id) } })
body: JSON.stringify({ query, variables: { id: parseInt(id.toString()) } })
});
const data = await response.json();
if (data.data && data.data.Media) {
return data.data.Media;
}
} catch(e) {
} catch (e) {
console.error("Fetch error:", e);
}
return { error: "Book not found" };
}
async function getTrendingBooks() {
export async function getTrendingBooks(): Promise<Book[]> {
const rows = await queryAll("SELECT full_data FROM trending_books ORDER BY rank ASC LIMIT 10");
return rows.map(r => JSON.parse(r.full_data));
return rows.map((r: { full_data: string; }) => JSON.parse(r.full_data));
}
async function getPopularBooks() {
export async function getPopularBooks(): Promise<Book[]> {
const rows = await queryAll("SELECT full_data FROM popular_books ORDER BY rank ASC LIMIT 10");
return rows.map(r => JSON.parse(r.full_data));
return rows.map((r: { full_data: string; }) => JSON.parse(r.full_data));
}
async function searchBooksLocal(query) {
export async function searchBooksLocal(query: string): Promise<Book[]> {
if (!query || query.length < 2) {
return [];
}
@@ -63,7 +64,7 @@ async function searchBooksLocal(query) {
const sql = `SELECT full_data FROM books WHERE full_data LIKE ? LIMIT 50`;
const rows = await queryAll(sql, [`%${query}%`]);
const results = rows.map(row => JSON.parse(row.full_data));
const results: Book[] = rows.map((row: { full_data: string; }) => JSON.parse(row.full_data));
const clean = results.filter(book => {
const searchTerms = [
@@ -71,7 +72,7 @@ async function searchBooksLocal(query) {
book.title.romaji,
book.title.native,
...(book.synonyms || [])
].filter(Boolean).map(t => t.toLowerCase());
].filter(Boolean).map(t => t!.toLowerCase());
return searchTerms.some(term => term.includes(query.toLowerCase()));
});
@@ -79,7 +80,7 @@ async function searchBooksLocal(query) {
return clean.slice(0, 10);
}
async function searchBooksAniList(query) {
export async function searchBooksAniList(query: string): Promise<Book[]> {
const gql = `
query ($search: String) {
Page(page: 1, perPage: 5) {
@@ -107,7 +108,7 @@ async function searchBooksAniList(query) {
return [];
}
async function searchBooksInExtension(ext, name, query) {
export async function searchBooksInExtension(ext: Extension | null, name: string, query: string): Promise<Book[]> {
if (!ext) return [];
if ((ext.type === 'book-board' || ext.type === 'manga-board') && ext.search) {
@@ -126,7 +127,7 @@ async function searchBooksInExtension(ext, name, query) {
return matches.map(m => ({
id: m.id,
extensionName: name,
title: { romaji: m.title, english: m.title },
title: { romaji: m.title, english: m.title, native: null },
coverImage: { large: m.image || '' },
averageScore: m.rating || m.score || null,
format: 'MANGA',
@@ -142,7 +143,7 @@ async function searchBooksInExtension(ext, name, query) {
return [];
}
async function searchBooksExtensions(query) {
export async function searchBooksExtensions(query: string): Promise<Book[]> {
const extensions = getAllExtensions();
for (const [name, ext] of extensions) {
@@ -153,16 +154,17 @@ async function searchBooksExtensions(query) {
return [];
}
async function getChaptersForBook(id) {
let bookData = null;
let searchTitle = null;
export async function getChaptersForBook(id: string): Promise<{ chapters: ChapterWithProvider[] }> {
let bookData: Book | null = null;
let searchTitle: string | null = null;
if (typeof id === "string" && isNaN(Number(id))) {
searchTitle = id.replaceAll("-", " ");
} else {
bookData = await queryOne("SELECT full_data FROM books WHERE id = ?", [id])
.then(row => row ? JSON.parse(row.full_data) : null)
.catch(() => null);
const result = await getBookById(id);
if (!('error' in result)) {
bookData = result;
}
if (!bookData) {
try {
@@ -181,17 +183,16 @@ async function getChaptersForBook(id) {
const d = await res.json();
if (d.data?.Media) bookData = d.data.Media;
} catch (e) {}
} catch (e) { }
}
if (!bookData) return { chapters: [] };
const titles = [bookData.title.english, bookData.title.romaji].filter(Boolean);
const titles = [bookData.title.english, bookData.title.romaji].filter(Boolean) as string[];
searchTitle = titles[0];
}
const allChapters = [];
const allChapters: ChapterWithProvider[] = [];
const extensions = getAllExtensions();
const searchPromises = Array.from(extensions.entries())
@@ -203,25 +204,25 @@ async function getChaptersForBook(id) {
try {
console.log(`[${name}] Searching chapters for: ${searchTitle}`);
const matches = await ext.search({
query: searchTitle,
const matches = await ext.search!({
query: searchTitle!,
media: bookData ? {
romajiTitle: bookData.title.romaji,
englishTitle: bookData.title.english,
startDate: bookData.startDate
} : {}
englishTitle: bookData.title.english || "",
startDate: bookData.startDate || { year: 0, month: 0, day: 0 }
} : { romajiTitle: searchTitle!, englishTitle: searchTitle!, startDate: { year: 0, month: 0, day: 0 } }
});
if (matches?.length) {
const best = matches[0];
const chaps = await ext.findChapters(best.id);
const chaps = await ext.findChapters!(best.id);
if (chaps?.length) {
console.log(`[${name}] Found ${chaps.length} chapters.`);
chaps.forEach(ch => {
chaps.forEach((ch: { id: any; number: { toString: () => string; }; title: any; releaseDate: any; }) => {
allChapters.push({
id: ch.id,
number: parseFloat(ch.number),
number: parseFloat(ch.number.toString()),
title: ch.title,
date: ch.releaseDate,
provider: name
@@ -232,16 +233,17 @@ async function getChaptersForBook(id) {
console.log(`[${name}] No matches found for book.`);
}
} catch (e) {
console.error(`Failed to fetch chapters from ${name}:`, e.message);
const error = e as Error;
console.error(`Failed to fetch chapters from ${name}:`, error.message);
}
});
await Promise.all(searchPromises);
return { chapters: allChapters.sort((a, b) => a.number - b.number) };
return { chapters: allChapters.sort((a, b) => Number(a.number) - Number(b.number)) };
}
async function getChapterContent(bookId, chapterIndex, providerName) {
export async function getChapterContent(bookId: string, chapterIndex: string, providerName: string): Promise<ChapterContent> {
const extensions = getAllExtensions();
const ext = extensions.get(providerName);
@@ -273,6 +275,10 @@ async function getChapterContent(bookId, chapterIndex, providerName) {
const chapterNumber = typeof selectedChapter.number === 'number' ? selectedChapter.number : index;
try {
if (!ext.findChapterPages) {
throw new Error("Extension doesn't support findChapterPages");
}
if (ext.mediaType === "manga") {
const pages = await ext.findChapterPages(chapterId);
return {
@@ -299,19 +305,8 @@ async function getChapterContent(bookId, chapterIndex, providerName) {
throw new Error("Unknown mediaType");
} catch (err) {
console.error(`[Chapter] Error loading from ${providerName}:`, err && err.message ? err.message : err);
const error = err as Error;
console.error(`[Chapter] Error loading from ${providerName}:`, error.message);
throw err;
}
}
module.exports = {
getBookById,
getTrendingBooks,
getPopularBooks,
searchBooksLocal,
searchBooksAniList,
searchBooksExtensions,
searchBooksInExtension,
getChaptersForBook,
getChapterContent
};
}

View File

@@ -1,8 +1,9 @@
const { proxyRequest, processM3U8Content, streamToReadable } = require('./proxy.service');
import { FastifyInstance, FastifyReply } from 'fastify';
import { proxyRequest, processM3U8Content, streamToReadable } from './proxy.service';
import { ProxyRequest } from '../../types';
async function proxyRoutes(fastify, options) {
fastify.get('/proxy', async (req, reply) => {
async function proxyRoutes(fastify: FastifyInstance) {
fastify.get('/proxy', async (req: ProxyRequest, reply: FastifyReply) => {
const { url, referer, origin, userAgent } = req.query;
if (!url) {
@@ -34,7 +35,7 @@ async function proxyRoutes(fastify, options) {
return processedContent;
} else {
return reply.send(streamToReadable(response.body));
return reply.send(streamToReadable(response.body!));
}
} catch (err) {
@@ -44,4 +45,4 @@ async function proxyRoutes(fastify, options) {
});
}
module.exports = proxyRoutes;
export default proxyRoutes;

View File

@@ -1,7 +1,19 @@
const { Readable } = require('stream');
import { Readable } from 'stream';
async function proxyRequest(url, { referer, origin, userAgent }) {
const headers = {
interface ProxyHeaders {
referer?: string;
origin?: string;
userAgent?: string;
}
interface ProxyResponse {
response: Response;
contentType: string | null;
isM3U8: boolean;
}
export async function proxyRequest(url: string, { referer, origin, userAgent }: ProxyHeaders): Promise<ProxyResponse> {
const headers: Record<string, string> = {
'User-Agent': userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9'
@@ -26,14 +38,18 @@ async function proxyRequest(url, { referer, origin, userAgent }) {
};
}
function processM3U8Content(text, baseUrl, { referer, origin, userAgent }) {
export function processM3U8Content(
text: string,
baseUrl: URL,
{ referer, origin, userAgent }: ProxyHeaders
): string {
return text.replace(/^(?!#)(?!\s*$).+/gm, (line) => {
line = line.trim();
let absoluteUrl;
let absoluteUrl: string;
try {
absoluteUrl = new URL(line, baseUrl).href;
} catch(e) {
} catch (e) {
return line;
}
@@ -47,12 +63,6 @@ function processM3U8Content(text, baseUrl, { referer, origin, userAgent }) {
});
}
function streamToReadable(webStream) {
return Readable.fromWeb(webStream);
}
module.exports = {
proxyRequest,
processM3U8Content,
streamToReadable
};
export function streamToReadable(webStream: ReadableStream): Readable {
return Readable.fromWeb(webStream as any);
}

204
src/types.ts Normal file
View File

@@ -0,0 +1,204 @@
import { FastifyRequest, FastifyReply } from 'fastify';
export interface AnimeTitle {
romaji: string;
english: string | null;
native: string | null;
userPreferred?: string;
}
export interface CoverImage {
extraLarge?: string;
large: string;
medium?: string;
color?: string;
}
export interface StartDate {
year: number;
month: number;
day: number;
}
export interface Anime {
id: number | string;
title: AnimeTitle;
coverImage: CoverImage;
bannerImage?: string;
description?: string;
averageScore: number | null;
format: string;
seasonYear: number | null;
startDate?: StartDate;
synonyms?: string[];
extensionName?: string;
isExtensionResult?: boolean;
}
export interface Book {
id: number | string;
title: AnimeTitle;
coverImage: CoverImage;
bannerImage?: string;
description?: string;
averageScore: number | null;
format: string;
seasonYear: number | null;
startDate?: StartDate;
synonyms?: string[];
extensionName?: string;
isExtensionResult?: boolean;
}
export interface ExtensionSearchOptions {
query: string;
dub?: boolean;
media?: {
romajiTitle: string;
englishTitle: string;
startDate: StartDate;
};
}
export interface ExtensionSearchResult {
id: string;
title: string;
image?: string;
rating?: number;
score?: number;
}
export interface Episode {
id: string;
number: number;
title?: string;
}
export interface Chapter {
id: string;
number: string | number;
title?: string;
releaseDate?: string;
}
export interface ChapterWithProvider extends Chapter {
provider: string;
date?: string;
}
export interface Extension {
type: 'anime-board' | 'book-board' | 'manga-board';
mediaType?: 'manga' | 'ln';
search?: (options: ExtensionSearchOptions) => Promise<ExtensionSearchResult[]>;
findEpisodes?: (id: string) => Promise<Episode[]>;
findEpisodeServer?: (episode: Episode, server: string) => Promise<any>;
findChapters?: (id: string) => Promise<Chapter[]>;
findChapterPages?: (chapterId: string) => Promise<any>;
getSettings?: () => ExtensionSettings;
}
export interface ExtensionSettings {
episodeServers: string[];
supportsDub: boolean;
}
export interface StreamData {
url?: string;
sources?: any[];
subtitles?: any[];
}
export interface MangaChapterContent {
type: 'manga';
chapterId: string;
title: string | null;
number: number;
provider: string;
pages: any[];
}
export interface LightNovelChapterContent {
type: 'ln';
chapterId: string;
title: string | null;
number: number;
provider: string;
content: any;
}
export type ChapterContent = MangaChapterContent | LightNovelChapterContent;
export interface AnimeParams {
id: string;
}
export interface AnimeQuery {
ext?: string;
}
export interface SearchQuery {
q: string;
}
export interface ExtensionNameParams {
name: string;
}
export interface WatchStreamQuery {
animeId: string;
episode: string;
server?: string;
category?: string;
ext: string;
}
export interface BookParams {
id: string;
}
export interface BookQuery {
ext?: string;
}
export interface ChapterParams {
bookId: string;
chapter: string;
provider: string;
}
export interface ProxyQuery {
url: string;
referer?: string;
origin?: string;
userAgent?: string;
}
export type AnimeRequest = FastifyRequest<{
Params: AnimeParams;
Querystring: AnimeQuery;
}>;
export type SearchRequest = FastifyRequest<{
Querystring: SearchQuery;
}>;
export type ExtensionNameRequest = FastifyRequest<{
Params: ExtensionNameParams;
}>;
export type WatchStreamRequest = FastifyRequest<{
Querystring: WatchStreamQuery;
}>;
export type BookRequest = FastifyRequest<{
Params: BookParams;
Querystring: BookQuery;
}>;
export type ChapterRequest = FastifyRequest<{
Params: ChapterParams;
}>;
export type ProxyRequest = FastifyRequest<{
Querystring: ProxyQuery;
}>;

View File

@@ -1,47 +1,48 @@
const fs = require('fs');
const path = require('path');
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import * as fs from 'fs';
import * as path from 'path';
async function viewsRoutes(fastify, options) {
async function viewsRoutes(fastify: FastifyInstance) {
fastify.get('/', (req, reply) => {
fastify.get('/', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'index.html'));
reply.type('text/html').send(stream);
});
fastify.get('/books', (req, reply) => {
fastify.get('/books', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'books.html'));
reply.type('text/html').send(stream);
});
fastify.get('/anime/:id', (req, reply) => {
fastify.get('/anime/:id', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'anime.html'));
reply.type('text/html').send(stream);
});
fastify.get('/anime/:extension/*', (req, reply) => {
fastify.get('/anime/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'anime.html'));
reply.type('text/html').send(stream);
});
fastify.get('/watch/:id/:episode', (req, reply) => {
fastify.get('/watch/:id/:episode', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'watch.html'));
reply.type('text/html').send(stream);
});
fastify.get('/book/:id', (req, reply) => {
fastify.get('/book/:id', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'book.html'));
reply.type('text/html').send(stream);
});
fastify.get('/book/:extension/*', (req, reply) => {
fastify.get('/book/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'book.html'));
reply.type('text/html').send(stream);
});
fastify.get('/read/:provider/:chapter/*', (req, reply) => {
fastify.get('/read/:provider/:chapter/*', (req: FastifyRequest, reply: FastifyReply) => {
const stream = fs.createReadStream(path.join(__dirname, '..', '..', 'views', 'read.html'));
reply.type('text/html').send(stream);
});
}
module.exports = viewsRoutes;
export default viewsRoutes;