diff --git a/lib/ChunkedBlob.js b/lib/ChunkedBlob.js
deleted file mode 100644
index 0ceb2cc..0000000
--- a/lib/ChunkedBlob.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const rankSize = 16
-
-function blobLength(b) {
- if (typeof b.byteLength !== 'undefined') return b.byteLength
- if (typeof b.size !== 'undefined') return b.size
- return b.length
-}
-
-export default class ChunkedBlob {
-
- constructor() {
- this.size = 0
- this.ranks = []
- }
-
- add(b) {
- this.size += blobLength(b)
- this.ranks.push(b)
- }
-
- toBlob() {
- return new Blob(this.ranks)
- }
-
-}
\ No newline at end of file
diff --git a/lib/DownloadFile.js b/lib/DownloadFile.js
deleted file mode 100644
index 38492c3..0000000
--- a/lib/DownloadFile.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import ChunkedBlob from './ChunkedBlob'
-
-export default class DownloadFile {
-
- constructor(name, size, type) {
- this.name = name
- this.size = size
- this.type = type
- this.chunks = new ChunkedBlob()
- }
-
- addChunk(b) {
- this.chunks.add(b)
- }
-
- clearChunks() {
- this.chunks = new ChunkedBlob()
- }
-
- isComplete() {
- return this.getProgress() === 1
- }
-
- getProgress() {
- return this.chunks.size / this.size
- }
-
- download() {
-
- let blob = this.chunks.toBlob()
- let url = URL.createObjectURL(blob)
-
- let a = document.createElement('a')
- document.body.appendChild(a)
- a.download = this.name
- a.href = url
- a.click()
-
- setTimeout(() => {
- URL.revokeObjectURL(url)
- document.body.removeChild(a)
- }, 0)
-
- }
-
- toJSON() {
- return {
- name: this.name,
- size: this.size,
- type: this.type
- }
- }
-
-}
diff --git a/lib/UploadFile.js b/lib/UploadFile.js
deleted file mode 100644
index d6cb0f1..0000000
--- a/lib/UploadFile.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const chunkSize = 256 * 1024
-
-export default class UploadFile {
-
- constructor(file) {
- this.name = file.name
- this.size = file.size
- this.type = file.type
- this.blob = file
- }
-
- countChunks() {
- return Math.ceil(this.size / chunkSize)
- }
-
- getChunk(i) {
- if (i < 0 || i >= this.countChunks())
- throw new Error('Chunk out of bounds')
-
- let start = i * chunkSize
- let end = Math.min(start + chunkSize, this.size)
- return this.blob.slice(start, end)
- }
-
- toJSON() {
- return {
- name: this.name,
- size: this.size,
- type: this.type
- }
- }
-
-}
diff --git a/lib/actions/DownloadActions.js b/lib/actions/DownloadActions.js
index 9cdfe8e..de2dcdf 100644
--- a/lib/actions/DownloadActions.js
+++ b/lib/actions/DownloadActions.js
@@ -3,8 +3,7 @@ import alt from '../alt'
export default alt.createActions(class DownloadActions {
constructor() {
this.generateActions(
- 'requestDownload',
- 'beginDownload'
+ 'requestDownload'
)
}
})
diff --git a/lib/actions/UploadActions.js b/lib/actions/UploadActions.js
index 42a3099..ae03df5 100644
--- a/lib/actions/UploadActions.js
+++ b/lib/actions/UploadActions.js
@@ -3,8 +3,6 @@ import alt from '../alt'
export default alt.createActions(class UploadActions {
constructor() {
this.generateActions(
- 'sendToDownloader',
- 'setUploadToken',
'uploadFile'
)
}
diff --git a/lib/components/ChromeNotice.js b/lib/components/ChromeNotice.js
index 692d9c0..cc5fd66 100644
--- a/lib/components/ChromeNotice.js
+++ b/lib/components/ChromeNotice.js
@@ -4,7 +4,7 @@ import SupportStore from '../stores/SupportStore'
function getState() {
return {
- active: SupportStore.getState().isChrome && DownloadStore.getState().file.size >= 500000000
+ active: SupportStore.getState().isChrome && DownloadStore.getState().fileSize >= 500000000
}
}
diff --git a/lib/components/DownloadPage.js b/lib/components/DownloadPage.js
index a42ac2a..840417e 100644
--- a/lib/components/DownloadPage.js
+++ b/lib/components/DownloadPage.js
@@ -2,10 +2,10 @@ import ChromeNotice from './ChromeNotice'
import DownloadActions from '../actions/DownloadActions'
import DownloadButton from './DownloadButton'
import DownloadStore from '../stores/DownloadStore'
+import ErrorPage from './ErrorPage'
import ProgressBar from './ProgressBar'
import React from 'react'
import Spinner from './Spinner'
-import peer from 'filepizza-peerjs'
export default class DownloadPage extends React.Component {
@@ -16,20 +16,14 @@ export default class DownloadPage extends React.Component {
this._onChange = () => {
this.setState(DownloadStore.getState())
}
-
- this._onConnection = (conn) => {
- DownloadActions.beginDownload(conn)
- }
}
componentDidMount() {
DownloadStore.listen(this._onChange)
- peer.on('connection', this._onConnection)
}
componentDidUnmount() {
DownloadStore.unlisten(this._onChange)
- peer.removeListener('connection', this._onConnection)
}
downloadFile() {
@@ -43,8 +37,8 @@ export default class DownloadPage extends React.Component {
FilePizza
+ name={this.state.fileName}
+ size={this.state.fileSize} />
@@ -57,39 +51,29 @@ export default class DownloadPage extends React.Component {
FilePizza
+ name={this.state.fileName}
+ size={this.state.fileSize} />
- case 'cancelled':
- return
-
-
FilePizza
-
-
-
-
-
-
-
case 'done':
return
FilePizza
+ name={this.state.fileName}
+ size={this.state.fileSize} />
+
+ default:
+ return
}
}
diff --git a/lib/components/UploadPage.js b/lib/components/UploadPage.js
index d7a491c..3d193e3 100644
--- a/lib/components/UploadPage.js
+++ b/lib/components/UploadPage.js
@@ -1,5 +1,4 @@
import DropZone from './DropZone'
-import ProgressBar from './ProgressBar'
import React from 'react'
import Spinner from './Spinner'
import Tempalink from './Tempalink'
@@ -12,46 +11,40 @@ export default class UploadPage extends React.Component {
constructor() {
super()
this.state = UploadStore.getState()
-
+
this._onChange = () => {
this.setState(UploadStore.getState())
}
-
- this._onDownload = (peerID) => {
- UploadActions.sendToDownloader(peerID)
- }
}
-
+
componentDidMount() {
UploadStore.listen(this._onChange)
- socket.on('download', this._onDownload)
}
-
+
componentDidUnmount() {
UploadStore.unlisten(this._onChange)
- socket.removeListener('download', this._onDownload)
}
-
+
uploadFile(file) {
UploadActions.uploadFile(file)
}
-
+
handleSelectedFile(event) {
- let files = event.target.files;
+ let files = event.target.files
if (files.length > 0) {
- UploadActions.uploadFile(files[0]);
+ UploadActions.uploadFile(files[0])
}
}
-
+
render() {
switch (this.state.status) {
case 'ready':
-
+
return
-
+
-
+
FilePizza
Free peer-to-peer file transfers in your browser.
We never store anything. Files only served fresh.
@@ -63,36 +56,29 @@ export default class UploadPage extends React.Component {
-
+
case 'processing':
return
-
+
-
+
FilePizza
Processing...
-
+
-
+
case 'uploading':
- var keys = Object.keys(this.state.peerProgress)
- keys.reverse()
return
-
+
FilePizza
-
-
+
+
Send someone this link to download.
This link will work as long as this page is open.
-
- {keys.length > 0 ?
Download Progress
: null}
-
- {keys.map((key) => {
- return
- })}
-
-
+
}
}
diff --git a/lib/db.js b/lib/db.js
index 6429096..245929c 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -35,5 +35,6 @@ export function find(token) {
}
export function remove(client) {
+ if (client == null) return
delete tokens[client.token]
}
diff --git a/lib/middleware/bootstrap.js b/lib/middleware/bootstrap.js
index 505b629..68f93cb 100644
--- a/lib/middleware/bootstrap.js
+++ b/lib/middleware/bootstrap.js
@@ -11,7 +11,10 @@ routes.get(/^\/([a-z]+-[a-z]+-[a-z]+-[a-z]+)$/, function (req, res, next) {
DownloadStore: {
status: 'ready',
token: uploader.token,
- file: uploader.metadata
+ fileSize: uploader.fileSize,
+ fileName: uploader.fileName,
+ fileType: uploader.fileType,
+ infoHash: uploader.infoHash
}
}
diff --git a/lib/middleware/react.js b/lib/middleware/react.js
index eefaea9..abab556 100644
--- a/lib/middleware/react.js
+++ b/lib/middleware/react.js
@@ -14,16 +14,16 @@ function isNotFound(state) {
module.exports = function (req, res) {
alt.bootstrap(JSON.stringify(res.locals.data || {}))
-
+
ReactRouter.run(routes, req.url, function (Handler, state) {
-
+
var html = React.renderToString()
alt.flush()
-
+
if (isNotFound(state)) res.status(404)
res.write('\n')
res.end(html)
-
+
})
}
diff --git a/lib/routes.js b/lib/routes.js
index c5a1aa0..5e5e2e7 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -14,3 +14,4 @@ export default (
)
+
diff --git a/lib/server.js b/lib/server.js
index 7e99ac8..25da942 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -3,7 +3,6 @@ var express = require('express')
var fs = require('fs')
var http = require('http')
var path = require('path')
-var peer = require('peer')
var socketIO = require('socket.io')
var winston = require('winston')
var expressWinston = require('express-winston')
@@ -32,8 +31,6 @@ server.listen(process.env.PORT || 3000, function () {
console.log('FilePizza listening on %s:%s', host, port)
})
-app.use('/peer', peer.ExpressPeerServer(server))
-
app.use(expressWinston.logger({
winstonInstance: winston,
expressFormat: true
@@ -55,22 +52,18 @@ io.on('connection', function (socket) {
socket.on('upload', function (metadata, res) {
if (upload) return
- upload = true
db.create(socket).then((u) => {
upload = u
- upload.metadata = metadata
+ upload.fileName = metadata.fileName
+ upload.fileSize = metadata.fileSize
+ upload.fileType = metadata.fileType
+ upload.infoHash = metadata.infoHash
res(upload.token)
})
})
- socket.on('download', function (data) {
- var uploader = db.find(data.token)
- if (!uploader) return
- uploader.socket.emit('download', data.peerID)
- })
-
socket.on('disconnect', function () {
- if (upload) db.remove(upload)
+ db.remove(upload)
})
})
diff --git a/lib/stores/DownloadStore.js b/lib/stores/DownloadStore.js
index b3285cf..362481b 100644
--- a/lib/stores/DownloadStore.js
+++ b/lib/stores/DownloadStore.js
@@ -1,62 +1,57 @@
import DownloadActions from '../actions/DownloadActions'
-import DownloadFile from '../DownloadFile'
-import peer from 'filepizza-peerjs'
+import WebTorrent from 'webtorrent'
import alt from '../alt'
import socket from 'filepizza-socket'
+function downloadBlobURL(name, blobURL) {
+ let a = document.createElement('a')
+ document.body.appendChild(a)
+ a.download = name
+ a.href = blobURL
+ a.click()
+}
+
export default alt.createStore(class DownloadStore {
constructor() {
this.bindActions(DownloadActions)
- this.status = 'ready'
- this.token = null
- this.file = null
+ this.fileName = ''
+ this.fileSize = 0
+ this.fileType = ''
this.progress = 0
-
- this.on('bootstrap', () => {
- if (this.file && !(this.file instanceof DownloadFile)) {
- this.file = new DownloadFile(this.file.name,
- this.file.size,
- this.file.type)
- }
- })
+ this.status = 'uninitialized'
+ this.token = null
+ this.infoHash = null
}
onRequestDownload() {
if (this.status !== 'ready') return
this.status = 'requesting'
- socket.emit('download', {
- peerID: peer.id,
- token: this.token
- })
- }
-
- onBeginDownload(conn) {
- if (this.status !== 'requesting') return
- this.status = 'downloading'
-
- conn.on('data', (chunk) => {
- if (this.status !== 'downloading') return
-
- this.file.addChunk(chunk)
-
- if (this.file.isComplete()) {
- this.setState({ status: 'done', progress: 1 })
- this.file.download()
- conn.close()
- } else {
- this.setState({ progress: this.file.getProgress() })
- conn.send('more')
- }
-
- })
-
- conn.on('close', () => {
- if (this.status !== 'downloading') return
- this.setState({ status: 'cancelled', progress: 0 })
- this.file.clearChunks()
+ const client = new WebTorrent()
+ client.download(this.infoHash, (torrent) => {
+ this.setState({ status: 'downloading' })
+
+ let downloaded = 0
+ const file = torrent.files[0]
+ const stream = file.createReadStream()
+ stream.on('data', (chunk) => {
+ if (this.status !== 'downloading') return
+
+ downloaded += chunk.length
+
+ if (downloaded === file.length) {
+ this.setState({ status: 'done', progress: 1 })
+ file.getBlobURL((err, blobURL) => {
+ if (err) throw err
+ downloadBlobURL(this.fileName, blobURL)
+ })
+ } else {
+ this.setState({ progress: downloaded / file.length })
+ }
+
+ })
})
}
diff --git a/lib/stores/UploadStore.js b/lib/stores/UploadStore.js
index 9f64e25..8012099 100644
--- a/lib/stores/UploadStore.js
+++ b/lib/stores/UploadStore.js
@@ -1,71 +1,43 @@
import UploadActions from '../actions/UploadActions'
-import UploadFile from '../UploadFile'
import alt from '../alt'
-import peer from 'filepizza-peerjs'
import socket from 'filepizza-socket'
+import WebTorrent from 'webtorrent'
export default alt.createStore(class UploadStore {
constructor() {
this.bindActions(UploadActions)
+ this.fileName = ''
+ this.fileSize = 0
+ this.fileType = ''
this.status = 'ready'
this.token = null
- this.file = null
- this.peerProgress = {}
+ this.infoHash = null
}
onUploadFile(file) {
if (this.status !== 'ready') return
this.status = 'processing'
- this.file = new UploadFile(file)
- socket.emit('upload', {
- name: this.file.name,
- size: this.file.size,
- type: this.file.type
- }, (token) => {
- this.setState({
- status: 'uploading',
- token: token
+ const client = new WebTorrent()
+ client.seed(file, (torrent) => {
+ socket.emit('upload', {
+ fileName: file.name,
+ fileSize: file.size,
+ fileType: file.type,
+ infoHash: torrent.infoHash
+ }, (token) => {
+ this.setState({
+ status: 'uploading',
+ token: token,
+ fileName: file.name,
+ fileSize: file.size,
+ fileType: file.type,
+ infoHash: torrent.infoHash
+ })
})
})
}
- onSendToDownloader(peerID) {
- if (this.status !== 'uploading') return
-
- let conn = peer.connect(peerID, {
- reliable: true
- })
-
- let totalChunks = this.file.countChunks()
- let i = 0
-
- let sendNextChunk = () => {
- if (i === totalChunks) return
- let packet = this.file.getChunk(i)
- conn.send(packet)
- i++
- this.peerProgress[peerID] = i/totalChunks
- }
-
- conn.on('open', () => {
- sendNextChunk()
- this.setState({ peerProgress: this.peerProgress })
- })
-
- conn.on('data', (data) => {
- if (data === 'more') sendNextChunk()
- this.setState({ peerProgress: this.peerProgress })
- })
-
- conn.on('close', () => {
- if (this.peerProgress[peerID] < 1) {
- this.peerProgress[peerID] = -1
- }
- this.setState({ peerProgress: this.peerProgress })
- })
- }
-
}, 'UploadStore')
diff --git a/package.json b/package.json
index ffc8dd6..e08ea3d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "filepizza",
- "version": "0.1.2",
+ "version": "0.2.0",
"description": "Free peer-to-peer file transfers in your browser.",
"main": "index.js",
"scripts": {
@@ -25,13 +25,10 @@
"classnames": "^1.2.0",
"express": "^4.12.0",
"express-winston": "^0.3.1",
- "filepizza-peerjs": "^1.0.0",
"filepizza-socket": "^1.0.0",
"newrelic": "^1.21.1",
"nib": "^1.1.0",
"node-uuid": "^1.4.3",
- "peer": "^0.2.8",
- "peerjs": "^0.3.14",
"react": "^0.13.0",
"react-frozenhead": "^0.3.0",
"react-google-analytics": "^0.2.0",
@@ -40,6 +37,7 @@
"socket.io-client": "^1.3.5",
"stylus": "^0.50.0",
"webrtcsupport": "^2.1.2",
+ "webtorrent": "^0.56.0",
"winston": "^1.0.1",
"xkcd-password": "^1.2.0"
},