From 80aaad27d4271f7d47cbf02615e77fd33bd342cb Mon Sep 17 00:00:00 2001 From: Viren070 Date: Wed, 28 May 2025 18:51:33 +0100 Subject: [PATCH] fix some stuff --- packages/core/src/db/schemas.ts | 2 +- packages/core/src/main.ts | 86 +++++++++++++++---- packages/core/src/parser/streams.ts | 7 ++ .../frontend/src/components/menu/services.tsx | 1 + .../server/src/routes/stremio/manifest.ts | 23 ++++- packages/server/src/routes/stremio/stream.ts | 2 + 6 files changed, 101 insertions(+), 20 deletions(-) diff --git a/packages/core/src/db/schemas.ts b/packages/core/src/db/schemas.ts index 1130cc40..32a5dead 100644 --- a/packages/core/src/db/schemas.ts +++ b/packages/core/src/db/schemas.ts @@ -411,7 +411,7 @@ const MetaPreviewSchema = z.object({ id: z.string().min(1), type: z.string().min(1), name: z.string().min(1), - poster: z.string(), + poster: z.string().optional(), posterShape: z.enum(['square', 'poster', 'landscape']).optional(), // discover sidebar //@deprecated use links instead diff --git a/packages/core/src/main.ts b/packages/core/src/main.ts index 20ab52ae..50e3d7d4 100644 --- a/packages/core/src/main.ts +++ b/packages/core/src/main.ts @@ -57,7 +57,7 @@ export class AIOStreams { streams: ParsedStream[]; errors: { addon: Addon; error: string }[]; }> { - logger.info(`getStreams: ${id}`); + logger.info(`Handling stream request`, { type, id }); // step 1 // get the public IP of the requesting user, using the proxy server if configured @@ -73,6 +73,10 @@ export class AIOStreams { const { streams, errors } = await this.getStreamsFromAddons(type, id); + logger.info( + `Received ${streams.length} streams and ${errors.length} errors` + ); + // step 3 // apply all filters to the streams. @@ -104,6 +108,9 @@ export class AIOStreams { // step 9 // return the final list of streams, followed by the error streams. + logger.info( + `Returning ${proxifiedStreams.length} streams and ${errors.length} errors` + ); return { streams: proxifiedStreams, errors: errors, @@ -117,7 +124,7 @@ export class AIOStreams { streams: ParsedStream[]; errors: { addon: Addon; error: string }[]; }): Promise { - const transformedStreams: Stream[] = []; + let transformedStreams: Stream[] = []; // need to generate a name, description, and other stremio-specific fields // use the configured formatter to generate the name and description. let formatter; @@ -131,7 +138,11 @@ export class AIOStreams { formatter = createFormatter(this.userData.formatter.id); } - await Promise.all( + logger.info( + `Transforming ${streams.length} streams, using formatter ${this.userData.formatter.id}` + ); + + transformedStreams = await Promise.all( streams.map(async (stream: ParsedStream): Promise => { const { name, description } = formatter.format(stream); const bingeGroup = `${stream.proxied ? 'proxied.' : ''}${stream.parsedFile.resolution}|${stream.parsedFile.quality}|${stream.parsedFile.encode}`; @@ -214,25 +225,44 @@ export class AIOStreams { } public async getMeta(type: string, id: string) { - logger.info(`getMeta: ${id}`); + logger.info(`Handling meta request`, { type, id }); // step 1 - // determine what addon has a meta resource with an id prefix that matches this id - + // First try to find an addon that has a matching idPrefix for (const [index, resources] of Object.entries(this.supportedResources)) { - const resource = resources.find((r) => - r.name === 'meta' && r.types.includes(type) && r.idPrefixes - ? r.idPrefixes.some((prefix) => id.startsWith(prefix)) - : true + const resource = resources.find( + (r) => + r.name === 'meta' && + r.types.includes(type) && + r.idPrefixes?.some((prefix) => id.startsWith(prefix)) ); if (resource) { const addon = this.getAddon(Number(index)); - logger.info(`Found addon that supports the requested meta resource`, { + logger.info(`Found addon with matching id prefix for meta resource`, { addonName: addon.name, addonIndex: index, }); return new Wrapper(addon).getMeta(type, id); } } + + // step 2 + // If no matching prefix found, use any addon that supports meta for this type + for (const [index, resources] of Object.entries(this.supportedResources)) { + const resource = resources.find( + (r) => r.name === 'meta' && r.types.includes(type) + ); + if (resource) { + const addon = this.getAddon(Number(index)); + logger.info(`Using fallback addon for meta resource`, { + addonName: addon.name, + addonIndex: index, + }); + return new Wrapper(addon).getMeta(type, id); + } + } + + logger.error(`No addon found supporting meta resource for type ${type}`); + throw new Error(`No addon found supporting meta resource for type ${type}`); } // subtitle resource @@ -552,21 +582,47 @@ export class AIOStreams { } } + logger.info( + `Found ${supportedAddons.length} addons that support the stream resource`, + { + supportedAddons: supportedAddons.map((a) => a.name), + } + ); + // fetch all streams in parallel, maintaining a list of errors too, let errors: { addon: Addon; error: string }[] = []; let parsedStreams: ParsedStream[] = []; await Promise.all( supportedAddons.map(async (addon) => { + let summaryMsg = ''; + try { const streams = await new Wrapper(addon).getStreams(type, id); parsedStreams.push(...streams); + + summaryMsg = ` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + 🟢 [${addon.name}] Scrape Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✔ Status : SUCCESS + 📦 Streams : ${streams.length} + 📋 Details : Successfully fetched streams. +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`; return streams; } catch (error) { - errors.push({ - addon: addon, - error: error instanceof Error ? error.message : String(error), - }); + const errMsg = error instanceof Error ? error.message : String(error); + errors.push({ addon, error: errMsg }); + summaryMsg = ` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + 🔴 [${addon.name}] Scrape Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✖ Status : FAILED + 🚫 Error : ${errMsg} +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + `; return []; + } finally { + logger.info(summaryMsg); } }) ); diff --git a/packages/core/src/parser/streams.ts b/packages/core/src/parser/streams.ts index 1a536fc3..d6578f98 100644 --- a/packages/core/src/parser/streams.ts +++ b/packages/core/src/parser/streams.ts @@ -10,6 +10,13 @@ class StreamParser { addon: this.addon, type: this.getStreamType(stream, undefined), parsedFile: {}, + url: stream.url, + externalUrl: stream.externalUrl, + ytId: stream.ytId, + requestHeaders: stream.behaviorHints?.proxyHeaders?.request, + responseHeaders: stream.behaviorHints?.proxyHeaders?.response, + notWebReady: stream.behaviorHints?.notWebReady, + videoHash: stream.behaviorHints?.videoHash, }; this.raiseErrorIfNecessary(stream); diff --git a/packages/frontend/src/components/menu/services.tsx b/packages/frontend/src/components/menu/services.tsx index 6efbff1b..f3bc9a89 100644 --- a/packages/frontend/src/components/menu/services.tsx +++ b/packages/frontend/src/components/menu/services.tsx @@ -104,6 +104,7 @@ function Content() { } return { ...service, + enabled: true, credentials: values, }; } diff --git a/packages/server/src/routes/stremio/manifest.ts b/packages/server/src/routes/stremio/manifest.ts index b39ab6c7..7aec051e 100644 --- a/packages/server/src/routes/stremio/manifest.ts +++ b/packages/server/src/routes/stremio/manifest.ts @@ -1,9 +1,16 @@ import { Router } from 'express'; -import { AIOStreams, Env, getSimpleTextHash, UserData } from '@aiostreams/core'; +import { + AIOStreams, + APIError, + constants, + Env, + getSimpleTextHash, + UserData, +} from '@aiostreams/core'; import { Manifest } from '@aiostreams/core'; import { createLogger } from '@aiostreams/core'; -const logger = createLogger('stremio/manifest'); +const logger = createLogger('server'); const router = Router(); export default router; @@ -18,7 +25,9 @@ const manifest = async (config?: UserData): Promise => { if (config) { const aiostreams = new AIOStreams(config); // wait till initialized + await aiostreams.initialise(); + catalogs = aiostreams.getCatalogs(); resources = aiostreams.getResources(); } @@ -50,7 +59,13 @@ const manifest = async (config?: UserData): Promise => { }; }; -router.get('/', async (req, res) => { +router.get('/', async (req, res, next) => { logger.debug('Manifest request received', { userData: req.userData }); - res.status(200).json(await manifest(req.userData)); + try { + res.status(200).json(await manifest(req.userData)); + } catch (error) { + logger.error(`Failed to generate manifest: ${error}`); + logger.verbose(JSON.stringify(req.userData, null, 2)); + next(new APIError(constants.ErrorCode.INTERNAL_SERVER_ERROR)); + } }); diff --git a/packages/server/src/routes/stremio/stream.ts b/packages/server/src/routes/stremio/stream.ts index 26c41e01..a10c27fb 100644 --- a/packages/server/src/routes/stremio/stream.ts +++ b/packages/server/src/routes/stremio/stream.ts @@ -49,6 +49,8 @@ router.get('/:type/:id.json', async (req, res) => { errors, }); + logger.info(`Returning ${transformedStreams.length} streams`); + res.status(200).json( createResponse( {