feat: add comet and improve method of getting choosable addons

This commit is contained in:
Viren070
2024-12-30 14:35:01 +00:00
parent e2f5792dc9
commit 4e5714db44
8 changed files with 268 additions and 7 deletions
+4
View File
@@ -1,5 +1,6 @@
import {
BaseWrapper,
getCometStreams,
getTorboxStreams,
getTorrentioStreams,
} from '@aiostreams/wrappers';
@@ -315,6 +316,9 @@ export class AIOStreams {
streamRequest
);
}
case 'comet': {
return await getCometStreams(this.config, addon.options, streamRequest);
}
case 'gdrive': {
if (!addon.options.addonUrl) {
throw new Error('The addon URL was undefined for GDrive');
+52 -1
View File
@@ -2,7 +2,7 @@ import { Config } from '@aiostreams/types';
import { AddonDetail } from '@aiostreams/types';
export const allowedFormatters = ['gdrive', 'torrentio', 'torbox'];
export const allowedAddons = ['torrentio', 'torbox', 'gdrive', 'custom'];
export const allowedAddons = ['torrentio', 'comet', 'torbox', 'gdrive', 'custom'];
export const MAX_ADDONS = 10;
export const MAX_SIZE = 150000000000; // 1500GB
@@ -11,6 +11,8 @@ export const addonDetails: AddonDetail[] = [
{
name: 'Torrentio',
id: 'torrentio',
requiresService: false,
supportedServices: ['realdebrid', 'alldebrid', 'premiumize', 'debridlink', 'torbox', 'offcloud', 'putio'],
options: [
{
id: 'overrideName',
@@ -49,9 +51,54 @@ export const addonDetails: AddonDetail[] = [
],
},
{
name: 'Comet',
id: 'comet',
requiresService: true,
supportedServices: ['realdebrid', 'alldebrid', 'premiumize', 'debridlink', 'torbox'],
options: [
{
id: 'prioritiseDebrid',
required: false,
label: 'Prioritise Debrid Service',
description:
'Prioritise a specific debrid service when fetching streams. This option is useful when you want to use a specific debrid service for fetching streams. By default, the addon will make a separate request for each debrid service. I highly recommend provding a value for this option as it will speed up the fetching process and remove redundant results.',
type: 'select',
options: [{ value: 'realdebrid', label: 'Real Debrid' }, { value: 'alldebrid', label: 'All Debrid' }, { value: 'premiumize', label: 'Premiumize' }, { value: 'debridlink', label: 'Debrid Link' }, { value: 'torbox', label: 'Torbox' }],
},
{
id: 'overrideName',
required: false,
label: 'Override Addon Name',
description: 'Override the name of the Comet addon that shows up in the results',
type: 'text',
},
{
id: 'overrideUrl',
required: false,
label: 'Override URL',
description:
'Override the URL used to fetch streams from the Comet addon. This option is incompatible with the prioritiseDebrid option. By default, the URL is generated based on the selected services and their credentials. Use this option to override the URL with a custom URL.',
type: 'text',
},
{
id: 'indexerTimeout',
required: false,
label: 'Override Indexer Timeout',
description: 'The timeout for fetching streams from the Comet addon. This is the time in milliseconds that the addon will wait for a response before timing out. Leave it empty to use the recommended timeout.',
type: 'number',
constraints: {
min: 1000,
max: 20000
}
}
]
},
{
name: 'Torbox',
id: 'torbox',
requiresService: true,
supportedServices: ['torbox'],
options: [
{
id: 'indexerTimeout',
@@ -76,6 +123,8 @@ export const addonDetails: AddonDetail[] = [
{
name: 'Google Drive (Viren070)',
id: 'gdrive',
requiresService: false,
supportedServices: [],
options: [
{
id: 'addonUrl',
@@ -107,6 +156,8 @@ export const addonDetails: AddonDetail[] = [
{
name: 'Custom',
id: 'custom',
requiresService: false,
supportedServices: [],
options: [
{
id: 'name',
+10 -5
View File
@@ -188,11 +188,16 @@ export default function Configure() {
const getChoosableAddons = () => {
// only if torbox service is enabled we can use torbox addon
const choosableAddons: string[] = ['torrentio', 'gdrive', 'custom'];
if (
services.some((service) => service.enabled && service.id === 'torbox')
) {
choosableAddons.push('torbox');
const choosableAddons: string[] = [];
for (const addon of addonDetails) {
if (addon.requiresService) {
// look through services and see if the ID of any service is in addon.supportedServices
if (services.some((service) => addon.supportedServices.includes(service.id) && service.enabled && Object.keys(service.credentials).length > 0)) {
choosableAddons.push(addon.id);
}
} else {
choosableAddons.push(addon.id);
}
}
return choosableAddons;
};
@@ -90,6 +90,7 @@
align-items: center;
}
.option select,
.option input[type='text'],
.option input[type='number']
{
@@ -115,6 +115,23 @@ const AddonsList: React.FC<AddonsListProps> = ({
className={styles.textInput}
/>
)}
{option.type === 'select' && (
<select
value={addon.options[option.id] || ''}
onChange={(e) =>
updateOption(index, option.id, e.target.value ? e.target.value : undefined)
}
className={styles.textInput}
>
<option value={undefined}>None</option>
{option.options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
)
}
{option.type === 'number' && (
<input
type="number"
+8 -1
View File
@@ -130,6 +130,11 @@ export interface TextOptionDetail extends BaseOptionDetail {
type: 'text';
}
export interface SelectOptionDetail extends BaseOptionDetail {
type: 'select';
options: { value: string, label: string }[];
}
export interface CheckboxOptionDetail extends BaseOptionDetail {
type: 'checkbox';
}
@@ -142,10 +147,12 @@ export interface NumberOptionDetail extends BaseOptionDetail {
}
}
export type AddonOptionDetail = TextOptionDetail | CheckboxOptionDetail | NumberOptionDetail;
export type AddonOptionDetail = TextOptionDetail | SelectOptionDetail | CheckboxOptionDetail | NumberOptionDetail;
export interface AddonDetail {
name: string;
id: string;
requiresService: boolean;
supportedServices: string[];
options?: AddonOptionDetail[];
}
+175
View File
@@ -0,0 +1,175 @@
import { ParsedNameData, StreamRequest } from '@aiostreams/types';
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
import { ParsedStream, Stream, Config } from '@aiostreams/types';
import { BaseWrapper } from './base';
interface CometStream extends Stream {
torrentTitle?: string;
torrentSize?: number;
}
export class Comet extends BaseWrapper {
constructor(configString: string | null, overrideUrl: string | null, indexerTimeout: number = 10000, addonName: string = 'Torrentio') {
if (overrideUrl && overrideUrl.endsWith('/manifest.json')) {
overrideUrl = overrideUrl.replace('/manifest.json', '/');
}
let url = overrideUrl
? overrideUrl
: 'https://comet.elfhosted.com/' +
(configString ? configString + '/' : '');
super(addonName, url, indexerTimeout);
}
protected parseStream(stream: CometStream): ParsedStream {
const filename = stream.behaviorHints?.filename?.trim() ||
stream.description?.split('\n')[0] ||
stream.torrentTitle;
const parsedFilename: ParsedNameData = parseFilename(filename || stream.description || '');
const sizeInBytes = stream.torrentSize
? stream.torrentSize
: stream.title
? extractSizeInBytes(stream.title, 1024)
: undefined;
const debridMatch = RegExp(/^\[([a-zA-Z]{2})(\⚡)\]/).exec(
stream.name!
);
const debrid = debridMatch
? {
provider: debridMatch[1],
cached: debridMatch[2] === '⚡',
}
: undefined;
const indexerMatch = RegExp(/🔎 ([a-zA-Z0-9]+)/).exec(stream.title!);
const indexer = indexerMatch ? indexerMatch[1] : undefined;
return {
...parsedFilename,
filename,
size: sizeInBytes,
addonName: this.addonName,
url: stream.url,
externalUrl: stream.externalUrl,
indexers: indexer,
provider: debrid
? {
name: debrid.provider,
cached: debrid.cached,
}
: undefined,
};
}
}
const getCometConfig = (debridService: string, debridApiKey: string) => {
return {
"indexers": [
"bitsearch",
"eztv",
"thepiratebay",
"therarbg",
"yts"
],
"maxResults": 0,
"maxResultsPerResolution": 0,
"maxSize": 0,
"reverseResultOrder": false,
"removeTrash": true,
"resultFormat": [
"All"
],
"resolutions": [
"All"
],
"languages": [
"All"
],
"debridService": debridService,
"debridApiKey": debridApiKey,
"stremthruUrl": "",
"debridStreamProxyPassword": ""
}
}
export async function getCometStreams(
config: Config,
cometOptions: {
prioritiseDebrid?: string;
overrideUrl?: string;
indexerTimeout?: string;
overrideName?: string;
},
streamRequest: StreamRequest
): Promise<ParsedStream[]> {
const supportedServices: string[] = [
'realdebrid',
'alldebrid',
'premiumize',
'torbox',
'debridlink',
];
const parsedStreams: ParsedStream[] = [];
const indexerTimeout = cometOptions.indexerTimeout ? parseInt(cometOptions.indexerTimeout) : undefined;
// If overrideUrl is provided, use it to get streams and skip all other steps
if (cometOptions.overrideUrl) {
const comet = new Comet(null, cometOptions.overrideUrl as string, indexerTimeout, cometOptions.overrideName);
return comet.getParsedStreams(streamRequest);
}
// find all usable services
const usableServices = config.services.filter((service) =>
supportedServices.includes(service.id)
);
// if no usable services found, raise an error
if (usableServices.length < 0) {
throw new Error('No usable services found');
}
// otherwise, depending on the configuration, create multiple instances of comet or use a single instance with the prioritised service
if (cometOptions.prioritiseDebrid && !supportedServices.includes(cometOptions.prioritiseDebrid)) {
throw new Error('Invalid debrid service');
}
if (cometOptions.prioritiseDebrid) {
const debridService = usableServices.find(
(service) => service.id === cometOptions.prioritiseDebrid
);
if (!debridService) {
throw new Error('Debrid service not found for ' + cometOptions.prioritiseDebrid);
}
if (!debridService.credentials.apiKey) {
throw new Error('Debrid service API key not found for ' + cometOptions.prioritiseDebrid);
}
// get the comet config and b64 encode it
const cometConfig = getCometConfig(cometOptions.prioritiseDebrid, debridService.credentials.apiKey);
const configString = Buffer.from(JSON.stringify(cometConfig)).toString('base64');
const comet = new Comet(configString, null, indexerTimeout, cometOptions.overrideName);
return comet.getParsedStreams(streamRequest);
}
// if no prioritised service is provided, create a comet instance for each service
const servicesToUse = usableServices.filter((service) => service.enabled);
if (servicesToUse.length < 1) {
throw new Error('No supported service(s) enabled');
}
for (const service of servicesToUse) {
const cometConfig = getCometConfig(service.id, service.credentials.apiKey);
const configString = Buffer.from(JSON.stringify(cometConfig)).toString('base64');
const comet = new Comet(configString, null, indexerTimeout, cometOptions.overrideName);
const streams = await comet.getParsedStreams(streamRequest);
parsedStreams.push(...streams);
}
return parsedStreams;
}
+1
View File
@@ -1,3 +1,4 @@
export * from './base';
export * from './torbox';
export * from './torrentio';
export * from './comet';