add easy debrid moch, closes #320
Some checks failed
Deploy Addon / build (SSH_HOST_2, SSH_KEY_2) (push) Has been cancelled
Deploy Addon / build (SSH_HOST_3, SSH_KEY_3) (push) Has been cancelled
Deploy Addon / build (SSH_HOST_4, SSH_KEY_4) (push) Has been cancelled

This commit is contained in:
TheBeastLT 2025-03-08 12:50:46 +02:00
parent 5bf7fb8230
commit bf13251928
6 changed files with 120 additions and 2 deletions

View file

@ -205,6 +205,7 @@ export default function landingTemplate(manifest, config = {}) {
const premiumizeApiKey = config[MochOptions.premiumize.key] || '';
const allDebridApiKey = config[MochOptions.alldebrid.key] || '';
const debridLinkApiKey = config[MochOptions.debridlink.key] || '';
const easyDebridApiKey = config[MochOptions.easydebrid.key] || '';
const offcloudApiKey = config[MochOptions.offcloud.key] || '';
const torboxApiKey = config[MochOptions.torbox.key] || '';
const putioKey = config[MochOptions.putio.key] || '';
@ -326,6 +327,11 @@ export default function landingTemplate(manifest, config = {}) {
<input type="text" id="iDebridLink" onchange="generateInstallLink()" class="input">
</div>
<div id="dEasyDebrid">
<label class="label" for="iEasyDebrid">EasyDebrid API Key:</label>
<input type="text" id="iEasyDebrid" onchange="generateInstallLink()" class="input">
</div>
<div id="dOffcloud">
<label class="label" for="iOffcloud">Offcloud API Key (Find it <a href='https://offcloud.com/#/account' target="_blank">here</a>):</label>
<input type="text" id="iOffcloud" onchange="generateInstallLink()" class="input">
@ -401,6 +407,7 @@ export default function landingTemplate(manifest, config = {}) {
$('#iPremiumize').val("${premiumizeApiKey}");
$('#iAllDebrid').val("${allDebridApiKey}");
$('#iDebridLink').val("${debridLinkApiKey}");
$('#iEasyDebrid').val("${easyDebridApiKey}");
$('#iOffcloud').val("${offcloudApiKey}");
$('#iTorbox').val("${torboxApiKey}");
$('#iPutioClientId').val("${putioClientId}");
@ -428,6 +435,7 @@ export default function landingTemplate(manifest, config = {}) {
$('#dPremiumize').toggle(provider === '${MochOptions.premiumize.key}');
$('#dAllDebrid').toggle(provider === '${MochOptions.alldebrid.key}');
$('#dDebridLink').toggle(provider === '${MochOptions.debridlink.key}');
$('#dEasyDebrid').toggle(provider === '${MochOptions.easydebrid.key}');
$('#dOffcloud').toggle(provider === '${MochOptions.offcloud.key}');
$('#dTorbox').toggle(provider === '${MochOptions.torbox.key}');
$('#dPutio').toggle(provider === '${MochOptions.putio.key}');
@ -447,6 +455,7 @@ export default function landingTemplate(manifest, config = {}) {
const allDebridValue = $('#iAllDebrid').val() || '';
const debridLinkValue = $('#iDebridLink').val() || ''
const premiumizeValue = $('#iPremiumize').val() || '';
const easyDebridValue = $('#iEasyDebrid').val() || '';
const offcloudValue = $('#iOffcloud').val() || '';
const torboxValue = $('#iTorbox').val() || '';
const putioClientIdValue = $('#iPutioClientId').val() || '';
@ -465,6 +474,7 @@ export default function landingTemplate(manifest, config = {}) {
const premiumize = premiumizeValue.length && premiumizeValue.trim();
const allDebrid = allDebridValue.length && allDebridValue.trim();
const debridLink = debridLinkValue.length && debridLinkValue.trim();
const easyDebrid = easyDebridValue.length && easyDebridValue.trim();
const offcloud = offcloudValue.length && offcloudValue.trim();
const torbox = torboxValue.length && torboxValue.trim();
const putio = putioClientIdValue.length && putioTokenValue.length && putioClientIdValue.trim() + '@' + putioTokenValue.trim();
@ -484,6 +494,7 @@ export default function landingTemplate(manifest, config = {}) {
['${MochOptions.premiumize.key}', premiumize],
['${MochOptions.alldebrid.key}', allDebrid],
['${MochOptions.debridlink.key}', debridLink],
['${MochOptions.easydebrid.key}', easyDebrid],
['${MochOptions.offcloud.key}', offcloud],
['${MochOptions.torbox.key}', torbox],
['${MochOptions.putio.key}', putio]

View file

@ -74,7 +74,7 @@ function getCatalogs(config) {
function getResources(config) {
const streamResource = {
name: 'stream',
types: [Type.MOVIE, Type.SERIES],
types: [Type.MOVIE, Type.SERIES, Type.ANIME],
idPrefixes: ['tt', 'kitsu']
};
const metaResource = {

83
addon/moch/easydebrid.js Normal file
View file

@ -0,0 +1,83 @@
import { EasyDebridClient } from '@paradise-cloud/easy-debrid';
import { isVideo, isArchive } from '../lib/extension.js';
import StaticResponse from './static.js';
import { BadTokenError, sameFilename, streamFilename } from './mochHelper.js';
import magnet from "magnet-uri";
const KEY = 'easydebrid';
export async function getCachedStreams(streams, apiKey) {
const options = await getDefaultOptions(apiKey);
const ED = new EasyDebridClient(options);
const hashes = streams.map(stream => stream.infoHash);
return ED.linkLookup(hashes)
.catch(error => {
if (toCommonError(error)) {
return Promise.reject(error);
}
console.warn('Failed EasyDebrid cached torrent availability request:', error);
return undefined;
})
.then(response => streams
.reduce((mochStreams, stream, index) => {
const filename = streamFilename(stream);
mochStreams[`${stream.infoHash}@${stream.fileIdx}`] = {
url: `${apiKey}/${stream.infoHash}/${filename}/${stream.fileIdx}`,
cached: response?.cached?.[index]
};
return mochStreams;
}, {}));
}
export async function resolve({ ip, isBrowser, apiKey, infoHash, cachedEntryInfo, fileIndex }) {
console.log(`Unrestricting EasyDebrid ${infoHash} [${fileIndex}]`);
const options = await getDefaultOptions(apiKey);
const ED = new EasyDebridClient(options);
return _getCachedLink(ED, infoHash, cachedEntryInfo, fileIndex, ip, isBrowser)
.catch(error => {
if (isAccessDeniedError(error)) {
console.log(`Access denied to EasyDebrid ${infoHash} [${fileIndex}]`);
return StaticResponse.FAILED_ACCESS;
}
return Promise.reject(`Failed EasyDebrid adding torrent ${JSON.stringify(error)}`);
});
}
async function _getCachedLink(ED, infoHash, encodedFileName, fileIndex, ip, isBrowser) {
const magnetLink = magnet.encode({ infoHash })
const cachedTorrent = await ED.generateDebridLink(magnetLink);
if (cachedTorrent?.files?.length) {
const files = cachedTorrent.files.map(file => ({
...file,
path: file.directory.join("/") + `/${file.filename}`,
}))
const targetFileName = decodeURIComponent(encodedFileName);
const videos = files.filter(file => isVideo(file.path)).sort((a, b) => b.size - a.size);
const targetVideo = Number.isInteger(fileIndex)
&& videos.find(video => sameFilename(video.path, targetFileName))
|| videos[0];
if (!targetVideo && videos.every(video => isArchive(video.path))) {
console.log(`Only EasyDebrid archive is available for [${infoHash}] ${fileIndex}`)
return StaticResponse.FAILED_RAR;
}
const unrestrictedLink = targetVideo.url;
console.log(`Unrestricted EasyDebrid ${infoHash} [${fileIndex}] to ${unrestrictedLink}`);
return unrestrictedLink;
}
return Promise.reject('No cached entry found');
}
export function toCommonError(error) {
if (error && error.message === 'Not logged in.') {
return BadTokenError;
}
return undefined;
}
function isAccessDeniedError(error) {
return ['Account not premium.'].some(value => error?.message?.includes(value));
}
async function getDefaultOptions(apiKey) {
return { accessToken: apiKey };
}

View file

@ -3,6 +3,7 @@ import * as realdebrid from './realdebrid.js';
import * as premiumize from './premiumize.js';
import * as alldebrid from './alldebrid.js';
import * as debridlink from './debridlink.js';
import * as easydebrid from './easydebrid.js';
import * as offcloud from './offcloud.js';
import * as torbox from './torbox.js';
import * as putio from './putio.js';
@ -44,6 +45,14 @@ export const MochOptions = {
shortName: 'DL',
catalogs: ['']
},
easydebrid: {
key: 'easydebrid',
instance: easydebrid,
name: 'EasyDebrid',
shortName: 'ED',
catalogs: [],
noDownloads: true
},
offcloud: {
key: 'offcloud',
instance: offcloud,
@ -192,9 +201,10 @@ function populateDownloadLinks(streams, results, config) {
const torrentStreams = streams.filter(stream => stream.infoHash);
const seededStreams = streams.filter(stream => !stream.title.includes('👤 0'));
torrentStreams.forEach(stream => mochResults.forEach(mochResult => {
const supportDownloads = !mochResult.moch.noDownloads;
const cachedEntry = mochResult.mochStreams[`${stream.infoHash}@${stream.fileIdx}`];
const isCached = cachedEntry?.cached;
if (!isCached && isHealthyStreamForDebrid(seededStreams, stream)) {
if (supportDownloads && !isCached && isHealthyStreamForDebrid(seededStreams, stream)) {
streams.push({
name: `[${mochResult.moch.shortName} download] ${stream.name}`,
title: stream.title,

View file

@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@keyv/mongo": "^3.0.1",
"@paradise-cloud/easy-debrid": "^3.0.0",
"@putdotio/api-client": "^8.42.0",
"all-debrid-api": "^1.2.0",
"axios": "^1.7.7",
@ -118,6 +119,18 @@
"sparse-bitfield": "^3.0.3"
}
},
"node_modules/@paradise-cloud/easy-debrid": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@paradise-cloud/easy-debrid/-/easy-debrid-3.0.0.tgz",
"integrity": "sha512-UrkvWQgnapw2nZKc5ZgNU29B52RrVe+fIDidJZWx2MZBowar7CBDSEc/E54q/Am9OKnsttMw9/gh2o3IuXwOHA==",
"license": "MIT",
"dependencies": {
"axios": "^1.6.8"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@putdotio/api-client": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@putdotio/api-client/-/api-client-8.42.0.tgz",

View file

@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@keyv/mongo": "^3.0.1",
"@paradise-cloud/easy-debrid": "^3.0.0",
"@putdotio/api-client": "^8.42.0",
"all-debrid-api": "^1.2.0",
"axios": "^1.7.7",