diff --git a/src/renderer/controllers/prefs-controller.js b/src/renderer/controllers/prefs-controller.js
index c45baa88ac..bdecb5b175 100644
--- a/src/renderer/controllers/prefs-controller.js
+++ b/src/renderer/controllers/prefs-controller.js
@@ -35,4 +35,12 @@ module.exports = class PrefsController {
dispatch('stateSaveImmediate')
dispatch('checkDownloadPath')
}
+
+ applyDownloadSpeedLimit (speed) {
+ ipcRenderer.send('wt-set-download-limit', speed)
+ }
+
+ applyUploadSpeedLimit (speed) {
+ ipcRenderer.send('wt-set-upload-limit', speed)
+ }
}
diff --git a/src/renderer/lib/state.js b/src/renderer/lib/state.js
index 97ad46fa08..df2ef19117 100644
--- a/src/renderer/lib/state.js
+++ b/src/renderer/lib/state.js
@@ -132,6 +132,10 @@ function setupStateSaved () {
autoAddTorrents: false,
torrentsFolderPath: '',
highestPlaybackPriority: true,
+ downloadSpeedLimitEnabled: false,
+ uploadSpeedLimitEnabled: false,
+ downloadSpeedLimit: 1000000,
+ uploadSpeedLimit: 1000000,
globalTrackers: defaultAnnounceList
},
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
diff --git a/src/renderer/main.js b/src/renderer/main.js
index 8e8ebbb20f..0f517cb945 100644
--- a/src/renderer/main.js
+++ b/src/renderer/main.js
@@ -144,6 +144,9 @@ function onState (err, _state) {
// Listen for messages from the main process
setupIpc()
+ // Apply the user's stored speed limits if they exist.
+ applySpeedLimits()
+
// Drag and drop files/text to start torrenting or seeding
dragDrop('body', {
onDrop: onOpen,
@@ -321,6 +324,10 @@ const dispatchHandlers = {
startFolderWatcher: () => controllers.folderWatcher().start(),
stopFolderWatcher: () => controllers.folderWatcher().stop(),
+ // Speed limits for transfers (in bytes per second)
+ updateDownloadSpeedLimit: (speed) => controllers.prefs().applyDownloadSpeedLimit(speed),
+ updateUploadSpeedLimit: (speed) => controllers.prefs().applyUploadSpeedLimit(speed),
+
// Update (check for new versions on Linux, where there's no auto updater)
updateAvailable: (version) => controllers.update().updateAvailable(version),
skipVersion: (version) => controllers.update().skipVersion(version),
@@ -398,6 +405,21 @@ function setupIpc () {
State.on('stateSaved', () => ipcRenderer.send('stateSaved'))
}
+// Checks user config for speed limits and applies them to webtorrent.
+function applySpeedLimits () {
+ // Check if the user has set an upload speed limit in the prefstore
+ if (state.saved.prefs.uploadSpeedLimitEnabled) {
+ // Apply the saved speed limit
+ controllers.prefs().applyUploadSpeedLimit(state.saved.prefs.uploadSpeedLimit)
+ }
+
+ // Check if the user has set an upload speed limit in the prefstore
+ if (state.saved.prefs.downloadSpeedLimitEnabled) {
+ // Apply the saved speed limit
+ controllers.prefs().applyDownloadSpeedLimit(state.saved.prefs.downloadSpeedLimit)
+ }
+}
+
// Quits any modal popovers and returns to the torrent list screen
function backToList () {
// Exit any modals and screens with a back button
diff --git a/src/renderer/pages/preferences-page.js b/src/renderer/pages/preferences-page.js
index 8bfe4000ba..2ba68a7555 100644
--- a/src/renderer/pages/preferences-page.js
+++ b/src/renderer/pages/preferences-page.js
@@ -35,6 +35,20 @@ class PreferencesPage extends React.Component {
const globalTrackers = this.props.state.getGlobalTrackers().join('\n')
+ // Upload Speed limits
+ this.handleUploadSpeedLimitToggle =
+ this.handleUploadSpeedLimitToggle.bind(this)
+
+ this.handleUploadSpeedLimitChange =
+ this.handleUploadSpeedLimitChange.bind(this)
+
+ // Download Speed limits
+ this.handleDownloadSpeedLimitToggle =
+ this.handleDownloadSpeedLimitToggle.bind(this)
+
+ this.handleDownloadSpeedLimitChange =
+ this.handleDownloadSpeedLimitChange.bind(this)
+
this.state = {
globalTrackers
}
@@ -272,6 +286,125 @@ class PreferencesPage extends React.Component {
dispatch('updateGlobalTrackers', announceList)
}
+ speedLimits () {
+ const DLspeedLimitInKBPS = this.props.state.saved.prefs.downloadSpeedLimit / 1000
+ const ULspeedLimitInKBPS = this.props.state.saved.prefs.uploadSpeedLimit / 1000
+
+ // Align the text fields
+ const textareaStyle = {
+ margin: 0
+ // marginTop: -40
+ }
+ const textFieldStyle = {
+ width: '25%',
+ marginTop: -12
+ }
+ const unitLabelStyle = {
+ marginTop: -0.01,
+ padding: 0.1,
+ marginLeft: 4
+ }
+
+ // webtorrent limits are in bytes, but our UI is in Kilobytes.
+ // So we do conversions in this file.
+ return (
+
+
+
+ )
+ }
+
+ // Download Speed Limit functions
+ handleDownloadSpeedLimitToggle (e, isChecked) {
+ // Store whether or not the limit is enabled
+ dispatch('updatePreferences', 'downloadSpeedLimitEnabled', isChecked)
+
+ // Adjust speedlimit in webtorrent (-1 means disabled)
+ dispatch('updateDownloadSpeedLimit', isChecked ? this.props.state.saved.prefs.downloadSpeedLimit : -1)
+ }
+
+ // Upload Speed Limit functions
+ handleUploadSpeedLimitToggle (e, isChecked) {
+ // Store whether or not the limit is enabled
+ dispatch('updatePreferences', 'uploadSpeedLimitEnabled', isChecked)
+
+ // Adjust speedlimit in webtorrent (-1 means disabled)
+ dispatch('updateUploadSpeedLimit', isChecked ? this.props.state.saved.prefs.uploadSpeedLimit : -1)
+ }
+
+ // This converts from KB to bytes and updates prefs and webtorrent.
+ handleDownloadSpeedLimitChange (e, speedLimitInKBPS) {
+ // First check if the number is too large. Over 1TBPS is too large.
+ if (speedLimitInKBPS > 1000000000) {
+ speedLimitInKBPS = 1000000000
+ }
+
+ const speedLimitInBPS = speedLimitInKBPS * 1000
+
+ // Store the new rate in the persistent prefstore
+ dispatch('updatePreferences', 'downloadSpeedLimit', speedLimitInBPS)
+
+ // Dispatch to call IPC
+ dispatch('updateDownloadSpeedLimit', speedLimitInBPS)
+ }
+
+ // This converts from KB to bytes and updates prefs and webtorrent.
+ handleUploadSpeedLimitChange (e, speedLimitInKBPS) {
+ // First check if the number is too large. Over 1TBPS is too large.
+ if (speedLimitInKBPS > 1000000000) {
+ speedLimitInKBPS = 1000000000
+ }
+
+ const speedLimitInBPS = speedLimitInKBPS * 1000
+
+ // Store the new rate in the persistent prefstore
+ dispatch('updatePreferences', 'uploadSpeedLimit', speedLimitInBPS)
+
+ // Dispatch to call IPC
+ dispatch('updateUploadSpeedLimit', speedLimitInBPS)
+ }
+
render () {
const style = {
color: colors.grey400,
@@ -290,6 +423,9 @@ class PreferencesPage extends React.Component {
{this.externalPlayerPathSelector()}
{this.highestPlaybackPriorityCheckbox()}
+
+ {this.speedLimits()}
+
{this.setDefaultAppButton()}
diff --git a/src/renderer/webtorrent.js b/src/renderer/webtorrent.js
index 6550aa7009..a0a1f1773d 100644
--- a/src/renderer/webtorrent.js
+++ b/src/renderer/webtorrent.js
@@ -79,6 +79,10 @@ function init () {
stopServer())
ipcRenderer.on('wt-select-files', (e, infoHash, selections) =>
selectFiles(infoHash, selections))
+ ipcRenderer.on('wt-set-download-limit', (e, speed) =>
+ setDownloadSpeedLimit(speed))
+ ipcRenderer.on('wt-set-upload-limit', (e, speed) =>
+ setUploadSpeedLimit(speed))
ipcRenderer.send('ipcReadyWebTorrent')
@@ -407,6 +411,20 @@ function selectFiles (torrentOrInfoHash, selections) {
})
}
+function setDownloadSpeedLimit (speed) {
+ // var nodeConsole = require('console');
+ // var myConsole = new nodeConsole.Console(process.stdout, process.stderr);
+ // myConsole.log('Setting download speed limit (bytes/second): ' + speed);
+ client.throttleDownload(speed)
+}
+
+function setUploadSpeedLimit (speed) {
+ // var nodeConsole = require('console');
+ // var myConsole = new nodeConsole.Console(process.stdout, process.stderr);
+ // myConsole.log('Setting upload speed limit (bytes/second): ' + speed);
+ client.throttleUpload(speed)
+}
+
// Gets a WebTorrent handle by torrentKey
// Throws an Error if we're not currently torrenting anything w/ that key
function getTorrent (torrentKey) {
diff --git a/static/main.css b/static/main.css
index 5597b55f0b..e15ff82a7d 100644
--- a/static/main.css
+++ b/static/main.css
@@ -983,3 +983,16 @@ video::-webkit-media-text-track-container {
.control * {
cursor: default !important;
}
+
+/*Remove spinner arrows from speed limits*/
+/* Chrome, Safari, Edge, Opera */
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+/* Firefox */
+input[type=number] {
+ -moz-appearance: textfield;
+}
\ No newline at end of file