style: format

This commit is contained in:
Viren070
2024-12-26 19:15:05 +00:00
parent 27b2ef9bd6
commit 3e2f25cd1a
14 changed files with 347 additions and 256 deletions
+1 -1
View File
@@ -29,4 +29,4 @@ jobs:
push: true
tags: |
ghcr.io/viren070/aiostreams:${{ github.ref_name }}
ghcr.io/viren070/aiostreams:latest
ghcr.io/viren070/aiostreams:latest
+25 -28
View File
@@ -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.
+19 -5
View File
@@ -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<ParsedStream[]> {
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);
}
+182 -112
View File
@@ -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);
};
return createResponse(true, null, null);
}
+1 -1
View File
@@ -1,2 +1,2 @@
export * from './addon';
export * from './config';
export * from './config';
+6 -5
View File
@@ -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);
+1 -1
View File
@@ -1,4 +1,4 @@
export * from './gdrive';
export * from './utils';
export * from './torrentio';
export * from './torbox';
export * from './torbox';
+8 -4
View File
@@ -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') {
@@ -5,8 +5,6 @@
min-height: 100vh;
}
.content {
background-color: #000000; /* Slightly lighter black */
border-radius: 8px;
+66 -58
View File
@@ -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<Resolution[]>(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);
},
})) || []
})) || []
}
/>
))}
<div className={styles.section} style={{ marginTop: '20px', marginBottom: '0px' }}>
<div className={styles.setting}>
<div className={styles.settingDescription}>
<h2 style={{ padding: '5px' }}>Only Show Cached Streams</h2>
<p style={{ padding: '5px' }}>
Only show streams that are cached by the enabled services.
</p>
</div>
<div className={styles.settingInput}>
<input
type="checkbox"
checked={onlyShowCachedStreams}
onChange={(e) => setOnlyShowCachedStreams(e.target.checked)}
// move to the right
style={{ marginLeft: 'auto', marginRight: '20px', width: '25px', height: '25px' }}
/>
<div
className={styles.section}
style={{ marginTop: '20px', marginBottom: '0px' }}
>
<div className={styles.setting}>
<div className={styles.settingDescription}>
<h2 style={{ padding: '5px' }}>Only Show Cached Streams</h2>
<p style={{ padding: '5px' }}>
Only show streams that are cached by the enabled services.
</p>
</div>
<div className={styles.settingInput}>
<input
type="checkbox"
checked={onlyShowCachedStreams}
onChange={(e) => setOnlyShowCachedStreams(e.target.checked)}
// move to the right
style={{
marginLeft: 'auto',
marginRight: '20px',
width: '25px',
height: '25px',
}}
/>
</div>
</div>
</div>
</div>
</div>
<div className={styles.section}>
<h2 style={{ padding: '5px' }}>Addons</h2>
+5 -20
View File
@@ -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"]
}
-1
View File
@@ -125,4 +125,3 @@ export interface AddonDetail {
type: 'text' | 'checkbox';
}[];
}
+14 -5
View File
@@ -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<ParsedStream[]> {
const torboxService = config.services.find((service) => service.id === 'torbox');
export async function getTorboxStreams(
config: Config,
streamRequest: StreamRequest
): Promise<ParsedStream[]> {
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);
}
}
+19 -13
View File
@@ -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<ParsedStream[]> {
export async function getTorrentioStreams(
config: Config,
torrentioOptions: { [key: string]: string },
streamRequest: StreamRequest
): Promise<ParsedStream[]> {
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);
}
}
}