From 344c902b38c4e0a9ed03f8803edcde5974b06d9e Mon Sep 17 00:00:00 2001 From: Viren070 Date: Thu, 6 Nov 2025 14:45:28 +0000 Subject: [PATCH] feat(presets/usenet-streamer): add preset --- packages/core/src/presets/presetManager.ts | 4 + packages/core/src/presets/usenetStreamer.ts | 157 ++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 packages/core/src/presets/usenetStreamer.ts diff --git a/packages/core/src/presets/presetManager.ts b/packages/core/src/presets/presetManager.ts index e6f7a016..912d29a5 100644 --- a/packages/core/src/presets/presetManager.ts +++ b/packages/core/src/presets/presetManager.ts @@ -65,6 +65,7 @@ import { KnabenPreset } from './knaben.js'; import { BitmagnetPreset } from './bitmagnet.js'; import { SootioPreset } from './sootio.js'; import { TorrentGalaxyPreset } from './torrentGalaxy.js'; +import { UsenetStreamerPreset } from './usenetStreamer.js'; let PRESET_LIST: string[] = [ 'custom', 'torznab', @@ -97,6 +98,7 @@ let PRESET_LIST: string[] = [ 'easynews', 'easynewsPlus', 'easynewsPlusPlus', + 'usenet-streamer', 'dmm-cast', 'nuvio-streams', 'webstreamr', @@ -278,6 +280,8 @@ export class PresetManager { return SootioPreset; case 'torrent-galaxy': return TorrentGalaxyPreset; + case 'usenet-streamer': + return UsenetStreamerPreset; default: throw new Error(`Preset ${id} not found`); } diff --git a/packages/core/src/presets/usenetStreamer.ts b/packages/core/src/presets/usenetStreamer.ts new file mode 100644 index 00000000..cc57c8d2 --- /dev/null +++ b/packages/core/src/presets/usenetStreamer.ts @@ -0,0 +1,157 @@ +import { baseOptions, Preset } from './preset.js'; +import { constants, Env } from '../utils/index.js'; +import { + PresetMetadata, + Option, + Addon, + UserData, + ParsedStream, + Stream, +} from '../db/index.js'; +import { StreamParser } from '../parser/index.js'; + +export class UsenetStreamerParser extends StreamParser { + protected override getStreamType( + stream: Stream, + service: ParsedStream['service'], + currentParsedStream: ParsedStream + ): ParsedStream['type'] { + return constants.USENET_STREAM_TYPE; + } + + protected override parseServiceData( + string: string + ): ParsedStream['service'] | undefined { + return { + id: constants.NZBDAV_SERVICE, + cached: true, + }; + } +} + +export class UsenetStreamerPreset extends Preset { + static override getParser(): typeof StreamParser { + return UsenetStreamerParser; + } + + static override get METADATA(): PresetMetadata { + const supportedServices = [constants.NZBDAV_SERVICE]; + const supportedResources = [constants.STREAM_RESOURCE]; + + const options: Option[] = [ + { + id: 'name', + name: 'Name', + description: 'What to call this addon', + type: 'string', + required: true, + default: 'Usenet Streamer', + }, + { + id: 'manifestUrl', + name: 'Manifest URL', + description: + 'The URL to the manifest.json for your self-hosted Usenet Streamer addon. e.g. https://usenet-streamer.example.com/manifest.json or http://usenet-streamer:7000/manifest.json', + type: 'string', + required: true, + }, + { + id: 'timeout', + name: 'Timeout (ms)', + description: 'The timeout for this addon', + type: 'number', + required: true, + default: Env.DEFAULT_TIMEOUT, + constraints: { + min: Env.MIN_TIMEOUT, + max: Env.MAX_TIMEOUT, + forceInUi: false, // large ranges don't work well + }, + }, + { + id: 'mediaTypes', + name: 'Media Types', + description: + 'Limits this addon to the selected media types for streams. For example, selecting "Movie" means this addon will only be used for movie streams (if the addon supports them). Leave empty to allow all.', + type: 'multi-select', + required: false, + options: [ + { label: 'Movie', value: 'movie' }, + { label: 'Series', value: 'series' }, + { label: 'Anime', value: 'anime' }, + ], + default: [], + showInSimpleMode: false, + }, + { + id: 'socials', + name: '', + description: '', + type: 'socials', + socials: [ + { + id: 'github', + url: 'https://github.com/Sanket9225/UsenetStreamer', + }, + ], + }, + ]; + + return { + ID: 'usenet-streamer', + NAME: 'Usenet Streamer', + DESCRIPTION: + 'Usenet-powered instant streams for Stremio via Prowlarr and NZBDav', + LOGO: `https://raw.githubusercontent.com/Sanket9225/UsenetStreamer/refs/heads/master/assets/icon.png`, + URL: Env.EASYNEWS_URL, + TIMEOUT: Env.DEFAULT_TIMEOUT, + USER_AGENT: Env.DEFAULT_USER_AGENT, + SUPPORTED_SERVICES: supportedServices, + SUPPORTED_RESOURCES: supportedResources, + SUPPORTED_STREAM_TYPES: [constants.USENET_STREAM_TYPE], + OPTIONS: options, + }; + } + + static async generateAddons( + userData: UserData, + options: Record + ): Promise { + let manifestUrl = options.manifestUrl; + try { + manifestUrl = new URL(manifestUrl); + } catch (error) { + throw new Error( + `${options.name} has an invalid Manifest URL. It must be a valid link to a manifest.json` + ); + } + if (!manifestUrl.pathname.endsWith('/manifest.json')) { + throw new Error( + `${options.name} has an invalid Manifest URL. It must be a valid link to a manifest.json` + ); + } + return [this.generateAddon(userData, options)]; + } + + private static generateAddon( + userData: UserData, + options: Record + ): Addon { + return { + name: options.name || this.METADATA.NAME, + manifestUrl: options.manifestUrl || '', + enabled: true, + mediaTypes: options.mediaTypes || [], + 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, + }, + }; + } +}