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 ( + +
+
+ + +

KB/s

+
+
+ + +

KB/s

+
+
+
+ ) + } + + // 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