mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
refactor: improve base parser
- probably breaks a few things
This commit is contained in:
@@ -33,6 +33,9 @@ export function emojiToLanguage(emoji: string): string | undefined {
|
||||
)?.[0];
|
||||
}
|
||||
|
||||
export function codeToLanguage(code: string): string | undefined {
|
||||
return codeLanguageMap[code];
|
||||
}
|
||||
/**
|
||||
* A mapping of language names to their corresponding emoji flags.
|
||||
*
|
||||
@@ -90,3 +93,54 @@ const languageEmojiMap: Record<string, string> = {
|
||||
latino: '💃🏻',
|
||||
Latino: '🇲🇽',
|
||||
};
|
||||
|
||||
const codeLanguageMap: Record<string, string> = {
|
||||
EN: 'english',
|
||||
JA: 'japanese',
|
||||
ZH: 'chinese',
|
||||
RU: 'russian',
|
||||
AR: 'arabic',
|
||||
PT: 'portuguese',
|
||||
ES: 'spanish',
|
||||
FR: 'french',
|
||||
DE: 'german',
|
||||
IT: 'italian',
|
||||
KO: 'korean',
|
||||
HI: 'hindi',
|
||||
BN: 'bengali',
|
||||
PA: 'punjabi',
|
||||
MR: 'marathi',
|
||||
GU: 'gujarati',
|
||||
TA: 'tamil',
|
||||
TE: 'telugu',
|
||||
KN: 'kannada',
|
||||
ML: 'malayalam',
|
||||
TH: 'thai',
|
||||
VI: 'vietnamese',
|
||||
ID: 'indonesian',
|
||||
TR: 'turkish',
|
||||
HE: 'hebrew',
|
||||
FA: 'persian',
|
||||
UK: 'ukrainian',
|
||||
EL: 'greek',
|
||||
LT: 'lithuanian',
|
||||
LV: 'latvian',
|
||||
ET: 'estonian',
|
||||
PL: 'polish',
|
||||
CS: 'czech',
|
||||
SK: 'slovak',
|
||||
HU: 'hungarian',
|
||||
RO: 'romanian',
|
||||
BG: 'bulgarian',
|
||||
SR: 'serbian',
|
||||
HR: 'croatian',
|
||||
SL: 'slovenian',
|
||||
NL: 'dutch',
|
||||
DA: 'danish',
|
||||
FI: 'finnish',
|
||||
SV: 'swedish',
|
||||
NO: 'norwegian',
|
||||
MS: 'malay',
|
||||
LA: 'latino',
|
||||
MX: 'Latino',
|
||||
};
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './parser';
|
||||
export * from './regex';
|
||||
export * from './utils';
|
||||
|
||||
+103
-97
@@ -1,111 +1,117 @@
|
||||
const createRegex = (pattern: string): RegExp =>
|
||||
new RegExp(`(?<![^\\s\\[(_\\-.,])(${pattern})(?=[\\s\\)\\]_.\\-,]|$)`, 'i');
|
||||
|
||||
export const PARSE_REGEX = {
|
||||
resolutions: {
|
||||
'2160p': /(?<![^ [(_\-.])(4k|2160p|uhd)(?=[ \)\]_.-]|$)/i,
|
||||
'1080p': /(?<![^ [(_\-.])(1080p|fhd)(?=[ \)\]_.-]|$)/i,
|
||||
'720p': /(?<![^ [(_\-.])(720p|hd)(?=[ \)\]_.-]|$)/i,
|
||||
'480p': /(?<![^ [(_\-.])(480p|sd)(?=[ \)\]_.-]|$)/i,
|
||||
'2160p': createRegex('4k|2160p|u(ltra)?hd'),
|
||||
'1080p': createRegex('1080p|f(ull)?hd'),
|
||||
'720p': createRegex('720p|hd'),
|
||||
'480p': createRegex('480p|sd'),
|
||||
},
|
||||
qualities: {
|
||||
'BluRay REMUX':
|
||||
/(?<![^ [(_\-.])((blu[ .\-_]?ray|bd|br|b|uhd)[ .\-_]?remux)(?=[ \)\]_.-]|$)/i,
|
||||
BluRay:
|
||||
/(?<![^ [(_\-.])(blu[ .\-_]?ray|((bd|br|b)[ .\-_]?(rip|r)?))(?![ .\-_]?remux)(?=[ \)\]_.-]|$)/i,
|
||||
'WEB-DL':
|
||||
/(?<![^ [(_\-.])(web[ .\-_]?(dl)?)(?![ .\-_]?DLRip)(?=[ \)\]_.-]|$)/i,
|
||||
WEBRip: /(?<![^ [(_\-.])(web[ .\-_]?rip)(?=[ \)\]_.-]|$)/i,
|
||||
HDRip:
|
||||
/(?<![^ [(_\-.])(hd[ .\-_]?rip|web[ .\-_]?dl[ .\-_]?rip)(?=[ \)\]_.-]|$)/i,
|
||||
'HC HD-Rip': /(?<![^ [(_\-.])(hc|hd[ .\-_]?rip)(?=[ \)\]_.-]|$)/i,
|
||||
DVDRip: /(?<![^ [(_\-.])(dvd[ .\-_]?(rip|mux|r|full|5|9))(?=[ \)\]_.-]|$)/i,
|
||||
HDTV: /(?<![^ [(_\-.])((hd|pd)tv|tv[ .\-_]?rip|hdtv[ .\-_]?rip|dsr(ip)?|sat[ .\-_]?rip)(?=[ \)\]_.-]|$)/i,
|
||||
CAM: /(?<![^ [(_\-.])(cam|hdcam|cam[ .\-_]?rip)(?=[ \)\]_.-]|$)/i,
|
||||
TS: /(?<![^ [(_\-.])(telesync|ts|hd[ .\-_]?ts|pdvd|predvd(rip)?)(?=[ \)\]_.-]|$)/i,
|
||||
TC: /(?<![^ [(_\-.])(telecine|tc|hd[ .\-_]?tc)(?=[ \)\]_.-]|$)/i,
|
||||
SCR: /(?<![^ [(_\-.])(((dvd|bd|web)?[ .\-_]?)?(scr(eener)?))(?=[ \)\]_.-]|$)/i,
|
||||
'BluRay REMUX': createRegex('(blu[ .\\-_]?ray|bd|br|b|uhd)[ .\\-_]?remux'),
|
||||
BluRay: createRegex(
|
||||
'blu[ .\\-_]?ray|((bd|br|b)[ .\\-_]?(rip|r)?)(?![ .\\-_]?remux)'
|
||||
),
|
||||
'WEB-DL': createRegex('web[ .\\-_]?(dl)?(?![ .\\-_]?(DLRip|cam))'),
|
||||
WEBRip: createRegex('web[ .\\-_]?rip'),
|
||||
HDRip: createRegex('hd[ .\\-_]?rip|web[ .\\-_]?dl[ .\\-_]?rip'),
|
||||
'HC HD-Rip': createRegex('hc|hd[ .\\-_]?rip'),
|
||||
DVDRip: createRegex('dvd[ .\\-_]?(rip|mux|r|full|5|9)'),
|
||||
HDTV: createRegex(
|
||||
'(hd|pd)tv|tv[ .\\-_]?rip|hdtv[ .\\-_]?rip|dsr(ip)?|sat[ .\\-_]?rip'
|
||||
),
|
||||
CAM: createRegex('cam|hdcam|cam[ .\\-_]?rip'),
|
||||
TS: createRegex('telesync|ts|hd[ .\\-_]?ts|pdvd|predvd(rip)?'),
|
||||
TC: createRegex('telecine|tc|hd[ .\\-_]?tc'),
|
||||
SCR: createRegex('((dvd|bd|web)?[ .\\-_]?)?(scr(eener)?)'),
|
||||
},
|
||||
visualTags: {
|
||||
'HDR10+':
|
||||
/(?<![^ [(_\-.])(hdr[ .\-_]?10[ .\-_]?(plus|[+]))(?=[ \)\]_.-]|$)/i,
|
||||
HDR10:
|
||||
/(?<![^ [(_\-.])(hdr[ .\-_]?10)(?![ .\-_]?(?:\+|plus))(?=[ \)\]_.-]|$)/i,
|
||||
HDR: /(?<![^ [(_\-.])(hdr)(?![ .\-_]?10)(?![ .\-_]?(?:\+|plus))(?=[ \)\]_.-]|$)/i,
|
||||
DV: /(?<![^ [(_\-.])(dolby[ .\-_]?vision(?:[ .\-_]?atmos)?|dv)(?=[ \)\]_.-]|$)/i,
|
||||
'3D': /(?<![^ [(_\-.])((3|three)[ .\-_]?(d(imension)?(al)?))(?=[ \)\]_.-]|$)/i,
|
||||
IMAX: /(?<![^ [(_\-.])(imax)(?=[ \)\]_.-]|$)/i,
|
||||
AI: /(?<![^ [(_\-.])(ai[ .\-_]?(upscale|enhanced|remaster)?)(?=[ \)\]_.-]|$)/i,
|
||||
'HDR10+': createRegex('hdr[ .\\-_]?10[ .\\-_]?(plus|[+])'),
|
||||
HDR10: createRegex('hdr[ .\\-_]?10(?![ .\\-_]?(?:\\+|plus))'),
|
||||
HDR: createRegex('hdr(?![ .\\-_]?10)(?![ .\\-_]?(?:\\+|plus))'),
|
||||
DV: createRegex('dolby[ .\\-_]?vision(?:[ .\\-_]?atmos)?|dv'),
|
||||
'3D': createRegex('(3|three)[ .\\-_]?(d(imension)?(al)?)'),
|
||||
IMAX: createRegex('imax'),
|
||||
AI: createRegex('ai[ .\\-_]?(upscale|enhanced|remaster)?'),
|
||||
},
|
||||
audioTags: {
|
||||
Atmos: /(?<![^ [(_\-.])(atmos)(?=[ \)\]_.-]|$)/i,
|
||||
'DD+':
|
||||
/(?<![^ [(_\-.])((?:d(olby)?[ .\-_]?d(igital)?[ .\-_]?(p(lus)?|\+))(?:[ .\-_]?(5[ .\-_]?1|7[ .\-_]?1))?)(?=[ \)\]_.-]|$)/i,
|
||||
DD: /(?<![^ [(_\-.])((?:d(olby)?[ .\-_]?d(igital)?)(?:[ .\-_]?(5[ .\-_]?1|7[ .\-_]?1))?)(?=[ \)\]_.-]|$)/i,
|
||||
'DTS-HD MA': /(?<![^ [(_\-.])(dts[ .\-_]?hd[ .\-_]?ma)(?=[ \)\]_.-]|$)/i,
|
||||
'DTS-HD': /(?<![^ [(_\-.])(dts[ .\-_]?hd)(?![ .\-_]?ma)(?=[ \)\]_.-]|$)/i,
|
||||
DTS: /(?<![^ [(_\-.])(dts(?![ .\-_]?hd[ .\-_]?ma|[ .\-_]?hd))(?=[ \)\]_.-]|$)/i,
|
||||
TrueHD: /(?<![^ [(_\-.])(true[ .\-_]?hd)(?=[ \)\]_.-]|$)/i,
|
||||
5.1: /(?<![^ [(_\-.])((?:d(olby)?[ .\-_]?d(igital)?[ .\-_]?(p(lus)?|\+)?)?[ .\-_]?5[ .\-_]?1)(?=[ \)\]_.-]|$)/i,
|
||||
7.1: /(?<![^ [(_\-.])((?:d(olby)?[ .\-_]?d(igital)?[ .\-_]?(p(lus)?|\+)?)?[ .\-_]?7[ .\-_]?1)(?=[ \)\]_.-]|$)/i,
|
||||
AC3: /(?<![^ [(_\-.])(ac[ .\-_]?3)(?=[ \)\]_.-]|$)/i,
|
||||
AAC: /(?<![^ [(_\-.])(aac)(?=[ \)\]_.-]|$)/i,
|
||||
Atmos: createRegex('atmos'),
|
||||
'DD+': createRegex(
|
||||
'd(olby)?[ .\\-_]?d(igital)?[ .\\-_]?(p(lus)?|\\+)(?:[ .\\-_]?(5[ .\\-_]?1|7[ .\\-_]?1))?'
|
||||
),
|
||||
DD: createRegex(
|
||||
'd(olby)?[ .\\-_]?d(igital)?(?:[ .\\-_]?(5[ .\\-_]?1|7[ .\\-_]?1))?'
|
||||
),
|
||||
'DTS-HD MA': createRegex('dts[ .\\-_]?hd[ .\\-_]?ma'),
|
||||
'DTS-HD': createRegex('dts[ .\\-_]?hd(?![ .\\-_]?ma)'),
|
||||
DTS: createRegex('dts(?![ .\\-_]?hd[ .\\-_]?ma|[ .\\-_]?hd)'),
|
||||
TrueHD: createRegex('true[ .\\-_]?hd'),
|
||||
5.1: createRegex(
|
||||
'd(olby)?[ .\\-_]?d(igital)?[ .\\-_]?(p(lus)?|\\+)?[ .\\-_]?5[ .\\-_]?1'
|
||||
),
|
||||
7.1: createRegex(
|
||||
'd(olby)?[ .\\-_]?d(igital)?[ .\\-_]?(p(lus)?|\\+)?[ .\\-_]?7[ .\\-_]?1'
|
||||
),
|
||||
AC3: createRegex('ac[ .\\-_]?3'),
|
||||
AAC: createRegex('aac'),
|
||||
},
|
||||
encodes: {
|
||||
HEVC: /(?<![^ [(_\-.])(hevc|x265|h265|h\.265)(?=[ \)\]_.-]|$)/i,
|
||||
AVC: /(?<![^ [(_\-.])(avc|x264|h264|h\.264)(?=[ \)\]_.-]|$)/i,
|
||||
AV1: /(?<![^ [(_\-.])(av1)(?=[ \)\]_.-]|$)/i,
|
||||
'H-OU':
|
||||
/(?<![^ [(_\-.])(h?(alf)?[ .\-_]?(ou|over[ .\-_]?under))(?=[ \)\]_.-]|$)/i,
|
||||
'H-SBS':
|
||||
/(?<![^ [(_\-.])(h?(alf)?[ .\-_]?(sbs|side[ .\-_]?by[ .\-_]?side))(?=[ \)\]_.-]|$)/i,
|
||||
HEVC: createRegex('hevc|x265|h265|h\\.265'),
|
||||
AVC: createRegex('avc|x264|h264|h\\.264'),
|
||||
AV1: createRegex('av1'),
|
||||
'H-OU': createRegex('h?(alf)?[ .\\-_]?(ou|over[ .\\-_]?under)'),
|
||||
'H-SBS': createRegex('h?(alf)?[ .\\-_]?(sbs|side[ .\\-_]?by[ .\\-_]?side)'),
|
||||
},
|
||||
languages: {
|
||||
Multi: /(?<![^ [(_\-.])(multi|multi[ .\-_]?audio)(?=[ \)\]_.-]|$)/i,
|
||||
'Dual Audio': /(?<![^ [(_\-.])(dual[ .\-_]?audio)(?=[ \)\]_.-]|$)/i,
|
||||
English: /(?<![^ [(_\-.])(english|eng)(?=[ \)\]_.-]|$)/i,
|
||||
Japanese: /(?<![^ [(_\-.])(japanese|jap)(?=[ \)\]_.-]|$)/i,
|
||||
Chinese: /(?<![^ [(_\-.])(chinese|chi)(?=[ \)\]_.-]|$)/i,
|
||||
Russian: /(?<![^ [(_\-.])(russian|rus)(?=[ \)\]_.-]|$)/i,
|
||||
Arabic: /(?<![^ [(_\-.])(arabic|ara)(?=[ \)\]_.-]|$)/i,
|
||||
Portuguese: /(?<![^ [(_\-.])(portuguese|por)(?=[ \)\]_.-]|$)/i,
|
||||
Spanish: /(?<![^ [(_\-.])(spanish|spa|esp)(?=[ \)\]_.-]|$)/i,
|
||||
French: /(?<![^ [(_\-.])(french|fra)(?=[ \)\]_.-]|$)/i,
|
||||
German: /(?<![^ [(_\-.])(german|ger)(?=[ \)\]_.-]|$)/i,
|
||||
Italian: /(?<![^ [(_\-.])(italian|ita)(?=[ \)\]_.-]|$)/i,
|
||||
Korean: /(?<![^ [(_\-.])(korean|kor)(?=[ \)\]_.-]|$)/i,
|
||||
Hindi: /(?<![^ [(_\-.])(hindi|hin)(?=[ \)\]_.-]|$)/i,
|
||||
Bengali: /(?<![^ [(_\-.])(bengali|ben)(?=[ \)\]_.-]|$)/i,
|
||||
Punjabi: /(?<![^ [(_\-.])(punjabi|pan)(?=[ \)\]_.-]|$)/i,
|
||||
Marathi: /(?<![^ [(_\-.])(marathi|mar)(?=[ \)\]_.-]|$)/i,
|
||||
Gujarati: /(?<![^ [(_\-.])(gujarati|guj)(?=[ \)\]_.-]|$)/i,
|
||||
Tamil: /(?<![^ [(_\-.])(tamil|tam)(?=[ \)\]_.-]|$)/i,
|
||||
Telugu: /(?<![^ [(_\-.])(telugu|tel)(?=[ \)\]_.-]|$)/i,
|
||||
Kannada: /(?<![^ [(_\-.])(kannada|kan)(?=[ \)\]_.-]|$)/i,
|
||||
Malayalam: /(?<![^ [(_\-.])(malayalam|mal)(?=[ \)\]_.-]|$)/i,
|
||||
Thai: /(?<![^ [(_\-.])(thai|tha)(?=[ \)\]_.-]|$)/i,
|
||||
Vietnamese: /(?<![^ [(_\-.])(vietnamese|vie)(?=[ \)\]_.-]|$)/i,
|
||||
Indonesian: /(?<![^ [(_\-.])(indonesian|ind)(?=[ \)\]_.-]|$)/i,
|
||||
Turkish: /(?<![^ [(_\-.])(turkish|tur)(?=[ \)\]_.-]|$)/i,
|
||||
Hebrew: /(?<![^ [(_\-.])(hebrew|heb)(?=[ \)\]_.-]|$)/i,
|
||||
Persian: /(?<![^ [(_\-.])(persian|per)(?=[ \)\]_.-]|$)/i,
|
||||
Ukrainian: /(?<![^ [(_\-.])(ukrainian|ukr)(?=[ \)\]_.-]|$)/i,
|
||||
Greek: /(?<![^ [(_\-.])(greek|ell)(?=[ \)\]_.-]|$)/i,
|
||||
Lithuanian: /(?<![^ [(_\-.])(lithuanian|lit)(?=[ \)\]_.-]|$)/i,
|
||||
Latvian: /(?<![^ [(_\-.])(latvian|lav)(?=[ \)\]_.-]|$)/i,
|
||||
Estonian: /(?<![^ [(_\-.])(estonian|est)(?=[ \)\]_.-]|$)/i,
|
||||
Polish: /(?<![^ [(_\-.])(polish|pol)(?=[ \)\]_.-]|$)/i,
|
||||
Czech: /(?<![^ [(_\-.])(czech|cze)(?=[ \)\]_.-]|$)/i,
|
||||
Slovak: /(?<![^ [(_\-.])(slovak|slo)(?=[ \)\]_.-]|$)/i,
|
||||
Hungarian: /(?<![^ [(_\-.])(hungarian|hun)(?=[ \)\]_.-]|$)/i,
|
||||
Romanian: /(?<![^ [(_\-.])(romanian|rum)(?=[ \)\]_.-]|$)/i,
|
||||
Bulgarian: /(?<![^ [(_\-.])(bulgarian|bul)(?=[ \)\]_.-]|$)/i,
|
||||
Serbian: /(?<![^ [(_\-.])(serbian|srp)(?=[ \)\]_.-]|$)/i,
|
||||
Croatian: /(?<![^ [(_\-.])(croatian|hrv)(?=[ \)\]_.-]|$)/i,
|
||||
Slovenian: /(?<![^ [(_\-.])(slovenian|slv)(?=[ \)\]_.-]|$)/i,
|
||||
Dutch: /(?<![^ [(_\-.])(dutch|dut)(?=[ \)\]_.-]|$)/i,
|
||||
Danish: /(?<![^ [(_\-.])(danish|dan)(?=[ \)\]_.-]|$)/i,
|
||||
Finnish: /(?<![^ [(_\-.])(finnish|fin)(?=[ \)\]_.-]|$)/i,
|
||||
Swedish: /(?<![^ [(_\-.])(swedish|swe)(?=[ \)\]_.-]|$)/i,
|
||||
Norwegian: /(?<![^ [(_\-.])(norwegian|nor)(?=[ \)\]_.-]|$)/i,
|
||||
Malay: /(?<![^ [(_\-.])(malay)(?=[ \)\]_.-]|$)/i,
|
||||
Latino: /(?<![^ [(_\-.])(latino|lat)(?=[ \)\]_.-]|$)/i,
|
||||
Multi: createRegex('multi|multi[ .\\-_]?audio'),
|
||||
'Dual Audio': createRegex('dual[ .\\-_]?audio'),
|
||||
English: createRegex('english|eng'),
|
||||
Japanese: createRegex('japanese|jap'),
|
||||
Chinese: createRegex('chinese|chi'),
|
||||
Russian: createRegex('russian|rus'),
|
||||
Arabic: createRegex('arabic|ara'),
|
||||
Portuguese: createRegex('portuguese|por'),
|
||||
Spanish: createRegex('spanish|spa|esp'),
|
||||
French: createRegex('french|fra'),
|
||||
German: createRegex('german|ger'),
|
||||
Italian: createRegex('italian|ita'),
|
||||
Korean: createRegex('korean|kor'),
|
||||
Hindi: createRegex('hindi|hin'),
|
||||
Bengali: createRegex('bengali|ben'),
|
||||
Punjabi: createRegex('punjabi|pan'),
|
||||
Marathi: createRegex('marathi|mar'),
|
||||
Gujarati: createRegex('gujarati|guj'),
|
||||
Tamil: createRegex('tamil|tam'),
|
||||
Telugu: createRegex('telugu|tel'),
|
||||
Kannada: createRegex('kannada|kan'),
|
||||
Malayalam: createRegex('malayalam|mal'),
|
||||
Thai: createRegex('thai|tha'),
|
||||
Vietnamese: createRegex('vietnamese|vie'),
|
||||
Indonesian: createRegex('indonesian|ind'),
|
||||
Turkish: createRegex('turkish|tur'),
|
||||
Hebrew: createRegex('hebrew|heb'),
|
||||
Persian: createRegex('persian|per'),
|
||||
Ukrainian: createRegex('ukrainian|ukr'),
|
||||
Greek: createRegex('greek|ell'),
|
||||
Lithuanian: createRegex('lithuanian|lit'),
|
||||
Latvian: createRegex('latvian|lav'),
|
||||
Estonian: createRegex('estonian|est'),
|
||||
Polish: createRegex('polish|pol'),
|
||||
Czech: createRegex('czech|cze'),
|
||||
Slovak: createRegex('slovak|slo'),
|
||||
Hungarian: createRegex('hungarian|hun'),
|
||||
Romanian: createRegex('romanian|rum'),
|
||||
Bulgarian: createRegex('bulgarian|bul'),
|
||||
Serbian: createRegex('serbian|srp'),
|
||||
Croatian: createRegex('croatian|hrv'),
|
||||
Slovenian: createRegex('slovenian|slv'),
|
||||
Dutch: createRegex('dutch|dut'),
|
||||
Danish: createRegex('danish|dan'),
|
||||
Finnish: createRegex('finnish|fin'),
|
||||
Swedish: createRegex('swedish|swe'),
|
||||
Norwegian: createRegex('norwegian|nor'),
|
||||
Malay: createRegex('malay'),
|
||||
Latino: createRegex('latino|lat'),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
export function extractSizeInBytes(string: string, k: number): number {
|
||||
const sizePattern = /(\d+(\.\d+)?)\s?(KB|MB|GB)/;
|
||||
const match = string.match(sizePattern);
|
||||
if (!match) return 0;
|
||||
|
||||
const value = parseFloat(match[1]);
|
||||
const unit = match[3];
|
||||
|
||||
switch (unit) {
|
||||
case 'GB':
|
||||
return value * k * k * k;
|
||||
case 'MB':
|
||||
return value * k * k;
|
||||
case 'KB':
|
||||
return value * k;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractDurationInMs(string: string): number {
|
||||
const durationPattern = /(?:(\d+)h[:\s])?(?:(\d+)m[:\s])?(\d+)s/;
|
||||
const match = string.match(durationPattern);
|
||||
if (!match) return 0;
|
||||
|
||||
const hours = parseInt(match[1] || '0');
|
||||
const minutes = parseInt(match[2] || '0');
|
||||
const seconds = parseInt(match[3]);
|
||||
|
||||
return (hours * 60 * 60 + minutes * 60 + seconds) * 1000;
|
||||
}
|
||||
+212
-75
@@ -5,7 +5,7 @@ import {
|
||||
ParsedNameData,
|
||||
Config,
|
||||
} from '@aiostreams/types';
|
||||
import { extractSizeInBytes, parseFilename } from '@aiostreams/parser';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import {
|
||||
getMediaFlowConfig,
|
||||
getMediaFlowPublicIp,
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Settings,
|
||||
} from '@aiostreams/utils';
|
||||
import { fetch as uFetch, ProxyAgent } from 'undici';
|
||||
import { emojiToLanguage, codeToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
export class BaseWrapper {
|
||||
private readonly streamPath: string = 'stream/{type}/{id}.json';
|
||||
@@ -153,10 +154,6 @@ export class BaseWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
protected extractInfoHash(url: string): string | undefined {
|
||||
return url.match(/(?<=[-/[(;&])[a-fA-F0-9]{40}(?=[-\]\)/;&])/)?.[0];
|
||||
}
|
||||
|
||||
protected createParsedResult(
|
||||
parsedInfo: ParsedNameData,
|
||||
stream: Stream,
|
||||
@@ -209,6 +206,125 @@ export class BaseWrapper {
|
||||
},
|
||||
};
|
||||
}
|
||||
protected parseStream(stream: { [key: string]: any }): ParsedStream {
|
||||
// attempt to look for filename in behaviorHints.filename
|
||||
let filename =
|
||||
stream?.behaviorHints?.filename || stream.torrentTitle || stream.filename;
|
||||
|
||||
// if filename behaviorHint is not present, attempt to look for a filename in the stream description or title
|
||||
let description = stream.description || stream.title;
|
||||
|
||||
if (!filename && description) {
|
||||
console.log(
|
||||
`|DBG| wrappers > base > parseStream: No filename found in behaviorHints, attempting to parse from description`
|
||||
);
|
||||
const lines = description.split('\n');
|
||||
filename =
|
||||
lines.find(
|
||||
(line: string) =>
|
||||
line.match(
|
||||
/(?<![^ [_(\-.]])(?:s(?:eason)?[ .\-_]?(\d+)[ .\-_]?(?:e(?:pisode)?[ .\-_]?(\d+))?|(\d+)[xX](\d+))(?![^ \])_.-])/
|
||||
) || line.match(/(?<![^ [_(\-.])(\d{4})(?=[ \])_.-]|$)/i)
|
||||
) || lines[0];
|
||||
console.log(
|
||||
`|DBG| wrappers > base > parseStream: With description: ${description.replace(/\n/g, ' ').trim()}, chose filename as: ${filename.replace(/\n/g, ' ').trim()}`
|
||||
);
|
||||
} else if (!description) {
|
||||
console.log(
|
||||
`|WRN| wrappers > base > parseStream: No description found, filename could not be determined`
|
||||
);
|
||||
}
|
||||
|
||||
let stringToParse: string = filename || description || '';
|
||||
if (
|
||||
!(
|
||||
filename.match(
|
||||
/(?<![^ [_(\-.]])(?:s(?:eason)?[ .\-_]?(\d+)[ .\-_]?(?:e(?:pisode)?[ .\-_]?(\d+))?|(\d+)[xX](\d+))(?![^ \])_.-])/
|
||||
) || filename.match(/(?<![^ [_(\-.])(\d{4})(?=[ \])_.-]|$)/i)
|
||||
)
|
||||
) {
|
||||
stringToParse = description.replace(/\n/g, ' ').trim();
|
||||
}
|
||||
let parsedInfo: ParsedNameData = parseFilename(stringToParse);
|
||||
|
||||
// look for size in one of the many random places it could be
|
||||
let size: number | undefined;
|
||||
size =
|
||||
stream.behaviorHints?.videoSize ||
|
||||
stream.size ||
|
||||
stream.sizebytes ||
|
||||
stream.sizeBytes ||
|
||||
stream.torrentSize ||
|
||||
(description && this.extractSizeInBytes(description, 1024)) ||
|
||||
(stream.name && this.extractSizeInBytes(stream.name, 1024)) ||
|
||||
undefined;
|
||||
|
||||
if (typeof size === 'string') {
|
||||
size = parseInt(size);
|
||||
}
|
||||
// look for seeders
|
||||
let seeders: string | undefined;
|
||||
if (description) {
|
||||
seeders = this.extractStringBetweenEmojis(['👥', '👤'], description);
|
||||
}
|
||||
|
||||
// look for indexer
|
||||
let indexer: string | undefined;
|
||||
if (description) {
|
||||
indexer = this.extractStringBetweenEmojis(
|
||||
['🌐', '⚙️', '🔗', '🔎', '☁️'],
|
||||
description
|
||||
);
|
||||
}
|
||||
|
||||
[
|
||||
...this.extractCountryFlags(description),
|
||||
...this.extractCountryCodes(description),
|
||||
]
|
||||
.map(
|
||||
(codeOrFlag) =>
|
||||
emojiToLanguage(codeOrFlag) || codeToLanguage(codeOrFlag)
|
||||
)
|
||||
.filter((lang) => lang !== undefined)
|
||||
.map((lang) =>
|
||||
lang
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(
|
||||
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
||||
)
|
||||
.join(' ')
|
||||
)
|
||||
.forEach((lang) => {
|
||||
if (lang && !parsedInfo.languages.includes(lang)) {
|
||||
parsedInfo.languages.push(lang);
|
||||
}
|
||||
});
|
||||
|
||||
const duration = stream.duration || this.extractDurationInMs(description);
|
||||
// look for providers
|
||||
let provider: ParsedStream['provider'] = this.parseServiceData(
|
||||
stream.name || ''
|
||||
);
|
||||
|
||||
if (stream.infoHash && provider) {
|
||||
// if its a p2p result, it is not from a debrid service
|
||||
provider = undefined;
|
||||
}
|
||||
return this.createParsedResult(
|
||||
parsedInfo,
|
||||
stream,
|
||||
filename,
|
||||
size,
|
||||
provider,
|
||||
seeders ? parseInt(seeders) : undefined,
|
||||
undefined,
|
||||
indexer,
|
||||
duration,
|
||||
stream.personal,
|
||||
stream.infoHash || this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
}
|
||||
|
||||
protected parseServiceData(
|
||||
string: string
|
||||
@@ -249,84 +365,105 @@ export class BaseWrapper {
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
protected parseStream(stream: {
|
||||
[key: string]: any;
|
||||
}): ParsedStream | undefined {
|
||||
// attempt to look for filename in behaviorHints.filename, return undefined if not found
|
||||
let filename = stream?.behaviorHints?.filename;
|
||||
protected extractSizeInBytes(string: string, k: number): number {
|
||||
const sizePattern = /(\d+(\.\d+)?)\s?(KB|MB|GB)/i;
|
||||
const match = string.match(sizePattern);
|
||||
if (!match) return 0;
|
||||
|
||||
// if filename behaviorHint is not present, attempt to look for a filename in the stream description or title
|
||||
let description = stream.description || stream.title;
|
||||
const value = parseFloat(match[1]);
|
||||
const unit = match[3];
|
||||
|
||||
if (!filename && description) {
|
||||
console.log(
|
||||
`|DBG| wrappers > base > parseStream: No filename found in behaviorHints, attempting to parse from description`
|
||||
);
|
||||
const lines = description.split('\n');
|
||||
filename =
|
||||
lines.find(
|
||||
(line: string) =>
|
||||
line.match(
|
||||
/(?<![^ [_(\-.]])(?:s(?:eason)?[ .\-_]?(\d+)[ .\-_]?(?:e(?:pisode)?[ .\-_]?(\d+))?|(\d+)[xX](\d+))(?![^ \])_.-])/
|
||||
) || line.match(/(?<![^ [_(\-.])(\d{4})(?=[ \])_.-]|$)/i)
|
||||
) || lines[0];
|
||||
console.log(
|
||||
`|DBG| wrappers > base > parseStream: With description: ${description}, found filename: ${filename}`
|
||||
);
|
||||
} else if (!description) {
|
||||
console.log(
|
||||
`|WRN| wrappers > base > parseStream: No description found, filename could not be determined`
|
||||
);
|
||||
switch (unit.toUpperCase()) {
|
||||
case 'TB':
|
||||
return value * k * k * k * k;
|
||||
case 'GB':
|
||||
return value * k * k * k;
|
||||
case 'MB':
|
||||
return value * k * k;
|
||||
case 'KB':
|
||||
return value * k;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected extractDurationInMs(input: string): number {
|
||||
// Regular expression to match different formats of time durations
|
||||
const regex =
|
||||
/(\d+)h[:\s]?(\d+)m[:\s]?(\d+)s|(\d+)h[:\s]?(\d+)m|(\d+)h|(\d+)m|(\d+)s/gi;
|
||||
const match = regex.exec(input);
|
||||
if (!match) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let parsedInfo: ParsedNameData = parseFilename(filename || '');
|
||||
const hours = parseInt(match[1] || match[4] || match[5] || '0', 10);
|
||||
const minutes = parseInt(match[2] || match[5] || match[6] || '0', 10);
|
||||
const seconds = parseInt(match[3] || match[6] || match[7] || '0', 10);
|
||||
|
||||
// look for size in one of the many random places it could be
|
||||
let size: number | undefined;
|
||||
size =
|
||||
stream.behaviorHints?.videoSize ||
|
||||
stream.size ||
|
||||
stream.sizebytes ||
|
||||
(description && extractSizeInBytes(description, 1024)) ||
|
||||
(stream.name && extractSizeInBytes(stream.name, 1024)) ||
|
||||
undefined;
|
||||
// Convert to milliseconds
|
||||
const totalMilliseconds = (hours * 3600 + minutes * 60 + seconds) * 1000;
|
||||
|
||||
// look for seeders
|
||||
let seeders: string | undefined;
|
||||
if (description) {
|
||||
seeders = description.match(/(👥|👤) (\d+)/)?.[2];
|
||||
}
|
||||
return totalMilliseconds;
|
||||
}
|
||||
|
||||
// look for indexer
|
||||
let indexer: string | undefined;
|
||||
if (description) {
|
||||
const indexerMatch = RegExp(
|
||||
/[🌐⚙️🔗] ([^\s\p{Emoji_Presentation}]+(?:\s[^\s\p{Emoji_Presentation}]+)*)/u
|
||||
).exec(description || '');
|
||||
indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
}
|
||||
protected extractStringBetweenEmojis(
|
||||
startingEmojis: string[],
|
||||
string: string,
|
||||
endingEmojis?: string[]
|
||||
): string | undefined {
|
||||
const emojiPattern = /[\p{Emoji_Presentation}]/u;
|
||||
const startPattern = new RegExp(`(${startingEmojis.join('|')})`, 'u');
|
||||
const endPattern = endingEmojis
|
||||
? new RegExp(`(${endingEmojis.join('|')}|$|\n)`, 'u')
|
||||
: new RegExp(`(${emojiPattern.source}|$|\n)`, 'u');
|
||||
|
||||
// look for providers
|
||||
let provider: ParsedStream['provider'] = this.parseServiceData(
|
||||
stream.name || ''
|
||||
);
|
||||
const startMatch = string.match(startPattern);
|
||||
if (!startMatch) return undefined;
|
||||
|
||||
if (stream.infoHash && provider) {
|
||||
// if its a p2p result, it is not from a debrid service
|
||||
provider = undefined;
|
||||
}
|
||||
return this.createParsedResult(
|
||||
parsedInfo,
|
||||
stream,
|
||||
filename,
|
||||
size,
|
||||
provider,
|
||||
seeders ? parseInt(seeders) : undefined,
|
||||
undefined,
|
||||
indexer,
|
||||
stream.duration,
|
||||
stream.personal,
|
||||
stream.infoHash || this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
const startIndex = startMatch.index! + startMatch[0].length;
|
||||
const remainingString = string.slice(startIndex);
|
||||
|
||||
const endMatch = remainingString.match(endPattern);
|
||||
const endIndex = endMatch ? endMatch.index! : remainingString.length;
|
||||
|
||||
return remainingString.slice(0, endIndex).trim();
|
||||
}
|
||||
|
||||
protected extractStringAfter(
|
||||
startingPattern: string,
|
||||
string: string,
|
||||
endingPattern?: string
|
||||
) {
|
||||
const startPattern = new RegExp(startingPattern, 'u');
|
||||
const endPattern = endingPattern
|
||||
? new RegExp(endingPattern, 'u')
|
||||
: new RegExp(/$/u);
|
||||
|
||||
const startMatch = string.match(startPattern);
|
||||
if (!startMatch) return undefined;
|
||||
|
||||
const startIndex = startMatch.index! + startMatch[0].length;
|
||||
const remainingString = string.slice(startIndex);
|
||||
|
||||
const endMatch = remainingString.match(endPattern);
|
||||
const endIndex = endMatch ? endMatch.index! : remainingString.length;
|
||||
|
||||
return remainingString.slice(0, endIndex).trim();
|
||||
}
|
||||
|
||||
protected extractCountryFlags(string: string): string[] {
|
||||
const countryFlagPattern = /[\p{Regional_Indicator}]/u;
|
||||
const matches = string.match(countryFlagPattern);
|
||||
return matches ? [...new Set(matches)] : [];
|
||||
}
|
||||
|
||||
protected extractCountryCodes(string: string): string[] {
|
||||
const countryCodePattern = /\b(?!AC|DV)[A-Z]{2}\b/g;
|
||||
const matches = string.match(countryCodePattern);
|
||||
return matches ? [...new Set(matches)] : [];
|
||||
}
|
||||
|
||||
protected extractInfoHash(url: string): string | undefined {
|
||||
return url.match(/(?<=[-/[(;&])[a-fA-F0-9]{40}(?=[-\]\)/;&])/)?.[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
interface CometStream extends Stream {
|
||||
torrentTitle?: string;
|
||||
torrentSize?: number;
|
||||
}
|
||||
|
||||
export class Comet extends BaseWrapper {
|
||||
constructor(
|
||||
configString: string | null,
|
||||
@@ -31,44 +25,6 @@ export class Comet extends BaseWrapper {
|
||||
indexerTimeout || Settings.DEFAULT_COMET_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
protected parseStream(stream: CometStream): ParsedStream {
|
||||
const filename =
|
||||
stream.behaviorHints?.filename?.trim() ||
|
||||
stream.description?.split('\n')[0] ||
|
||||
stream.torrentTitle;
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(
|
||||
filename || stream.description || ''
|
||||
);
|
||||
const sizeInBytes = stream.torrentSize
|
||||
? stream.torrentSize
|
||||
: stream.description
|
||||
? extractSizeInBytes(stream.description, 1024)
|
||||
: undefined;
|
||||
|
||||
const debrid = this.parseServiceData(stream.name || '');
|
||||
|
||||
const indexerMatch = RegExp(/🔎 ([a-zA-Z0-9]+)/).exec(
|
||||
stream.description || ''
|
||||
);
|
||||
const indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
undefined,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
return parsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
const getCometConfig = (debridService: string, debridApiKey: string) => {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
import { emojiToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
export class Debridio extends BaseWrapper {
|
||||
constructor(
|
||||
@@ -29,66 +27,19 @@ export class Debridio extends BaseWrapper {
|
||||
}
|
||||
|
||||
protected parseStream(stream: Stream): ParsedStream {
|
||||
const [filename, metaString, languageString] =
|
||||
stream.title?.split('\n') || [];
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(
|
||||
filename || stream.title || ''
|
||||
);
|
||||
|
||||
const sizeInBytes = stream.behaviorHints?.videoSize
|
||||
? stream.behaviorHints.videoSize
|
||||
: metaString
|
||||
? extractSizeInBytes(metaString, 1024)
|
||||
: undefined;
|
||||
|
||||
const debrid = this.parseServiceData(
|
||||
stream.name?.split('\n')?.[0] || ''
|
||||
) || {
|
||||
id: 'easydebrid',
|
||||
cached: false,
|
||||
};
|
||||
const seedersMatch = RegExp(/👤 (\d+)/).exec(stream.title!);
|
||||
const seeders = seedersMatch ? parseInt(seedersMatch[1]) : undefined;
|
||||
|
||||
const indexerMatch = RegExp(/⚙️ (.+)/).exec(
|
||||
stream.title?.split('\n')[1] || ''
|
||||
);
|
||||
const indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
|
||||
if (languageString) {
|
||||
const languages = languageString.replace('🌐', '').split('|');
|
||||
languages.forEach((language, index) => {
|
||||
let convertedLanguage = language.trim();
|
||||
if (convertedLanguage === 'Multi Audio') {
|
||||
convertedLanguage = 'Multi';
|
||||
}
|
||||
|
||||
convertedLanguage =
|
||||
emojiToLanguage(convertedLanguage) || convertedLanguage;
|
||||
convertedLanguage = convertedLanguage.replace(/\b\w/g, (char) =>
|
||||
char.toUpperCase()
|
||||
);
|
||||
if (!parsedFilename.languages.includes(convertedLanguage)) {
|
||||
parsedFilename.languages.push(convertedLanguage);
|
||||
}
|
||||
});
|
||||
const superParsedStream = super.parseStream(stream);
|
||||
if (!superParsedStream) {
|
||||
return superParsedStream;
|
||||
}
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
seeders,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
return parsedStream;
|
||||
if (!superParsedStream.provider) {
|
||||
superParsedStream.provider = {
|
||||
id: 'easydebrid',
|
||||
cached: false,
|
||||
};
|
||||
}
|
||||
|
||||
return superParsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import {
|
||||
ParsedStream,
|
||||
Stream,
|
||||
@@ -43,8 +43,8 @@ export class DMMCast extends BaseWrapper {
|
||||
: stream.behaviorHints?.filename?.trim();
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(filename || '');
|
||||
const sizeInBytes = stream.title
|
||||
? extractSizeInBytes(stream.title, 1024)
|
||||
const sizeInBytes = stream.title?.split('\n').pop()?.includes('📦')
|
||||
? this.extractSizeInBytes(stream.title.split('\n').pop()!, 1024)
|
||||
: 0;
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import {
|
||||
parseFilename,
|
||||
extractSizeInBytes,
|
||||
extractDurationInMs,
|
||||
} from '@aiostreams/parser';
|
||||
import { ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { serviceDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
export class Easynews extends BaseWrapper {
|
||||
@@ -39,7 +35,7 @@ export class Easynews extends BaseWrapper {
|
||||
filename || stream.description || ''
|
||||
);
|
||||
const sizeInBytes = stream.description
|
||||
? extractSizeInBytes(sizeString, 1024)
|
||||
? this.extractSizeInBytes(sizeString, 1024)
|
||||
: undefined;
|
||||
|
||||
const provider = {
|
||||
@@ -47,7 +43,7 @@ export class Easynews extends BaseWrapper {
|
||||
cached: true,
|
||||
};
|
||||
|
||||
const durationInMs = extractDurationInMs(durationString || '');
|
||||
const durationInMs = this.extractDurationInMs(durationString || '');
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import {
|
||||
parseFilename,
|
||||
extractSizeInBytes,
|
||||
extractDurationInMs,
|
||||
} from '@aiostreams/parser';
|
||||
import { ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { serviceDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
export class EasynewsPlus extends BaseWrapper {
|
||||
@@ -41,7 +37,7 @@ export class EasynewsPlus extends BaseWrapper {
|
||||
const sizeInBytes = stream.behaviorHints?.videoSize
|
||||
? stream.behaviorHints.videoSize
|
||||
: sizeString
|
||||
? extractSizeInBytes(sizeString, 1024)
|
||||
? this.extractSizeInBytes(sizeString, 1024)
|
||||
: undefined;
|
||||
|
||||
const provider = {
|
||||
@@ -49,7 +45,7 @@ export class EasynewsPlus extends BaseWrapper {
|
||||
cached: true,
|
||||
};
|
||||
|
||||
const durationInMs = extractDurationInMs(durationString || '');
|
||||
const durationInMs = this.extractDurationInMs(durationString || '');
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
/*{"maxTorrents":30,"priotizePackTorrents":2,"excludeKeywords":[],"debridId":"alldebrid","hideUncached":false,"sortCached":[["quality",true],["size",true]],"sortUncached":[["seeders",true]],"forceCacheNextEpisode":false,"priotizeLanguages":[],"indexerTimeoutSec":60,"metaLanguage":"","enableMediaFlow":false,"mediaflowProxyUrl":"","mediaflowApiPassword":"","mediaflowPublicIp":"","qualities":[0,360,480,720,1080,2160],"indexers":["bitsearch","eztv","thepiratebay","therarbg","yts"],"debridApiKey":"KfejOmW7c5gUC3WNFCCq"}*/
|
||||
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
import { emojiToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
// name, title, url
|
||||
export class Jackettio extends BaseWrapper {
|
||||
@@ -30,66 +26,6 @@ export class Jackettio extends BaseWrapper {
|
||||
indexerTimeout || Settings.DEFAULT_JACKETTIO_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
protected parseStream(stream: Stream): ParsedStream {
|
||||
const filename =
|
||||
stream.behaviorHints?.filename?.trim() ||
|
||||
stream.title?.split('\n')[0] ||
|
||||
undefined;
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(
|
||||
filename || stream.title || ''
|
||||
);
|
||||
const sizeInBytes = stream.title
|
||||
? extractSizeInBytes(stream.title, 1024)
|
||||
: undefined;
|
||||
|
||||
const debrid = this.parseServiceData(stream.name || '');
|
||||
if (debrid?.id && debrid.cached === undefined) {
|
||||
debrid.cached = false;
|
||||
}
|
||||
|
||||
const indexerAndLanguages = RegExp(
|
||||
/⚙️([^\p{Emoji_Presentation}]+)([\p{Emoji_Presentation}\s]*)$/u
|
||||
).exec(stream.title || '');
|
||||
const indexer = indexerAndLanguages?.[1];
|
||||
indexerAndLanguages?.[2]
|
||||
.trim()
|
||||
.split(' ')
|
||||
.filter((lang) => lang)
|
||||
.map((lang) => {
|
||||
let convertedLanguage = lang.trim();
|
||||
if (convertedLanguage === 'Multi Audio') {
|
||||
convertedLanguage = 'Multi';
|
||||
}
|
||||
convertedLanguage =
|
||||
emojiToLanguage(convertedLanguage) || convertedLanguage;
|
||||
convertedLanguage = convertedLanguage.replace(/\b\w/g, (char) =>
|
||||
char.toUpperCase()
|
||||
);
|
||||
if (!parsedFilename.languages.includes(convertedLanguage)) {
|
||||
parsedFilename.languages.push(convertedLanguage);
|
||||
}
|
||||
});
|
||||
|
||||
const seederMatch = stream.title?.match(/(👥|👤) (\d+)/)?.[2];
|
||||
const seeders = seederMatch ? parseInt(seederMatch[1]) : undefined;
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
seeders,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
return parsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
const getJackettioConfigString = (
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails, Settings } from '@aiostreams/utils';
|
||||
import { emojiToLanguage } from '@aiostreams/formatters';
|
||||
import { addonDetails, Settings } from '@aiostreams/utils';
|
||||
|
||||
export class MediaFusion extends BaseWrapper {
|
||||
constructor(
|
||||
@@ -28,70 +26,15 @@ export class MediaFusion extends BaseWrapper {
|
||||
}
|
||||
|
||||
protected parseStream(stream: Stream): ParsedStream {
|
||||
let filename =
|
||||
stream.behaviorHints?.filename?.trim() ||
|
||||
stream.description?.split('\n')[0].replace('📂 ', '');
|
||||
|
||||
const parsedStream: ParsedStream = super.parseStream(stream);
|
||||
// handle content warning streams and join all lines into a single string
|
||||
if (
|
||||
filename &&
|
||||
parsedStream.filename &&
|
||||
stream.description &&
|
||||
filename.includes('Content Warning')
|
||||
parsedStream.filename.includes('Content Warning')
|
||||
) {
|
||||
filename = stream.description.split('\n').join(' ');
|
||||
parsedStream.filename = stream.description.split('\n').join(' ');
|
||||
}
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(
|
||||
filename || stream.behaviorHints?.bingeGroup || stream.description || ''
|
||||
);
|
||||
const sizeInBytes = stream.behaviorHints?.videoSize
|
||||
? stream.behaviorHints.videoSize
|
||||
: stream.description
|
||||
? extractSizeInBytes(stream.description, 1024)
|
||||
: undefined;
|
||||
|
||||
const debrid = this.parseServiceData(stream.name || '');
|
||||
|
||||
const indexerMatch = RegExp(
|
||||
/🔗 ([^\s\p{Emoji_Presentation}]+(?:\s[^\s\p{Emoji_Presentation}]+)*)/u
|
||||
).exec(stream.description || '');
|
||||
const indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
|
||||
const seedersMatch = RegExp(/👤 (\d+)/).exec(stream.description || '');
|
||||
const seeders = seedersMatch ? parseInt(seedersMatch[1]) : undefined;
|
||||
|
||||
stream.description?.split('\n').forEach((line) => {
|
||||
if (line.startsWith('🌐')) {
|
||||
// the line contains the languages separated by ' + '.
|
||||
// the languages can either be flag emojis or the language name.
|
||||
const normaliseLanguage = (lang: string) => {
|
||||
// convert emojis to language names, and uppercase the first letter of each word
|
||||
return (emojiToLanguage(lang) || lang).replace(/\b\w/g, (char) =>
|
||||
char.toUpperCase()
|
||||
);
|
||||
};
|
||||
const languages = line.replace('🌐 ', '').split(' + ');
|
||||
languages.forEach((lang) => {
|
||||
const normalisedLanguage = normaliseLanguage(lang);
|
||||
if (!parsedFilename.languages.includes(normalisedLanguage)) {
|
||||
parsedFilename.languages.push(normalisedLanguage);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
seeders,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
return parsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
import { emojiToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
// name, title, url
|
||||
export class OrionStremioAddon extends BaseWrapper {
|
||||
@@ -31,44 +29,14 @@ export class OrionStremioAddon extends BaseWrapper {
|
||||
}
|
||||
|
||||
protected parseStream(stream: Stream): ParsedStream {
|
||||
const parsedStream: ParsedStream = super.parseStream(stream);
|
||||
// handle error streams and join all lines into a single string
|
||||
const filename = stream.title
|
||||
? stream.title.includes('ERROR')
|
||||
? `Error: ${stream.title.split('\n')[1]} - ${stream.title.split('\n')[2]}`
|
||||
: stream.title.split('\n')[0]
|
||||
: stream.behaviorHints?.filename?.trim();
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(filename || '');
|
||||
const sizeInBytes = stream.title
|
||||
? extractSizeInBytes(stream.title, 1024)
|
||||
: 0;
|
||||
|
||||
const debrid = this.parseServiceData(stream.name?.split('\n')?.[1] || '');
|
||||
if (debrid?.id && !debrid.cached) {
|
||||
debrid.cached = true;
|
||||
}
|
||||
const seedersMatch = RegExp(/👤(\d+)/).exec(stream.title!);
|
||||
const seeders = seedersMatch ? parseInt(seedersMatch[1]) : undefined;
|
||||
|
||||
const indexerMatch = RegExp(/☁️(.+)/).exec(
|
||||
stream.title?.split('\n')[1] || ''
|
||||
);
|
||||
const indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
|
||||
// TODO: orion returns 2 letter language codes, convert them to full names
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
seeders,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
parsedStream.filename = filename;
|
||||
return parsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
import { emojiToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
interface PeerflixStream extends Stream {
|
||||
seed?: string;
|
||||
@@ -34,54 +32,6 @@ export class Peerflix extends BaseWrapper {
|
||||
indexerTimeout || Settings.DEFAULT_PEERFLIX_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
protected parseStream(stream: PeerflixStream): ParsedStream {
|
||||
const filename = stream.title
|
||||
? stream.title.split('\n')[0]
|
||||
: stream.behaviorHints?.filename?.trim();
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(filename || '');
|
||||
const sizeInBytes =
|
||||
stream.sizeBytes ||
|
||||
(stream.title ? extractSizeInBytes(stream.title, 1024) : 0);
|
||||
|
||||
const debrid = this.parseServiceData(stream.name || '');
|
||||
const seedersMatch = RegExp(/👤 (\d+)/).exec(stream.title!);
|
||||
const seeders =
|
||||
parseInt(stream.seed || seedersMatch?.[1] || '0') || undefined;
|
||||
|
||||
const indexerMatch = RegExp(
|
||||
/[🌐] ([^\s\p{Emoji_Presentation}]+(?:\s[^\s\p{Emoji_Presentation}]+)*)/u
|
||||
).exec(stream.title || stream.description || '');
|
||||
const indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
|
||||
if (
|
||||
(stream.language == 'en' || stream.name?.includes('🇬🇧')) &&
|
||||
!parsedFilename.languages.includes('English')
|
||||
) {
|
||||
parsedFilename.languages.push('English');
|
||||
} else if (
|
||||
(stream.language === 'es' || stream.name?.includes('🇪🇸')) &&
|
||||
!parsedFilename.languages.includes('Spanish')
|
||||
) {
|
||||
parsedFilename.languages.push('Spanish');
|
||||
}
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
seeders,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
return parsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPeerflixStreams(
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Stream,
|
||||
StreamRequest,
|
||||
} from '@aiostreams/types';
|
||||
import { extractSizeInBytes, parseFilename } from '@aiostreams/parser';
|
||||
import { parseFilename } from '@aiostreams/parser';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
|
||||
interface TorboxStream extends Stream {
|
||||
@@ -44,7 +44,7 @@ export class Torbox extends BaseWrapper {
|
||||
);
|
||||
}
|
||||
|
||||
protected parseStream(stream: TorboxStream): ParsedStream | undefined {
|
||||
protected parseStream(stream: TorboxStream): ParsedStream {
|
||||
let type = stream.type;
|
||||
let personal = false;
|
||||
if (stream.name.includes('Your Media')) {
|
||||
@@ -112,7 +112,7 @@ export class Torbox extends BaseWrapper {
|
||||
const sizeInBytes =
|
||||
stream.size ||
|
||||
validateBehaviorHintSize(stream.behaviorHints?.videoSize) ||
|
||||
(dSize ? extractSizeInBytes(dSize, 1000) : undefined);
|
||||
(dSize ? this.extractSizeInBytes(dSize, 1000) : undefined);
|
||||
|
||||
const provider = {
|
||||
id: 'torbox',
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { AddonDetail, ParsedNameData, StreamRequest } from '@aiostreams/types';
|
||||
import { parseFilename, extractSizeInBytes } from '@aiostreams/parser';
|
||||
import { ParsedStream, Stream, Config } from '@aiostreams/types';
|
||||
import { AddonDetail, StreamRequest } from '@aiostreams/types';
|
||||
import { ParsedStream, Config } from '@aiostreams/types';
|
||||
import { BaseWrapper } from './base';
|
||||
import { addonDetails, serviceDetails } from '@aiostreams/utils';
|
||||
import { addonDetails } from '@aiostreams/utils';
|
||||
import { Settings } from '@aiostreams/utils';
|
||||
import { emojiToLanguage } from '@aiostreams/formatters';
|
||||
|
||||
export class Torrentio extends BaseWrapper {
|
||||
constructor(
|
||||
@@ -27,71 +25,6 @@ export class Torrentio extends BaseWrapper {
|
||||
indexerTimeout || Settings.DEFAULT_TORRENTIO_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
protected parseStream(stream: Stream): ParsedStream {
|
||||
const filename = stream.title
|
||||
? stream.title.split('\n')[0]
|
||||
: stream.behaviorHints?.filename?.trim();
|
||||
|
||||
const parsedFilename: ParsedNameData = parseFilename(filename || '');
|
||||
const sizeInBytes = stream.title
|
||||
? extractSizeInBytes(stream.title, 1024)
|
||||
: 0;
|
||||
|
||||
const debrid = this.parseServiceData(stream.name?.split('\n')?.[0] || '');
|
||||
const seedersMatch = RegExp(/👤 (\d+)/).exec(stream.title!);
|
||||
const seeders = seedersMatch ? parseInt(seedersMatch[1]) : undefined;
|
||||
|
||||
const indexerMatch = RegExp(/⚙️ (.+)/).exec(
|
||||
stream.title?.split('\n')[1] || ''
|
||||
);
|
||||
const indexer = indexerMatch ? indexerMatch[1] : undefined;
|
||||
|
||||
const lastLine = stream.title?.split('\n').pop();
|
||||
if (
|
||||
lastLine &&
|
||||
!(
|
||||
lastLine.includes('👤') &&
|
||||
lastLine.includes('💾') &&
|
||||
lastLine.includes('⚙️')
|
||||
)
|
||||
) {
|
||||
// this line contains languages separated by ' / '.
|
||||
const languages = lastLine.split(' / ');
|
||||
// 'Multi Audio' can be converted to 'Multi'
|
||||
// other ones are flag emojis and need to be converted to languages.
|
||||
languages.forEach((language, index) => {
|
||||
let convertedLanguage = language.trim();
|
||||
if (convertedLanguage === 'Multi Audio') {
|
||||
convertedLanguage = 'Multi';
|
||||
} else {
|
||||
convertedLanguage = emojiToLanguage(language) || language;
|
||||
// uppercase the first letter of each word
|
||||
convertedLanguage = convertedLanguage.replace(/\b\w/g, (char) =>
|
||||
char.toUpperCase()
|
||||
);
|
||||
}
|
||||
if (!parsedFilename.languages.includes(convertedLanguage)) {
|
||||
parsedFilename.languages.push(convertedLanguage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const parsedStream: ParsedStream = this.createParsedResult(
|
||||
parsedFilename,
|
||||
stream,
|
||||
filename,
|
||||
sizeInBytes,
|
||||
debrid,
|
||||
seeders,
|
||||
undefined,
|
||||
indexer,
|
||||
undefined,
|
||||
undefined,
|
||||
this.extractInfoHash(stream.url || '')
|
||||
);
|
||||
return parsedStream;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTorrentioStreams(
|
||||
|
||||
Reference in New Issue
Block a user