diff --git a/addon/lib/landingTemplate.js b/addon/lib/landingTemplate.js
index 59206d7..39a0e1d 100644
--- a/addon/lib/landingTemplate.js
+++ b/addon/lib/landingTemplate.js
@@ -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 = {}) {
+
@@ -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]
diff --git a/addon/lib/manifest.js b/addon/lib/manifest.js
index 4036e01..5972ba0 100644
--- a/addon/lib/manifest.js
+++ b/addon/lib/manifest.js
@@ -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 = {
diff --git a/addon/moch/easydebrid.js b/addon/moch/easydebrid.js
new file mode 100644
index 0000000..e8c1f8b
--- /dev/null
+++ b/addon/moch/easydebrid.js
@@ -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 };
+}
diff --git a/addon/moch/moch.js b/addon/moch/moch.js
index 75ac805..e3af154 100644
--- a/addon/moch/moch.js
+++ b/addon/moch/moch.js
@@ -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,
diff --git a/addon/package-lock.json b/addon/package-lock.json
index 58edcdb..49c5651 100644
--- a/addon/package-lock.json
+++ b/addon/package-lock.json
@@ -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",
diff --git a/addon/package.json b/addon/package.json
index 31491ef..1270af7 100644
--- a/addon/package.json
+++ b/addon/package.json
@@ -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",