-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add EasyDebrid support #320
Comments
@TheBeastLT not sure if you are aware but easy debrid uses same api key as PM. one improvement is regarding the header allowing to choose the closest CDN for the user which debridio add-on supports atm and torrentio doesn't. cheers |
Will this be implemented soon? |
idk if this helps (hope it does) https://easydebrid.com/apidoc/index.html import magnet from 'magnet-uri';
import { isVideo, isArchive } from '../lib/extension.js';
import { getMagnetLink } from '../lib/magnetHelper.js';
import { Type } from '../lib/types.js';
import { BadTokenError, chunkArray, sameFilename } from './mochHelper.js';
import StaticResponse from './static.js';
const KEY = 'easydebrid';
export async function getCachedStreams(streams, apiKey) {
const options = await getDefaultOptions();
const ED = new EasyDebridClient({ accessToken: apiKey, ...options })
return Promise.all(chunkArray(streams, 100)
.map(chunkedStreams => _getCachedStreams(ED, apiKey, chunkedStreams)))
.then(results => {
return results.reduce((all, result) => Object.assign(all, result), {})
});
}
async function _getCachedStreams(ED, apiKey, streams) {
const urls = streams.map(stream => magnet.encode({ infoHash: stream.infoHash }));
return ED.linkLookup(urls)
.catch(error => {
if (toCommonError(error)) {
return Promise.reject(error);
}
console.warn('Failed EasyDebrid cached torrent availability request:', error);
return undefined;
})
.then(available => streams
.reduce((mochStreams, stream, index) => {
const streamTitleParts = stream.title.replace(/\n👤.*/s, '').split('\n');
const fileName = streamTitleParts[streamTitleParts.length - 1];
const fileIndex = streamTitleParts.length === 2 ? stream.fileIdx : null;
const encodedFileName = encodeURIComponent(fileName);
mochStreams[`${stream.infoHash}`] = {
url: `${apiKey}/${stream.infoHash}/${encodedFileName}/${fileIndex}`,
cached: available?.cached[index]
};
return mochStreams;
}, {}));
}
export async function getCatalog(apiKey, offset = 0) {
console.log('Getting EasyDebrid catalog:', offset);
console.log(`Api key ${apiKey}`);
if (offset > 0) {
return [];
}
const options = await getDefaultOptions();
// const cached = getCachedStreams([{ infoHash: 'test', title: 'test' }], apiKey)
// console.log('Cached:', cached);
return [];
// const options = await getDefaultOptions();
// const ED = new EasyDebridClient({ accessToken: apiKey, ...options });
// return ED.folder.list()
// .then(response => response.content)
// .then(torrents => (torrents || [])
// .filter(torrent => torrent && torrent.type === 'folder')
// .map(torrent => ({
// id: `${KEY}:${torrent.id}`,
// type: Type.OTHER,
// name: torrent.name
// })));
}
export async function getItemMeta(itemId, apiKey, ip) {
const options = await getDefaultOptions();
const ED = new EasyDebridClient(apiKey, options);
const rootFolder = await ED.folder.list(itemId, null);
const infoHash = await _findInfoHash(ED, itemId);
return getFolderContents(ED, itemId, ip)
.then(contents => ({
id: `${KEY}:${itemId}`,
type: Type.OTHER,
name: rootFolder.name,
infoHash: infoHash,
videos: contents
.map((file, index) => ({
id: `${KEY}:${file.id}:${index}`,
title: file.name,
released: new Date(file.created_at * 1000 - index).toISOString(),
streams: [{ url: file.link || file.stream_link }]
}))
}))
}
async function getFolderContents(ED, itemId, ip, folderPrefix = '') {
return ED.folder.list(itemId, null, ip)
.then(response => response.content)
.then(contents => Promise.all(contents
.filter(content => content.type === 'folder')
.map(content => getFolderContents(ED, content.id, ip, [folderPrefix, content.name].join('/'))))
.then(otherContents => otherContents.reduce((a, b) => a.concat(b), []))
.then(otherContents => contents
.filter(content => content.type === 'file' && isVideo(content.name))
.map(content => ({ ...content, name: [folderPrefix, content.name].join('/') }))
.concat(otherContents)));
}
export async function resolve({ ip, isBrowser, apiKey, infoHash, cachedEntryInfo, fileIndex }) {
console.log(`Unrestricting EasyDebrid ${infoHash} [${fileIndex}] for IP ${ip} from browser=${isBrowser}`);
const options = await getDefaultOptions();
const ED = new EasyDebridClient({ accessToken: apiKey, ...options });
return _getCachedLink(ED, infoHash, cachedEntryInfo, fileIndex, ip, isBrowser)
.catch(() => _resolve(ED, infoHash, cachedEntryInfo, fileIndex, ip, isBrowser))
.catch(error => {
if (error?.message?.includes('Account not premium.')) {
console.log(`Access denied to EasyDebrid ${infoHash} [${fileIndex}]`);
return StaticResponse.FAILED_ACCESS;
}
return Promise.reject(`Failed EasyDebrid adding torrent ${JSON.stringify(error)}`);
});
}
async function _resolve(ED, infoHash, cachedEntryInfo, fileIndex, ip, isBrowser) {
const torrent = await _createOrFindTorrent(ED, infoHash);
if (torrent && statusReady(torrent.status)) {
return _getCachedLink(ED, infoHash, cachedEntryInfo, fileIndex, ip, isBrowser);
} else if (torrent && statusDownloading(torrent.status)) {
console.log(`Downloading to EasyDebrid ${infoHash} [${fileIndex}]...`);
return StaticResponse.DOWNLOADING;
} else if (torrent && statusError(torrent.status)) {
console.log(`Retrying downloading to EasyDebrid ${infoHash} [${fileIndex}]...`);
return _retryCreateTorrent(ED, infoHash, cachedEntryInfo, fileIndex);
}
return Promise.reject(`Failed EasyDebrid adding torrent ${JSON.stringify(torrent)}`);
}
async function _getCachedLink(ED, infoHash, encodedFileName, fileIndex, ip, isBrowser) {
const magnetLink = magnet.encode({ infoHash });
const response = await ED.generateDebridLink(magnetLink);
if (response?.files?.length) {
const targetFileName = decodeURIComponent(encodedFileName);
const videos = response.files.filter(file => isVideo(file.path));
const targetVideo = Number.isInteger(fileIndex)
? videos.find(video => sameFilename(video.path, targetFileName))
: videos.sort((a, b) => b.size - a.size)[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');
}
async function _createOrFindTorrent(ED, infoHash) {
return _findTorrent(ED, infoHash)
.catch(() => _createTorrent(ED, infoHash));
}
async function _findTorrent(ED, infoHash) {
const torrents = await ED.transfer.list().then(response => response.transfers);
const foundTorrents = torrents.filter(torrent => torrent.src.toLowerCase().includes(infoHash));
const nonFailedTorrent = foundTorrents.find(torrent => !statusError(torrent.statusCode));
const foundTorrent = nonFailedTorrent || foundTorrents[0];
return foundTorrent || Promise.reject('No recent torrent found');
}
async function _findInfoHash(ED, itemId) {
const torrents = await ED.transfer.list().then(response => response.transfers);
const foundTorrent = torrents.find(torrent => `${torrent.file_id}` === itemId || `${torrent.folder_id}` === itemId);
return foundTorrent?.src ? magnet.decode(foundTorrent.src).infoHash : undefined;
}
async function _createTorrent(ED, infoHash) {
const magnetLink = await getMagnetLink(infoHash);
return ED.transfer.create(magnetLink).then(() => _findTorrent(ED, infoHash));
}
async function _retryCreateTorrent(ED, infoHash, encodedFileName, fileIndex) {
const newTorrent = await _createTorrent(ED, infoHash).then(() => _findTorrent(ED, infoHash));
return newTorrent && statusReady(newTorrent.status)
? _getCachedLink(ED, infoHash, encodedFileName, fileIndex)
: StaticResponse.FAILED_DOWNLOAD;
}
export function toCommonError(error) {
if (error && error.message === 'Not logged in.') {
return BadTokenError;
}
return undefined;
}
function statusError(status) {
return ['deleted', 'error', 'timeout'].includes(status);
}
function statusDownloading(status) {
return ['waiting', 'queued', 'running'].includes(status);
}
function statusReady(status) {
return ['finished', 'seeding'].includes(status);
}
async function getDefaultOptions(ip) {
return { timeout: 5000 };
}
easydebrid.js
10 KB ``` |
this is no longer the case |
Same problem here. PM no longer accepts the ED API. It worked yesterday but not today :-( |
I tried Debridio and Comet and neither worked for me. |
Debridio should work and paradise comet. |
Please add easy debrid support |
@TheBeastLT I came here to ask if you could please do this. pretty please! |
@TheBeastLT any chance we could see Easy Debrid added soon? If so, when? I prefer it to other providers. Thanks a lot for your hard work! |
Do you have plans to add EasyDebrid to Torrentio? |
Can you please add this, I know you also added torbox recently but this uses the premiuimize cache which is currently the largest available and pairs extremely well with torrentio. |
Yes, can we please support this |
@TheBeastLT It would be extremely appreciated and beneficial to the commumity if support for easydebrid was be added, thank you so much and appreciate your work. |
Not optimal and presents buffer for those that dont live in the United States, it needs to be integrated correctly by the developer, in this case you are presenting mediafusion obliterates stremthru for EU users, we need to support torrentio dev and kindly ask for integration. |
Most are already aware of this, this thread is about getting official support not a work around. |
easydebrid no longer work with torrentio-pm cos pm just block ed from using its api. please make official support of easydebrid like how fenlight, umbrella, comet etc eventhough there already pm account but they added easy debrid account to log in independently. |
What's the holdup? |
Please, add support |
@TheBeastLT is there anything we can do to help add official support? |
No description provided.
The text was updated successfully, but these errors were encountered: