feat: add sootio preset

This commit is contained in:
Viren070
2025-09-22 18:10:16 +01:00
parent cfa854d242
commit 36ed320a2d
4 changed files with 196 additions and 0 deletions
+4
View File
@@ -644,6 +644,10 @@ PRUNE_MAX_DAYS=-1
# FORCE_STREMTHRU_TORZ_PORT=
# FORCE_STREMTHRU_TORZ_PROTOCOL=
# --------- Sootio ---------
# SOOTIO_URL=https://sootio.elfhosted.com
# DEFAULT_SOOTIO_TIMEOUT=
# --------- EASYNEWS+ ADDON ---------
# EASYNEWS_PLUS_URL=https://b89262c192b0-stremio-easynews-addon.baby-beamup.club/
# DEFAULT_EASYNEWS_PLUS_TIMEOUT=
@@ -63,6 +63,7 @@ import { JackettPreset } from './jackett.js';
import { NZBHydraPreset } from './nzbhydra.js';
import { KnabenPreset } from './knaben.js';
import { BitmagnetPreset } from './bitmagnet.js';
import { SootioPreset } from './sootio.js';
let PRESET_LIST: string[] = [
'custom',
@@ -74,6 +75,7 @@ let PRESET_LIST: string[] = [
'mediafusion',
'stremthruTorz',
'stremthruStore',
'sootio',
'zilean',
'knaben',
Env.BUILTIN_BITMAGNET_URL ? 'bitmagnet' : '',
@@ -272,6 +274,8 @@ export class PresetManager {
return KnabenPreset;
case 'bitmagnet':
return BitmagnetPreset;
case 'sootio':
return SootioPreset;
default:
throw new Error(`Preset ${id} not found`);
}
+175
View File
@@ -0,0 +1,175 @@
import {
Addon,
Option,
UserData,
Resource,
Stream,
ParsedStream,
} from '../db/index.js';
import { baseOptions, Preset } from './preset.js';
import { createLogger, Env, getSimpleTextHash } from '../utils/index.js';
import { constants, ServiceId } from '../utils/index.js';
import { StreamParser } from '../parser/index.js';
class SootioStreamParser extends StreamParser {
protected override getInLibrary(
stream: Stream,
currentParsedStream: ParsedStream
): boolean {
return stream.description?.includes('[Cloud]') ?? false;
}
protected override getIndexer(
stream: Stream,
currentParsedStream: ParsedStream
): string | undefined {
return /[☁️💾][^|]*\|\s*(.*)$/s.exec(stream.description || '')?.[1];
}
}
export class SootioPreset extends Preset {
static override getParser(): typeof StreamParser {
return SootioStreamParser;
}
static override get METADATA() {
const supportedServices: ServiceId[] = [
constants.REALDEBRID_SERVICE,
constants.OFFCLOUD_SERVICE,
];
const supportedResources = [
constants.STREAM_RESOURCE,
constants.CATALOG_RESOURCE,
];
const options: Option[] = [
...baseOptions(
'Sootio',
supportedResources,
Env.DEFAULT_SOOTIO_TIMEOUT,
Env.SOOTIO_URL
),
{
id: 'services',
name: 'Services',
description:
'Optionally override the services that are used. If not specified, then the services that are enabled and supported will be used.',
type: 'multi-select',
required: false,
showInNoobMode: false,
options: supportedServices.map((service) => ({
value: service,
label: constants.SERVICE_DETAILS[service].name,
})),
default: undefined,
emptyIsUndefined: true,
},
{
id: 'socials',
name: '',
description: '',
type: 'socials',
socials: [
{
id: 'github',
url: 'https://github.com/sooti/stremio-addon-debrid-search',
},
],
},
];
return {
ID: 'sootio',
NAME: 'Sootio',
LOGO: `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cdefs%3E%3ClinearGradient id='grad' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%2364ffda;stop-opacity:1' /%3E%3Cstop offset='100%25' style='stop-color:%2300A7B5;stop-opacity:1' /%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath fill='url(%23grad)' d='M50,5 C74.85,5 95,25.15 95,50 C95,74.85 74.85,95 50,95 C35,95 22.33,87.6 15,76 C25,85 40,85 50,80 C60,75 65,65 65,50 C65,35 55,25 40,25 C25,25 15,40 15,50 C15,55 16,60 18,64 C8.5,58 5,45 5,50 C5,25.15 25.15,5 50,5 Z'/%3E%3C/svg%3E`,
URL: Env.SOOTIO_URL[0],
TIMEOUT: Env.DEFAULT_SOOTIO_TIMEOUT || Env.DEFAULT_TIMEOUT,
USER_AGENT: Env.DEFAULT_SOOTIO_USER_AGENT || Env.DEFAULT_USER_AGENT,
SUPPORTED_SERVICES: supportedServices,
DESCRIPTION: 'Debrid addon.',
OPTIONS: options,
SUPPORTED_STREAM_TYPES: [constants.DEBRID_STREAM_TYPE],
SUPPORTED_RESOURCES: supportedResources,
};
}
static async generateAddons(
userData: UserData,
options: Record<string, any>
): Promise<Addon[]> {
if (options?.url?.endsWith('/manifest.json')) {
return [this.generateAddon(userData, options, undefined)];
}
const usableServices = this.getUsableServices(userData, options.services);
if (!usableServices || usableServices.length === 0) {
throw new Error(
`${this.METADATA.NAME} requires at least one usable service, but none were found. Please enable at least one of the following services: ${this.METADATA.SUPPORTED_SERVICES.join(
', '
)}`
);
}
let addons = usableServices.map((service) => {
return this.generateAddon(userData, options, service.id);
});
return addons;
}
private static generateAddon(
userData: UserData,
options: Record<string, any>,
serviceId?: ServiceId
): Addon {
return {
name: options.name || this.METADATA.NAME,
identifier: serviceId
? `${constants.SERVICE_DETAILS[serviceId].shortName}`
: undefined,
displayIdentifier: serviceId
? `${constants.SERVICE_DETAILS[serviceId].shortName}`
: undefined,
manifestUrl: this.generateManifestUrl(userData, options, serviceId),
enabled: true,
resources: options.resources || this.METADATA.SUPPORTED_RESOURCES,
timeout: options.timeout || this.METADATA.TIMEOUT,
preset: {
id: '',
type: this.METADATA.ID,
options: options,
},
headers: {
'User-Agent': this.METADATA.USER_AGENT,
},
};
}
private static generateManifestUrl(
userData: UserData,
options: Record<string, any>,
serviceId?: ServiceId
) {
const url = (options.url || this.METADATA.URL).replace(/\/$/, '');
if (url.endsWith('/manifest.json')) {
return url;
}
if (!serviceId) {
throw new Error(
`${this.METADATA.NAME} requires at least one usable service, but none were found. Please enable at least one of the following services: ${this.METADATA.SUPPORTED_SERVICES.join(
', '
)}`
);
}
const serviceNameMap: Partial<Record<ServiceId, string>> = {
realdebrid: 'RealDebrid',
offcloud: 'OffCloud',
};
const config = {
DebridProvider: serviceNameMap[serviceId],
DebridApiKey: this.getServiceCredential(serviceId, userData),
};
return `${url}/${this.urlEncodeJSON(config)}/manifest.json`;
}
}
+13
View File
@@ -1185,6 +1185,19 @@ export const Env = cleanEnv(process.env, {
desc: 'Default StreamFusion StremThru URL',
}),
SOOTIO_URL: urlOrUrlList({
default: ['https://sootio.elfhosted.com'],
desc: 'Sootio URL',
}),
DEFAULT_SOOTIO_TIMEOUT: num({
default: undefined,
desc: 'Default Sootio timeout',
}),
DEFAULT_SOOTIO_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Sootio user agent',
}),
// DMM Cast settings
DEFAULT_DMM_CAST_TIMEOUT: num({
default: undefined,