Skip to content

Commit

Permalink
WebTorrent: Add "Save All Files..." feature
Browse files Browse the repository at this point in the history
Currently there is no ability to save all files at once. User has to save each file separately. This commit fixes that.

Fixes: brave/brave-browser#1230
  • Loading branch information
feross committed Aug 13, 2019
1 parent 32b35ba commit 31af2e6
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export const serverUpdated = (torrent: Torrent, serverURL: string) =>
export const startTorrent = (torrentId: string, tabId: number) => action(types.WEBTORRENT_START_TORRENT, { torrentId, tabId })
export const stopDownload = (tabId: number) => action(types.WEBTORRENT_STOP_DOWNLOAD, { tabId })
export const torrentParsed = (torrentId: string, tabId: number, infoHash: string | undefined, errorMsg: string | undefined, parsedTorrent?: Instance) => action(types.WEBTORRENT_TORRENT_PARSED, { torrentId, tabId, infoHash, errorMsg, parsedTorrent })
export const saveAllFiles = (infoHash: string) => action(types.WEBTORRENT_SAVE_ALL_FILES, { infoHash })
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import * as torrentTypes from '../../constants/webtorrent_types'
import { File, TorrentState, TorrentsState } from '../../constants/webtorrentState'

// Utils
import { addTorrent, delTorrent, findTorrent } from '../webtorrent'
import {
addTorrent,
delTorrent,
findTorrent,
saveAllFiles as webtorrentSaveAllFiles
} from '../webtorrent'
import { getTabData } from '../api/tabs_api'
import { parseTorrentRemote } from '../api/torrent_api'

Expand Down Expand Up @@ -224,6 +229,11 @@ const torrentParsed = (torrentId: string, tabId: number, infoHash: string | unde
return { ...state, torrentObjMap, torrentStateMap }
}

const saveAllFiles = (state: TorrentsState, infoHash: string) => {
webtorrentSaveAllFiles(infoHash)
return { ...state }
}

const defaultState: TorrentsState = { currentWindowId: -1, activeTabIds: {}, torrentStateMap: {}, torrentObjMap: {} }
export const webtorrentReducer = (state: TorrentsState = defaultState, action: any) => { // TODO: modify any to be actual action type
const payload = action.payload
Expand Down Expand Up @@ -289,6 +299,9 @@ export const webtorrentReducer = (state: TorrentsState = defaultState, action: a
state = torrentParsed(payload.torrentId, payload.tabId, payload.infoHash,
payload.errorMsg, payload.parsedTorrent, state)
break
case torrentTypes.types.WEBTORRENT_SAVE_ALL_FILES:
state = saveAllFiles(state, payload.infoHash)
break
}

return state
Expand Down
61 changes: 61 additions & 0 deletions components/brave_webtorrent/extension/background/webtorrent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { addTorrentEvents, removeTorrentEvents } from './events/torrentEvents'
import { addWebtorrentEvents } from './events/webtorrentEvents'
import { AddressInfo } from 'net'
import { Instance } from 'parse-torrent'
import { basename, extname } from 'path'
import * as JSZip from 'jszip'

let webTorrent: WebTorrent.Instance | undefined
let servers: { [key: string]: any } = { }
Expand Down Expand Up @@ -79,3 +81,62 @@ export const delTorrent = (infoHash: string) => {

maybeDestroyWebTorrent()
}

export const saveAllFiles = (infoHash: string) => {
const torrent = findTorrent(infoHash)

if (!torrent || !torrent.name || !torrent.files) return

const files: WebTorrent.TorrentFile[] = torrent.files

let zip: any = new JSZip()
const zipFilename = basename(torrent.name, extname(torrent.name)) + '.zip'

const downloadBlob = (blob: Blob) => {
const url = URL.createObjectURL(blob)

chrome.downloads.download({
url: url,
filename: zipFilename,
saveAs: false,
conflictAction: 'uniquify'
})
}

const downloadZip = () => {
if (files.length > 1) {
// generate the zip relative to the torrent folder
zip = zip.folder(torrent.name)
}

zip.generateAsync({ type: 'blob' })
.then(
(blob: Blob) => downloadBlob(blob),
(err: Error) => console.error(err)
)
}

const addFilesToZip = () => {
let addedFiles = 0

files.forEach(file => {
file.getBlob((err, blob) => {
if (err) {
console.error(err)
} else {
// add file to zip
zip.file(file.path, blob)
}

addedFiles += 1

// start the download when all files have been added
if (addedFiles === files.length) {
downloadZip()
}
})
})
}

addFilesToZip()
}
4 changes: 1 addition & 3 deletions components/brave_webtorrent/extension/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,8 @@ export class BraveWebtorrentPage extends React.Component<Props, {}> {
<TorrentViewer
actions={actions}
name={name}
torrentId={torrentState.torrentId}
errorMsg={torrentState.errorMsg}
torrentState={torrentState}
torrent={torrentObj}
tabId={torrentState.tabId}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import { File, TorrentObj } from '../constants/webtorrentState'

interface Props {
torrentId: string
torrent?: TorrentObj
torrent?: TorrentObj,
onSaveAllFiles: () => void
}

export default class TorrentFileList extends React.PureComponent<Props, {}> {
render () {
const { torrent, torrentId } = this.props
const { torrent, torrentId, onSaveAllFiles } = this.props
if (!torrent || !torrent.files) {
return (
<div className='torrentSubhead'>
Expand Down Expand Up @@ -89,9 +90,23 @@ export default class TorrentFileList extends React.PureComponent<Props, {}> {
}
})

const saveAllFiles = () => {
if (!torrent.serverURL || !torrent.files || torrent.progress !== 1) {
return
}
onSaveAllFiles()
}

return (
<div>
<Heading children='Files' level={2} className='torrentHeading' />
<a
href='#'
onClick={saveAllFiles}
className={torrent.progress === 1 ? 'active' : 'inactive'}
>
Save All Files...
</a>
<Table header={header} rows={rows}>
<div className='loadingContainer'>
<div className='__icon'>
Expand Down
17 changes: 11 additions & 6 deletions components/brave_webtorrent/extension/components/torrentViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ import TorrentFileList from './torrentFileList'
import TorrentViewerFooter from './torrentViewerFooter'

// Constants
import { TorrentObj } from '../constants/webtorrentState'
import { TorrentObj, TorrentState } from '../constants/webtorrentState'

interface Props {
actions: any
tabId: number
name?: string | string[]
torrentId: string
errorMsg?: string
torrent?: TorrentObj
torrentState: TorrentState
}

export default class TorrentViewer extends React.PureComponent<Props, {}> {
render () {
const { actions, tabId, name, torrentId, torrent, errorMsg } = this.props
const { actions, name, torrent, torrentState } = this.props
const { torrentId, tabId, errorMsg, infoHash } = torrentState

const onSaveAllFiles = () => actions.saveAllFiles(infoHash)

return (
<div className='torrent-viewer'>
Expand All @@ -37,7 +38,11 @@ export default class TorrentViewer extends React.PureComponent<Props, {}> {
onStopDownload={actions.stopDownload}
/>
<TorrentStatus torrent={torrent} errorMsg={errorMsg} />
<TorrentFileList torrentId={torrentId} torrent={torrent} />
<TorrentFileList
torrentId={torrentId}
torrent={torrent}
onSaveAllFiles={onSaveAllFiles}
/>
<TorrentViewerFooter torrent={torrent} />
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export const enum types {
WEBTORRENT_SERVER_UPDATED = '@@webtorrent/WEBTORRENT_SERVER_UPDATED',
WEBTORRENT_START_TORRENT = '@@webtorrent/WEBTORRENT_START_TORRENT',
WEBTORRENT_STOP_DOWNLOAD = '@@webtorrent/WEBTORRENT_STOP_DOWNLOAD',
WEBTORRENT_TORRENT_PARSED = '@@webtorrent/WEBTORRENT_TORRENT_PARSED'
WEBTORRENT_TORRENT_PARSED = '@@webtorrent/WEBTORRENT_TORRENT_PARSED',
WEBTORRENT_SAVE_ALL_FILES = '@@webtorrent/WEBTORRENT_SAVE_ALL_FILES'
}
2 changes: 1 addition & 1 deletion components/brave_webtorrent/extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"scripts": ["extension/out/brave_webtorrent_background.bundle.js"],
"persistent": true
},
"permissions": ["dns", "tabs", "windows", "<all_urls>"],
"permissions": ["downloads", "dns", "tabs", "windows", "<all_urls>"],
"sockets": {
"udp": {
"send": "*",
Expand Down
8 changes: 8 additions & 0 deletions components/brave_webtorrent/extension/styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,11 @@ a {
#iframe {
border: 0;
}

a.inactive {
color: gray;
}

a.inactive:hover {
cursor: default;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import TorrentViewer from '../../../brave_webtorrent/extension/components/torren
describe('torrentViewer component', () => {
describe('torrentViewer dumb component', () => {
it('renders the component', () => {
const tabId = 1
const torrentId = 'id'
const torrentState: TorrentState = {
tabId: 1,
torrentId: 'id'
}
const name = 'name'
const wrapper = shallow(
<TorrentViewer
actions={{}}
tabId={tabId}
torrentId={torrentId}
name={name}
torrent={torrentObj}
torrentState={torrentState}
/>
)
const assertion = wrapper.find('.torrent-viewer')
Expand Down
50 changes: 41 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,13 @@
"webpack-cli": "^3.0.8"
},
"dependencies": {
"@types/jszip": "^3.1.6",
"@types/parse-torrent": "^5.8.3",
"@types/webtorrent": "^0.98.5",
"bignumber.js": "^7.2.1",
"bluebird": "^3.5.1",
"clipboard-copy": "^2.0.0",
"jszip": "^3.2.2",
"prettier-bytes": "^1.0.4",
"qr-image": "^3.2.0",
"react-chrome-redux": "^1.5.1",
Expand Down

0 comments on commit 31af2e6

Please sign in to comment.