Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Merge pull request #7351 from brave/feross/torrent-files
Browse files Browse the repository at this point in the history
Support .torrent files in Torrent Viewer
  • Loading branch information
bsclifton authored Apr 3, 2017
2 parents b73d117 + a03d8c0 commit ef59f09
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 128 deletions.
68 changes: 67 additions & 1 deletion app/browser/webtorrent.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
const electron = require('electron')
const ipc = electron.ipcMain
const appUrlUtil = require('../../js/lib/appUrlUtil')
const messages = require('../../js/constants/messages')
const Filtering = require('../filtering')

// Set to see communication between WebTorrent and torrent viewer tabs
const DEBUG_IPC = false
if (DEBUG_IPC) console.log('WebTorrent IPC debugging enabled')

var VIEWER_URL = appUrlUtil.getTorrentExtUrl('webtorrent.html')

function getViewerURL (torrentUrl) {
return VIEWER_URL + '#' + encodeURIComponent(torrentUrl)
}

// Connects to the BitTorrent network
// Communicates with the WebTorrentRemoteClients via message passing
let server = null
Expand All @@ -23,7 +31,7 @@ function init (state, action) {
channels[msg.clientKey] = e.sender
server.receive(msg)
})

setupFiltering()
return state
}

Expand All @@ -43,6 +51,64 @@ function send (msg) {
channel.send(messages.TORRENT_MESSAGE, msg)
}

function setupFiltering () {
Filtering.registerHeadersReceivedFilteringCB(function (details, isPrivate) {
if (details.method !== 'GET') {
return {}
}
if (!isTorrentFile(details)) {
return {}
}

var viewerUrl = getViewerURL(details.url)

return {
responseHeaders: {
'Location': [ viewerUrl ]
},
statusLine: 'HTTP/1.1 301 Moved Permanently',
resourceName: 'webtorrent'
}
})
}

/**
* Check if the request is a torrent file.
* @param {Object} details First argument of the webRequest.onHeadersReceived
* event. The properties "responseHeaders" and "url"
* are read.
* @return {boolean} True if the resource is a torrent file.
*/
function isTorrentFile (details) {
var header = getHeader(details.responseHeaders, 'content-type')
if (header) {
var headerValue = header.toLowerCase().split(';', 1)[0].trim()
if (headerValue === 'application/x-bittorrent') {
return true
}
if (headerValue === 'application/octet-stream') {
if (details.url.toLowerCase().indexOf('.torrent') > 0) {
return true
}
var cdHeader =
getHeader(details.responseHeaders, 'content-disposition')
if (cdHeader && /\.torrent(["']|$)/i.test(cdHeader)) {
return true
}
}
}
return false
}

function getHeader (headers, headerName) {
var headerNames = Object.keys(headers)
for (var i = 0; i < headerNames.length; ++i) {
if (headerNames[i].toLowerCase() === headerName) {
return headers[headerNames[i]][0]
}
}
}

module.exports = {
init,
resourceName: 'webtorrent'
Expand Down
4 changes: 3 additions & 1 deletion app/extensions/torrent/locales/en-US/app.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
startPrompt=Start Downloading "{{name}}"?
startPromptUntitled=Start Downloading?
startDownload=Start Download
saveTorrentFile=Save Torrent File
saveTorrentFile=Save Torrent File...
legalNotice=When you start a torrent, its data will be made available to others by means of upload. You are responsible for abiding by your local laws.
missingFilesList=Click "Start Download" to load the torrent file list.
loadingFilesList=Loading the torrent file list...
Expand All @@ -18,3 +18,5 @@ downloadFile=Save File
torrentStatus=Torrent Status
torrentLoadingInfo=Loading torrent info...
torrentLoadingMedia=Loading...
copyMagnetLink=Copy Magnet Link
webtorrentPage=WebTorrent
2 changes: 1 addition & 1 deletion app/extensions/torrent/webtorrent.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
<link rel="shortcut icon" type="image/x-icon" href="img/webtorrent-128.png">
<title data-l10n-id="webtorrentPage"></title>
<script src="ext/l20n.min.js"></script>
<script src='gen/webtorrentPage.entry.js'></script>
<link rel="localization" href="locales/{locale}/app.properties">
</head>
<body>
<div id="appContainer" />
<script src='gen/webtorrentPage.entry.js'></script>
</body>
</html>
6 changes: 4 additions & 2 deletions app/filtering.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,6 @@ function registerForBeforeSendHeaders (session, partition) {
*/
function registerForHeadersReceived (session, partition) {
const isPrivate = module.exports.isPrivate(partition)
// Note that onBeforeRedirect listener doesn't take a callback
session.webRequest.onHeadersReceived(function (details, cb) {
// Using an electron binary which isn't from Brave
if (shouldIgnoreUrl(details)) {
Expand All @@ -307,7 +306,10 @@ function registerForHeadersReceived (session, partition) {
continue
}
if (results.responseHeaders) {
cb({responseHeaders: results.responseHeaders})
cb({
responseHeaders: results.responseHeaders,
statusLine: results.statusLine
})
return
}
}
Expand Down
35 changes: 35 additions & 0 deletions js/webtorrent/components/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const React = require('react')

const MediaViewer = require('./mediaViewer')
const TorrentViewer = require('./torrentViewer')

class App extends React.Component {
render () {
const {
ix,
name,
torrentId,
torrentIdProtocol,
torrent,
serverUrl,
errorMessage
} = this.props.store

if (torrent && ix != null) {
return <MediaViewer torrent={torrent} serverUrl={serverUrl} ix={ix} />
} else {
return (
<TorrentViewer
name={name}
torrentId={torrentId}
torrentIdProtocol={torrentIdProtocol}
torrent={torrent}
serverUrl={serverUrl}
errorMessage={errorMessage}
dispatch={this.props.dispatch} />
)
}
}
}

module.exports = App
12 changes: 7 additions & 5 deletions js/webtorrent/components/mediaViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,26 @@ const SUPPORTED_AUDIO_EXTENSIONS = [

module.exports = class MediaViewer extends React.Component {
render () {
const torrent = this.props.torrent
const ix = this.props.ix
const { torrent, serverUrl, ix } = this.props

const file = torrent.files[ix]
const fileURL = serverUrl && (serverUrl + '/' + ix)

const fileExt = file && getExtension(file.name)
const isVideo = SUPPORTED_VIDEO_EXTENSIONS.includes(fileExt)
const isAudio = SUPPORTED_AUDIO_EXTENSIONS.includes(fileExt)
const fileURL = torrent.serverURL && (torrent.serverURL + '/' + ix)

let content
if (torrent.serverURL == null) {
if (!file || !serverUrl) {
content = <div data-l10n-id='torrentLoadingMedia' />
} else if (isVideo) {
content = <video src={fileURL} autoPlay controls />
} else if (isAudio) {
content = <audio src={fileURL} autoPlay controls />
} else {
// For security, sandbox and disallow scripts.
// We need allow-same-origin so that the iframe can load from http://localhost:...
// We need allow-same-origin so that the iframe can load from
// http://localhost:...
content = <iframe src={fileURL} sandbox='allow-same-origin' />
}

Expand Down
17 changes: 10 additions & 7 deletions js/webtorrent/components/torrentFileList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const SortableTable = require('../../components/sortableTable')

class TorrentFileList extends React.Component {
render () {
const torrent = this.props.torrent
const { torrent, stateOwner } = this.props
const files = torrent && torrent.files

let content
Expand All @@ -27,7 +27,7 @@ class TorrentFileList extends React.Component {
rowObjects={files}
columnClassNames={['num', 'name', 'downloadFile', 'size']}
addHoverClass
stateOwner={this.props.stateOwner} />
stateOwner={stateOwner} />
]
}

Expand All @@ -40,18 +40,21 @@ class TorrentFileList extends React.Component {
}

renderFileLink (file, isDownload) {
const { torrent, torrentID } = this.props
const { torrentId, torrent, serverUrl } = this.props
const ix = torrent.files.indexOf(file)
if (isDownload) {
if (torrent.serverURL) {
const httpURL = torrent.serverURL + '/' + ix
if (serverUrl) {
const httpURL = serverUrl + '/' + ix
return <a href={httpURL} download={file.name}></a>
} else {
return <div /> // No download links until the server is ready
}
} else {
const magnetURL = torrentID + '&ix=' + ix
return <a href={magnetURL}>{file.name}</a>
const suffix = /^https?:/.test(torrentId)
? '#ix=' + ix
: '&ix=' + ix
const href = torrentId + suffix
return <a href={href}>{file.name}</a>
}
}
}
Expand Down
43 changes: 35 additions & 8 deletions js/webtorrent/components/torrentViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,18 @@ class TorrentViewer extends React.Component {
}

render () {
const {torrent, torrentID, name, errorMessage, dispatch} = this.props
const {
name,
torrentId,
torrent,
serverUrl,
errorMessage,
torrentIdProtocol,
dispatch
} = this.props

let titleElem, mainButtonId, saveButton

let titleElem, mainButtonId
if (torrent) {
if (name) {
// No localization, just use the torrent name
Expand All @@ -36,6 +45,24 @@ class TorrentViewer extends React.Component {
mainButtonId = 'startDownload'
}

if (torrentIdProtocol === 'magnet:') {
saveButton = (
<Button
l10nId='copyMagnetLink'
className='whiteButton copyMagnetLink'
onClick={() => dispatch('copyMagnetLink')}
/>
)
} else {
saveButton = (
<Button
l10nId='saveTorrentFile'
className='whiteButton saveTorrentFile'
onClick={() => dispatch('saveTorrentFile')}
/>
)
}

const legalNotice = torrent != null
? <a className='legalNotice' data-l10n-id='poweredByWebTorrent' href='https://webtorrent.io' target='_blank' />
: <div className='legalNotice' data-l10n-id='legalNotice' />
Expand All @@ -49,20 +76,20 @@ class TorrentViewer extends React.Component {
l10nId={mainButtonId}
className='primaryButton mainButton'
disabled={!!torrent}
onClick={() => dispatch('start')} />
<Button
l10nId='saveTorrentFile'
className='whiteButton saveTorrentFile'
onClick={() => dispatch('saveTorrentFile')} />
onClick={() => dispatch('start')}
/>
{saveButton}
</div>
</div>

<div className='siteDetailsPageContent'>
<TorrentStatus torrent={torrent} errorMessage={errorMessage} />
<TorrentFileList
torrentId={torrentId}
torrent={torrent}
serverUrl={serverUrl}
stateOwner={this}
torrentID={torrentID} />
/>
{legalNotice}
</div>
</div>
Expand Down
Loading

0 comments on commit ef59f09

Please sign in to comment.