diff --git a/packages/core/src/presets/presetManager.ts b/packages/core/src/presets/presetManager.ts index 7afb81ab..a5e1fe97 100644 --- a/packages/core/src/presets/presetManager.ts +++ b/packages/core/src/presets/presetManager.ts @@ -37,6 +37,7 @@ import { TMDBAddonPreset } from './tmdb'; import { TorrentsDbPreset } from './torrentsDb'; import { USATVPreset } from './usaTv'; import { ArgentinaTVPreset } from './argentinaTv'; +import { SubSourcePreset } from './subsource'; import { SubDLPreset } from './subdl'; const PRESET_LIST: string[] = [ @@ -77,6 +78,7 @@ const PRESET_LIST: string[] = [ 'dc-universe', 'doctor-who-universe', 'opensubtitles', + 'subsource', 'subdl', 'aiostreams', ]; @@ -176,6 +178,8 @@ export class PresetManager { return USATVPreset; case 'argentina-tv': return ArgentinaTVPreset; + case 'subsource': + return SubSourcePreset; case 'subdl': return SubDLPreset; default: diff --git a/packages/core/src/presets/subsource.ts b/packages/core/src/presets/subsource.ts new file mode 100644 index 00000000..07387a50 --- /dev/null +++ b/packages/core/src/presets/subsource.ts @@ -0,0 +1,223 @@ +import { Addon, Option, UserData } from '../db'; +import { Preset, baseOptions } from './preset'; +import { Env, RESOURCES, SUBTITLES_RESOURCE } from '../utils'; + +export class SubSourcePreset extends Preset { + static override get METADATA() { + const supportedResources = [SUBTITLES_RESOURCE]; + + const languages = [ + { label: 'Abkhazian', value: 'Abkhazian' }, + { label: 'Afrikaans', value: 'Afrikaans' }, + { label: 'Albanian', value: 'Albanian' }, + { label: 'Amharic', value: 'Amharic' }, + { label: 'Arabic', value: 'Arabic' }, + { label: 'Aragonese', value: 'Aragonese' }, + { label: 'Armenian', value: 'Armenian' }, + { label: 'Assamese', value: 'Assamese' }, + { label: 'Asturian', value: 'Asturian' }, + { label: 'Azerbaijani', value: 'Azerbaijani' }, + { label: 'Basque', value: 'Basque' }, + { label: 'Belarusian', value: 'Belarusian' }, + { label: 'Bengali', value: 'Bengali' }, + { label: 'Big 5 code', value: 'Big 5 code' }, + { label: 'Bosnian', value: 'Bosnian' }, + { label: 'Brazillian Portuguese', value: 'Brazillian Portuguese' }, + { label: 'Breton', value: 'Breton' }, + { label: 'Bulgarian', value: 'Bulgarian' }, + { label: 'Burmese', value: 'Burmese' }, + { label: 'Catalan', value: 'Catalan' }, + { label: 'Chinese', value: 'Chinese' }, + { label: 'Chinese BG code', value: 'Chinese BG code' }, + { label: 'Chinese (Cantonese)', value: 'Chinese (Cantonese)' }, + { label: 'Chinese (Simplified)', value: 'Chinese (Simplified)' }, + { label: 'Chinese (Traditional)', value: 'Chinese (Traditional)' }, + { label: 'Chinese Bilingual', value: 'Chinese Bilingual' }, + { label: 'Croatian', value: 'Croatian' }, + { label: 'Czech', value: 'Czech' }, + { label: 'Danish', value: 'Danish' }, + { label: 'Dari', value: 'Dari' }, + { label: 'Dutch', value: 'Dutch' }, + { label: 'English', value: 'English' }, + { label: 'Esperanto', value: 'Esperanto' }, + { label: 'Estonian', value: 'Estonian' }, + { label: 'Extremaduran', value: 'Extremaduran' }, + { label: 'Farsi/Persian', value: 'Farsi/Persian' }, + { label: 'Finnish', value: 'Finnish' }, + { label: 'French', value: 'French' }, + { label: 'French (Canada)', value: 'French (Canada)' }, + { label: 'French (France)', value: 'French (France)' }, + { label: 'Gaelic', value: 'Gaelic' }, + { label: 'Galician', value: 'Galician' }, + { label: 'Georgian', value: 'Georgian' }, + { label: 'German', value: 'German' }, + { label: 'Greek', value: 'Greek' }, + { label: 'Greenlandic', value: 'Greenlandic' }, + { label: 'Hebrew', value: 'Hebrew' }, + { label: 'Hindi', value: 'Hindi' }, + { label: 'Hungarian', value: 'Hungarian' }, + { label: 'Icelandic', value: 'Icelandic' }, + { label: 'Igbo', value: 'Igbo' }, + { label: 'Indonesian', value: 'Indonesian' }, + { label: 'Interlingua', value: 'Interlingua' }, + { label: 'Irish', value: 'Irish' }, + { label: 'Italian', value: 'Italian' }, + { label: 'Japanese', value: 'Japanese' }, + { label: 'Kannada', value: 'Kannada' }, + { label: 'Kazakh', value: 'Kazakh' }, + { label: 'Khmer', value: 'Khmer' }, + { label: 'Korean', value: 'Korean' }, + { label: 'Kurdish', value: 'Kurdish' }, + { label: 'Kyrgyz', value: 'Kyrgyz' }, + { label: 'Latvian', value: 'Latvian' }, + { label: 'Lithuanian', value: 'Lithuanian' }, + { label: 'Luxembourgish', value: 'Luxembourgish' }, + { label: 'Macedonian', value: 'Macedonian' }, + { label: 'Malay', value: 'Malay' }, + { label: 'Malayalam', value: 'Malayalam' }, + { label: 'Manipuri', value: 'Manipuri' }, + { label: 'Marathi', value: 'Marathi' }, + { label: 'Mongolian', value: 'Mongolian' }, + { label: 'Montenegrin', value: 'Montenegrin' }, + { label: 'Navajo', value: 'Navajo' }, + { label: 'Nepali', value: 'Nepali' }, + { label: 'Northen Sami', value: 'Northen Sami' }, + { label: 'Norwegian', value: 'Norwegian' }, + { label: 'Occitan', value: 'Occitan' }, + { label: 'Odia', value: 'Odia' }, + { label: 'Pashto', value: 'Pashto' }, + { label: 'Polish', value: 'Polish' }, + { label: 'Portuguese', value: 'Portuguese' }, + { label: 'Romanian', value: 'Romanian' }, + { label: 'Russian', value: 'Russian' }, + { label: 'Santli', value: 'Santli' }, + { label: 'Serbian', value: 'Serbian' }, + { label: 'Sindhi', value: 'Sindhi' }, + { label: 'Sinhala', value: 'Sinhala' }, + { label: 'Sinhalese', value: 'Sinhalese' }, + { label: 'Slovak', value: 'Slovak' }, + { label: 'Slovenian', value: 'Slovenian' }, + { label: 'Somali', value: 'Somali' }, + { label: 'Sorbian', value: 'Sorbian' }, + { label: 'Spanish', value: 'Spanish' }, + { label: 'Spanish (Latin America)', value: 'Spanish (Latin America)' }, + { label: 'Spanish (Spain)', value: 'Spanish (Spain)' }, + { label: 'Swahili', value: 'Swahili' }, + { label: 'Swedish', value: 'Swedish' }, + { label: 'Syriac', value: 'Syriac' }, + { label: 'Tagalog', value: 'Tagalog' }, + { label: 'Tamil', value: 'Tamil' }, + { label: 'Tatar', value: 'Tatar' }, + { label: 'Telugu', value: 'Telugu' }, + { label: 'Tetum', value: 'Tetum' }, + { label: 'Thai', value: 'Thai' }, + { label: 'Toki Pona', value: 'Toki Pona' }, + { label: 'Turkish', value: 'Turkish' }, + { label: 'Turkmen', value: 'Turkmen' }, + { label: 'Ukrainian', value: 'Ukrainian' }, + { label: 'Urdu', value: 'Urdu' }, + { label: 'Uzbek', value: 'Uzbek' }, + { label: 'Vietnamese', value: 'Vietnamese' }, + { label: 'Welsh', value: 'Welsh' }, + ]; + + const options: Option[] = [ + ...baseOptions( + 'SubSource', + supportedResources, + Env.DEFAULT_SUBSOURCE_TIMEOUT || Env.DEFAULT_TIMEOUT + ), + { + id: 'language', + type: 'multi-select', + name: 'Language', + description: 'Select the languages you want subtitles in', + options: languages, + required: true, + constraints: { + min: 1, + max: 5, + }, + }, + { + id: 'hearingImpairment', + type: 'select', + name: 'Hearing Impairment', + description: 'Select the hearing impairment you want subtitles in', + options: [ + { label: 'Include', value: 'hiInclude' }, + { label: 'Exclude', value: 'hiExclude' }, + { label: 'Only', value: 'hiOnly' }, + ], + required: true, + default: 'hiInclude', + }, + { + id: 'socials', + type: 'socials', + name: '', + description: '', + socials: [ + { + id: 'donate', + url: 'https://sociabuzz.com/apisapis/donate', + }, + ], + }, + ]; + + return { + ID: 'subsource', + NAME: 'SubSource', + LOGO: 'https://raw.githubusercontent.com/nexusdiscord/tv-logo/master/ss.png', + URL: Env.SUBSOURCE_URL, + TIMEOUT: Env.DEFAULT_SUBSOURCE_TIMEOUT || Env.DEFAULT_TIMEOUT, + USER_AGENT: Env.DEFAULT_SUBSOURCE_USER_AGENT || Env.DEFAULT_USER_AGENT, + SUPPORTED_SERVICES: [], + DESCRIPTION: 'SubSource addon', + OPTIONS: options, + SUPPORTED_STREAM_TYPES: [], + SUPPORTED_RESOURCES: supportedResources, + }; + } + + static async generateAddons( + userData: UserData, + options: Record + ): Promise { + return [this.generateAddon(userData, options)]; + } + + private static generateAddon( + userData: UserData, + options: Record + ): Addon { + return { + name: options.name || this.METADATA.NAME, + manifestUrl: this.generateManifestUrl(options), + enabled: true, + library: false, + 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(options: Record): string { + if (options.url?.endsWith('/manifest.json')) { + return options.url; + } + + const host = options.url || this.METADATA.URL; + + const config = Buffer.from( + `${options.language.join(',')}/${options.hearingImpairment}` + ).toString('base64'); + + return `${host}/${config}/manifest.json`; + } +} diff --git a/packages/core/src/utils/env.ts b/packages/core/src/utils/env.ts index bec84be4..050140d3 100644 --- a/packages/core/src/utils/env.ts +++ b/packages/core/src/utils/env.ts @@ -1180,6 +1180,19 @@ export const Env = cleanEnv(process.env, { desc: 'Default SubDL user agent', }), + SUBSOURCE_URL: url({ + default: 'https://subsource.strem.top', + desc: 'SubSource URL', + }), + DEFAULT_SUBSOURCE_TIMEOUT: num({ + default: undefined, + desc: 'Default SubSource timeout', + }), + DEFAULT_SUBSOURCE_USER_AGENT: userAgent({ + default: undefined, + desc: 'Default SubSource user agent', + }), + // Rate limiting settings DISABLE_RATE_LIMITS: bool({ default: false,