brunner56's picture
implement app
0bfe2e3
import dotenv from 'dotenv';
import path from 'path';
import p from '../../../package.json';
import {
cleanEnv,
str,
host,
bool,
json,
makeValidator,
num,
EnvError,
port,
} from 'envalid';
try {
dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
} catch (error) {
console.error('Error loading .env file', error);
}
// define default timeouts and urls here so we can use them in the validators
// so we can use them in the validators
const DEFAULT_TIMEOUT = 15000; // 15 seconds
const secretKey = makeValidator((x) => {
if (typeof x !== 'string') {
throw new Error('Secret key must be a string');
}
// backwards compatibility for 32 character secret keys
if (x.length === 32) {
return x;
}
if (x.length === 64) {
if (!/^[0-9a-fA-F]{64}$/.test(x)) {
throw new EnvError('64-character secret key must be a hex string');
}
return x;
}
throw new EnvError('Secret key must be a 64-character hex string');
});
const regex = makeValidator((x) => {
if (typeof x !== 'string') {
throw new Error('Regex pattern must be a string');
}
try {
new RegExp(x);
return x;
} catch (e) {
throw new Error(`Invalid regex pattern: ${x}`);
}
});
const multipleRegex = makeValidator((x) => {
if (typeof x !== 'string') {
throw new EnvError('Regex pattern must be a string');
}
const patterns = x.split(/\s+/).filter(Boolean);
if (patterns.length > parseInt(process.env.MAX_REGEX_SORT_PATTERNS || '30')) {
throw new EnvError(
`Too many regex sort patterns in environment variables (max is ${process.env.MAX_REGEX_SORT_PATTERNS || '30'})`
);
}
// try compiling each pattern to check for validity
// each "pattern" is regexName::regexPattern, where regexName is optional
// we need to only extract the regexPattern part,
patterns.forEach((p) => {
try {
const delimiterIndex = p.indexOf('<::>');
if (delimiterIndex !== -1) {
p = p.slice(delimiterIndex + 2);
}
new RegExp(p);
} catch (e) {
throw new EnvError(`Invalid regex pattern: ${p}`);
}
});
// return normal input
return x;
});
const url = makeValidator((x) => {
if (x === '') {
return x;
}
try {
new URL(x);
} catch (e) {
throw new EnvError(`Invalid URL: ${x}`);
}
return x.endsWith('/') ? x : `${x}/`;
});
export const forcedPort = makeValidator<string>((input: string) => {
if (input === '') {
return '';
}
const coerced = +input;
if (
Number.isNaN(coerced) ||
`${coerced}` !== `${input}` ||
coerced % 1 !== 0 ||
coerced < 1 ||
coerced > 65535
) {
throw new EnvError(`Invalid port input: "${input}"`);
}
return coerced.toString();
});
const userAgent = makeValidator((x) => {
if (typeof x !== 'string') {
throw new Error('User agent must be a string');
}
// replace {version} with the version of the addon
return x.replace(/{version}/g, p.version);
});
const customConfigs = makeValidator((x) => {
try {
const parsed = JSON.parse(x);
if (typeof parsed !== 'object') {
throw new Error('Custom configs must be an object');
}
// must be a simple key-value object, of string-string
for (const key in parsed) {
if (typeof parsed[key] !== 'string') {
throw new Error(
`Custom config ${key} must be a string, got ${typeof parsed[key]}`
);
}
}
return parsed;
} catch (e) {
throw new Error('Custom configs must be a valid JSON string');
}
});
export const Settings = cleanEnv(process.env, {
ADDON_NAME: str({
default: 'AIOStreams',
desc: 'Name of the addon',
}),
ADDON_ID: str({
default: 'aiostreams.viren070.com',
desc: 'ID of the addon',
}),
PORT: port({
default: 3000,
desc: 'Port to run the addon on',
}),
BRANDING: str({
default: process.env.NEXT_PUBLIC_ELFHOSTED_BRANDING,
desc: 'Branding for the addon',
}),
SECRET_KEY: secretKey({
default: '',
desc: 'Secret key for the addon, used for encryption and must be 64 characters of hex',
}),
API_KEY: str({
default: '',
desc: 'API key for the addon, can be set to anything',
}),
SHOW_DIE: bool({
default: false,
desc: 'Show a game die emoji in streams for non-custom formats',
}),
DETERMINISTIC_ADDON_ID: bool({
default: true,
desc: 'Deterministic addon ID',
}),
ADDON_PROXY: url({
default: '',
desc: 'Proxy URL for the addon',
}),
ADDON_PROXY_CONFIG: str({
default: undefined,
desc: 'Proxy config for the addon in format of comma separated hostname:boolean',
}),
CUSTOM_CONFIGS: customConfigs<Record<string, string>>({
default: {},
desc: 'Custom configs for the addon in JSON key value format, using the key as the config name and the value as the config string',
}),
DISABLE_CUSTOM_CONFIG_GENERATOR_ROUTE: bool({
default: false,
desc: 'Disable custom config generator route',
}),
// logging settings
LOG_SENSITIVE_INFO: bool({
default: false,
desc: 'Log sensitive information',
}),
LOG_LEVEL: str({
default: 'info',
desc: 'Log level for the addon',
choices: ['info', 'debug', 'warn', 'error'],
}),
LOG_FORMAT: str({
default: 'text',
desc: 'Log format for the addon',
choices: ['text', 'json'],
}),
DISABLE_TORRENTIO: bool({
default: false,
desc: 'Disable Torrentio addon',
}),
DISABLE_TORRENTIO_MESSAGE: str({
default:
'The Torrentio addon has been disabled, please remove it to use this addon.',
desc: 'Message to show when the Torrentio addon is disabled',
}),
STREMIO_ADDONS_CONFIG_ISSUER: url({
default: 'https://stremio-addons.net',
desc: 'Issuer for the Stremio addons config',
}),
STREMIO_ADDONS_CONFIG_SIGNATURE: str({
default: undefined,
desc: 'Signature for the Stremio addons config',
}),
DEFAULT_USER_AGENT: userAgent({
default: `AIOStreams/${p.version}`,
desc: 'Default user agent for the addon',
}),
CACHE_MEDIAFLOW_IP_TTL: num({
default: 900,
desc: 'Cache TTL for MediaFlow IPs',
}),
CACHE_STREMTHRU_IP_TTL: num({
default: 900,
desc: 'Cache TTL for StremThru IPs',
}),
MAX_CACHE_SIZE: num({
default: 100000,
desc: 'Max cache size for the addon',
}),
// configuration settings
MAX_ADDONS: num({
default: 15,
desc: 'Max number of addons',
}),
MAX_KEYWORD_FILTERS: num({
default: 30,
desc: 'Max number of keyword filters',
}),
MAX_REGEX_SORT_PATTERNS: num({
default: 30,
desc: 'Max number of regex sort patterns',
}),
MAX_MOVIE_SIZE: num({
default: 161061273600,
desc: 'Max movie size in bytes',
}),
MAX_EPISODE_SIZE: num({
default: 161061273600,
desc: 'Max episode size in bytes',
}),
MAX_TIMEOUT: num({
default: 50000,
desc: 'Max timeout for the addon',
}),
MIN_TIMEOUT: num({
default: 1000,
desc: 'Min timeout for the addon',
}),
DEFAULT_TIMEOUT: num({
default: 15000,
desc: 'Default timeout for the addon',
}),
DEFAULT_REGEX_EXCLUDE_PATTERN: regex({
default: undefined,
desc: 'Default regex exclude pattern',
}),
DEFAULT_REGEX_INCLUDE_PATTERN: regex({
default: undefined,
desc: 'Default regex include pattern',
}),
DEFAULT_REGEX_SORT_PATTERNS: multipleRegex({
default: undefined,
desc: 'Default regex sort patterns',
}),
// MediaFlow settings
FORCE_MEDIAFLOW_HOSTNAME: host({
default: undefined,
desc: 'Force MediaFlow hostname',
}),
FORCE_MEDIAFLOW_PORT: forcedPort({
default: undefined,
desc: 'Force MediaFlow port',
}),
FORCE_MEDIAFLOW_PROTOCOL: str({
default: undefined,
desc: 'Force MediaFlow protocol',
choices: ['http', 'https'],
}),
DEFAULT_MEDIAFLOW_URL: url({
default: '',
desc: 'Default MediaFlow URL',
}),
DEFAULT_MEDIAFLOW_API_PASSWORD: str({
default: '',
desc: 'Default MediaFlow API password',
}),
DEFAULT_MEDIAFLOW_PUBLIC_IP: str({
default: '',
desc: 'Default MediaFlow public IP',
}),
MEDIAFLOW_IP_TIMEOUT: num({
default: 30000,
desc: 'MediaFlow IP timeout',
}),
ENCRYPT_MEDIAFLOW_URLS: bool({
default: true,
desc: 'Encrypt MediaFlow URLs',
}),
// StremThru settings
DEFAULT_STREMTHRU_URL: url({
default: '',
desc: 'Default StremThru URL',
}),
DEFAULT_STREMTHRU_CREDENTIAL: str({
default: '',
desc: 'Default StremThru credential',
}),
DEFAULT_STREMTHRU_PUBLIC_IP: str({
default: '',
desc: 'Default StremThru public IP',
}),
FORCE_STREMTHRU_HOSTNAME: host({
default: undefined,
desc: 'Force StremThru hostname',
}),
FORCE_STREMTHRU_PORT: forcedPort({
default: undefined,
desc: 'Force StremThru port',
}),
FORCE_STREMTHRU_PROTOCOL: str({
default: undefined,
desc: 'Force StremThru protocol',
choices: ['http', 'https'],
}),
STREMTHRU_TIMEOUT: num({
default: 30000,
desc: 'StremThru timeout',
}),
ENCRYPT_STREMTHRU_URLS: bool({
default: true,
desc: 'Encrypt StremThru URLs',
}),
COMET_URL: url({
default: 'https://comet.elfhosted.com/',
desc: 'Comet URL',
}),
COMET_INDEXERS: json({
default: ['dmm_public_hash_shares_only'],
desc: 'Comet indexers',
}),
FORCE_COMET_HOSTNAME: host({
default: undefined,
desc: 'Force Comet hostname',
}),
FORCE_COMET_PORT: forcedPort({
default: undefined,
desc: 'Force Comet port',
}),
FORCE_COMET_PROTOCOL: str({
default: undefined,
desc: 'Force Comet protocol',
choices: ['http', 'https'],
}),
DEFAULT_COMET_TIMEOUT: num({
default: undefined,
desc: 'Default Comet timeout',
}),
DEFAULT_COMET_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Comet user agent',
}),
// MediaFusion settings
MEDIAFUSION_URL: url({
default: 'https://mediafusion.elfhosted.com/',
desc: 'MediaFusion URL',
}),
MEDIAFUSION_API_PASSWORD: str({
default: '',
desc: 'MediaFusion API password',
}),
DEFAULT_MEDIAFUSION_TIMEOUT: num({
default: undefined,
desc: 'Default MediaFusion timeout',
}),
MEDIAFUSION_CONFIG_TIMEOUT: num({
default: 5000,
desc: 'MediaFusion config timeout',
}),
DEFAULT_MEDIAFUSION_USER_AGENT: userAgent({
default: undefined,
desc: 'Default MediaFusion user agent',
}),
FORCE_MEDIAFUSION_HOSTNAME: host({
default: undefined,
desc: 'Force MediaFusion hostname',
}),
FORCE_MEDIAFUSION_PORT: forcedPort({
default: undefined,
desc: 'Force MediaFusion port',
}),
FORCE_MEDIAFUSION_PROTOCOL: str({
default: undefined,
desc: 'Force MediaFusion protocol',
choices: ['http', 'https'],
}),
// Jackettio settings
JACKETTIO_URL: url({
default: 'https://jackettio.elfhosted.com/',
desc: 'Jackettio URL',
}),
DEFAULT_JACKETTIO_INDEXERS: json({
default: ['eztv', 'thepiratebay', 'therarbg', 'yts'],
desc: 'Default Jackettio indexers',
}),
DEFAULT_JACKETTIO_STREMTHRU_URL: url({
default: 'https://stremthru.13377001.xyz',
desc: 'Default Jackettio StremThru URL',
}),
DEFAULT_JACKETTIO_TIMEOUT: num({
default: undefined,
desc: 'Default Jackettio timeout',
}),
FORCE_JACKETTIO_HOSTNAME: host({
default: undefined,
desc: 'Force Jackettio hostname',
}),
FORCE_JACKETTIO_PORT: forcedPort({
default: undefined,
desc: 'Force Jackettio port',
}),
FORCE_JACKETTIO_PROTOCOL: str({
default: undefined,
desc: 'Force Jackettio protocol',
choices: ['http', 'https'],
}),
DEFAULT_JACKETTIO_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Jackettio user agent',
}),
// Stremio Jackett settings
STREMIO_JACKETT_URL: url({
default: 'https://stremio-jackett.elfhosted.com/',
desc: 'Stremio Jackett URL',
}),
FORCE_STREMIO_JACKETT_HOSTNAME: host({
default: undefined,
desc: 'Force Stremio Jackett hostname',
}),
FORCE_STREMIO_JACKETT_PORT: forcedPort({
default: undefined,
desc: 'Force Stremio Jackett port',
}),
FORCE_STREMIO_JACKETT_PROTOCOL: str({
default: undefined,
desc: 'Force Stremio Jackett protocol',
choices: ['http', 'https'],
}),
DEFAULT_STREMIO_JACKETT_JACKETT_URL: url({
default: undefined,
desc: 'Default Stremio Jackett Jackett URL',
}),
DEFAULT_STREMIO_JACKETT_JACKETT_API_KEY: str({
default: undefined,
desc: 'Default Stremio Jackett Jackett API key',
}),
DEFAULT_STREMIO_JACKETT_TMDB_API_KEY: str({
default: undefined,
desc: 'Default Stremio Jackett TMDB API key',
}),
DEFAULT_STREMIO_JACKETT_TIMEOUT: num({
default: undefined,
desc: 'Default Stremio Jackett timeout',
}),
DEFAULT_STREMIO_JACKETT_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Stremio Jackett user agent',
}),
// Torrentio settings
TORRENTIO_URL: url({
default: 'https://torrentio.strem.fun/',
desc: 'Torrentio URL',
}),
DEFAULT_TORRENTIO_TIMEOUT: num({
default: undefined,
desc: 'Default Torrentio timeout',
}),
DEFAULT_TORRENTIO_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Torrentio user agent',
}),
FORCE_TORRENTIO_HOSTNAME: host({
default: undefined,
desc: 'Force Torrentio hostname',
}),
FORCE_TORRENTIO_PORT: forcedPort({
default: undefined,
desc: 'Force Torrentio port',
}),
FORCE_TORRENTIO_PROTOCOL: str({
default: undefined,
desc: 'Force Torrentio protocol',
choices: ['http', 'https'],
}),
// Orion settings
ORION_STREMIO_ADDON_URL: url({
default: 'https://5a0d1888fa64-orion.baby-beamup.club/',
desc: 'Orion Stremio addon URL',
}),
DEFAULT_ORION_TIMEOUT: num({
default: undefined,
desc: 'Default Orion timeout',
}),
DEFAULT_ORION_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Orion user agent',
}),
// Peerflix settings
PEERFLIX_URL: url({
default: 'https://peerflix-addon.onrender.com/',
desc: 'Peerflix URL',
}),
DEFAULT_PEERFLIX_TIMEOUT: num({
default: undefined,
desc: 'Default Peerflix timeout',
}),
DEFAULT_PEERFLIX_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Peerflix user agent',
}),
FORCE_PEERFLIX_HOSTNAME: host({
default: undefined,
desc: 'Force Peerflix hostname',
}),
FORCE_PEERFLIX_PORT: forcedPort({
default: undefined,
desc: 'Force Peerflix port',
}),
FORCE_PEERFLIX_PROTOCOL: str({
default: undefined,
desc: 'Force Peerflix protocol',
choices: ['http', 'https'],
}),
// Torbox settings
TORBOX_STREMIO_URL: url({
default: 'https://stremio.torbox.app/',
desc: 'Torbox Stremio URL',
}),
DEFAULT_TORBOX_TIMEOUT: num({
default: undefined,
desc: 'Default Torbox timeout',
}),
DEFAULT_TORBOX_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Torbox user agent',
}),
// Easynews settings
EASYNEWS_URL: url({
default: 'https://ea627ddf0ee7-easynews.baby-beamup.club/',
desc: 'Easynews URL',
}),
DEFAULT_EASYNEWS_TIMEOUT: num({
default: undefined,
desc: 'Default Easynews timeout',
}),
DEFAULT_EASYNEWS_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Easynews user agent',
}),
// Easynews+ settings
EASYNEWS_PLUS_URL: url({
default: 'https://b89262c192b0-stremio-easynews-addon.baby-beamup.club/',
desc: 'Easynews+ URL',
}),
DEFAULT_EASYNEWS_PLUS_TIMEOUT: num({
default: undefined,
desc: 'Default Easynews+ timeout',
}),
DEFAULT_EASYNEWS_PLUS_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Easynews+ user agent',
}),
// Easynews++ settings
EASYNEWS_PLUS_PLUS_URL: url({
default: 'https://easynews-cloudflare-worker.jqrw92fchz.workers.dev/',
desc: 'Easynews++ URL',
}),
EASYNEWS_PLUS_PLUS_PUBLIC_URL: url({
default: undefined,
desc: 'Easynews++ public URL',
}),
DEFAULT_EASYNEWS_PLUS_PLUS_TIMEOUT: num({
default: undefined,
desc: 'Default Easynews++ timeout',
}),
DEFAULT_EASYNEWS_PLUS_PLUS_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Easynews++ user agent',
}),
// Debridio Settings
DEBRIDIO_URL: url({
default: 'https://debridio.adobotec.com/',
desc: 'Debridio URL',
}),
DEFAULT_DEBRIDIO_TIMEOUT: num({
default: undefined,
desc: 'Default Debridio timeout',
}),
DEFAULT_DEBRIDIO_USER_AGENT: userAgent({
default: undefined,
desc: 'Default Debridio user agent',
}),
FORCE_DEBRIDIO_HOSTNAME: host({
default: undefined,
desc: 'Force Debridio hostname',
}),
FORCE_DEBRIDIO_PORT: forcedPort({
default: undefined,
desc: 'Force Debridio port',
}),
FORCE_DEBRIDIO_PROTOCOL: str({
default: undefined,
desc: 'Force Debridio protocol',
choices: ['http', 'https'],
}),
// StremThru Store settings
STREMTHRU_STORE_URL: url({
default: 'https://stremthru.elfhosted.com/stremio/store/',
desc: 'StremThru Store URL',
}),
DEFAULT_STREMTHRU_STORE_TIMEOUT: num({
default: undefined,
desc: 'Default StremThru Store timeout',
}),
DEFAULT_STREMTHRU_STORE_USER_AGENT: userAgent({
default: undefined,
desc: 'Default StremThru Store user agent',
}),
FORCE_STREMTHRU_STORE_HOSTNAME: host({
default: undefined,
desc: 'Force StremThru Store hostname',
}),
FORCE_STREMTHRU_STORE_PORT: forcedPort({
default: undefined,
desc: 'Force StremThru Store port',
}),
FORCE_STREMTHRU_STORE_PROTOCOL: str({
default: undefined,
desc: 'Force StremThru Store protocol',
choices: ['http', 'https'],
}),
// DMM Cast settings
DEFAULT_DMM_CAST_TIMEOUT: num({
default: undefined,
desc: 'Default DMM Cast timeout',
}),
DEFAULT_DMM_CAST_USER_AGENT: userAgent({
default: undefined,
desc: 'Default DMM Cast user agent',
}),
// GDrive settings
DEFAULT_GDRIVE_TIMEOUT: num({
default: undefined,
desc: 'Default GDrive timeout',
}),
DEFAULT_GDRIVE_USER_AGENT: userAgent({
default: undefined,
desc: 'Default GDrive user agent',
}),
});