From 0b329b83287dfdb7bcc1555252e14c697d7e6d6d Mon Sep 17 00:00:00 2001 From: Alexey Kasyanchuk Date: Tue, 28 Aug 2018 04:20:48 +0300 Subject: [PATCH] feat(downloading): ability to choose files which you wanna download --- src/app/torrent-page.js | 77 +++++++++++++++++++++++++++----- src/background/api.js | 78 +++++++++++++++++++++++++++++++++ src/background/torrentClient.js | 12 ++++- 3 files changed, 155 insertions(+), 12 deletions(-) diff --git a/src/app/torrent-page.js b/src/app/torrent-page.js index cb101ce..5ec36c7 100644 --- a/src/app/torrent-page.js +++ b/src/app/torrent-page.js @@ -7,6 +7,7 @@ import Subheader from 'material-ui/Subheader'; import {Tabs, Tab} from 'material-ui/Tabs'; import ActionInfo from 'material-ui/svg-icons/action/info'; import RaisedButton from 'material-ui/RaisedButton'; +import Toggle from 'material-ui/Toggle'; import FileFolder from 'material-ui/svg-icons/file/folder'; import NoImage from './images/no-image-icon.png' @@ -42,11 +43,21 @@ let buildFilesTree = (filesList) => { filesList.forEach((file) => { let pathTree = file.path.split('/'); let currentItem = rootTree; - pathTree.forEach((pathItem) => { + pathTree.forEach((pathItem, index) => { if(!(pathItem in currentItem)) { - currentItem[pathItem] = { - __sizeBT: 0 + // крайний индекс, значит это объект файла, объединяем объекты + if(index === pathTree.length - 1) + { + file.__sizeBT = 0 + file.__fileBT = true + currentItem[pathItem] = file + } + else + { + currentItem[pathItem] = { + __sizeBT: 0 + } } } currentItem = currentItem[pathItem] @@ -57,22 +68,59 @@ let buildFilesTree = (filesList) => { return rootTree; } -const treeToTorrentFiles = (tree) => { +const treeToTorrentFiles = (tree, torrent, toggles) => { + // toggles for button disable/enable torrent/directory in torrent client + if(toggles) + { + if(tree.__fileBT && typeof tree.downloadIndex !== 'undefined') + { + toggles.push({ + downloadIndex: tree.downloadIndex, + selected: typeof tree.downloadSelected === 'undefined' || tree.downloadSelected + }) + } + } + + // this is already file, return + if(tree.__fileBT) + return + let arr = []; for(let file in tree) { if(file == '__sizeBT') continue; + const newToggles = [] + arr.push( 1 ? : contentIcon(fileTypeDetect({path: file}))} + leftIcon={!tree[file].__fileBT ? : contentIcon(fileTypeDetect({path: file}))} + rightToggle={ + newToggles.length > 0 + && + selected )} + onToggle={(e, checked) => { + e.preventDefault() + e.stopPropagation() + let toggleValues = {} + newToggles.forEach(({downloadIndex}) => toggleValues[downloadIndex] = checked) + window.torrentSocket.emit('downloadSelectFiles', torrent, toggleValues) + }} + />} />); + + if(toggles) + { + for(const newToggle of newToggles) + toggles.push(newToggle) + } } return arr; } @@ -87,7 +135,7 @@ const TorrentFiles = (props) => { ?
{__('Content of the torrent')}: - {treeToTorrentFiles(tree)} + {treeToTorrentFiles(tree, {hash: props.torrent.hash})}
:
@@ -206,7 +254,7 @@ export default class TorrentPage extends Page { // Получаем более новую статистику пира if((Date.now() / 1000) - this.torrent.trackersChecked > 10 * 60) { window.torrentSocket.emit('checkTrackers', this.torrent.hash); - } + } } }, () => { this.setState({ @@ -221,11 +269,20 @@ export default class TorrentPage extends Page { componentDidMount() { super.componentDidMount(); - this.filesUpdated = (hash) => { + this.filesUpdated = (hash, filesList) => { if(this.props.hash != hash) return; - this.getTorrentInfo(); + if(filesList) + { + if(this.torrent) + { + this.torrent.filesList = filesList + this.forceUpdate() + } + } + else + this.getTorrentInfo(); } window.torrentSocket.on('filesReady', this.filesUpdated); diff --git a/src/background/api.js b/src/background/api.js index 9f0c040..acc2e7c 100644 --- a/src/background/api.js +++ b/src/background/api.js @@ -57,6 +57,7 @@ module.exports = async ({ removeOnDone: download.removeOnDone, paused: torrent.paused || torrent._paused } + torrent.filesList = download.files.length > 0 ? downloadFilesList(download) : torrent.filesList } } @@ -81,6 +82,14 @@ module.exports = async ({ Fn(...rest, (data) => callback(mergeTorrentsWithDownloads(data, copy))) } + const downloadFilesList = (torrent) => torrent.files.map((file, index) => ({ + path: file.path.replace(/\\/g, '/'), + size: file.length, + downloadIndex: index, + downloadSelected: file.selected + })) + + recive('recentTorrents', function(callback) { if(typeof callback != 'function') @@ -692,6 +701,7 @@ module.exports = async ({ delete torrent._paused torrent._pause() } + send('filesReady', torrent.infoHash, downloadFilesList(torrent)) }) torrent.on('done', () => { @@ -762,6 +772,49 @@ module.exports = async ({ return _destroy.call(torrent, ...args) } + torrent.selectFiles = (selection) => { + if(Array.isArray(selection)) + { + if(selection.length !== torrent.files.length) + { + logTE('downloader', 'selection map not full', torrent.files.length, selection.length) + return + } + for(let i = 0; i < selection.length; i++) + { + torrent.files[i].selected = !!selection[i] + } + } + else + { + for(let fileId in selection) + { + fileId = parseInt(fileId) + if(fileId >= torrent.files.length) + { + logTE('downloader', 'selection map wrong', selection) + return + } + torrent.files[fileId].selected = !!selection[fileId] + } + } + torrent.updateFilesSelection() + } + + torrent.updateFilesSelection = () => { + torrent.deselect(0, torrent.pieces.length - 1, false) + + for(const file of torrent.files) + { + const {selected} = file + if(typeof selected === 'undefined' || selected) + file.select() + else + file.deselect() + } + logT('downloader', 'selection updated') + } + if(callback) callback(true) @@ -834,6 +887,31 @@ module.exports = async ({ }) }) + recive('downloadSelectFiles', ({hash}, files, callback) => + { + logT('downloader', 'call update selection', hash, files.length) + const id = torrentClientHashMap[hash] + if(!id) + { + logT('downloader', 'cant find torrent for selection', hash) + if(callback) + callback(false) + return + } + + const torrent = torrentClient.get(id) + if(!torrent) { + logT('downloader', 'no torrent for selection founded') + return + } + + torrent.selectFiles(files) + send('filesReady', torrent.infoHash, downloadFilesList(torrent)) + + if(callback) + callback(true) + }) + recive('downloads', (callback) => { callback(torrentClient.torrents.map(torrent => ({ diff --git a/src/background/torrentClient.js b/src/background/torrentClient.js index 0a95015..1248e7b 100644 --- a/src/background/torrentClient.js +++ b/src/background/torrentClient.js @@ -10,7 +10,8 @@ torrentClient.saveSession = (sessionFile) => { torrent: torrent.torrentObject, removeOnDone: torrent.removeOnDone, - paused: torrent.paused || torrent._paused + paused: torrent.paused || torrent._paused, + selection: torrent.files.map(file => typeof file.selected === 'undefined' || file.selected) })) }, null, 4), 'utf8'); } @@ -34,7 +35,7 @@ torrentClient.loadSession = (sessionFile) => { return } const {torrents} = obj - torrents.forEach(({torrent, infoHash, path, removeOnDone, paused}) => { + torrents.forEach(({torrent, infoHash, path, removeOnDone, paused, selection}) => { if(!torrent || !infoHash || !path) { logT('downloader', 'no info for starting download this torrent') @@ -51,6 +52,13 @@ torrentClient.loadSession = (sessionFile) => { { download._paused = true } + if(selection) + { + download.on('metadata', () => { + logT('downloader', 'load torrent selection from session') + download.selectFiles(selection) + }) + } } }) }