mirror of
https://github.com/zoriya/flood.git
synced 2026-06-07 04:15:06 +00:00
server: fetch torrents from http/https URLs directly from Flood
This allows more control over the fetching process. Specifically, it allows us to add a feature to attach Cookies to requests in a later commit.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest/presets/js-with-babel',
|
||||
rootDir: './../',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['<rootDir>/routes/api/auth.test.ts'],
|
||||
setupFilesAfterEnv: ['<rootDir>/.jest/auth.setup.js'],
|
||||
globals: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest/presets/js-with-babel',
|
||||
rootDir: './../',
|
||||
testEnvironment: 'node',
|
||||
testPathIgnorePatterns: ['auth.test.ts'],
|
||||
setupFilesAfterEnv: ['<rootDir>/.jest/test.setup.js'],
|
||||
globals: {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import geoip from 'geoip-country';
|
||||
import {moveSync} from 'fs-extra';
|
||||
@@ -32,7 +31,7 @@ import ClientGatewayService from '../interfaces/clientGatewayService';
|
||||
import ClientRequestManager from './clientRequestManager';
|
||||
import scgiUtil from './util/scgiUtil';
|
||||
import {getMethodCalls, processMethodCallResponse} from './util/rTorrentMethodCallUtil';
|
||||
import {getTempPath} from '../../models/TemporaryStorage';
|
||||
import {fetchURLToTempFile, saveBufferToTempFile} from '../../util/tempFileUtil';
|
||||
import torrentFileUtil from '../../util/torrentFileUtil';
|
||||
import {
|
||||
encodeTags,
|
||||
@@ -57,24 +56,12 @@ class RTorrentClientGatewayService extends ClientGatewayService {
|
||||
clientRequestManager = new ClientRequestManager(this.user.client as RTorrentConnectionSettings);
|
||||
|
||||
async addTorrentsByFile({files, destination, tags, isBasePath, start}: AddTorrentByFileOptions): Promise<void> {
|
||||
const tempPath = path.join(
|
||||
getTempPath(this.user._id),
|
||||
'torrents',
|
||||
`${Date.now()}-${crypto.randomBytes(4).toString('hex')}`,
|
||||
);
|
||||
await createDirectory(tempPath);
|
||||
|
||||
const torrentPaths = await Promise.all(
|
||||
files.map(async (file, index) => {
|
||||
const torrentPath = path.join(tempPath, `${index}.torrent`);
|
||||
fs.writeFileSync(torrentPath, Buffer.from(file, 'base64'), {});
|
||||
return torrentPath;
|
||||
files.map(async (file) => {
|
||||
return saveBufferToTempFile(Buffer.from(file, 'base64'), 'torrent');
|
||||
}),
|
||||
);
|
||||
|
||||
// Delete temp files after 5 minutes. This is more than enough.
|
||||
setTimeout(() => fs.rmdirSync(tempPath, {recursive: true}), 1000 * 60 * 5);
|
||||
|
||||
return this.addTorrentsByURL({urls: torrentPaths, destination, tags, isBasePath, start});
|
||||
}
|
||||
|
||||
@@ -87,7 +74,24 @@ class RTorrentClientGatewayService extends ClientGatewayService {
|
||||
|
||||
await createDirectory(destinationPath);
|
||||
|
||||
const methodCalls: MultiMethodCalls = urls.map((url) => {
|
||||
const torrentPaths = await Promise.all(
|
||||
urls.map(async (url) => {
|
||||
if (/^(http|https):\/\//.test(url)) {
|
||||
// TODO: properly handle error and let frontend know
|
||||
const torrentPath = await fetchURLToTempFile(url, 'torrent').catch((e) => console.error(e));
|
||||
|
||||
if (typeof torrentPath === 'string') {
|
||||
return torrentPath;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle potential other types of downloads
|
||||
|
||||
return url;
|
||||
}),
|
||||
);
|
||||
|
||||
const methodCalls: MultiMethodCalls = torrentPaths.map((torrentPath) => {
|
||||
const additionalCalls: Array<string> = [];
|
||||
|
||||
additionalCalls.push(`${isBasePath ? 'd.directory_base.set' : 'd.directory.set'}="${destinationPath}"`);
|
||||
@@ -100,7 +104,7 @@ class RTorrentClientGatewayService extends ClientGatewayService {
|
||||
|
||||
return {
|
||||
methodName: start ? 'load.start' : 'load.normal',
|
||||
params: ['', url].concat(additionalCalls),
|
||||
params: ['', torrentPath].concat(additionalCalls),
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import axios, {AxiosResponse} from 'axios';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
|
||||
import {getTempPath} from '../models/TemporaryStorage';
|
||||
|
||||
/**
|
||||
* Gets a randomly generated file path for temp file.
|
||||
*
|
||||
* @return {string} - path
|
||||
*/
|
||||
const getTempFilePath = (extension = 'tmp'): string => {
|
||||
return getTempPath(`${Date.now()}-${crypto.randomBytes(8).toString('hex')}.${extension}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves buffer to temporary storage as a file.
|
||||
*
|
||||
* @param {Buffer} buffer - buffer
|
||||
* @param {string} extension - file extension of temp file
|
||||
* @return {string} - path of saved temporary file. deleted after 5 minutes.
|
||||
*/
|
||||
export const saveBufferToTempFile = async (buffer: Buffer, extension?: string): Promise<string> => {
|
||||
const tempPath = getTempFilePath(extension);
|
||||
|
||||
fs.writeFileSync(tempPath, buffer);
|
||||
|
||||
setTimeout(() => fs.unlinkSync(tempPath), 1000 * 60 * 5);
|
||||
|
||||
return tempPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches from URL to temporary storage.
|
||||
*
|
||||
* @param {string} url - URL
|
||||
* @param {string} extension - file extension of temp file
|
||||
* @return {string} - path of saved temporary file. deleted after 5 minutes.
|
||||
*/
|
||||
export const fetchURLToTempFile = async (url: string, extension?: string): Promise<string> => {
|
||||
const tempPath = getTempFilePath(extension);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url,
|
||||
responseType: 'stream',
|
||||
}).then((res: AxiosResponse) => {
|
||||
res.data.pipe(fs.createWriteStream(tempPath)).on('finish', () => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(() => fs.unlinkSync(tempPath), 1000 * 60 * 5);
|
||||
|
||||
return tempPath;
|
||||
};
|
||||
Reference in New Issue
Block a user