mirror of
https://github.com/knightcrawler-stremio/knightcrawler.git
synced 2024-12-20 03:29:51 +00:00
[addon] adds putio moch provider closes #6
This commit is contained in:
1
addon/moch/delay.js
Normal file
1
addon/moch/delay.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = duration => new Promise((resolve) => setTimeout(resolve, duration));
|
||||
@@ -3,6 +3,7 @@ const options = require('./options');
|
||||
const realdebrid = require('./realdebrid');
|
||||
const premiumize = require('./premiumize');
|
||||
const alldebrid = require('./alldebrid');
|
||||
const putio = require('./putio');
|
||||
const StaticResponse = require('./static');
|
||||
const { cacheWrapResolvedUrl } = require('../lib/cache');
|
||||
|
||||
@@ -22,6 +23,11 @@ const MOCHS = {
|
||||
key: 'alldebrid',
|
||||
instance: alldebrid,
|
||||
shortName: 'AD'
|
||||
},
|
||||
'putio': {
|
||||
key: 'putio',
|
||||
instance: putio,
|
||||
shortName: 'Putio'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
146
addon/moch/putio.js
Normal file
146
addon/moch/putio.js
Normal file
@@ -0,0 +1,146 @@
|
||||
const PutioAPI = require('@putdotio/api-client').default
|
||||
const { encode } = require('magnet-uri');
|
||||
const isVideo = require('../lib/video');
|
||||
const delay = require('./delay');
|
||||
const StaticResponse = require('./static');
|
||||
|
||||
async function getCachedStreams(streams, apiKey) {
|
||||
return streams
|
||||
.reduce((mochStreams, stream) => {
|
||||
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: false
|
||||
};
|
||||
return mochStreams;
|
||||
}, {});
|
||||
}
|
||||
|
||||
async function resolve({ ip, apiKey, infoHash, cachedEntryInfo, fileIndex }) {
|
||||
console.log(`Unrestricting ${infoHash} [${fileIndex}]`);
|
||||
const clientId = apiKey.replace(/@.*/, '');
|
||||
const token = apiKey.replace(/.*@/, '');
|
||||
const Putio = new PutioAPI({ clientID: clientId });
|
||||
Putio.setToken(token);
|
||||
|
||||
const torrent = await _createOrFindTorrent(Putio, infoHash);
|
||||
if (torrent && statusReady(torrent.status)) {
|
||||
return _unrestrictLink(Putio, torrent, cachedEntryInfo, fileIndex);
|
||||
} else if (torrent && statusDownloading(torrent.status)) {
|
||||
return StaticResponse.DOWNLOADING;
|
||||
} else if (torrent && statusError(torrent.status)) {
|
||||
return _retryCreateTorrent(Putio, infoHash, cachedEntryInfo, fileIndex);
|
||||
}
|
||||
return Promise.reject("Failed Putio adding torrent");
|
||||
}
|
||||
|
||||
async function _createOrFindTorrent(Putio, infoHash) {
|
||||
return _findTorrent(Putio, infoHash)
|
||||
.catch(() => _createTorrent(Putio, infoHash))
|
||||
.catch(error => {
|
||||
console.warn('Failed Putio torrent retrieval', error);
|
||||
return error;
|
||||
});
|
||||
}
|
||||
|
||||
async function _retryCreateTorrent(Putio, infoHash, encodedFileName, fileIndex) {
|
||||
const newTorrent = await _createTorrent(Putio, infoHash);
|
||||
return newTorrent && statusReady(newTorrent.status)
|
||||
? _unrestrictLink(Putio, newTorrent, encodedFileName, fileIndex)
|
||||
: StaticResponse.FAILED_DOWNLOAD;
|
||||
}
|
||||
|
||||
async function _findTorrent(Putio, infoHash) {
|
||||
const torrents = await Putio.Transfers.Query().then(response => response.data.transfers);
|
||||
const foundTorrents = torrents.filter(torrent => torrent.source.toLowerCase().includes(infoHash));
|
||||
const nonFailedTorrent = foundTorrents.find(torrent => !statusError(torrent.status));
|
||||
const foundTorrent = nonFailedTorrent || foundTorrents[0];
|
||||
return foundTorrent || Promise.reject('No recent torrent found');
|
||||
}
|
||||
|
||||
async function _createTorrent(Putio, infoHash) {
|
||||
const magnetLink = encode({ infoHash });
|
||||
// Add the torrent and then delay for 3 secs for putio to process it and then check it's status.
|
||||
return Putio.Transfers.Add({ url: magnetLink })
|
||||
.then(response => _getNewTorrent(Putio, response.data.transfer.id));
|
||||
}
|
||||
|
||||
async function _getNewTorrent(Putio, torrentId, pollCounter = 0, pollRate = 2000, maxPollNumber = 15) {
|
||||
return Putio.Transfers.Get(torrentId)
|
||||
.then(response => response.data.transfer)
|
||||
.then(torrent => statusProcessing(torrent.status) && pollCounter < maxPollNumber
|
||||
? delay(pollRate).then(() => _getNewTorrent(Putio, torrentId, pollCounter + 1))
|
||||
: torrent);
|
||||
}
|
||||
|
||||
async function _unrestrictLink(Putio, torrent, encodedFileName, fileIndex) {
|
||||
const targetVideo = await _getTargetFile(Putio, torrent, encodedFileName, fileIndex)
|
||||
const publicToken = await _getPublicToken(Putio, targetVideo.id);
|
||||
const publicFile = await Putio.File.Public(publicToken).then(response => response.data.parent);
|
||||
|
||||
if (!publicFile.stream_url || !publicFile.stream_url.length) {
|
||||
return Promise.reject("No available links found");
|
||||
}
|
||||
console.log(`Unrestricted ${torrent.hash} [${fileIndex}] to ${publicFile.stream_url}`);
|
||||
return publicFile.stream_url;
|
||||
}
|
||||
|
||||
async function _getTargetFile(Putio, torrent, encodedFileName, fileIndex) {
|
||||
const targetFileName = decodeURIComponent(encodedFileName);
|
||||
let targetFile;
|
||||
let files = await _getFiles(Putio, torrent.file_id);
|
||||
let videos = [];
|
||||
|
||||
while (!targetFile && files.length) {
|
||||
const folders = files.filter(file => file.file_type === 'FOLDER');
|
||||
videos = videos.concat(files.filter(file => isVideo(file.name)));
|
||||
// when specific file index is defined search by filename
|
||||
// when it's not defined find all videos and take the largest one
|
||||
targetFile = Number.isInteger(fileIndex)
|
||||
? videos.find(video => targetFileName.includes(video.name))
|
||||
: !folders.length && videos.sort((a, b) => b.size - a.size)[0];
|
||||
files = !targetFile
|
||||
? await Promise.all(folders.map(folder => _getFiles(Putio, folder.id)))
|
||||
.then(results => results.reduce((a, b) => a.concat(b), []))
|
||||
: [];
|
||||
}
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
async function _getFiles(Putio, fileId) {
|
||||
return Putio.Files.Query(fileId).then(response => response.data.files);
|
||||
}
|
||||
|
||||
async function _getPublicToken(Putio, targetVideoId) {
|
||||
const publicLinks = await Putio.Files.PublicShares().then(response => response.data.links);
|
||||
const alreadySharedLink = publicLinks.find(link => link.user_file.id === targetVideoId);
|
||||
if (alreadySharedLink) {
|
||||
return alreadySharedLink.token;
|
||||
}
|
||||
if (publicLinks.length >= 10) {
|
||||
// maximum public shares reached, revoke last one;
|
||||
await Putio.File.RevokePublicLink(publicLinks[0].id);
|
||||
}
|
||||
return Putio.File.CreatePublicLink(targetVideoId).then(response => response.data.token);
|
||||
}
|
||||
|
||||
function statusError(status) {
|
||||
return ['ERROR'].includes(status);
|
||||
}
|
||||
|
||||
function statusDownloading(status) {
|
||||
return ['WAITING', 'IN_QUEUE', 'DOWNLOADING'].includes(status);
|
||||
}
|
||||
|
||||
function statusProcessing(status) {
|
||||
return ['WAITING', 'IN_QUEUE', 'COMPLETING'].includes(status);
|
||||
}
|
||||
|
||||
function statusReady(status) {
|
||||
return ['COMPLETED', 'SEEDING'].includes(status);
|
||||
}
|
||||
|
||||
module.exports = { getCachedStreams, resolve };
|
||||
@@ -1,6 +1,7 @@
|
||||
const RealDebridClient = require('real-debrid-api');
|
||||
const { encode } = require('magnet-uri');
|
||||
const isVideo = require('../lib/video');
|
||||
const delay = require('./delay');
|
||||
const StaticResponse = require('./static');
|
||||
const { getRandomProxy, getRandomUserAgent } = require('../lib/request_helper');
|
||||
const { cacheWrapProxy, cacheUserAgent } = require('../lib/cache');
|
||||
@@ -103,8 +104,7 @@ async function _selectTorrentFiles(RD, torrent, cachedFileIds) {
|
||||
torrent = torrent.status ? torrent : await RD.torrents.info(torrent.id);
|
||||
if (torrent && statusOpening(torrent.status)) {
|
||||
// sleep for 2 seconds, maybe the torrent will be converted
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
torrent = await RD.torrents.info(torrent.id);
|
||||
torrent = await delay(2000).then(() => RD.torrents.info(torrent.id));
|
||||
}
|
||||
if (torrent && torrent.files && statusWaitingSelection(torrent.status)) {
|
||||
const videoFileIds = torrent.files
|
||||
|
||||
Reference in New Issue
Block a user