extension allows filtering for search
This commit is contained in:
@@ -10,7 +10,6 @@ import net from 'net';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
export async function getAnime(req: AnimeRequest, reply: FastifyReply) {
|
export async function getAnime(req: AnimeRequest, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
@@ -82,12 +81,20 @@ export async function search(req: SearchRequest, reply: FastifyReply) {
|
|||||||
export async function searchInExtension(req: any, reply: FastifyReply) {
|
export async function searchInExtension(req: any, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const extensionName = req.params.extension;
|
const extensionName = req.params.extension;
|
||||||
const query = req.query.q;
|
const { q, ...rest } = req.query;
|
||||||
|
|
||||||
const ext = getExtension(extensionName);
|
const ext = getExtension(extensionName);
|
||||||
if (!ext) return { results: [] };
|
if (!ext) return { results: [] };
|
||||||
|
|
||||||
const results = await animeService.searchAnimeInExtension(ext, extensionName, query);
|
const results = await animeService.searchAnimeInExtension(
|
||||||
|
ext,
|
||||||
|
extensionName,
|
||||||
|
{
|
||||||
|
query: q || '',
|
||||||
|
filters: Object.keys(rest).length ? rest : undefined
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { results };
|
return { results };
|
||||||
} catch {
|
} catch {
|
||||||
return { results: [] };
|
return { results: [] };
|
||||||
@@ -340,3 +347,27 @@ export async function openInMPV(req: any, reply: any) {
|
|||||||
return { error: (e as Error).message };
|
return { error: (e as Error).message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function searchAdvanced(req: FastifyRequest, reply: FastifyReply) {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
q,
|
||||||
|
type = 'ANIME',
|
||||||
|
year,
|
||||||
|
season,
|
||||||
|
status,
|
||||||
|
format,
|
||||||
|
genre,
|
||||||
|
minScore,
|
||||||
|
sort
|
||||||
|
} = req.query as any;
|
||||||
|
|
||||||
|
const results = await animeService.searchMediaAdvanced(q, type, {
|
||||||
|
year, season, status, format, genre, minScore, sort
|
||||||
|
});
|
||||||
|
|
||||||
|
return { results };
|
||||||
|
} catch (e) {
|
||||||
|
return { results: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ async function animeRoutes(fastify: FastifyInstance) {
|
|||||||
fastify.get('/trending', controller.getTrending);
|
fastify.get('/trending', controller.getTrending);
|
||||||
fastify.get('/top-airing', controller.getTopAiring);
|
fastify.get('/top-airing', controller.getTopAiring);
|
||||||
fastify.get('/search', controller.search);
|
fastify.get('/search', controller.search);
|
||||||
|
fastify.get('/search/advanced', controller.searchAdvanced);
|
||||||
fastify.get('/search/:extension', controller.searchInExtension);
|
fastify.get('/search/:extension', controller.searchInExtension);
|
||||||
fastify.get('/watch/stream', controller.getWatchStream);
|
fastify.get('/watch/stream', controller.getWatchStream);
|
||||||
fastify.post('/watch/mpv', controller.openInMPV);
|
fastify.post('/watch/mpv', controller.openInMPV);
|
||||||
|
|||||||
@@ -272,6 +272,70 @@ export async function searchAnimeLocal(query: string): Promise<Anime[]> {
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AdvancedFilters = {
|
||||||
|
year?: string;
|
||||||
|
season?: string;
|
||||||
|
status?: string;
|
||||||
|
format?: string;
|
||||||
|
genre?: string;
|
||||||
|
minScore?: string;
|
||||||
|
sort?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function searchMediaAdvanced(
|
||||||
|
query: string,
|
||||||
|
type: 'ANIME' | 'MANGA',
|
||||||
|
filters: AdvancedFilters
|
||||||
|
): Promise<Anime[]> {
|
||||||
|
|
||||||
|
const gql = `
|
||||||
|
query (
|
||||||
|
$type: MediaType
|
||||||
|
$search: String
|
||||||
|
$seasonYear: Int
|
||||||
|
$season: MediaSeason
|
||||||
|
$status: MediaStatus
|
||||||
|
$format: MediaFormat
|
||||||
|
$genres: [String]
|
||||||
|
$minScore: Int
|
||||||
|
$sort: [MediaSort]
|
||||||
|
) {
|
||||||
|
Page(page: 1, perPage: 20) {
|
||||||
|
media(
|
||||||
|
type: $type
|
||||||
|
search: $search
|
||||||
|
seasonYear: $seasonYear
|
||||||
|
season: $season
|
||||||
|
status: $status
|
||||||
|
format: $format
|
||||||
|
genre_in: $genres
|
||||||
|
averageScore_greater: $minScore
|
||||||
|
sort: $sort
|
||||||
|
isAdult: false
|
||||||
|
) {
|
||||||
|
${MEDIA_FIELDS}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const vars = {
|
||||||
|
type,
|
||||||
|
search: query || undefined,
|
||||||
|
seasonYear: filters.year ? Number(filters.year) : undefined,
|
||||||
|
season: filters.season || undefined,
|
||||||
|
status: filters.status || undefined,
|
||||||
|
format: filters.format || undefined,
|
||||||
|
genres: filters.genre ? [filters.genre] : undefined,
|
||||||
|
minScore: filters.minScore ? Number(filters.minScore) : undefined,
|
||||||
|
sort: filters.sort ? [filters.sort] : ['POPULARITY_DESC']
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await fetchAniList(gql, vars);
|
||||||
|
return data?.Page?.media || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getAnimeInfoExtension(ext: Extension | null, id: string): Promise<Anime | { error: string }> {
|
export async function getAnimeInfoExtension(ext: Extension | null, id: string): Promise<Anime | { error: string }> {
|
||||||
if (!ext) return { error: "not found" };
|
if (!ext) return { error: "not found" };
|
||||||
|
|
||||||
@@ -316,22 +380,31 @@ export async function getAnimeInfoExtension(ext: Extension | null, id: string):
|
|||||||
return { error: "not found" };
|
return { error: "not found" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchAnimeInExtension(ext: Extension | null, name: string, query: string) {
|
export async function searchAnimeInExtension(
|
||||||
|
ext: Extension | null,
|
||||||
|
name: string,
|
||||||
|
searchObj: { query: string; filters?: any }
|
||||||
|
) {
|
||||||
if (!ext) return [];
|
if (!ext) return [];
|
||||||
|
|
||||||
if (ext.type === 'anime-board' && ext.search) {
|
if (ext.type === 'anime-board' && ext.search) {
|
||||||
try {
|
try {
|
||||||
console.log(`[${name}] Searching for anime: ${query}`);
|
const payload: any = {
|
||||||
const matches = await ext.search({
|
query: searchObj.query,
|
||||||
query: query,
|
|
||||||
media: {
|
media: {
|
||||||
romajiTitle: query,
|
romajiTitle: searchObj.query,
|
||||||
englishTitle: query,
|
englishTitle: searchObj.query,
|
||||||
startDate: { year: 0, month: 0, day: 0 }
|
startDate: { year: 0, month: 0, day: 0 }
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
if (matches && matches.length > 0) {
|
if (searchObj.filters) {
|
||||||
|
payload.filters = searchObj.filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matches = await ext.search(payload);
|
||||||
|
|
||||||
|
if (matches?.length) {
|
||||||
return matches.map(m => ({
|
return matches.map(m => ({
|
||||||
id: m.id,
|
id: m.id,
|
||||||
extensionName: name,
|
extensionName: name,
|
||||||
|
|||||||
@@ -69,16 +69,24 @@ export async function searchBooks(req: SearchRequest, reply: FastifyReply) {
|
|||||||
export async function searchBooksInExtension(req: any, reply: FastifyReply) {
|
export async function searchBooksInExtension(req: any, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const extensionName = req.params.extension;
|
const extensionName = req.params.extension;
|
||||||
const query = req.query.q;
|
|
||||||
|
const { q, ...rawFilters } = req.query;
|
||||||
|
|
||||||
const ext = getExtension(extensionName);
|
const ext = getExtension(extensionName);
|
||||||
if (!ext) return { results: [] };
|
if (!ext) return { results: [] };
|
||||||
|
|
||||||
const results = await booksService.searchBooksInExtension(ext, extensionName, query);
|
const results = await booksService.searchBooksInExtension(
|
||||||
|
ext,
|
||||||
|
extensionName,
|
||||||
|
{
|
||||||
|
query: q || '',
|
||||||
|
filters: rawFilters
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { results };
|
return { results };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = e as Error;
|
console.error("Search Error:", (e as Error).message);
|
||||||
console.error("Search Error:", error.message);
|
|
||||||
return { results: [] };
|
return { results: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,16 +300,18 @@ export async function getBookInfoExtension(ext: Extension | null, id: string): P
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchBooksInExtension(ext: Extension | null, name: string, query: string): Promise<Book[]> {
|
export async function searchBooksInExtension(ext: Extension | null, name: string, searchObj: { query: string; filters?: any }): Promise<Book[]> {
|
||||||
if (!ext) return [];
|
if (!ext) return [];
|
||||||
|
|
||||||
if ((ext.type === 'book-board') && ext.search) {
|
if (ext.type === 'book-board' && ext.search) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`[${name}] Searching for book: ${query}`);
|
const { query, filters } = searchObj;
|
||||||
|
|
||||||
|
console.log(`[${name}] Searching for book: ${query}`, filters);
|
||||||
|
|
||||||
const matches = await ext.search({
|
const matches = await ext.search({
|
||||||
query: query,
|
query,
|
||||||
|
filters,
|
||||||
media: {
|
media: {
|
||||||
romajiTitle: query,
|
romajiTitle: query,
|
||||||
englishTitle: query,
|
englishTitle: query,
|
||||||
@@ -330,7 +332,6 @@ export async function searchBooksInExtension(ext: Extension | null, name: string
|
|||||||
url: m.url,
|
url: m.url,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Extension search failed for ${name}:`, e);
|
console.error(`Extension search failed for ${name}:`, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,3 +153,24 @@ export async function uninstallExtension(req: any, reply: FastifyReply) {
|
|||||||
return reply.code(500).send({ success: false, error: `Failed to uninstall extension ${fileName}.` });
|
return reply.code(500).send({ success: false, error: `Failed to uninstall extension ${fileName}.` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getExtensionFilters(req: any, reply: FastifyReply) {
|
||||||
|
try {
|
||||||
|
const extensionName = req.params.extension;
|
||||||
|
const ext = getExtension(extensionName);
|
||||||
|
|
||||||
|
if (!ext) {
|
||||||
|
return { filters: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof ext.getFilters === 'function') {
|
||||||
|
const filters = ext.getFilters();
|
||||||
|
return { filters: filters || {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { filters: {} };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('getExtensionFilters error:', e);
|
||||||
|
return { filters: {} };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ async function extensionsRoutes(fastify: FastifyInstance) {
|
|||||||
fastify.get('/extensions/:name/settings', controller.getExtensionSettings);
|
fastify.get('/extensions/:name/settings', controller.getExtensionSettings);
|
||||||
fastify.post('/extensions/install', controller.installExtension);
|
fastify.post('/extensions/install', controller.installExtension);
|
||||||
fastify.post('/extensions/uninstall', controller.uninstallExtension);
|
fastify.post('/extensions/uninstall', controller.uninstallExtension);
|
||||||
|
fastify.get('/extensions/:extension/filters', controller.getExtensionFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default extensionsRoutes;
|
export default extensionsRoutes;
|
||||||
@@ -97,7 +97,7 @@ export interface Extension {
|
|||||||
getMetadata: any;
|
getMetadata: any;
|
||||||
type: 'anime-board' | 'book-board' | 'manga-board';
|
type: 'anime-board' | 'book-board' | 'manga-board';
|
||||||
mediaType?: 'manga' | 'ln';
|
mediaType?: 'manga' | 'ln';
|
||||||
search?: (options: ExtensionSearchOptions) => Promise<ExtensionSearchResult[]>;
|
search?: (options: any) => Promise<ExtensionSearchResult[]>;
|
||||||
findEpisodes?: (id: string) => Promise<Episode[]>;
|
findEpisodes?: (id: string) => Promise<Episode[]>;
|
||||||
findEpisodeServer?: (s: any, server1: string | undefined, category: string | undefined) => Promise<any>;
|
findEpisodeServer?: (s: any, server1: string | undefined, category: string | undefined) => Promise<any>;
|
||||||
findChapters?: (id: string) => Promise<Chapter[]>;
|
findChapters?: (id: string) => Promise<Chapter[]>;
|
||||||
|
|||||||
@@ -56,6 +56,30 @@ export async function getTopAiring(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function searchAdvanced(req: FastifyRequest, reply: FastifyReply) {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
q,
|
||||||
|
type = 'ANIME',
|
||||||
|
year,
|
||||||
|
season,
|
||||||
|
status,
|
||||||
|
format,
|
||||||
|
genre,
|
||||||
|
minScore,
|
||||||
|
sort
|
||||||
|
} = req.query as any;
|
||||||
|
|
||||||
|
const results = await animeService.searchMediaAdvanced(q, type, {
|
||||||
|
year, season, status, format, genre, minScore, sort
|
||||||
|
});
|
||||||
|
|
||||||
|
return { results };
|
||||||
|
} catch (e) {
|
||||||
|
return { results: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function search(req: SearchRequest, reply: FastifyReply) {
|
export async function search(req: SearchRequest, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const query = req.query.q;
|
const query = req.query.q;
|
||||||
@@ -73,12 +97,20 @@ export async function search(req: SearchRequest, reply: FastifyReply) {
|
|||||||
export async function searchInExtension(req: any, reply: FastifyReply) {
|
export async function searchInExtension(req: any, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const extensionName = req.params.extension;
|
const extensionName = req.params.extension;
|
||||||
const query = req.query.q;
|
const { q, ...rest } = req.query;
|
||||||
|
|
||||||
const ext = getExtension(extensionName);
|
const ext = getExtension(extensionName);
|
||||||
if (!ext) return { results: [] };
|
if (!ext) return { results: [] };
|
||||||
|
|
||||||
const results = await animeService.searchAnimeInExtension(ext, extensionName, query);
|
const results = await animeService.searchAnimeInExtension(
|
||||||
|
ext,
|
||||||
|
extensionName,
|
||||||
|
{
|
||||||
|
query: q || '',
|
||||||
|
filters: Object.keys(rest).length ? rest : undefined
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { results };
|
return { results };
|
||||||
} catch {
|
} catch {
|
||||||
return { results: [] };
|
return { results: [] };
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ async function animeRoutes(fastify: FastifyInstance) {
|
|||||||
fastify.get('/trending', controller.getTrending);
|
fastify.get('/trending', controller.getTrending);
|
||||||
fastify.get('/top-airing', controller.getTopAiring);
|
fastify.get('/top-airing', controller.getTopAiring);
|
||||||
fastify.get('/search', controller.search);
|
fastify.get('/search', controller.search);
|
||||||
|
fastify.get('/search/advanced', controller.searchAdvanced);
|
||||||
fastify.get('/search/:extension', controller.searchInExtension);
|
fastify.get('/search/:extension', controller.searchInExtension);
|
||||||
fastify.get('/watch/stream', controller.getWatchStream);
|
fastify.get('/watch/stream', controller.getWatchStream);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,6 +272,70 @@ export async function searchAnimeLocal(query: string): Promise<Anime[]> {
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AdvancedFilters = {
|
||||||
|
year?: string;
|
||||||
|
season?: string;
|
||||||
|
status?: string;
|
||||||
|
format?: string;
|
||||||
|
genre?: string;
|
||||||
|
minScore?: string;
|
||||||
|
sort?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function searchMediaAdvanced(
|
||||||
|
query: string,
|
||||||
|
type: 'ANIME' | 'MANGA',
|
||||||
|
filters: AdvancedFilters
|
||||||
|
): Promise<Anime[]> {
|
||||||
|
|
||||||
|
const gql = `
|
||||||
|
query (
|
||||||
|
$type: MediaType
|
||||||
|
$search: String
|
||||||
|
$seasonYear: Int
|
||||||
|
$season: MediaSeason
|
||||||
|
$status: MediaStatus
|
||||||
|
$format: MediaFormat
|
||||||
|
$genres: [String]
|
||||||
|
$minScore: Int
|
||||||
|
$sort: [MediaSort]
|
||||||
|
) {
|
||||||
|
Page(page: 1, perPage: 20) {
|
||||||
|
media(
|
||||||
|
type: $type
|
||||||
|
search: $search
|
||||||
|
seasonYear: $seasonYear
|
||||||
|
season: $season
|
||||||
|
status: $status
|
||||||
|
format: $format
|
||||||
|
genre_in: $genres
|
||||||
|
averageScore_greater: $minScore
|
||||||
|
sort: $sort
|
||||||
|
isAdult: false
|
||||||
|
) {
|
||||||
|
${MEDIA_FIELDS}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const vars = {
|
||||||
|
type,
|
||||||
|
search: query || undefined,
|
||||||
|
seasonYear: filters.year ? Number(filters.year) : undefined,
|
||||||
|
season: filters.season || undefined,
|
||||||
|
status: filters.status || undefined,
|
||||||
|
format: filters.format || undefined,
|
||||||
|
genres: filters.genre ? [filters.genre] : undefined,
|
||||||
|
minScore: filters.minScore ? Number(filters.minScore) : undefined,
|
||||||
|
sort: filters.sort ? [filters.sort] : ['POPULARITY_DESC']
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await fetchAniList(gql, vars);
|
||||||
|
return data?.Page?.media || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getAnimeInfoExtension(ext: Extension | null, id: string): Promise<Anime | { error: string }> {
|
export async function getAnimeInfoExtension(ext: Extension | null, id: string): Promise<Anime | { error: string }> {
|
||||||
if (!ext) return { error: "not found" };
|
if (!ext) return { error: "not found" };
|
||||||
|
|
||||||
@@ -316,22 +380,31 @@ export async function getAnimeInfoExtension(ext: Extension | null, id: string):
|
|||||||
return { error: "not found" };
|
return { error: "not found" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchAnimeInExtension(ext: Extension | null, name: string, query: string) {
|
export async function searchAnimeInExtension(
|
||||||
|
ext: Extension | null,
|
||||||
|
name: string,
|
||||||
|
searchObj: { query: string; filters?: any }
|
||||||
|
) {
|
||||||
if (!ext) return [];
|
if (!ext) return [];
|
||||||
|
|
||||||
if (ext.type === 'anime-board' && ext.search) {
|
if (ext.type === 'anime-board' && ext.search) {
|
||||||
try {
|
try {
|
||||||
console.log(`[${name}] Searching for anime: ${query}`);
|
const payload: any = {
|
||||||
const matches = await ext.search({
|
query: searchObj.query,
|
||||||
query: query,
|
|
||||||
media: {
|
media: {
|
||||||
romajiTitle: query,
|
romajiTitle: searchObj.query,
|
||||||
englishTitle: query,
|
englishTitle: searchObj.query,
|
||||||
startDate: { year: 0, month: 0, day: 0 }
|
startDate: { year: 0, month: 0, day: 0 }
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
if (matches && matches.length > 0) {
|
if (searchObj.filters) {
|
||||||
|
payload.filters = searchObj.filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matches = await ext.search(payload);
|
||||||
|
|
||||||
|
if (matches?.length) {
|
||||||
return matches.map(m => ({
|
return matches.map(m => ({
|
||||||
id: m.id,
|
id: m.id,
|
||||||
extensionName: name,
|
extensionName: name,
|
||||||
|
|||||||
@@ -69,16 +69,24 @@ export async function searchBooks(req: SearchRequest, reply: FastifyReply) {
|
|||||||
export async function searchBooksInExtension(req: any, reply: FastifyReply) {
|
export async function searchBooksInExtension(req: any, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const extensionName = req.params.extension;
|
const extensionName = req.params.extension;
|
||||||
const query = req.query.q;
|
|
||||||
|
const { q, ...rawFilters } = req.query;
|
||||||
|
|
||||||
const ext = getExtension(extensionName);
|
const ext = getExtension(extensionName);
|
||||||
if (!ext) return { results: [] };
|
if (!ext) return { results: [] };
|
||||||
|
|
||||||
const results = await booksService.searchBooksInExtension(ext, extensionName, query);
|
const results = await booksService.searchBooksInExtension(
|
||||||
|
ext,
|
||||||
|
extensionName,
|
||||||
|
{
|
||||||
|
query: q || '',
|
||||||
|
filters: rawFilters
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { results };
|
return { results };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = e as Error;
|
console.error("Search Error:", (e as Error).message);
|
||||||
console.error("Search Error:", error.message);
|
|
||||||
return { results: [] };
|
return { results: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,16 +300,18 @@ export async function getBookInfoExtension(ext: Extension | null, id: string): P
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchBooksInExtension(ext: Extension | null, name: string, query: string): Promise<Book[]> {
|
export async function searchBooksInExtension(ext: Extension | null, name: string, searchObj: { query: string; filters?: any }): Promise<Book[]> {
|
||||||
if (!ext) return [];
|
if (!ext) return [];
|
||||||
|
|
||||||
if ((ext.type === 'book-board') && ext.search) {
|
if (ext.type === 'book-board' && ext.search) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`[${name}] Searching for book: ${query}`);
|
const { query, filters } = searchObj;
|
||||||
|
|
||||||
|
console.log(`[${name}] Searching for book: ${query}`, filters);
|
||||||
|
|
||||||
const matches = await ext.search({
|
const matches = await ext.search({
|
||||||
query: query,
|
query,
|
||||||
|
filters,
|
||||||
media: {
|
media: {
|
||||||
romajiTitle: query,
|
romajiTitle: query,
|
||||||
englishTitle: query,
|
englishTitle: query,
|
||||||
@@ -330,7 +332,6 @@ export async function searchBooksInExtension(ext: Extension | null, name: string
|
|||||||
url: m.url,
|
url: m.url,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Extension search failed for ${name}:`, e);
|
console.error(`Extension search failed for ${name}:`, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,3 +153,24 @@ export async function uninstallExtension(req: any, reply: FastifyReply) {
|
|||||||
return reply.code(500).send({ success: false, error: `Failed to uninstall extension ${fileName}.` });
|
return reply.code(500).send({ success: false, error: `Failed to uninstall extension ${fileName}.` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getExtensionFilters(req: any, reply: FastifyReply) {
|
||||||
|
try {
|
||||||
|
const extensionName = req.params.extension;
|
||||||
|
const ext = getExtension(extensionName);
|
||||||
|
|
||||||
|
if (!ext) {
|
||||||
|
return { filters: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof ext.getFilters === 'function') {
|
||||||
|
const filters = ext.getFilters();
|
||||||
|
return { filters: filters || {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { filters: {} };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('getExtensionFilters error:', e);
|
||||||
|
return { filters: {} };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ async function extensionsRoutes(fastify: FastifyInstance) {
|
|||||||
fastify.get('/extensions/:name/settings', controller.getExtensionSettings);
|
fastify.get('/extensions/:name/settings', controller.getExtensionSettings);
|
||||||
fastify.post('/extensions/install', controller.installExtension);
|
fastify.post('/extensions/install', controller.installExtension);
|
||||||
fastify.post('/extensions/uninstall', controller.uninstallExtension);
|
fastify.post('/extensions/uninstall', controller.uninstallExtension);
|
||||||
|
fastify.get('/extensions/:extension/filters', controller.getExtensionFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default extensionsRoutes;
|
export default extensionsRoutes;
|
||||||
@@ -97,7 +97,7 @@ export interface Extension {
|
|||||||
getMetadata: any;
|
getMetadata: any;
|
||||||
type: 'anime-board' | 'book-board' | 'manga-board';
|
type: 'anime-board' | 'book-board' | 'manga-board';
|
||||||
mediaType?: 'manga' | 'ln';
|
mediaType?: 'manga' | 'ln';
|
||||||
search?: (options: ExtensionSearchOptions) => Promise<ExtensionSearchResult[]>;
|
search?: (options: any) => Promise<ExtensionSearchResult[]>;
|
||||||
findEpisodes?: (id: string) => Promise<Episode[]>;
|
findEpisodes?: (id: string) => Promise<Episode[]>;
|
||||||
findEpisodeServer?: (s: any, server1: string | undefined, category: string | undefined) => Promise<any>;
|
findEpisodeServer?: (s: any, server1: string | undefined, category: string | undefined) => Promise<any>;
|
||||||
findChapters?: (id: string) => Promise<Chapter[]>;
|
findChapters?: (id: string) => Promise<Chapter[]>;
|
||||||
|
|||||||
Reference in New Issue
Block a user