brunner56's picture
implement app
0bfe2e3
import { Addon, Option, UserData, Resource } from '../db';
import { baseOptions, Preset } from './preset';
import { Env } from '../utils';
import { constants, ServiceId } from '../utils';
export class StreamFusionPreset extends Preset {
static override get METADATA() {
const supportedServices: ServiceId[] = [
constants.REALDEBRID_SERVICE,
constants.PREMIUMIZE_SERVICE,
constants.ALLEDEBRID_SERVICE,
constants.TORBOX_SERVICE,
constants.EASYDEBRID_SERVICE,
constants.DEBRIDLINK_SERVICE,
constants.OFFCLOUD_SERVICE,
constants.PIKPAK_SERVICE,
];
const supportedResources = [
constants.STREAM_RESOURCE,
constants.CATALOG_RESOURCE,
constants.META_RESOURCE,
];
const options: Option[] = [
...baseOptions(
'StreamFusion',
supportedResources,
Env.DEFAULT_STREAMFUSION_TIMEOUT
),
{
id: 'streamFusionApiKey',
name: 'StreamFusion API Key',
description:
'The API key for the StreamFusion service. You can get it by sending the `/generate` command to the [StremioFR Telegram bot](https://t.me/Stremiofr_bot)',
type: 'password',
required: true,
},
{
id: 'torboxSearch',
name: 'Torbox Search',
description:
"Enable or disable the use of Torbox's Public Torrent Search Engine",
type: 'boolean',
required: false,
default: false,
},
{
id: 'torboxUsenet',
name: 'Torbox Usenet',
description:
"Enable or disable the use of Torbox's Usenet search and download functionality.",
type: 'boolean',
required: false,
default: false,
},
{
id: 'catalogs',
name: 'Catalogs',
description: 'What catalogs should be displayed',
type: 'multi-select',
required: false,
options: [
{
value: 'yggtorrent',
label: 'YggTorrent',
},
{
value: 'yggflix',
label: 'YggFlix',
},
],
default: ['yggtorrent', 'yggflix'],
},
{
id: 'torrenting',
name: 'Torrenting',
description:
"Use direct torrent streaming instead of debrid. If you haven't provided any debrid SERVICES, torrenting is automatically used and this option does not apply to you.",
type: 'boolean',
required: false,
default: false,
},
{
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,
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/Telkaoss/stream-fusion',
},
{ id: 'discord', url: 'https://discord.gg/ZhWvKVmTuh' },
],
},
];
return {
ID: 'streamfusion',
NAME: 'StreamFusion',
LOGO: 'https://stream-fusion.stremiofr.com/static/logo-stream-fusion.png',
URL: Env.DEFAULT_STREAMFUSION_URL,
TIMEOUT: Env.DEFAULT_STREAMFUSION_TIMEOUT || Env.DEFAULT_TIMEOUT,
USER_AGENT: Env.DEFAULT_STREAMFUSION_USER_AGENT || Env.DEFAULT_USER_AGENT,
SUPPORTED_SERVICES: supportedServices,
DESCRIPTION: 'Stremio addon focusing on french content',
OPTIONS: options,
SUPPORTED_STREAM_TYPES: [
constants.DEBRID_STREAM_TYPE,
constants.P2P_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, [])];
}
const usableServices =
this.getUsableServices(userData, options.services) || [];
return [
this.generateAddon(
userData,
options,
usableServices.map((service) => service.id)
),
];
}
private static generateAddon(
userData: UserData,
options: Record<string, any>,
serviceIds: ServiceId[]
): Addon {
return {
name: options.name || this.METADATA.NAME,
identifier:
serviceIds.length > 0
? serviceIds.length > 1
? 'multi'
: constants.SERVICE_DETAILS[serviceIds[0]].shortName
: 'p2p',
displayIdentifier:
serviceIds.length > 0
? serviceIds.length > 1
? serviceIds
.map((id) => constants.SERVICE_DETAILS[id].shortName)
.join(' | ')
: constants.SERVICE_DETAILS[serviceIds[0]].shortName
: 'P2P',
manifestUrl: this.generateManifestUrl(userData, options, serviceIds),
enabled: true,
resources: options.resources || this.METADATA.SUPPORTED_RESOURCES,
timeout: options.timeout || this.METADATA.TIMEOUT,
presetType: this.METADATA.ID,
presetInstanceId: '',
headers: {
'User-Agent': this.METADATA.USER_AGENT,
},
};
}
private static generateManifestUrl(
userData: UserData,
options: Record<string, any>,
serviceIds: ServiceId[]
) {
let url = options.url || this.METADATA.URL;
if (url.endsWith('/manifest.json')) {
return url;
}
const specialCases = {
[constants.OFFCLOUD_SERVICE]: (credentials: any) =>
`${credentials.email}:${credentials.password}`,
[constants.PIKPAK_SERVICE]: (credentials: any) =>
`${credentials.email}:${credentials.password}`,
};
url = url.replace(/\/$/, '');
const configString = this.base64EncodeJSON({
addonHost: options.url ? new URL(options.url).origin : this.METADATA.URL,
apiKey: options.streamFusionApiKey,
service: serviceIds.map(
(serviceId) => constants.SERVICE_DETAILS[serviceId].name
),
// this probably doesnt work for RD and AD as configuration page uses oauth flow and puts json response from RD/AD as values.
RDToken: serviceIds.includes(constants.REALDEBRID_SERVICE)
? this.getServiceCredential(constants.REALDEBRID_SERVICE, userData)
: '',
ADToken: serviceIds.includes(constants.ALLEDEBRID_SERVICE)
? this.getServiceCredential(constants.ALLEDEBRID_SERVICE, userData)
: '',
TBToken: serviceIds.includes(constants.TORBOX_SERVICE)
? this.getServiceCredential(constants.TORBOX_SERVICE, userData)
: '',
PMToken: serviceIds.includes(constants.PREMIUMIZE_SERVICE)
? this.getServiceCredential(constants.PREMIUMIZE_SERVICE, userData)
: '',
debridlinkApiKey: serviceIds.includes(constants.DEBRIDLINK_SERVICE)
? this.getServiceCredential(constants.DEBRIDLINK_SERVICE, userData)
: '',
easydebridApiKey: serviceIds.includes(constants.EASYDEBRID_SERVICE)
? this.getServiceCredential(constants.EASYDEBRID_SERVICE, userData)
: '',
offcloudCredentials: serviceIds.includes(constants.OFFCLOUD_SERVICE)
? this.getServiceCredential(
constants.OFFCLOUD_SERVICE,
userData,
specialCases
)
: '',
pikpakCredentials: serviceIds.includes(constants.PIKPAK_SERVICE)
? this.getServiceCredential(
constants.PIKPAK_SERVICE,
userData,
specialCases
)
: '',
TBUsenet: options.torboxUsenet,
TBSearch: options.torboxSearch,
maxSize: 150,
exclusionKeywords: [],
languages: ['en', 'fr', 'multi'],
sort: 'quality',
resultsPerQuality: 30,
maxResults: 100,
minCachedResults: 10,
exclusion: [],
cacheUrl: 'https://stremio-jackett-cacher.elfhosted.com/',
cache: true,
zilean: true,
yggflix: true,
sharewood: true,
yggtorrentCtg: options.catalogs?.includes('yggtorrent') ?? false,
yggflixCtg: options.catalogs?.includes('yggflix') ?? false,
torrenting:
serviceIds.length === 0 ? true : (options.torrenting ?? false),
debrid: serviceIds.length > 0,
metadataProvider: 'tmdb',
debridDownloader:
serviceIds.length > 0
? constants.SERVICE_DETAILS[serviceIds[0]].name
: '',
stremthru: true,
stremthruUrl: Env.DEFAULT_STREAMFUSION_STREMTHRU_URL,
});
return `${url}${configString ? '/' + configString : ''}/manifest.json`;
}
}