retrieve resolver host from request instead of static value

This commit is contained in:
TheBeastLT
2023-10-31 11:23:30 +02:00
parent cf1ab4212d
commit 9990acf36e
6 changed files with 47 additions and 36 deletions

View File

@@ -40,5 +40,5 @@ jobs:
docker load -i /tmp/docker/torrentio_addon_latest.tar
docker stop torrentio-addon
docker rm torrentio-addon
docker run -p 80:7000 -d --name torrentio-addon --restart always -e MONGODB_URI=${{ secrets.MONGODB_URI }} -e DATABASE_URI=${{ secrets.DATABASE_URI }} -e RESOLVER_HOST=${{ secrets.RESOLVER_HOST }} -e PROXY_HOSTS=${{ secrets.PROXY_HOSTS }} -e PROXY_USERNAME=${{ secrets.PROXY_USERNAME }} -e PROXY_PASSWORD=${{ secrets.PROXY_PASSWORD }} torrentio-addon:latest
docker run -p 80:7000 -d --name torrentio-addon --restart always -e MONGODB_URI=${{ secrets.MONGODB_URI }} -e DATABASE_URI=${{ secrets.DATABASE_URI }} -e PROXY_HOSTS=${{ secrets.PROXY_HOSTS }} -e PROXY_USERNAME=${{ secrets.PROXY_USERNAME }} -e PROXY_PASSWORD=${{ secrets.PROXY_PASSWORD }} torrentio-addon:latest
docker image prune -f

View File

@@ -5,6 +5,7 @@ const serverless = require('./serverless');
const { initBestTrackers } = require('./lib/magnetHelper');
const app = express();
app.enable('trust proxy');
const limiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hours
max: 300, // limit each IP to 300 requests per windowMs

View File

@@ -1,6 +1,6 @@
const cacheManager = require('cache-manager');
const mangodbStore = require('cache-manager-mongodb');
const { RESOLVER_HOST } = require('../moch/static')
const { isStaticUrl } = require('../moch/static')
const GLOBAL_KEY_PREFIX = 'torrentio-addon';
const STREAM_KEY_PREFIX = `${GLOBAL_KEY_PREFIX}|stream`;
@@ -68,7 +68,7 @@ function cacheWrapStream(id, method) {
function cacheWrapResolvedUrl(id, method) {
return cacheWrap(memoryCache, `${RESOLVED_URL_KEY_PREFIX}:${id}`, method, {
ttl: (url) => url.startsWith(RESOLVER_HOST) ? MESSAGE_VIDEO_URL_TTL : STREAM_TTL
ttl: (url) => isStaticUrl(url) ? MESSAGE_VIDEO_URL_TTL : STREAM_TTL
});
}

View File

@@ -10,11 +10,11 @@ const StaticResponse = require('./static');
const { cacheWrapResolvedUrl } = require('../lib/cache');
const { timeout } = require('../lib/promises');
const { BadTokenError, streamFilename, AccessDeniedError, enrichMeta } = require('./mochHelper');
const { isStaticUrl } = require("./static");
const RESOLVE_TIMEOUT = 2 * 60 * 1000; // 2 minutes
const MIN_API_KEY_SYMBOLS = 15;
const TOKEN_BLACKLIST = [];
const RESOLVER_HOST = process.env.RESOLVER_HOST || 'http://localhost:7050';
const MOCHS = {
realdebrid: {
key: 'realdebrid',
@@ -65,11 +65,11 @@ const unrestrictQueue = new namedQueue((task, callback) => task.method()
.catch((error => callback(error))), 20);
function hasMochConfigured(config) {
return Object.keys(MOCHS).find(moch => config && config[moch])
return Object.keys(MOCHS).find(moch => config?.[moch])
}
async function applyMochs(streams, config) {
if (!streams || !streams.length || !hasMochConfigured(config)) {
if (!streams?.length || !hasMochConfigured(config)) {
return streams;
}
return Promise.all(Object.keys(config)
@@ -94,18 +94,19 @@ async function applyMochs(streams, config) {
async function resolve(parameters) {
const moch = MOCHS[parameters.mochKey];
if (!moch) {
return Promise.reject(`Not a valid moch provider: ${parameters.mochKey}`);
return Promise.reject(new Error(`Not a valid moch provider: ${parameters.mochKey}`));
}
if (!parameters.apiKey || !parameters.infoHash || !parameters.cachedEntryInfo) {
return Promise.reject("No valid parameters passed");
return Promise.reject(new Error("No valid parameters passed"));
}
const id = `${parameters.ip}_${parameters.mochKey}_${parameters.apiKey}_${parameters.infoHash}_${parameters.fileIndex}`;
const method = () => timeout(RESOLVE_TIMEOUT, cacheWrapResolvedUrl(id, () => moch.instance.resolve(parameters)))
.catch(error => {
console.warn(error);
return StaticResponse.FAILED_UNEXPECTED;
});
})
.then(url => isStaticUrl(url) ? `${parameters.host}/${url}` : url);
return new Promise(((resolve, reject) => {
unrestrictQueue.push({ id, method }, (error, result) => result ? resolve(result) : reject(error));
}));
@@ -114,10 +115,10 @@ async function resolve(parameters) {
async function getMochCatalog(mochKey, config) {
const moch = MOCHS[mochKey];
if (!moch) {
return Promise.reject(`Not a valid moch provider: ${mochKey}`);
return Promise.reject(new Error(`Not a valid moch provider: ${mochKey}`));
}
if (isInvalidToken(config[mochKey], mochKey)) {
return Promise.reject(`Invalid API key for moch provider: ${mochKey}`);
return Promise.reject(new Error(`Invalid API key for moch provider: ${mochKey}`));
}
return moch.instance.getCatalog(config[moch.key], config.skip, config.ip);
}
@@ -125,7 +126,7 @@ async function getMochCatalog(mochKey, config) {
async function getMochItemMeta(mochKey, itemId, config) {
const moch = MOCHS[mochKey];
if (!moch) {
return Promise.reject(`Not a valid moch provider: ${mochKey}`);
return Promise.reject(new Error(`Not a valid moch provider: ${mochKey}`));
}
return moch.instance.getItemMeta(itemId, config[moch.key], config.ip)
@@ -135,14 +136,14 @@ async function getMochItemMeta(mochKey, itemId, config) {
.map(video => video.streams)
.reduce((a, b) => a.concat(b), [])
.filter(stream => !stream.url.startsWith('http'))
.forEach(stream => stream.url = `${RESOLVER_HOST}/${moch.key}/${stream.url}`)
.forEach(stream => stream.url = `${config.host}/${moch.key}/${stream.url}`)
return meta;
});
}
function processMochResults(streams, config, results) {
const errorResults = results
.map(result => errorStreamResponse(result.moch.key, result.error))
.map(result => errorStreamResponse(result.moch.key, result.error, config))
.filter(errorResponse => errorResponse);
if (errorResults.length) {
return errorResults;
@@ -150,22 +151,22 @@ function processMochResults(streams, config, results) {
const includeTorrentLinks = options.includeTorrentLinks(config);
const excludeDownloadLinks = options.excludeDownloadLinks(config);
const mochResults = results.filter(result => result && result.mochStreams);
const mochResults = results.filter(result => result?.mochStreams);
const cachedStreams = mochResults
.reduce((resultStreams, mochResult) => populateCachedLinks(resultStreams, mochResult), streams);
const resultStreams = excludeDownloadLinks ? cachedStreams : populateDownloadLinks(cachedStreams, mochResults);
.reduce((resultStreams, mochResult) => populateCachedLinks(resultStreams, mochResult, config), streams);
const resultStreams = excludeDownloadLinks ? cachedStreams : populateDownloadLinks(cachedStreams, mochResults, config);
return includeTorrentLinks ? resultStreams : resultStreams.filter(stream => stream.url);
}
function populateCachedLinks(streams, mochResult) {
function populateCachedLinks(streams, mochResult, config) {
return streams.map(stream => {
const cachedEntry = stream.infoHash && mochResult.mochStreams[stream.infoHash];
if (cachedEntry && cachedEntry.cached) {
if (cachedEntry?.cached) {
return {
name: `[${mochResult.moch.shortName}+] ${stream.name}`,
title: stream.title,
url: `${RESOLVER_HOST}/${mochResult.moch.key}/${cachedEntry.url}/${streamFilename(stream)}`,
url: `${config.host}/${mochResult.moch.key}/${cachedEntry.url}/${streamFilename(stream)}`,
behaviorHints: stream.behaviorHints
};
}
@@ -173,17 +174,17 @@ function populateCachedLinks(streams, mochResult) {
});
}
function populateDownloadLinks(streams, mochResults) {
function populateDownloadLinks(streams, mochResults, config) {
const torrentStreams = streams.filter(stream => stream.infoHash);
const seededStreams = streams.filter(stream => !stream.title.includes('👤 0'));
torrentStreams.forEach(stream => mochResults.forEach(mochResult => {
const cachedEntry = mochResult.mochStreams[stream.infoHash];
const isCached = cachedEntry && cachedEntry.cached;
const isCached = cachedEntry?.cached;
if (!isCached && isHealthyStreamForDebrid(seededStreams, stream)) {
streams.push({
name: `[${mochResult.moch.shortName} download] ${stream.name}`,
title: stream.title,
url: `${RESOLVER_HOST}/${mochResult.moch.key}/${cachedEntry.url}/${streamFilename(stream)}`,
url: `${config.host}/${mochResult.moch.key}/${cachedEntry.url}/${streamFilename(stream)}`,
behaviorHints: stream.behaviorHints
})
}
@@ -208,19 +209,19 @@ function blackListToken(token, mochKey) {
TOKEN_BLACKLIST.push(tokenKey);
}
function errorStreamResponse(mochKey, error) {
function errorStreamResponse(mochKey, error, config) {
if (error === BadTokenError) {
return {
name: `Torrentio\n${MOCHS[mochKey].shortName} error`,
title: `Invalid ${MOCHS[mochKey].name} ApiKey/Token!`,
url: StaticResponse.FAILED_ACCESS
url: `${config.host}/${StaticResponse.FAILED_ACCESS}`
};
}
if (error === AccessDeniedError) {
return {
name: `Torrentio\n${MOCHS[mochKey].shortName} error`,
title: `Expired/invalid ${MOCHS[mochKey].name} subscription!`,
url: StaticResponse.FAILED_ACCESS
url: `${config.host}/${StaticResponse.FAILED_ACCESS}`
};
}
return undefined;

View File

@@ -1,12 +1,18 @@
const RESOLVER_HOST = process.env.RESOLVER_HOST || 'http://localhost:7050';
const staticVideoUrls = {
DOWNLOADING: `videos/downloading_v2.mp4`,
FAILED_DOWNLOAD: `videos/download_failed_v2.mp4`,
FAILED_ACCESS: `videos/failed_access_v2.mp4`,
FAILED_RAR: `videos/failed_rar_v2.mp4`,
FAILED_OPENING: `videos/failed_opening_v2.mp4`,
FAILED_UNEXPECTED: `videos/failed_unexpected_v2.mp4`,
FAILED_INFRINGEMENT: `videos/failed_infringement_v2.mp4`
}
function isStaticUrl(url) {
return Object.values(staticVideoUrls).some(videoUrl => url?.endsWith(videoUrl));
}
module.exports = {
RESOLVER_HOST: RESOLVER_HOST,
DOWNLOADING: `${RESOLVER_HOST}/videos/downloading_v2.mp4`,
FAILED_DOWNLOAD: `${RESOLVER_HOST}/videos/download_failed_v2.mp4`,
FAILED_ACCESS: `${RESOLVER_HOST}/videos/failed_access_v2.mp4`,
FAILED_RAR: `${RESOLVER_HOST}/videos/failed_rar_v2.mp4`,
FAILED_OPENING: `${RESOLVER_HOST}/videos/failed_opening_v2.mp4`,
FAILED_UNEXPECTED: `${RESOLVER_HOST}/videos/failed_unexpected_v2.mp4`,
FAILED_INFRINGEMENT: `${RESOLVER_HOST}/videos/failed_infringement_v2.mp4`
...staticVideoUrls,
isStaticUrl
}

View File

@@ -37,7 +37,9 @@ router.get('/:configuration?/manifest.json', (req, res) => {
router.get('/:configuration/:resource/:type/:id/:extra?.json', (req, res, next) => {
const { configuration, resource, type, id } = req.params;
const extra = req.params.extra ? qs.parse(req.url.split('/').pop().slice(0, -5)) : {}
const configValues = { ...extra, ...parseConfiguration(configuration), ip: requestIp.getClientIp(req) };
const ip = requestIp.getClientIp(req);
const host = `${req.protocol}://${req.headers.host}`;
const configValues = { ...extra, ...parseConfiguration(configuration), ip, host };
addonInterface.get(resource, type, id, configValues)
.then(resp => {
const cacheHeaders = {
@@ -78,6 +80,7 @@ router.get('/:moch/:apiKey/:infoHash/:cachedEntryInfo/:fileIndex/:filename?', (r
fileIndex: isNaN(req.params.fileIndex) ? undefined : parseInt(req.params.fileIndex),
cachedEntryInfo: req.params.cachedEntryInfo,
ip: requestIp.getClientIp(req),
host: `${req.protocol}://${req.headers.host}`,
isBrowser: !userAgent.includes('Stremio') && !!userAgentParser(userAgent).browser.name
}
moch.resolve(parameters)