refactor: simplify MediaFusion stream retrieval using encoded_user_data, avoiding the need for encrypting configurations

This commit is contained in:
Viren070
2025-04-28 14:53:55 +01:00
parent 657abaef4c
commit 2e82c1cd33
+115 -184
View File
@@ -1,13 +1,7 @@
import { AddonDetail, ParseResult, StreamRequest } from '@aiostreams/types';
import { ParsedStream, Stream, Config } from '@aiostreams/types';
import { BaseWrapper } from './base';
import {
addonDetails,
getTextHash,
getTimeTakenSincePoint,
Settings,
createLogger,
} from '@aiostreams/utils';
import { addonDetails, Settings, createLogger } from '@aiostreams/utils';
const logger = createLogger('wrappers');
@@ -20,16 +14,19 @@ export class MediaFusion extends BaseWrapper {
userConfig: Config,
indexerTimeout?: number
) {
let url = overrideUrl
? overrideUrl
: Settings.MEDIAFUSION_URL + (configString ? configString + '/' : '');
let url = overrideUrl ? overrideUrl : Settings.MEDIAFUSION_URL;
const headers =
configString && !overrideUrl
? { encoded_user_data: configString }
: undefined;
super(
addonName,
url,
addonId,
userConfig,
indexerTimeout || Settings.DEFAULT_MEDIAFUSION_TIMEOUT
indexerTimeout || Settings.DEFAULT_MEDIAFUSION_TIMEOUT,
headers
);
}
@@ -70,40 +67,6 @@ export async function getMediafusionStreams(
: undefined;
const liveSearchStreams =
mediafusionOptions.liveSearchStreams === 'true' ? true : false;
const cache = config.instanceCache;
const getConfigString = async (data: any): Promise<string> => {
const startTime = Date.now();
const cacheKey = getTextHash(`mediafusionConfig:${JSON.stringify(data)}`);
const cachedConfig = cache ? cache.get(cacheKey) : null;
if (cachedConfig) {
logger.info(`Returning cached config string`, {
func: 'mediafusion.getConfigString',
});
return cachedConfig;
}
try {
const encryptedStr = await _getConfigString(data);
logger.info(
`Config encryption took ${getTimeTakenSincePoint(startTime)}`,
{
func: 'mediafusion.getConfigString',
}
);
cache?.set(cacheKey, encryptedStr, Settings.CACHE_MEDIAFUSION_CONFIG_TTL);
return encryptedStr;
} catch (error: any) {
if (error.name === 'TimeoutError') {
throw new Error(
`Config encryption timed out after ${getTimeTakenSincePoint(
startTime
)}`
);
}
throw new Error(`Failed to get config string: ${error.message}`);
}
};
// If overrideUrl is provided, use it to get streams and skip all other steps
if (mediafusionOptions.overrideUrl) {
@@ -125,13 +88,12 @@ export async function getMediafusionStreams(
// if no usable services found, use mediafusion without debrid
if (usableServices.length < 1) {
const configString = await getConfigString(
getMediaFusionConfig(
mediafusionOptions.filterCertificationLevels,
mediafusionOptions.filterNudity,
liveSearchStreams
)
const configString = getConfigString(
mediafusionOptions.filterCertificationLevels,
mediafusionOptions.filterNudity,
liveSearchStreams
);
const mediafusion = new MediaFusion(
configString,
null,
@@ -170,16 +132,15 @@ export async function getMediafusionStreams(
}
// get the encrypted mediafusion string
const mediafusionConfig = getMediaFusionConfig(
const mediafusionConfig = getConfigString(
mediafusionOptions.filterCertificationLevels,
mediafusionOptions.filterNudity,
liveSearchStreams,
debridService.id,
debridService.credentials
);
const encryptedStr = await getConfigString(mediafusionConfig);
const mediafusion = new MediaFusion(
encryptedStr,
mediafusionConfig,
null,
mediafusionOptions.overrideName,
addonId,
@@ -197,30 +158,18 @@ export async function getMediafusionStreams(
throw new Error(`No enabled services found for MediaFusion`);
}
const promises = servicesToUse.map(async (service) => {
const mediafusionConfig = getMediaFusionConfig(
logger.info(`Getting MediaFusion streams for ${service.id}`, {
func: 'mediafusion',
});
const encodedConfigString = getConfigString(
mediafusionOptions.filterCertificationLevels,
mediafusionOptions.filterNudity,
liveSearchStreams,
service.id,
service.credentials
);
logger.info(`Getting MediaFusion streams for ${service.id}`, {
func: 'mediafusion',
});
let encryptedStr: string = '';
try {
encryptedStr = await getConfigString(mediafusionConfig);
if (!encryptedStr) {
throw new Error(
'An unknown error occurred while getting config string'
);
}
} catch (error: any) {
logger.error(`${error.message}`, { func: 'mediafusion' });
throw new Error(error.message);
}
const mediafusion = new MediaFusion(
encryptedStr,
encodedConfigString,
null,
mediafusionOptions.overrideName,
addonId,
@@ -246,7 +195,7 @@ export async function getMediafusionStreams(
};
}
const getMediaFusionConfig = (
const getConfigString = (
filterCertificationLevels?: string,
filterNudity?: string,
liveSearchStreams: boolean = false,
@@ -263,116 +212,98 @@ const getMediaFusionConfig = (
const levels = filterNudity.split(',');
nudityFilter = levels.map((level) => level.trim());
}
return {
streaming_provider: service
? {
token: !['pikpak'].includes(service) ? credentials.apiKey : undefined,
email: credentials.email,
password: credentials.password,
service: service,
enable_watchlists_catalogs: false,
download_via_browser: false,
only_show_cached_streams: false,
}
: null,
selected_catalogs: [],
selected_resolutions: [
'4k',
'2160p',
'1440p',
'1080p',
'720p',
'576p',
'480p',
'360p',
'240p',
null,
],
enable_catalogs: true,
enable_imdb_metadata: false,
max_size: 'inf',
max_streams_per_resolution: '500',
torrent_sorting_priority: [
{ key: 'language', direction: 'desc' },
{ key: 'cached', direction: 'desc' },
{ key: 'resolution', direction: 'desc' },
{ key: 'quality', direction: 'desc' },
{ key: 'size', direction: 'desc' },
{ key: 'seeders', direction: 'desc' },
{ key: 'created_at', direction: 'desc' },
],
show_full_torrent_name: true,
show_language_country_flag: true,
nudity_filter: nudityFilter,
certification_filter: certificationFilter,
language_sorting: [
'English',
'Tamil',
'Hindi',
'Malayalam',
'Kannada',
'Telugu',
'Chinese',
'Russian',
'Arabic',
'Japanese',
'Korean',
'Taiwanese',
'Latino',
'French',
'Spanish',
'Portuguese',
'Italian',
'German',
'Ukrainian',
'Polish',
'Czech',
'Thai',
'Indonesian',
'Vietnamese',
'Dutch',
'Bengali',
'Turkish',
'Greek',
'Swedish',
null,
],
quality_filter: [
'BluRay/UHD',
'WEB/HD',
'DVD/TV/SAT',
'CAM/Screener',
'Unknown',
],
api_password: Settings.MEDIAFUSION_API_PASSWORD || null,
mediaflow_config: null,
rpdb_config: null,
live_search_streams: liveSearchStreams,
contribution_streams: false,
mdblist_config: null,
};
return encodeURIComponent(
Buffer.from(
JSON.stringify({
streaming_provider: service
? {
token: !['pikpak'].includes(service)
? credentials.apiKey
: undefined,
email: credentials.email,
password: credentials.password,
service: service,
enable_watchlists_catalogs: false,
download_via_browser: false,
only_show_cached_streams: false,
}
: null,
selected_catalogs: [],
selected_resolutions: [
'4k',
'2160p',
'1440p',
'1080p',
'720p',
'576p',
'480p',
'360p',
'240p',
null,
],
enable_catalogs: true,
enable_imdb_metadata: false,
max_size: 'inf',
max_streams_per_resolution: '500',
torrent_sorting_priority: [
{ key: 'language', direction: 'desc' },
{ key: 'cached', direction: 'desc' },
{ key: 'resolution', direction: 'desc' },
{ key: 'quality', direction: 'desc' },
{ key: 'size', direction: 'desc' },
{ key: 'seeders', direction: 'desc' },
{ key: 'created_at', direction: 'desc' },
],
show_full_torrent_name: true,
show_language_country_flag: true,
nudity_filter: nudityFilter,
certification_filter: certificationFilter,
language_sorting: [
'English',
'Tamil',
'Hindi',
'Malayalam',
'Kannada',
'Telugu',
'Chinese',
'Russian',
'Arabic',
'Japanese',
'Korean',
'Taiwanese',
'Latino',
'French',
'Spanish',
'Portuguese',
'Italian',
'German',
'Ukrainian',
'Polish',
'Czech',
'Thai',
'Indonesian',
'Vietnamese',
'Dutch',
'Bengali',
'Turkish',
'Greek',
'Swedish',
null,
],
quality_filter: [
'BluRay/UHD',
'WEB/HD',
'DVD/TV/SAT',
'CAM/Screener',
'Unknown',
],
api_password: Settings.MEDIAFUSION_API_PASSWORD || null,
mediaflow_config: null,
rpdb_config: null,
live_search_streams: liveSearchStreams,
contribution_streams: false,
mdblist_config: null,
})
).toString('base64')
);
};
async function _getConfigString(data: any): Promise<string> {
const encryptUrl = `${Settings.MEDIAFUSION_URL}encrypt-user-data`;
const response = await fetch(encryptUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
signal: AbortSignal.timeout(
Settings.MEDIAFUSION_CONFIG_TIMEOUT ||
Settings.DEFAULT_MEDIAFUSION_TIMEOUT ||
Settings.DEFAULT_TIMEOUT
),
});
const encryptedData = await response.json();
if (encryptedData.status !== 'success') {
throw new Error(
`Config encryption failed: ${encryptedData.message || 'Unknown error'}`
);
}
return encryptedData.encrypted_str;
}