diff --git a/packages/addon/src/addon.ts b/packages/addon/src/addon.ts index 36697be4..e32b50c2 100644 --- a/packages/addon/src/addon.ts +++ b/packages/addon/src/addon.ts @@ -359,13 +359,15 @@ export class AIOStreams { const initialStreams = filteredResults; const normaliseFilename = (filename?: string): string | undefined => filename - ?.replace( - /\.(mkv|mp4|avi|mov|wmv|flv|webm|m4v|mpg|mpeg|3gp|3g2|m2ts|ts|vob|ogv|ogm|divx|xvid|rm|rmvb|asf|mxf|mka|mks|mk3d|webm|f4v|f4p|f4a|f4b)$/i, - '' - ) - .replace(/[^\p{L}\p{N}+]/gu, '') - .replace(/\s+/g, '') - .toLowerCase(); + ? filename + ?.replace( + /\.(mkv|mp4|avi|mov|wmv|flv|webm|m4v|mpg|mpeg|3gp|3g2|m2ts|ts|vob|ogv|ogm|divx|xvid|rm|rmvb|asf|mxf|mka|mks|mk3d|webm|f4v|f4p|f4a|f4b)$/i, + '' + ) + .replace(/[^\p{L}\p{N}+]/gu, '') + .replace(/\s+/g, '') + .toLowerCase() + : undefined; const groupStreamsByKey = ( streams: ParsedStream[], @@ -398,7 +400,13 @@ export class AIOStreams { ); logger.info( - `Found ${Object.keys(streamsGroupedByFilename).length} unique filenames` + `Found ${Object.keys(streamsGroupedByFilename).length} unique filenames with ${ + initialStreams.length - + Object.values(streamsGroupedByFilename).reduce( + (sum, group) => sum + group.length, + 0 + ) + } streams not grouped` ); // Process grouped streams by filename diff --git a/packages/formatters/src/torbox.ts b/packages/formatters/src/torbox.ts index 8c814c94..1b2102f5 100644 --- a/packages/formatters/src/torbox.ts +++ b/packages/formatters/src/torbox.ts @@ -24,7 +24,7 @@ export function torboxFormat(stream: ParsedStream): { let description: string = ''; - description += `Quality: ${stream.quality}\nName: ${stream.filename}\nSize: ${formatSize(stream.size || 0)}\nLanguage: ${stream.languages.length > 0 ? stream.languages.join(', ') : 'Unknown'}`; + description += `Quality: ${stream.quality}\nName: ${stream.filename}\nSize: ${formatSize(stream.size || 0)}${stream.indexers ? `| Source: ${stream.indexers}` : ''}\nLanguage: ${stream.languages.length > 0 ? stream.languages.join(', ') : 'Unknown'}`; let streamType = stream.torrent ? 'Torrent' diff --git a/packages/wrappers/src/base.ts b/packages/wrappers/src/base.ts index 7ae84fb2..0a368c08 100644 --- a/packages/wrappers/src/base.ts +++ b/packages/wrappers/src/base.ts @@ -229,12 +229,14 @@ export class BaseWrapper { indexer?: string, duration?: number, personal?: boolean, - infoHash?: string + infoHash?: string, + message?: string ): ParseResult { return { type: 'stream', result: { ...parsedInfo, + message: message, addon: { name: this.addonName, id: this.addonId }, filename: filename, size: size, diff --git a/packages/wrappers/src/torbox.ts b/packages/wrappers/src/torbox.ts index ed4eb392..7d5582eb 100644 --- a/packages/wrappers/src/torbox.ts +++ b/packages/wrappers/src/torbox.ts @@ -15,10 +15,10 @@ const logger = createLogger('wrappers'); interface TorboxStream extends Stream { name: string; url: string; - hash: string; - is_cached: boolean; - size: number; description: string; + hash?: string; + is_cached?: boolean; + size?: number; magnet?: string; nzb?: string; seeders?: number; @@ -28,6 +28,7 @@ interface TorboxStream extends Stream { language?: string; type?: string; adult?: boolean; + user_search?: boolean; } export class Torbox extends BaseWrapper { @@ -48,104 +49,47 @@ export class Torbox extends BaseWrapper { } protected parseStream(stream: TorboxStream): ParseResult { - let type = stream.type; - let personal = false; - if (stream.name.includes('Your Media')) { - logger.debug(`${stream.name} was detected as a personal stream.`, { - func: 'torbox', - }); - personal = true; + const filename = + stream.behaviorHints?.filename || + stream.description.match(/Name:\s*([^\n]+)/)?.[1]; + let message = undefined; + if (stream.description.includes('Click play to start streaming')) { + message = stream.description; } - const [dQuality, dFilename, dSize, dLanguage, dAgeOrSeeders] = - stream.description.split('\n').map((field: string) => { - if (field.startsWith('Type')) { - // the last line can either contain only the type or the type and the seeders/age - // we will always return the age or seeders and assign the type to the variable declared outside the map - const parts = field.split('|'); - type = ['torrent', 'usenet', 'web'].includes(type || '') - ? type - : parts[0].split(':')[1].trim().toLowerCase(); - if (parts.length > 1) { - return parts[1].split(':')[1].trim(); - } - // since the last line only contains the type, we will return undefined - return undefined; - } - const [_, value] = field.split(':'); - return value.trim(); - }); - const filename = stream.behaviorHints?.filename || dFilename; const parsedFilename: ParsedNameData = parseFilename( filename || stream.description ); - /* If the quality from Torbox is not one of the qualities in the Config, they get filtered out - So, for now, we will not update the quality from Torbox - We can revisit this later and match the quality from Torbox to one of the qualities in the Config - if (parsedFilename.quality === 'Unknown' && quality !== 'Unknown') { - parsedFilename.quality = quality; - } - */ - - const language = stream.language || dLanguage; - const normaliseLanguage = (lang: string) => { - if (lang.toLowerCase() === 'multi audio') { - return 'Multi'; - } - return lang - .split(' ') - .map( - (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() - ) - .join(' '); - }; - if (language) { - const normalisedLanguage = normaliseLanguage(language); - if ( - normalisedLanguage !== 'Unknown' && - !parsedFilename.languages.includes(normalisedLanguage) - ) { - parsedFilename.languages.push(normalisedLanguage); - } - } - - // usenet results provide size as a string, we need to convert it to a number - const validateBehaviorHintSize = (size: string | number | undefined) => - typeof size === 'string' ? parseInt(size) : size; - const sizeInBytes = - stream.size || - validateBehaviorHintSize(stream.behaviorHints?.videoSize) || - (dSize ? this.extractSizeInBytes(dSize, 1000) : undefined); - + const size = stream.behaviorHints?.videoSize || stream.size; + const seeders = + stream.seeders && stream.seeders !== -1 ? stream.seeders : undefined; + const age = + stream.description.match(/\|\sAge:\s([0-9]+[dmyh])/)?.[1] || undefined; + const source = + stream.description.match(/Source:\s*([^\n]+)/)?.[1] || undefined; + const infoHash = + stream.hash || + stream.magnet?.match(/btih:([0-9a-fA-F]{40,})/)?.[1] || + undefined; + const personal = stream.name.includes('Your Media'); const provider = { id: 'torbox', - cached: stream.is_cached, + cached: stream.is_cached || true, }; - const seeders = - type === 'torrent' - ? stream.seeders || - (dAgeOrSeeders ? parseInt(dAgeOrSeeders) : undefined) - : undefined; - const age = type === 'usenet' ? dAgeOrSeeders || undefined : undefined; - let infoHash = stream.hash || this.extractInfoHash(stream.url); - if (age && infoHash) { - // we only need the infoHash for torrents - infoHash = undefined; - } - const parsedStream: ParseResult = this.createParsedResult( parsedFilename, stream, filename, - sizeInBytes, + size, provider, seeders, age, - undefined, + source, undefined, personal, - infoHash + infoHash, + message ); return parsedStream;