From 2cc8ebc9f8827bcd87ee4bf07164a9d049a55957 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 18:32:58 -0700 Subject: [PATCH 1/9] Windows: register protocol handler for magnet links --- main/index.js | 7 +++++-- main/register-protocol-handler.js | 26 ++++++++++++++++++++++++++ package.json | 3 ++- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 main/register-protocol-handler.js diff --git a/main/index.js b/main/index.js index 20fee9b7..56080042 100644 --- a/main/index.js +++ b/main/index.js @@ -1,10 +1,12 @@ var electron = require('electron') + +var app = electron.app + var ipc = require('./ipc') var menu = require('./menu') var shortcuts = require('./shortcuts') var windows = require('./windows') - -var app = electron.app +var registerProtocolHandler = require('./register-protocol-handler') app.on('open-file', onOpen) app.on('open-url', onOpen) @@ -16,6 +18,7 @@ app.on('ready', function () { menu.init() windows.createMainWindow() shortcuts.init() + registerProtocolHandler() }) app.on('before-quit', function () { diff --git a/main/register-protocol-handler.js b/main/register-protocol-handler.js new file mode 100644 index 00000000..cc167b10 --- /dev/null +++ b/main/register-protocol-handler.js @@ -0,0 +1,26 @@ +module.exports = function () { + if (process.platform === 'win32') { + registerProtocolHandler('magnet', 'URL:BitTorrent Magnet URL', 'WebTorrent.exe') + } +} + +function registerProtocolHandler (protocol, name, command) { + var Registry = require('winreg') + + var protocolKey = new Registry({ + hive: Registry.HKCR, // HKEY_CLASSES_ROOT + key: '\\' + protocol + }) + protocolKey.set('', Registry.REG_SZ, name, callback) + protocolKey.set('URL Protocol', Registry.REG_SZ, '', callback) + + var commandKey = new Registry({ + hive: Registry.HKCR, + key: '\\' + protocol + '\\shell\\open\\command' + }) + commandKey.set('', Registry.REG_SZ, '"' + command + '" "%1"', callback) + + function callback (err) { + if (err) console.error(err.message || err) + } +} diff --git a/package.json b/package.json index e4f60318..976664c7 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "prettier-bytes": "^1.0.1", "upload-element": "^1.0.1", "virtual-dom": "^2.1.1", - "webtorrent": "^0.86.0" + "webtorrent": "^0.86.0", + "winreg": "^1.0.1" }, "devDependencies": { "electron-packager": "^5.0.0", From ae935af15322b18b60e6249dc817691446215204 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 18:59:20 -0700 Subject: [PATCH 2/9] Windows: register protocol handler without needing Admin privledges --- main/register-protocol-handler.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/register-protocol-handler.js b/main/register-protocol-handler.js index cc167b10..69d36b1a 100644 --- a/main/register-protocol-handler.js +++ b/main/register-protocol-handler.js @@ -8,15 +8,15 @@ function registerProtocolHandler (protocol, name, command) { var Registry = require('winreg') var protocolKey = new Registry({ - hive: Registry.HKCR, // HKEY_CLASSES_ROOT - key: '\\' + protocol + hive: Registry.HKCU, // HKEY_CURRENT_USER + key: '\\Software\\Classes\\' + protocol }) protocolKey.set('', Registry.REG_SZ, name, callback) protocolKey.set('URL Protocol', Registry.REG_SZ, '', callback) var commandKey = new Registry({ - hive: Registry.HKCR, - key: '\\' + protocol + '\\shell\\open\\command' + hive: Registry.HKCU, + key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command' }) commandKey.set('', Registry.REG_SZ, '"' + command + '" "%1"', callback) From 3d1fd56450ea3fdc02bc7acb3e7737ab7e893f8a Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 19:10:22 -0700 Subject: [PATCH 3/9] Use real path to WebTorrent.exe --- main/register-protocol-handler.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main/register-protocol-handler.js b/main/register-protocol-handler.js index 69d36b1a..a468e964 100644 --- a/main/register-protocol-handler.js +++ b/main/register-protocol-handler.js @@ -1,6 +1,10 @@ +var electron = require('electron') + +var app = electron.app + module.exports = function () { if (process.platform === 'win32') { - registerProtocolHandler('magnet', 'URL:BitTorrent Magnet URL', 'WebTorrent.exe') + registerProtocolHandler('magnet', 'URL:BitTorrent Magnet URL', app.getPath('exe')) } } From e70e32c5f8d0d7b5af8b631837d7404e10442d89 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 19:23:23 -0700 Subject: [PATCH 4/9] use process.execPath instead of app.getPath('exec') MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They’re equivalent --- main/register-protocol-handler.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/main/register-protocol-handler.js b/main/register-protocol-handler.js index a468e964..22a20c5b 100644 --- a/main/register-protocol-handler.js +++ b/main/register-protocol-handler.js @@ -1,10 +1,6 @@ -var electron = require('electron') - -var app = electron.app - module.exports = function () { if (process.platform === 'win32') { - registerProtocolHandler('magnet', 'URL:BitTorrent Magnet URL', app.getPath('exe')) + registerProtocolHandler('magnet', 'URL:BitTorrent Magnet URL', process.execPath) } } From afe5151e289ec468693234fab27fce19376fa8b7 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 19:23:35 -0700 Subject: [PATCH 5/9] Handle command line arguments --- main/index.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/main/index.js b/main/index.js index 56080042..e1da3a45 100644 --- a/main/index.js +++ b/main/index.js @@ -8,6 +8,8 @@ var shortcuts = require('./shortcuts') var windows = require('./windows') var registerProtocolHandler = require('./register-protocol-handler') +var argv = process.argv.slice(2) + app.on('open-file', onOpen) app.on('open-url', onOpen) @@ -21,6 +23,12 @@ app.on('ready', function () { registerProtocolHandler() }) +app.on('ipcReady', function () { + argv.forEach(function (torrentId) { + windows.main.send('dispatch', 'onOpen', torrentId) + }) +}) + app.on('before-quit', function () { app.isQuitting = true }) @@ -44,12 +52,8 @@ ipc.init() function onOpen (e, torrentId) { e.preventDefault() if (app.ipcReady) { - onReadyOpen() - } else { - app.on('ipcReady', onReadyOpen) - } - function onReadyOpen () { windows.main.send('dispatch', 'onOpen', torrentId) - windows.main.focus() + } else { + argv.push(torrentId) } } From 81bb8eea7b6ff40324dfb72b45843a026d1992e7 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 19:40:22 -0700 Subject: [PATCH 6/9] do all logging in the renderer --- main/ipc.js | 4 ---- renderer/index.js | 8 +++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/main/ipc.js b/main/ipc.js index 08e305ff..8d7dd3ac 100644 --- a/main/ipc.js +++ b/main/ipc.js @@ -57,10 +57,6 @@ function init () { ipcMain.on('blockPowerSave', blockPowerSave) ipcMain.on('unblockPowerSave', unblockPowerSave) - - ipcMain.on('log', function (e, message) { - console.log(message) - }) } function setBounds (bounds) { diff --git a/renderer/index.js b/renderer/index.js index 4ed209bb..d1b32d0d 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -257,9 +257,7 @@ function jumpToTime (time) { function setupIpc () { ipcRenderer.send('ipcReady') - ipcRenderer.on('dispatch', function (e, action, ...args) { - dispatch(action, ...args) - }) + ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args)) ipcRenderer.on('showOpenTorrentAddress', function (e) { state.modal = 'open-torrent-address-modal' @@ -283,7 +281,7 @@ function setupIpc () { function loadState (callback) { cfg.read(function (err, data) { if (err) console.error(err) - ipcRenderer.send('log', 'loaded state from ' + cfg.filePath) + console.log('loaded state from ' + cfg.filePath) // populate defaults if they're not there state.saved = Object.assign({}, state.defaultSavedState, data) @@ -305,7 +303,7 @@ function resumeTorrents () { // Write state.saved to the JSON state file function saveState () { - ipcRenderer.send('log', 'saving state to ' + cfg.filePath) + console.log('saving state to ' + cfg.filePath) cfg.write(state.saved, function (err) { if (err) console.error(err) update() From 84a87dd1dee524fcc18c6e69e4224ab61a75e3bf Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 19:40:49 -0700 Subject: [PATCH 7/9] log command line arguments --- main/index.js | 3 +++ renderer/index.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/main/index.js b/main/index.js index e1da3a45..042d47d2 100644 --- a/main/index.js +++ b/main/index.js @@ -24,6 +24,9 @@ app.on('ready', function () { }) app.on('ipcReady', function () { + if (argv.length) { + windows.main.send('log', 'command line args:', argv) + } argv.forEach(function (torrentId) { windows.main.send('dispatch', 'onOpen', torrentId) }) diff --git a/renderer/index.js b/renderer/index.js index d1b32d0d..9989eb75 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -257,6 +257,8 @@ function jumpToTime (time) { function setupIpc () { ipcRenderer.send('ipcReady') + ipcRenderer.on('log', (e, ...args) => console.log(...args)) + ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args)) ipcRenderer.on('showOpenTorrentAddress', function (e) { From 765d3bde56ae9ae37f50b6d2ecdeecbc2ef4850f Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 20:15:24 -0700 Subject: [PATCH 8/9] detect if app is running in production MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If app is running in “production” (i.e. it has been packaged), then it will be invoked with one less argument. WebTorrent.exe arguments vs. electron.exe /path/to/app arguments We need to detect this to correctly handle command line arguments. --- config.js | 14 ++++++++++++++ main/index.js | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/config.js b/config.js index 6f6cce73..8095b556 100644 --- a/config.js +++ b/config.js @@ -10,6 +10,8 @@ module.exports = { INDEX: 'file://' + path.join(__dirname, 'renderer', 'index.html'), + IS_PRODUCTION: isProduction(), + SOUND_ADD: 'file://' + path.join(__dirname, 'static', 'sound', 'add.wav'), SOUND_DELETE: 'file://' + path.join(__dirname, 'static', 'sound', 'delete.wav'), SOUND_DISABLE: 'file://' + path.join(__dirname, 'static', 'sound', 'disable.wav'), @@ -19,3 +21,15 @@ module.exports = { SOUND_PLAY: 'file://' + path.join(__dirname, 'static', 'sound', 'play.wav'), SOUND_STARTUP: 'file://' + path.join(__dirname, 'static', 'sound', 'startup.wav') } + +function isProduction () { + if (process.platform === 'darwin') { + return !/\/Electron\.app\/Contents\/MacOS\/Electron$/.test(process.execPath) + } + if (process.platform === 'win32') { + return !/\\electron\.exe$/.test(process.execPath) + } + if (process.platform === 'linux') { + // TODO + } +} diff --git a/main/index.js b/main/index.js index 042d47d2..9a6cae7d 100644 --- a/main/index.js +++ b/main/index.js @@ -2,13 +2,14 @@ var electron = require('electron') var app = electron.app +var config = require('../config') var ipc = require('./ipc') var menu = require('./menu') +var registerProtocolHandler = require('./register-protocol-handler') var shortcuts = require('./shortcuts') var windows = require('./windows') -var registerProtocolHandler = require('./register-protocol-handler') -var argv = process.argv.slice(2) +var argv = process.argv.slice(config.IS_PRODUCTION ? 1 : 2) app.on('open-file', onOpen) app.on('open-url', onOpen) @@ -24,8 +25,9 @@ app.on('ready', function () { }) app.on('ipcReady', function () { + windows.main.send('log', 'IS_PRODUCTION:', config.IS_PRODUCTION) if (argv.length) { - windows.main.send('log', 'command line args:', argv) + windows.main.send('log', 'command line args:', process.argv) } argv.forEach(function (torrentId) { windows.main.send('dispatch', 'onOpen', torrentId) From 29138fc83c34c2af377d9499420db5a1d0cd71e7 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Sat, 19 Mar 2016 20:29:44 -0700 Subject: [PATCH 9/9] add documentation for registerProtocolHandler --- main/register-protocol-handler.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/main/register-protocol-handler.js b/main/register-protocol-handler.js index 22a20c5b..6e43a03e 100644 --- a/main/register-protocol-handler.js +++ b/main/register-protocol-handler.js @@ -1,9 +1,29 @@ module.exports = function () { if (process.platform === 'win32') { - registerProtocolHandler('magnet', 'URL:BitTorrent Magnet URL', process.execPath) + registerProtocolHandler('magnet', 'BitTorrent Magnet URL', process.execPath) } } +/** + * To add a protocol handler on Windows, the following keys must be added to the Windows + * registry: + * + * HKEY_CLASSES_ROOT + * $PROTOCOL + * (Default) = "URL:$NAME" + * URL Protocol = "" + * shell + * open + * command + * (Default) = "$COMMAND" "%1" + * + * Source: https://msdn.microsoft.com/en-us/library/aa767914.aspx + * + * However, the "HKEY_CLASSES_ROOT" key can only be written by the Administrator user. + * So, we instead write to "HKEY_CURRENT_USER\Software\Classes", which is inherited by + * "HKEY_CLASSES_ROOT" anyway, and can be written by unprivileged users. + */ + function registerProtocolHandler (protocol, name, command) { var Registry = require('winreg') @@ -11,7 +31,7 @@ function registerProtocolHandler (protocol, name, command) { hive: Registry.HKCU, // HKEY_CURRENT_USER key: '\\Software\\Classes\\' + protocol }) - protocolKey.set('', Registry.REG_SZ, name, callback) + protocolKey.set('', Registry.REG_SZ, 'URL:' + name, callback) protocolKey.set('URL Protocol', Registry.REG_SZ, '', callback) var commandKey = new Registry({