diff --git a/AUTHORS.md b/AUTHORS.md index bceeb904..76d4d3f9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -37,5 +37,10 @@ - Alexey Romanov (romanalexey@gmail.com) - Karan Thakkar (karanjthakkar@gmail.com) - Nuno Campos (nuno.campos@me.com) +- Ebrahim Byagowi (ebrahim@gnu.org) +- Emil Bay (github@tixz.dk) +- Auyer (rafa_auyer@icloud.com) +- SimplyAhmazing (ahmad19526@gmail.com) +- Cezar Carneiro (cezargcarneiro@gmail.com) #### Generated by bin/update-authors.sh. diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9e33f8..87e2503c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ # WebTorrent Desktop Version History -## v0.18.0 +## v0.19.0 - 2018-01-26 + +### Added +- Added watch folder feature: Automatically add new torrent files added to a folder on disk (#1154) +- Added highest playback priority feature: pauses other active torrents when playback starts (#840) +- Add 'Start Speaking' and 'Stop Speaking' menu item (Mac) (#439) +- Add pinch-to-zoom gesture to enter/exit fullscreen (#1148) + +### Changed +- [SECURITY] Mitigate Electron protocol handler issue (Windows) +- Moved project from Feross's GitHub account to the WebTorrent GitHub organization +- Updated to electron@1.6.16 +- Updated to material-ui@0.17 +- Treat .FLAC as playable audio (#1127) + +### Fixed +- Fix time and duration so it doesn't bounce in the UI (#1233) +- Fix 'About WebTorrent' menu location on Windows (#1120) + +## v0.18.0 - 2017-02-03 ### Added - Add a new "Transfers" menu for pausing or resuming all torrents (#1027) @@ -67,7 +86,7 @@ ## v0.16.0 - 2016-09-18 ### Added -- **Windows 64-bit support!** ([#931](https://github.com/webtorrent/webtorrent-desktop/pull/931)) +- **Windows 64-bit support!** (#931) - Existing 32-bit users will update to 64-bit automatically in next release - 64-bit reduces likelihood of out-of-memory errors by increasing the address space diff --git a/package.json b/package.json index fe2bec47..73532c7b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "webtorrent-desktop", "description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.", - "version": "0.18.0", + "version": "0.19.0", "author": { "name": "WebTorrent, LLC", "email": "feross@webtorrent.io", @@ -17,6 +17,7 @@ "auto-launch": "^4.0.1", "bitfield": "^1.0.2", "capture-frame": "^1.0.0", + "chokidar": "^1.6.1", "chromecasts": "^1.8.0", "cp-file": "^4.0.1", "create-torrent": "^3.24.5", @@ -53,7 +54,7 @@ "buble": "^0.15.2", "cross-zip": "^2.0.1", "depcheck": "^0.6.4", - "electron": "1.6.0", + "electron": "1.6.16", "electron-osx-sign": "0.4.3", "electron-packager": "~8.5.1", "electron-winstaller": "~2.5.2", diff --git a/src/config.js b/src/config.js index 43b5c57f..6f478bee 100644 --- a/src/config.js +++ b/src/config.js @@ -23,7 +23,7 @@ module.exports = { CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report', TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry', - APP_COPYRIGHT: 'Copyright © 2014-2017 ' + APP_TEAM, + APP_COPYRIGHT: 'Copyright © 2014-2018 ' + APP_TEAM, APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'), APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'), APP_NAME: APP_NAME, diff --git a/src/main/folder-watcher.js b/src/main/folder-watcher.js new file mode 100644 index 00000000..d628d35b --- /dev/null +++ b/src/main/folder-watcher.js @@ -0,0 +1,50 @@ +const chokidar = require('chokidar') +const log = require('./log') + +class FolderWatcher { + constructor ({window, state}) { + this.window = window + this.state = state + this.torrentsFolderPath = null + this.watching = false + } + + isEnabled () { + return this.state.saved.prefs.autoAddTorrents + } + + start () { + // Stop watching previous folder before + // start watching a new one. + if (this.watching) this.stop() + + const torrentsFolderPath = this.state.saved.prefs.torrentsFolderPath + this.torrentsFolderPath = torrentsFolderPath + if (!torrentsFolderPath) return + + const glob = `${torrentsFolderPath}/**/*.torrent` + log('Folder Watcher: watching: ', glob) + + const options = { + ignoreInitial: true, + awaitWriteFinish: true + } + this.watcher = chokidar.watch(glob, options) + this.watcher + .on('add', (path) => { + log('Folder Watcher: added torrent: ', path) + this.window.dispatch('addTorrent', path) + }) + + this.watching = true + } + + stop () { + log('Folder Watcher: stop.') + if (!this.watching) return + this.watcher.close() + this.watching = false + } +} + +module.exports = FolderWatcher diff --git a/src/main/handlers.js b/src/main/handlers.js index e3dff9a9..805cd5ce 100644 --- a/src/main/handlers.js +++ b/src/main/handlers.js @@ -44,7 +44,7 @@ function installDarwin () { function uninstallDarwin () {} -const EXEC_COMMAND = [ process.execPath ] +const EXEC_COMMAND = [ process.execPath, '--' ] if (!config.IS_PRODUCTION) { EXEC_COMMAND.push(config.ROOT_PATH) diff --git a/src/main/index.js b/src/main/index.js index 497b9072..9d952f87 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -72,13 +72,16 @@ function init () { if (err) throw err isReady = true + const state = results.state - windows.main.init(results.state, {hidden: hidden}) + windows.main.init(state, {hidden: hidden}) windows.webtorrent.init() menu.init() // To keep app startup fast, some code is delayed. - setTimeout(delayedInit, config.DELAYED_INIT) + setTimeout(() => { + delayedInit(state) + }, config.DELAYED_INIT) // Report uncaught exceptions process.on('uncaughtException', (err) => { @@ -121,17 +124,24 @@ function init () { }) } -function delayedInit () { +function delayedInit (state) { if (app.isQuitting) return const announcement = require('./announcement') const dock = require('./dock') const updater = require('./updater') + const FolderWatcher = require('./folder-watcher') + const folderWatcher = new FolderWatcher({window: windows.main, state}) announcement.init() dock.init() updater.init() + ipc.setModule('folderWatcher', folderWatcher) + if (folderWatcher.isEnabled()) { + folderWatcher.start() + } + if (process.platform === 'win32') { const userTasks = require('./user-tasks') userTasks.init() diff --git a/src/main/ipc.js b/src/main/ipc.js index e6110626..4c0a3d42 100644 --- a/src/main/ipc.js +++ b/src/main/ipc.js @@ -1,5 +1,6 @@ module.exports = { - init + init, + setModule } const electron = require('electron') @@ -13,6 +14,14 @@ const windows = require('./windows') // Messages from the main process, to be sent once the WebTorrent process starts const messageQueueMainToWebTorrent = [] +// Will hold modules injected from the app that will be used on fired +// IPC events. +const modules = {} + +function setModule (name, module) { + modules[name] = module +} + function init () { const ipc = electron.ipcMain @@ -58,7 +67,7 @@ function init () { }) /** - * Events + * Player Events */ ipc.on('onPlayerOpen', function () { @@ -106,6 +115,28 @@ function init () { thumbar.onPlayerPause() }) + /** + * Folder Watcher Events + */ + + ipc.on('startFolderWatcher', function () { + if (!modules['folderWatcher']) { + log('IPC ERR: folderWatcher module is not defined.') + return + } + + modules['folderWatcher'].start() + }) + + ipc.on('stopFolderWatcher', function () { + if (!modules['folderWatcher']) { + log('IPC ERR: folderWatcher module is not defined.') + return + } + + modules['folderWatcher'].stop() + }) + /** * Shell */ diff --git a/src/renderer/controllers/folder-watcher-controller.js b/src/renderer/controllers/folder-watcher-controller.js new file mode 100644 index 00000000..1454dba1 --- /dev/null +++ b/src/renderer/controllers/folder-watcher-controller.js @@ -0,0 +1,13 @@ +const {ipcRenderer} = require('electron') + +module.exports = class FolderWatcherController { + start () { + console.log('-- IPC: start folder watcher') + ipcRenderer.send('startFolderWatcher') + } + + stop () { + console.log('-- IPC: stop folder watcher') + ipcRenderer.send('stopFolderWatcher') + } +} diff --git a/src/renderer/lib/state.js b/src/renderer/lib/state.js index 6a909d96..68c85bb0 100644 --- a/src/renderer/lib/state.js +++ b/src/renderer/lib/state.js @@ -121,7 +121,9 @@ function setupStateSaved (cb) { isFileHandler: false, openExternalPlayer: false, externalPlayerPath: null, - startup: false + startup: false, + autoAddTorrents: false, + torrentsFolderPath: '' }, torrents: config.DEFAULT_TORRENTS.map(createTorrentObject), torrentsToResume: [], diff --git a/src/renderer/main.js b/src/renderer/main.js index 7acb24f5..1fc431df 100644 --- a/src/renderer/main.js +++ b/src/renderer/main.js @@ -111,6 +111,10 @@ function onState (err, _state) { update: createGetter(() => { const UpdateController = require('./controllers/update-controller') return new UpdateController(state) + }), + folderWatcher: createGetter(() => { + const FolderWatcherController = require('./controllers/folder-watcher-controller') + return new FolderWatcherController() }) } @@ -296,6 +300,8 @@ const dispatchHandlers = { 'preferences': () => controllers.prefs().show(), 'updatePreferences': (key, value) => controllers.prefs().update(key, value), 'checkDownloadPath': checkDownloadPath, + 'startFolderWatcher': () => controllers.folderWatcher().start(), + 'stopFolderWatcher': () => controllers.folderWatcher().stop(), // Update (check for new versions on Linux, where there's no auto updater) 'updateAvailable': (version) => controllers.update().updateAvailable(version), diff --git a/src/renderer/pages/player-page.js b/src/renderer/pages/player-page.js index 76d0d911..04e2e6c1 100644 --- a/src/renderer/pages/player-page.js +++ b/src/renderer/pages/player-page.js @@ -160,6 +160,7 @@ function renderMedia (state) { } else { // When the last video completes, pause the video instead of looping state.playing.isPaused = true + if (state.window.isFullScreen) dispatch('toggleFullScreen') } } @@ -339,7 +340,7 @@ function renderCastScreen (state) { isCast = false } else if (state.playing.location === 'error') { castIcon = 'error_outline' - castType = 'Error' + castType = 'Unable to Play' isCast = false } diff --git a/src/renderer/pages/preferences-page.js b/src/renderer/pages/preferences-page.js index bf729141..76097109 100644 --- a/src/renderer/pages/preferences-page.js +++ b/src/renderer/pages/preferences-page.js @@ -108,6 +108,59 @@ class PreferencesPage extends React.Component { dispatch('updatePreferences', 'externalPlayerPath', filePath) } + autoAddTorrentsCheckbox () { + return ( + + { this.handleAutoAddTorrentsChange(e, value) }} + /> + + ) + } + + handleAutoAddTorrentsChange (e, isChecked) { + const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath + if (isChecked && !torrentsFolderPath) { + alert('Select a torrents folder first.') // eslint-disable-line + e.preventDefault() + return + } + + dispatch('updatePreferences', 'autoAddTorrents', isChecked) + + if (isChecked) { + dispatch('startFolderWatcher', null) + return + } + + dispatch('stopFolderWatcher', null) + } + + torrentsFolderPathSelector () { + const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath + + return ( + + + + ) + } + + handletorrentsFolderPathChange (filePath) { + dispatch('updatePreferences', 'torrentsFolderPath', filePath) + } + setDefaultAppButton () { const isFileHandler = this.props.state.unsaved.prefs.isFileHandler if (isFileHandler) { @@ -163,8 +216,10 @@ class PreferencesPage extends React.Component { } return (
- + {this.downloadPathSelector()} + {this.autoAddTorrentsCheckbox()} + {this.torrentsFolderPathSelector()} {this.openExternalPlayerCheckbox()}