mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat(addon): support a custom formatter
not configurable in frontend yet
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
gdriveFormat,
|
||||
torrentioFormat,
|
||||
torboxFormat,
|
||||
customFormat,
|
||||
} from '@aiostreams/formatters';
|
||||
|
||||
export class AIOStreams {
|
||||
@@ -168,6 +169,16 @@ export class AIOStreams {
|
||||
description = _description;
|
||||
break;
|
||||
}
|
||||
case 'custom': {
|
||||
if (!this.config.customFormatter) {
|
||||
throw new Error('Custom formatter is not defined');
|
||||
}
|
||||
const { name: _name, description: _description } =
|
||||
customFormat(parsedStream, this.config.customFormatter);
|
||||
name = _name;
|
||||
description = _description;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error('Unsupported formatter');
|
||||
}
|
||||
|
||||
@@ -169,13 +169,22 @@ export function validateConfig(config: Config): {
|
||||
}
|
||||
|
||||
if (!allowedFormatters.includes(config.formatter)) {
|
||||
return createResponse(
|
||||
false,
|
||||
'invalidFormatter',
|
||||
`Invalid formatter: ${config.formatter}`
|
||||
);
|
||||
if (config.formatter === 'custom') {
|
||||
if (!config.customFormatter) {
|
||||
return createResponse(
|
||||
false,
|
||||
'missingCustomFormatter',
|
||||
'Custom formatter is required if custom formatter is selected'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return createResponse(
|
||||
false,
|
||||
'invalidFormatter',
|
||||
`Invalid formatter: ${config.formatter}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const service of config.services) {
|
||||
if (service.enabled) {
|
||||
const serviceDetail = serviceDetails.find(
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"description": "Library to take parsed information and return a formatted Stremio stream name and description",
|
||||
"dependencies": {
|
||||
"@aiostreams/types": "^1.0.0"
|
||||
"@aiostreams/types": "^1.0.0",
|
||||
"@aiostreams/wrappers": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Config, ParsedStream } from '@aiostreams/types';
|
||||
import { formatSize } from './utils';
|
||||
import { serviceDetails } from '@aiostreams/wrappers';
|
||||
import { languageToEmoji } from './utils';
|
||||
|
||||
/*
|
||||
{
|
||||
splitter: string; // e.g. ' - ', ' | ' etc. the string used to split the tags
|
||||
// we need to have a key for where the value has to be determined from the stream data.
|
||||
// examples of this is provider and seedersOrAge. provider.cached has different states and seedersOrAge has different states
|
||||
|
||||
// available values:
|
||||
// {resolution}, {quality}, {streamType} {encode}, {visualTags}, {audioTags}, {languages}, {provider}, {seedersOrAge}, {filename}, {size}, {addonName}, {seedersOrAge}
|
||||
languages: {
|
||||
useEmojis: boolean; // e.g. 🇬🇧
|
||||
}
|
||||
hideIfUnknown: boolean; // if true, the tag will not be shown if the value is unknown
|
||||
provider: {
|
||||
trueCacheStatus: string; // e.g. ⚡️
|
||||
falseCacheStatus: string; // e.g. ⏳
|
||||
undefinedCacheStatus: string; // e.g. ❓
|
||||
finalString: string; // e.g. [{providerShortName}{cacheStatus}] -> [TB⚡️] or this could be left empty
|
||||
}
|
||||
streamType: {
|
||||
torrent: string; // e.g. 🧲
|
||||
usenet: string; // e.g. 📡
|
||||
direct: string; // e.g. 📥
|
||||
unknown: string; // e.g. ❓
|
||||
finalString: string; // e.g. {streamType} -> 🧲
|
||||
}
|
||||
seedersOrAge: {
|
||||
whenSeeders: string; // e.g. 👤
|
||||
whenAge: string; // e.g. 📅
|
||||
finalString: string; // e.g. {seedersOrAge} {seedersOrAgeValue}
|
||||
}
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
*/
|
||||
|
||||
export function customFormat(stream: ParsedStream, customFormatter: Config['customFormatter']): {
|
||||
name: string;
|
||||
description: string;
|
||||
} {
|
||||
|
||||
if (!customFormatter) {
|
||||
throw new Error('Custom formatter not provided');
|
||||
}
|
||||
let name: string = customFormatter.name;
|
||||
let description: string = customFormatter.description;
|
||||
|
||||
|
||||
|
||||
if (stream.provider) {
|
||||
const cacheStatus = stream.provider.cached
|
||||
? customFormatter.provider.trueCacheStatus
|
||||
: stream.provider.cached === undefined
|
||||
? customFormatter.provider.undefinedCacheStatus
|
||||
: customFormatter.provider.falseCacheStatus;
|
||||
|
||||
name = name.replace('{provider}', customFormatter.provider.finalString.replace('{cacheStatus}', cacheStatus).replace('{providerShortName}', stream.provider.name));
|
||||
description = description.replace('{provider}', customFormatter.provider.finalString.replace('{cacheStatus}', cacheStatus).replace('{providerShortName}', stream.provider.name));
|
||||
}
|
||||
|
||||
if (stream.torrent?.infoHash) {
|
||||
name = name.replace('{streamType}', customFormatter.streamType.torrent);
|
||||
description = description.replace('{streamType}', customFormatter.streamType.torrent);
|
||||
}
|
||||
|
||||
if (stream.usenet) {
|
||||
name = name.replace('{streamType}', customFormatter.streamType.usenet);
|
||||
description = description.replace('{streamType}', customFormatter.streamType.usenet);
|
||||
}
|
||||
|
||||
if (stream.url) {
|
||||
name = name.replace('{streamType}', customFormatter.streamType.direct);
|
||||
description = description.replace('{streamType}', customFormatter.streamType.direct);
|
||||
}
|
||||
|
||||
if (!stream.torrent && !stream.usenet && !stream.url) {
|
||||
name = name.replace('{streamType}', customFormatter.streamType.unknown);
|
||||
description = description.replace('{streamType}', customFormatter.streamType.unknown);
|
||||
}
|
||||
|
||||
name = name.replace('{addonName}', stream.addonName);
|
||||
name = name.replace('{resolution}', stream.resolution);
|
||||
name = name.replace('{quality}', stream.quality);
|
||||
name = name.replace('{encode}', stream.encode);
|
||||
name = name.replace('{visualTags}', stream.visualTags.join(customFormatter.splitter));
|
||||
name = name.replace('{audioTags}', stream.audioTags.join(customFormatter.splitter));
|
||||
name = name.replace('{filename}', stream.filename || 'Unknown Name');
|
||||
name = name.replace('{size}', formatSize(stream.size || 0));
|
||||
name = name.replace(/\\n/g, '\n');
|
||||
|
||||
description = description.replace('{addonName}', stream.addonName);
|
||||
description = description.replace('{resolution}', stream.resolution);
|
||||
description = description.replace('{quality}', stream.quality);
|
||||
description = description.replace('{encode}', stream.encode);
|
||||
description = description.replace('{visualTags}', stream.visualTags.join(customFormatter.splitter));
|
||||
description = description.replace('{audioTags}', stream.audioTags.join(customFormatter.splitter));
|
||||
description = description.replace('{filename}', stream.filename || 'Unknown Name');
|
||||
description = description.replace('{size}', formatSize(stream.size || 0));
|
||||
description = description.replace(/\\n/g, '\n');
|
||||
|
||||
if (stream.torrent?.seeders) {
|
||||
const seedersStr = customFormatter.seedersOrAge.finalString.replace('{seedersOrAge}', customFormatter.seedersOrAge.whenSeeders).replace('{seedersOrAgeValue}', stream.torrent.seeders.toString());
|
||||
name = name.replace('{seedersOrAge}', seedersStr);
|
||||
description = description.replace('{seedersOrAge}', seedersStr);
|
||||
} else {
|
||||
name = name.replace('{seedersOrAge}', '');
|
||||
description = description.replace('{seedersOrAge}', '');
|
||||
}
|
||||
|
||||
if (stream.usenet?.age) {
|
||||
const ageStr = customFormatter.seedersOrAge.finalString.replace('{seedersOrAge}', customFormatter.seedersOrAge.whenAge).replace('{seedersOrAgeValue}', stream.usenet.age);
|
||||
name = name.replace('{seedersOrAge}', ageStr);
|
||||
description = description.replace('{seedersOrAge}', ageStr);
|
||||
} else {
|
||||
name = name.replace('{seedersOrAge}', '');
|
||||
description = description.replace('{seedersOrAge}', '');
|
||||
}
|
||||
|
||||
|
||||
if (customFormatter.languages.useEmojis) {
|
||||
description = description.replace('{languages}', stream.languages.map((lang) => {
|
||||
const emoji = languageToEmoji(lang);
|
||||
return emoji ? emoji : lang;
|
||||
}).join(customFormatter.splitter));
|
||||
name = name.replace('{languages}', stream.languages.map((lang) => {
|
||||
const emoji = languageToEmoji(lang);
|
||||
return emoji ? emoji : lang;
|
||||
}).join(customFormatter.splitter));
|
||||
} else {
|
||||
description = description.replace('{languages}', stream.languages.join(customFormatter.splitter));
|
||||
name = name.replace('{languages}', stream.languages.join(customFormatter.splitter));
|
||||
}
|
||||
|
||||
return {name, description}
|
||||
}
|
||||
@@ -2,3 +2,4 @@ export * from './gdrive';
|
||||
export * from './utils';
|
||||
export * from './torrentio';
|
||||
export * from './torbox';
|
||||
export * from './custom';
|
||||
@@ -8,6 +8,9 @@
|
||||
"references": [
|
||||
{
|
||||
"path": "../types"
|
||||
},
|
||||
{
|
||||
"path": "../wrappers"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -104,6 +104,38 @@ export interface Config {
|
||||
onlyShowCachedStreams: boolean;
|
||||
prioritiseLanguage: string | null;
|
||||
formatter: string;
|
||||
customFormatter?: {
|
||||
splitter: string; // e.g. ' - ', ' | ' etc. the string used to split the tags
|
||||
// we need to have a key for where the value has to be determined from the stream data.
|
||||
// examples of this is provider and seedersOrAge. provider.cached has different states and seedersOrAge has different states
|
||||
|
||||
// available values:
|
||||
// {resolution}, {quality}, {streamType} {encode}, {visualTags}, {audioTags}, {languages}, {provider}, {seedersOrAge}, {filename}, {size}, {addonName}, {seedersOrAge}
|
||||
languages: {
|
||||
useEmojis: boolean; // e.g. 🇬🇧
|
||||
}
|
||||
hideIfUnknown: boolean; // if true, the tag will not be shown if the value is unknown
|
||||
provider: {
|
||||
trueCacheStatus: string; // e.g. ⚡️
|
||||
falseCacheStatus: string; // e.g. ⏳
|
||||
undefinedCacheStatus: string; // e.g. ❓
|
||||
finalString: string; // e.g. [{providerShortName}{cacheStatus}] -> [TB⚡️] or this could be left empty
|
||||
}
|
||||
streamType: {
|
||||
torrent: string; // e.g. 🧲
|
||||
usenet: string; // e.g. 📡
|
||||
direct: string; // e.g. 📥
|
||||
unknown: string; // e.g. ❓
|
||||
finalString: string; // e.g. {streamType} -> 🧲
|
||||
}
|
||||
seedersOrAge: {
|
||||
whenSeeders: string; // e.g. 👤
|
||||
whenAge: string; // e.g. 📅
|
||||
finalString: string; // e.g. {seedersOrAge} {seedersOrAgeValue}
|
||||
}
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
maxSize?: number | null;
|
||||
minSize?: number | null;
|
||||
maxMovieSize: number | null;
|
||||
|
||||
Reference in New Issue
Block a user