diff --git a/.github/workflows/deploy-docker.yml b/.github/workflows/deploy-docker.yml index cd975a2e..eb141bfc 100644 --- a/.github/workflows/deploy-docker.yml +++ b/.github/workflows/deploy-docker.yml @@ -29,4 +29,4 @@ jobs: push: true tags: | ghcr.io/viren070/aiostreams:${{ github.ref_name }} - ghcr.io/viren070/aiostreams:latest \ No newline at end of file + ghcr.io/viren070/aiostreams:latest diff --git a/README.md b/README.md index 4c4f4368..cd75abba 100644 --- a/README.md +++ b/README.md @@ -3,54 +3,54 @@ > [!WARNING] > This addon is currently still in development. Things may not work as expected. -Combines streams from other addons into one and provides much greater customisation: +Combines streams from other addons into one and provides much greater customisation: + - Change the format of the resutls - Filter all results by resolution, quality, visual tags. - Sort all results by quality, resolution, size, cached, visual tags, seeders - Prioritise specific languages - Specify a minimum and/or maximum size -You simply configure your options, add any API keys for any services you use, then enable whichever addons you want, and install. +You simply configure your options, add any API keys for any services you use, then enable whichever addons you want, and install. -The addon will scrape all results from all addons, apply your configuration, and give the results back to you in one go. +The addon will scrape all results from all addons, apply your configuration, and give the results back to you in one go. > [!NOTE] > Do not install other addons that you have enabled through this addon. You will only cause unnecessary requests to the addon. -> I also do not recommend installing/enabling too many addons as they all scrape mostly the same sources. - - +> I also do not recommend installing/enabling too many addons as they all scrape mostly the same sources. ## FAQ -### How does it work? +### How does it work? -The addon has parsers for specific addons and can extract the relevant information. -It goes through each addon you selected and obtains the results with all the parsed information. +The addon has parsers for specific addons and can extract the relevant information. +It goes through each addon you selected and obtains the results with all the parsed information. -Once it has all the parsed information for each result, it can apply your configured sorting and filtering options. +Once it has all the parsed information for each result, it can apply your configured sorting and filtering options. -### What are the currently supported addons? +### What are the currently supported addons? + +It currently supports: -It currently supports: - Torrentio - Torbox - Custom: You can input an addon URL and name and it will parse as much information as it can. -> [!NOTE] -> The URL can either be a URL to the manifest.json or the url without the manifest.json -> e.g. `https://torrentio.strem.fun/` or `https://torrentio.strem.fun/manifest.json` + > [!NOTE] + > The URL can either be a URL to the manifest.json or the url without the manifest.json + > e.g. `https://torrentio.strem.fun/` or `https://torrentio.strem.fun/manifest.json` -### What are the supported formatters? +### What are the supported formatters? -The addon can display your results in different formats. The two formats available are: +The addon can display your results in different formats. The two formats available are: - gdrive: Uses the format from this [Stremio GDrive](https://github.com/Viren070/stremio-gdrive-addon) addon - + ![image](https://github.com/user-attachments/assets/9d9c74ab-afde-41f9-ba94-eaf8904b580b) - torrentio: Uses the format from the Torrentio addon. - + ![image](https://github.com/user-attachments/assets/343dccb4-13c5-4b57-998a-82b763e9ebf9) - torbox: @@ -58,11 +58,9 @@ The addon can display your results in different formats. The two formats availab ![image](https://github.com/user-attachments/assets/21f90ee2-e81d-4a56-9e64-8937fb7ab2bc) - - ## Self-Hosting -### From source +### From source You need Node.js and git installed. Node v22 and npm v10.9 were used in the development of this project. I can not guarantee earlier versions will work. @@ -73,19 +71,18 @@ You need Node.js and git installed. Node v22 and npm v10.9 were used in the deve ``` cd aiostreams ``` -3. Install project dependencies +2. Install project dependencies ``` npm i ``` -4. Build project +3. Build project ``` npm run build ``` -5. Run project +4. Run project ``` npm run start:addon ``` -6. Go to `http://localhost:3000/configure` - -You can change the PORT environment variable to change the port that the addon will listen on. +5. Go to `http://localhost:3000/configure` +You can change the PORT environment variable to change the port that the addon will listen on. diff --git a/packages/addon/src/addon.ts b/packages/addon/src/addon.ts index 27c5f362..54d52ae9 100644 --- a/packages/addon/src/addon.ts +++ b/packages/addon/src/addon.ts @@ -1,6 +1,14 @@ -import { BaseWrapper, getTorboxStreams, getTorrentioStreams } from '@aiostreams/wrappers'; +import { + BaseWrapper, + getTorboxStreams, + getTorrentioStreams, +} from '@aiostreams/wrappers'; import { Stream, ParsedStream, StreamRequest, Config } from '@aiostreams/types'; -import { gdriveFormat, torrentioFormat, torboxFormat } from '@aiostreams/formatters'; +import { + gdriveFormat, + torrentioFormat, + torboxFormat, +} from '@aiostreams/formatters'; export class AIOStreams { private config: Config; @@ -244,7 +252,7 @@ export class AIOStreams { streamRequest: StreamRequest ): Promise { const parsedStreams: ParsedStream[] = []; - + for (const addon of this.config.addons) { try { const streams = await this.getStreamsFromAddon(addon.id, streamRequest); @@ -270,10 +278,16 @@ export class AIOStreams { return await getTorboxStreams(this.config, streamRequest); } case 'torrentio': { - return await getTorrentioStreams(this.config, addon.options, streamRequest); + return await getTorrentioStreams( + this.config, + addon.options, + streamRequest + ); } default: { - console.log(`Using base wrapper for addon ${addon.options.name} with url ${addon.options.url}`); + console.log( + `Using base wrapper for addon ${addon.options.name} with url ${addon.options.url}` + ); const wrapper = new BaseWrapper(addon.options.name, addon.options.url); return await wrapper.getParsedStreams(streamRequest); } diff --git a/packages/addon/src/config.ts b/packages/addon/src/config.ts index e3015a6d..398fb4c1 100644 --- a/packages/addon/src/config.ts +++ b/packages/addon/src/config.ts @@ -1,10 +1,8 @@ -import { Config } from "@aiostreams/types"; -import { AddonDetail } from "@aiostreams/types"; - +import { Config } from '@aiostreams/types'; +import { AddonDetail } from '@aiostreams/types'; export const allowedFormatters = ['gdrive', 'torrentio', 'torbox']; - export const addonDetails: AddonDetail[] = [ { name: 'Torrentio', @@ -64,13 +62,18 @@ export const addonDetails: AddonDetail[] = [ description: 'The name of the custom addon', label: 'Name', type: 'text', - } + }, ], }, ]; - -export const allowedLanguages = ['English', 'Spanish', 'French', 'German', 'Chinese']; +export const allowedLanguages = [ + 'English', + 'Spanish', + 'French', + 'German', + 'Chinese', +]; export const serviceCredentials = [ { @@ -81,8 +84,8 @@ export const serviceCredentials = [ label: 'API Key', id: 'apiKey', link: 'https://real-debrid.com/apitoken', - } - ] + }, + ], }, { name: 'All Debrid', @@ -92,8 +95,8 @@ export const serviceCredentials = [ label: 'API Key', id: 'apiKey', link: 'https://alldebrid.com/apikeys', - } - ] + }, + ], }, { name: 'Premiumize', @@ -103,8 +106,8 @@ export const serviceCredentials = [ label: 'API Key', id: 'apiKey', link: 'https://www.premiumize.me/account', - } - ] + }, + ], }, { name: 'Debrid Link', @@ -114,8 +117,8 @@ export const serviceCredentials = [ label: 'API Key', id: 'apiKey', link: 'https://debrid-link.com/webapp/apikey', - } - ] + }, + ], }, { name: 'Torbox', @@ -125,8 +128,8 @@ export const serviceCredentials = [ label: 'API Key', id: 'apiKey', link: 'https://torbox.app/settings', - } - ] + }, + ], }, { name: 'Offcloud', @@ -136,8 +139,8 @@ export const serviceCredentials = [ label: 'API Key', id: 'apiKey', link: 'https://offcloud.com/#/account', - } - ] + }, + ], }, { name: 'put.io', @@ -152,8 +155,8 @@ export const serviceCredentials = [ label: 'Token', id: 'token', link: 'https://put.io/oauth', - } - ] + }, + ], }, { name: 'Easynews', @@ -168,115 +171,182 @@ export const serviceCredentials = [ label: 'Password', id: 'password', link: 'https://www.easynews.com/', - } - ] + }, + ], }, -] +]; -export function validateConfig(config: Config): { valid: boolean; errorCode: string | null; errorMessage: string | null } { +export function validateConfig(config: Config): { + valid: boolean; + errorCode: string | null; + errorMessage: string | null; +} { + const createResponse = ( + valid: boolean, + errorCode: string | null, + errorMessage: string | null + ) => { + return { valid, errorCode, errorMessage }; + }; - const createResponse = (valid: boolean, errorCode: string | null, errorMessage: string | null) => { - return { valid, errorCode, errorMessage }; - } + // check for any duplicate addons where both the ID and options are the same + const duplicateAddons = config.addons.filter( + (addon, index) => + config.addons.findIndex( + (a) => + a.id === addon.id && + JSON.stringify(a.options) === JSON.stringify(addon.options) + ) !== index + ); - // check for any duplicate addons where both the ID and options are the same - const duplicateAddons = config.addons.filter( - (addon, index) => - config.addons.findIndex( - (a) => - a.id === addon.id && - JSON.stringify(a.options) === JSON.stringify(addon.options) - ) !== index + if (duplicateAddons.length > 0) { + return createResponse( + false, + 'duplicateAddons', + 'Duplicate addons found. Please remove any duplicates' ); + } - if (duplicateAddons.length > 0) { - return createResponse(false, 'duplicateAddons', 'Duplicate addons found. Please remove any duplicates'); - } - - - for (const addon of config.addons) { - // if torbox addon is enabled, torbox service must be enabled and torbox api key must be set - if (addon.id === 'torbox') { - const torboxService = config.services.find( - (service) => service.id === 'torbox' + for (const addon of config.addons) { + // if torbox addon is enabled, torbox service must be enabled and torbox api key must be set + if (addon.id === 'torbox') { + const torboxService = config.services.find( + (service) => service.id === 'torbox' + ); + if (!torboxService) { + return createResponse( + false, + 'torboxServiceNotEnabled', + 'Torbox service must be enabled to use the Torbox addon' ); - if (!torboxService) { - return createResponse(false, 'torboxServiceNotEnabled', 'Torbox service must be enabled to use the Torbox addon'); - } - if (!torboxService.credentials.apiKey) { - return createResponse(false, 'torboxApiKeyNotSet', 'Torbox API Key must be set to use the Torbox addon'); - - } } - const details = addonDetails.find((detail) => detail.id === addon.id); - if (!details) { - return createResponse(false, 'invalidAddon', `Invalid addon: ${addon.id}`); - } - if (details.options) { - for (const option of details.options) { - if (option.required && !addon.options[option.id]) { - return createResponse(false, 'missingRequiredOption', `Option ${option.label} is required for addon ${addon.id}`); - } - - if (option.id.toLowerCase().includes('url') && addon.options[option.id]) { - console.log('checking url', addon.options[option.id]); - try { - new URL(addon.options[option.id]); - } catch (_) { - return createResponse(false, 'invalidUrl', `Invalid URL for ${option.label}`); - } - } - } - } - } - - if (!allowedFormatters.includes(config.formatter)) { - return createResponse(false, 'invalidFormatter', `Invalid formatter: ${config.formatter}`); - } - - for (const service of config.services) { - if (service.enabled) { - const serviceDetail = serviceCredentials.find( - (detail) => detail.id === service.id + if (!torboxService.credentials.apiKey) { + return createResponse( + false, + 'torboxApiKeyNotSet', + 'Torbox API Key must be set to use the Torbox addon' ); - if (!serviceDetail) { - return createResponse(false, 'invalidService', `Invalid service: ${service.id}`); - + } + } + const details = addonDetails.find((detail) => detail.id === addon.id); + if (!details) { + return createResponse( + false, + 'invalidAddon', + `Invalid addon: ${addon.id}` + ); + } + if (details.options) { + for (const option of details.options) { + if (option.required && !addon.options[option.id]) { + return createResponse( + false, + 'missingRequiredOption', + `Option ${option.label} is required for addon ${addon.id}` + ); } - for (const credential of serviceDetail.credentials) { - if (!service.credentials[credential.id]) { - return createResponse(false, 'missingCredential', `${credential.label} is required for ${service.name}`); + + if ( + option.id.toLowerCase().includes('url') && + addon.options[option.id] + ) { + console.log('checking url', addon.options[option.id]); + try { + new URL(addon.options[option.id]); + } catch (_) { + return createResponse( + false, + 'invalidUrl', + `Invalid URL for ${option.label}` + ); } } } } + } - // need at least one visual tag, resolution, quality - if (config.visualTags.length === 0) { - return createResponse(false, 'noVisualTags', 'At least one visual tag must be selected'); + if (!allowedFormatters.includes(config.formatter)) { + return createResponse( + false, + 'invalidFormatter', + `Invalid formatter: ${config.formatter}` + ); + } + + for (const service of config.services) { + if (service.enabled) { + const serviceDetail = serviceCredentials.find( + (detail) => detail.id === service.id + ); + if (!serviceDetail) { + return createResponse( + false, + 'invalidService', + `Invalid service: ${service.id}` + ); + } + for (const credential of serviceDetail.credentials) { + if (!service.credentials[credential.id]) { + return createResponse( + false, + 'missingCredential', + `${credential.label} is required for ${service.name}` + ); + } + } } + } - if (config.resolutions.length === 0) { - return createResponse(false, 'noResolutions', 'At least one resolution must be selected'); + // need at least one visual tag, resolution, quality + if (config.visualTags.length === 0) { + return createResponse( + false, + 'noVisualTags', + 'At least one visual tag must be selected' + ); + } + + if (config.resolutions.length === 0) { + return createResponse( + false, + 'noResolutions', + 'At least one resolution must be selected' + ); + } + + if (config.qualities.length === 0) { + return createResponse( + false, + 'noQualities', + 'At least one quality must be selected' + ); + } + + if (config.minSize && config.maxSize) { + if (config.minSize >= config.maxSize) { + return createResponse( + false, + 'invalidSizeRange', + "Your minimum size limit can't be greater than or equal to your maximum size limit" + ); } + } - if (config.qualities.length === 0) { - return createResponse(false, 'noQualities', 'At least one quality must be selected'); - } + if (config.addons.length < 1) { + return createResponse( + false, + 'noAddons', + 'At least one addon must be selected' + ); + } - if (config.minSize && config.maxSize) { - if (config.minSize >= config.maxSize) { - return createResponse(false, 'invalidSizeRange', 'Your minimum size limit can\'t be greater than or equal to your maximum size limit'); - } - } + if (config.addons.length > 10) { + return createResponse( + false, + 'tooManyAddons', + 'You can only select a maximum of 10 addons' + ); + } - if (config.addons.length < 1) { - return createResponse(false, 'noAddons', 'At least one addon must be selected'); - } - - if (config.addons.length > 10) { - return createResponse(false, 'tooManyAddons', 'You can only select a maximum of 10 addons'); - } - - return createResponse(true, null, null); - }; \ No newline at end of file + return createResponse(true, null, null); +} diff --git a/packages/addon/src/index.ts b/packages/addon/src/index.ts index 5b7735b8..b5e919ae 100644 --- a/packages/addon/src/index.ts +++ b/packages/addon/src/index.ts @@ -1,2 +1,2 @@ export * from './addon'; -export * from './config'; \ No newline at end of file +export * from './config'; diff --git a/packages/addon/src/server.ts b/packages/addon/src/server.ts index cb4e09b3..91a9151b 100644 --- a/packages/addon/src/server.ts +++ b/packages/addon/src/server.ts @@ -85,14 +85,13 @@ app.get('/:config/stream/:type/:id', (req: Request, res: Response) => { const decodedPath = decodeURIComponent(req.path); - const streamMatch = new RegExp( `/${config}/stream/(movie|series)/tt([0-9]{7,})(?::([0-9]+):([0-9]+))?\.json` ).exec(decodedPath); if (!streamMatch) { // log after removing config if present - console.error(`Invalid request: ${decodedPath.replace(`/${config}`, '')}`); + console.error(`Invalid request: ${decodedPath.replace(`/${config}`, '')}`); res.status(400).send('Invalid request'); return; } @@ -109,7 +108,9 @@ app.get('/:config/stream/:type/:id', (req: Request, res: Response) => { case 'series': if (!season || !episode) { res.status(400).send('Invalid request'); - console.log(`Request type was series but season or episode was missing`); + console.log( + `Request type was series but season or episode was missing` + ); return; } streamRequest = { @@ -146,8 +147,8 @@ app.get('/:config/stream/:type/:id', (req: Request, res: Response) => { name: 'Invalid Config', description: errorMessage, }, - ] - }) + ], + }); } const aioStreams = new AIOStreams(configJson); diff --git a/packages/formatters/src/index.ts b/packages/formatters/src/index.ts index 1c526e63..6a97bf69 100644 --- a/packages/formatters/src/index.ts +++ b/packages/formatters/src/index.ts @@ -1,4 +1,4 @@ export * from './gdrive'; export * from './utils'; export * from './torrentio'; -export * from './torbox'; \ No newline at end of file +export * from './torbox'; diff --git a/packages/formatters/src/torbox.ts b/packages/formatters/src/torbox.ts index 2ee28999..65b9c9b6 100644 --- a/packages/formatters/src/torbox.ts +++ b/packages/formatters/src/torbox.ts @@ -8,7 +8,7 @@ export function torboxFormat(stream: ParsedStream): { let name: string = ''; name += `${stream.addonName}`; - + if (stream.provider) { name += stream.provider.cached ? ` (Instant ${stream.provider.name})` @@ -21,13 +21,17 @@ export function torboxFormat(stream: ParsedStream): { name += ` (${stream.resolution})`; - - let description: string = ''; description += `Quality: ${stream.quality}\nName: ${stream.filename}\nSize: ${formatSize(stream.size || 0)}\nLanguage: ${stream.languages.length > 0 ? stream.languages.join(', ') : 'Unknown'}`; - let streamType = stream.torrent ? 'Torrent' : stream.usenet ? 'Usenet' : stream.url ? 'Direct' : 'Unknown'; + let streamType = stream.torrent + ? 'Torrent' + : stream.usenet + ? 'Usenet' + : stream.url + ? 'Direct' + : 'Unknown'; description += `\nType: ${streamType}`; if (streamType === 'Torrent' || streamType === 'Usenet') { diff --git a/packages/frontend/src/app/configure/page.module.css b/packages/frontend/src/app/configure/page.module.css index b88b1b3f..17ef4057 100644 --- a/packages/frontend/src/app/configure/page.module.css +++ b/packages/frontend/src/app/configure/page.module.css @@ -5,8 +5,6 @@ min-height: 100vh; } - - .content { background-color: #000000; /* Slightly lighter black */ border-radius: 8px; diff --git a/packages/frontend/src/app/configure/page.tsx b/packages/frontend/src/app/configure/page.tsx index 10e10aba..922885c3 100644 --- a/packages/frontend/src/app/configure/page.tsx +++ b/packages/frontend/src/app/configure/page.tsx @@ -14,7 +14,13 @@ import ServiceInput from '../../components/ServiceInput'; import AddonsList from '../../components/AddonsList'; import { Slide, ToastContainer, toast } from 'react-toastify'; import addonPackage from '../../../package.json'; -import { allowedFormatters, allowedLanguages, addonDetails, validateConfig, serviceCredentials } from '@aiostreams/config'; +import { + allowedFormatters, + allowedLanguages, + addonDetails, + validateConfig, + serviceCredentials, +} from '@aiostreams/config'; const version = addonPackage.version; @@ -85,63 +91,52 @@ const defaultServices = [ name: 'Real Debrid', id: 'realdebrid', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'All Debrid', id: 'alldebrid', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'Premiumize', id: 'premiumize', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'Debrid Link', id: 'debridlink', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'Torbox', id: 'torbox', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'Offcloud', id: 'offcloud', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'put.io', id: 'putio', enabled: false, - credentials: { - }, + credentials: {}, }, { name: 'Easynews', id: 'easynews', enabled: false, - credentials: { - }, + credentials: {}, }, ]; - - - export default function Configure() { const [resolutions, setResolutions] = useState(defaultResolutions); @@ -209,7 +204,11 @@ export default function Configure() { const { valid, errorCode, errorMessage } = validateConfig(config); if (!valid) { - showToast(errorMessage || 'Invalid config', 'error', errorCode || 'error'); + showToast( + errorMessage || 'Invalid config', + 'error', + errorCode || 'error' + ); return false; } return true; @@ -477,51 +476,60 @@ export default function Configure() { newServices[serviceIndex] = { ...service, enabled }; setServices(newServices); }} - fields={serviceCredentials.find( - (detail) => detail.id === service.id - )?.credentials.map((credential) => ({ - label: credential.label, - link: credential.link, - value: service.credentials[credential.id] || '', - setValue: (value) => { - const newServices = [...services]; - const serviceIndex = newServices.findIndex( - (s) => s.id === service.id - ); - newServices[serviceIndex] = { - ...service, - credentials: { - ...service.credentials, - [credential.id]: value, + fields={ + serviceCredentials + .find((detail) => detail.id === service.id) + ?.credentials.map((credential) => ({ + label: credential.label, + link: credential.link, + value: service.credentials[credential.id] || '', + setValue: (value) => { + const newServices = [...services]; + const serviceIndex = newServices.findIndex( + (s) => s.id === service.id + ); + newServices[serviceIndex] = { + ...service, + credentials: { + ...service.credentials, + [credential.id]: value, + }, + }; + setServices(newServices); }, - }; - setServices(newServices); - }, - })) || [] + })) || [] } /> ))} -
-
-
-

Only Show Cached Streams

-

- Only show streams that are cached by the enabled services. -

-
-
- setOnlyShowCachedStreams(e.target.checked)} - // move to the right - style={{ marginLeft: 'auto', marginRight: '20px', width: '25px', height: '25px' }} - /> +
+
+
+

Only Show Cached Streams

+

+ Only show streams that are cached by the enabled services. +

+
+
+ setOnlyShowCachedStreams(e.target.checked)} + // move to the right + style={{ + marginLeft: 'auto', + marginRight: '20px', + width: '25px', + height: '25px', + }} + /> +
-

Addons

diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json index 2c007b37..2c8a4a80 100644 --- a/packages/frontend/tsconfig.json +++ b/packages/frontend/tsconfig.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,12 +16,8 @@ } ], "paths": { - "@/*": [ - "./src/*" - ], - "@aiostreams/config": [ - "../addon/src/config" - ] + "@/*": ["./src/*"], + "@aiostreams/config": ["../addon/src/config"] }, "noEmit": false, "isolatedModules": true @@ -38,13 +30,6 @@ "path": "../addon" } ], - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index a3252143..6438e9aa 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -125,4 +125,3 @@ export interface AddonDetail { type: 'text' | 'checkbox'; }[]; } - diff --git a/packages/wrappers/src/torbox.ts b/packages/wrappers/src/torbox.ts index 5236cd0e..9c1988e2 100644 --- a/packages/wrappers/src/torbox.ts +++ b/packages/wrappers/src/torbox.ts @@ -1,5 +1,10 @@ import { BaseWrapper } from './base'; -import { Config, ParsedNameData, ParsedStream, StreamRequest } from '@aiostreams/types'; +import { + Config, + ParsedNameData, + ParsedStream, + StreamRequest, +} from '@aiostreams/types'; import { parseFilename } from '@aiostreams/parser'; interface TorboxStream { @@ -93,9 +98,13 @@ export class Torbox extends BaseWrapper { } } -export async function getTorboxStreams(config: Config, streamRequest: StreamRequest): Promise { - - const torboxService = config.services.find((service) => service.id === 'torbox'); +export async function getTorboxStreams( + config: Config, + streamRequest: StreamRequest +): Promise { + const torboxService = config.services.find( + (service) => service.id === 'torbox' + ); if (!torboxService) { throw new Error('Torbox service not found'); } @@ -107,4 +116,4 @@ export async function getTorboxStreams(config: Config, streamRequest: StreamRequ const torbox = new Torbox(torboxApiKey); return await torbox.getParsedStreams(streamRequest); -} \ No newline at end of file +} diff --git a/packages/wrappers/src/torrentio.ts b/packages/wrappers/src/torrentio.ts index a5d4d9eb..8739bdd9 100644 --- a/packages/wrappers/src/torrentio.ts +++ b/packages/wrappers/src/torrentio.ts @@ -7,12 +7,14 @@ export class Torrentio extends BaseWrapper { private readonly name: string = 'Torrentio'; constructor(configString: string | null, overrideUrl: string | null) { - if (overrideUrl && overrideUrl.endsWith('/manifest.json')) { overrideUrl = overrideUrl.replace('/manifest.json', '/'); } - let url = overrideUrl ? overrideUrl : 'https://torrentio.strem.fun/' + (configString ? configString + '/' : ''); + let url = overrideUrl + ? overrideUrl + : 'https://torrentio.strem.fun/' + + (configString ? configString + '/' : ''); super('Torrentio', url); } @@ -64,7 +66,11 @@ export class Torrentio extends BaseWrapper { } } -export async function getTorrentioStreams(config: Config, torrentioOptions: { [key: string]: string }, streamRequest: StreamRequest): Promise { +export async function getTorrentioStreams( + config: Config, + torrentioOptions: { [key: string]: string }, + streamRequest: StreamRequest +): Promise { const supportedServices: string[] = [ 'realdebrid', 'alldebrid', @@ -75,7 +81,7 @@ export async function getTorrentioStreams(config: Config, torrentioOptions: { [k 'debridlink', ]; const parsedStreams: ParsedStream[] = []; - + // If overrideUrl is provided, use it to get streams and skip all other steps if (torrentioOptions.overrideUrl) { const torrentio = new Torrentio(null, torrentioOptions.overrideUrl); @@ -87,7 +93,6 @@ export async function getTorrentioStreams(config: Config, torrentioOptions: { [k supportedServices.includes(service.id) ); - // if no usable services found, use torrentio without any configuration if (usableServices.length < 0) { const torrentio = new Torrentio(null, null); @@ -96,9 +101,14 @@ export async function getTorrentioStreams(config: Config, torrentioOptions: { [k // otherwise, depending on the configuration, create multiple instances of torrentio or use a single instance with all services - const getServicePair = (serviceId: string, credentials: { [key: string]: string }) => { - return serviceId === 'putio' ? `${serviceId}=${credentials.clientId}@${credentials.token}` : `${serviceId}=${credentials.apiKey}`; - } + const getServicePair = ( + serviceId: string, + credentials: { [key: string]: string } + ) => { + return serviceId === 'putio' + ? `${serviceId}=${credentials.clientId}@${credentials.token}` + : `${serviceId}=${credentials.apiKey}`; + }; if (torrentioOptions.useMultipleInstances) { for (const service of usableServices) { @@ -123,8 +133,4 @@ export async function getTorrentioStreams(config: Config, torrentioOptions: { [k const torrentio = new Torrentio(configString, null); return await torrentio.getParsedStreams(streamRequest); } - - - - -} \ No newline at end of file +}