mirror of
https://github.com/zoriya/flood.git
synced 2026-06-01 18:47:44 +00:00
AddTorrentByFile: use base64 instead of Blob to transfer file
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import type {
|
||||
AddTorrentByFileOptions,
|
||||
AddTorrentByURLOptions,
|
||||
CheckTorrentsOptions,
|
||||
DeleteTorrentsOptions,
|
||||
@@ -41,18 +42,18 @@ const TorrentActions = {
|
||||
},
|
||||
),
|
||||
|
||||
addTorrentsByFiles: (formData: FormData, destination: string) =>
|
||||
addTorrentsByFiles: (options: AddTorrentByFileOptions) =>
|
||||
axios
|
||||
.post(`${baseURI}api/client/add-files`, formData)
|
||||
.post(`${baseURI}api/client/add-files`, options)
|
||||
.then((json) => json.data)
|
||||
.then(
|
||||
(response) => {
|
||||
(data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: 'CLIENT_ADD_TORRENT_SUCCESS',
|
||||
data: {
|
||||
count: formData.getAll('torrents').length,
|
||||
destination,
|
||||
response,
|
||||
count: options.files.length,
|
||||
destination: options.destination,
|
||||
data,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
+73
-30
@@ -1,26 +1,43 @@
|
||||
import {FormattedMessage, injectIntl} from 'react-intl';
|
||||
import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import React from 'react';
|
||||
|
||||
import {Form, FormRow, FormRowItem, Textbox} from '../../../ui';
|
||||
import AddTorrentsActions from './AddTorrentsActions';
|
||||
import Close from '../../icons/Close';
|
||||
import File from '../../icons/File';
|
||||
import Files from '../../icons/Files';
|
||||
import CloseIcon from '../../icons/Close';
|
||||
import FileIcon from '../../icons/File';
|
||||
import FilesIcon from '../../icons/Files';
|
||||
import SettingsStore from '../../../stores/SettingsStore';
|
||||
import TorrentActions from '../../../actions/TorrentActions';
|
||||
import TorrentDestination from '../../general/filesystem/TorrentDestination';
|
||||
|
||||
class AddTorrentsByFile extends React.Component {
|
||||
formRef = null;
|
||||
interface AddTorrentsByFileFormData {
|
||||
destination: string;
|
||||
start: boolean;
|
||||
tags: string;
|
||||
isBasePath: boolean;
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
interface AddTorrentsByFileStates {
|
||||
errors: Record<string, unknown>;
|
||||
files: Array<{
|
||||
name: string;
|
||||
data: string;
|
||||
}>;
|
||||
tags: string;
|
||||
isAddingTorrents: boolean;
|
||||
}
|
||||
|
||||
class AddTorrentsByFile extends React.Component<WrappedComponentProps, AddTorrentsByFileStates> {
|
||||
formRef: Form | null = null;
|
||||
|
||||
constructor(props: WrappedComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
errors: {},
|
||||
isAddingTorrents: false,
|
||||
files: [],
|
||||
tags: '',
|
||||
isAddingTorrents: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,19 +48,23 @@ class AddTorrentsByFile extends React.Component {
|
||||
const files = this.state.files.map((file, index) => (
|
||||
<li className="dropzone__selected-files__file interactive-list__item" key={file.name} title={file.name}>
|
||||
<span className="interactive-list__icon">
|
||||
<File />
|
||||
<FileIcon />
|
||||
</span>
|
||||
<span className="interactive-list__label">{file.name}</span>
|
||||
<span
|
||||
className="interactive-list__icon interactive-list__icon--action interactive-list__icon--action--warning"
|
||||
onClick={() => this.handleFileRemove(index)}>
|
||||
<Close />
|
||||
<CloseIcon />
|
||||
</span>
|
||||
</li>
|
||||
));
|
||||
|
||||
fileContent = (
|
||||
<ul className="dropzone__selected-files interactive-list" onClick={this.handleFilesClick}>
|
||||
<ul
|
||||
className="dropzone__selected-files interactive-list"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}>
|
||||
{files}
|
||||
</ul>
|
||||
);
|
||||
@@ -63,7 +84,7 @@ class AddTorrentsByFile extends React.Component {
|
||||
<input {...getInputProps()} />
|
||||
<div className="dropzone__copy">
|
||||
<div className="dropzone__icon">
|
||||
<Files />
|
||||
<FilesIcon />
|
||||
</div>
|
||||
<FormattedMessage id="torrents.add.tab.file.drop" />{' '}
|
||||
<span className="dropzone__browse-button">
|
||||
@@ -78,47 +99,69 @@ class AddTorrentsByFile extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
handleFileDrop = (files) => {
|
||||
handleFileDrop = (files: Array<File>) => {
|
||||
const nextErrorsState = this.state.errors;
|
||||
|
||||
if (nextErrorsState.files != null) {
|
||||
delete nextErrorsState.files;
|
||||
}
|
||||
|
||||
this.setState((state) => ({errors: nextErrorsState, files: state.files.concat(files)}));
|
||||
files.forEach((file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.setState((state) => {
|
||||
if (e.target != null && e.target.result != null && typeof e.target.result === 'string') {
|
||||
return {
|
||||
errors: nextErrorsState,
|
||||
files: state.files.concat({
|
||||
name: file.name,
|
||||
data: e.target.result.split('base64,')[1],
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {errors: nextErrorsState, files: state.files};
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
handleFileRemove = (fileIndex) => {
|
||||
handleFileRemove = (fileIndex: number) => {
|
||||
const {files} = this.state;
|
||||
files.splice(fileIndex, 1);
|
||||
this.setState({files});
|
||||
};
|
||||
|
||||
handleFilesClick(event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
handleAddTorrents = () => {
|
||||
if (this.formRef == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = this.formRef.getFormData();
|
||||
this.setState({isAddingTorrents: true});
|
||||
|
||||
const fileData = new FormData();
|
||||
const {destination, start, tags, isBasePath} = formData;
|
||||
const {destination, start, tags, isBasePath} = formData as Partial<AddTorrentsByFileFormData>;
|
||||
|
||||
const filesData: Array<string> = [];
|
||||
this.state.files.forEach((file) => {
|
||||
fileData.append('torrents', file);
|
||||
filesData.push(file.data);
|
||||
});
|
||||
|
||||
tags.split(',').forEach((tag) => {
|
||||
fileData.append('tags', tag);
|
||||
if (filesData.length === 0 || destination == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TorrentActions.addTorrentsByFiles({
|
||||
files: filesData,
|
||||
destination,
|
||||
tags: tags != null ? tags.split(',') : undefined,
|
||||
isBasePath: isBasePath || false,
|
||||
start: start || false,
|
||||
});
|
||||
|
||||
fileData.append('destination', destination);
|
||||
fileData.append('isBasePath', isBasePath);
|
||||
fileData.append('start', start);
|
||||
|
||||
TorrentActions.addTorrentsByFiles(fileData, destination);
|
||||
SettingsStore.setFloodSetting('startTorrentsOnLoad', start);
|
||||
if (start != null) {
|
||||
SettingsStore.setFloodSetting('startTorrentsOnLoad', start);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -95,25 +95,33 @@ class TorrentListContainer extends React.Component {
|
||||
};
|
||||
|
||||
handleFileDrop = (files) => {
|
||||
const destination =
|
||||
SettingsStore.getFloodSetting('torrentDestination') || SettingsStore.getClientSetting('directoryDefault') || '';
|
||||
const filesData = [];
|
||||
|
||||
const isBasePath = false;
|
||||
const callback = (data) => {
|
||||
filesData.concat(data);
|
||||
|
||||
const start = SettingsStore.getFloodSetting('startTorrentsOnLoad');
|
||||
|
||||
const fileData = new FormData();
|
||||
if (filesData.length === files.length) {
|
||||
TorrentActions.addTorrentsByFiles({
|
||||
files: filesData,
|
||||
destination:
|
||||
SettingsStore.getFloodSetting('torrentDestination') ||
|
||||
SettingsStore.getClientSetting('directoryDefault') ||
|
||||
'',
|
||||
isBasePath: false,
|
||||
start: SettingsStore.getFloodSetting('startTorrentsOnLoad'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
files.forEach((file) => {
|
||||
fileData.append('torrents', file);
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (e.target != null && e.target.result != null && typeof e.target.result === 'string') {
|
||||
callback(e.target.result.split('base64,')[1]);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
fileData.append('destination', destination);
|
||||
fileData.append('isBasePath', isBasePath);
|
||||
fileData.append('start', start);
|
||||
fileData.append('tags', '');
|
||||
|
||||
TorrentActions.addTorrentsByFiles(fileData, destination);
|
||||
};
|
||||
|
||||
handleTorrentFilterChange = () => {
|
||||
|
||||
Generated
-109
@@ -1988,15 +1988,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/multer": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.4.tgz",
|
||||
"integrity": "sha512-wdfkiKBBEMTODNbuF3J+qDDSqJxt50yB9pgDiTcFew7f97Gcc7/sM4HR66ofGgpJPOALWOqKAch4gPyqEXSkeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/nedb": {
|
||||
"version": "1.8.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/nedb/-/nedb-1.8.11.tgz",
|
||||
@@ -2810,12 +2801,6 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"append-field": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=",
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
@@ -3943,42 +3928,6 @@
|
||||
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
|
||||
"dev": true
|
||||
},
|
||||
"busboy": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dicer": "0.2.5",
|
||||
"readable-stream": "1.1.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||
@@ -6198,42 +6147,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dicer": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "1.1.x",
|
||||
"streamsearch": "0.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
@@ -11210,22 +11123,6 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"multer": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz",
|
||||
"integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^0.2.11",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"object-assign": "^4.1.1",
|
||||
"on-finished": "^2.3.0",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"multicast-dns": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
|
||||
@@ -16959,12 +16856,6 @@
|
||||
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
|
||||
"dev": true
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
"@types/http-errors": "^1.8.0",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/morgan": "^1.9.1",
|
||||
"@types/multer": "^1.4.4",
|
||||
"@types/nedb": "^1.8.11",
|
||||
"@types/node": "^14.11.2",
|
||||
"@types/passport": "^1.0.4",
|
||||
@@ -124,7 +123,6 @@
|
||||
"minami": "^1.2.3",
|
||||
"mini-css-extract-plugin": "^0.11.2",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.2",
|
||||
"nedb": "^1.8.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"nodemon": "^2.0.4",
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@ app.set('etag', false);
|
||||
app.use(morgan('dev'));
|
||||
app.use(passport.initialize());
|
||||
app.use(compression());
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({extended: false}));
|
||||
app.use(bodyParser.json({limit: '50mb'}));
|
||||
app.use(bodyParser.urlencoded({extended: false, limit: '50mb'}));
|
||||
app.use(cookieParser());
|
||||
|
||||
passportConfig(passport);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import type {Request, Response, NextFunction} from 'express';
|
||||
|
||||
export default (key: string) => (req: Request, _res: Response, next: NextFunction) => {
|
||||
const value = req.body && req.body[key];
|
||||
|
||||
if (value && typeof value === 'string') {
|
||||
req.body[key] = value === 'true';
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
@@ -114,13 +114,12 @@ class ClientRequest {
|
||||
// TODO: Separate these and add support for additional clients.
|
||||
// rTorrent method calls.
|
||||
addFiles(options) {
|
||||
const files = getEnsuredArray(options.files);
|
||||
const {path: destinationPath, isBasePath, start, tags: tagsArr} = options;
|
||||
const {files, path: destinationPath, isBasePath, start, tags: tagsArr} = options;
|
||||
|
||||
files.forEach((file) => {
|
||||
let methodCall = 'load.raw_start';
|
||||
let parameters = ['', file.buffer];
|
||||
const methodCall = start ? 'load.raw_start' : 'load.raw';
|
||||
const timeAdded = Math.floor(Date.now() / 1000);
|
||||
let parameters = ['', Buffer.from(file, 'base64')];
|
||||
|
||||
if (destinationPath) {
|
||||
if (isBasePath) {
|
||||
@@ -132,15 +131,9 @@ class ClientRequest {
|
||||
|
||||
parameters = addTagsToRequest(tagsArr, parameters);
|
||||
|
||||
parameters.push(`d.custom.set=x-filename,${file.originalname}`);
|
||||
// parameters.push(`d.custom.set=x-filename,${file.originalname}`);
|
||||
parameters.push(`d.custom.set=addtime,${timeAdded}`);
|
||||
|
||||
// The start value is a string because it was appended to a FormData
|
||||
// object.
|
||||
if (start === 'false') {
|
||||
methodCall = 'load.raw';
|
||||
}
|
||||
|
||||
this.requests.push(getMethodCall(methodCall, parameters));
|
||||
});
|
||||
}
|
||||
|
||||
+3
-13
@@ -15,15 +15,8 @@ import torrentFileUtil from '../util/torrentFileUtil';
|
||||
import torrentTrackerPropsMap from '../../shared/constants/torrentTrackerPropsMap';
|
||||
|
||||
const client = {
|
||||
addFiles(user, services, req, callback) {
|
||||
const {files} = req;
|
||||
const {destination: destinationPath, isBasePath, start} = req.body;
|
||||
let {tags} = req.body;
|
||||
const request = new ClientRequest(user, services);
|
||||
|
||||
if (!Array.isArray(tags)) {
|
||||
tags = tags.split(',');
|
||||
}
|
||||
addFiles(user, services, options, callback) {
|
||||
const {destination: destinationPath, files, isBasePath, start, tags} = options;
|
||||
|
||||
const resolvedPath = fileUtil.sanitizePath(destinationPath);
|
||||
if (!fileUtil.isAllowedPath(resolvedPath)) {
|
||||
@@ -32,17 +25,14 @@ const client = {
|
||||
}
|
||||
|
||||
fileUtil.createDirectory({path: resolvedPath});
|
||||
request.send();
|
||||
|
||||
// Each torrent is sent individually because rTorrent accepts a total
|
||||
// filesize of 524 kilobytes or less. This allows the user to send many
|
||||
// torrent files reliably.
|
||||
files.forEach((file, index) => {
|
||||
file.originalname = encodeURIComponent(file.originalname);
|
||||
|
||||
const fileRequest = new ClientRequest(user, services);
|
||||
fileRequest.addFiles({
|
||||
files: file,
|
||||
files: [file],
|
||||
path: resolvedPath,
|
||||
isBasePath,
|
||||
start,
|
||||
|
||||
+2
-10
@@ -1,5 +1,4 @@
|
||||
import express from 'express';
|
||||
import multer from 'multer';
|
||||
|
||||
import type {
|
||||
CheckTorrentsOptions,
|
||||
@@ -10,17 +9,10 @@ import type {
|
||||
} from '@shared/types/Action';
|
||||
|
||||
import ajaxUtil from '../util/ajaxUtil';
|
||||
import booleanCoerce from '../middleware/booleanCoerce';
|
||||
import client from '../models/client';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const upload = multer({
|
||||
dest: 'uploads/',
|
||||
limits: {fileSize: 10000000},
|
||||
storage: multer.memoryStorage(),
|
||||
});
|
||||
|
||||
router.get('/connection-test', (req, res) => {
|
||||
req.services?.clientGatewayService
|
||||
.testGateway()
|
||||
@@ -47,8 +39,8 @@ router.post('/add', (req, res) => {
|
||||
client.addUrls(req.user, req.services, req.body, ajaxUtil.getResponseFn(res));
|
||||
});
|
||||
|
||||
router.post('/add-files', upload.array('torrents'), booleanCoerce('isBasePath'), (req, res) => {
|
||||
client.addFiles(req.user, req.services, req, ajaxUtil.getResponseFn(res));
|
||||
router.post('/add-files', (req, res) => {
|
||||
client.addFiles(req.user, req.services, req.body, ajaxUtil.getResponseFn(res));
|
||||
});
|
||||
|
||||
router.get('/settings', (req, res) => {
|
||||
|
||||
@@ -9,6 +9,20 @@ export interface AddTorrentByURLOptions {
|
||||
tags?: Array<string>;
|
||||
}
|
||||
|
||||
// POST /api/client/add-files
|
||||
export interface AddTorrentByFileOptions {
|
||||
// Torrent files in base64
|
||||
files: Array<string>;
|
||||
// Path of destination
|
||||
destination: string;
|
||||
// Tags
|
||||
tags?: Array<string>;
|
||||
// Whether destination is the base path
|
||||
isBasePath: boolean;
|
||||
// Whether to start torrent
|
||||
start: boolean;
|
||||
}
|
||||
|
||||
// POST /api/client/torrents/check-hash
|
||||
export interface CheckTorrentsOptions {
|
||||
// An array of string representing hashes of torrents to be checked
|
||||
|
||||
Reference in New Issue
Block a user