diff --git a/config.js b/config.js index ea891229..fc77ff6a 100644 --- a/config.js +++ b/config.js @@ -17,7 +17,6 @@ module.exports = { APP_VERSION: APP_VERSION, APP_WINDOW_TITLE: APP_NAME + ' (BETA)', - AUTO_UPDATE_CHECK_STARTUP_DELAY: 5 * 1000 /* 5 seconds */, AUTO_UPDATE_URL: 'https://webtorrent.io/desktop/update' + '?version=' + APP_VERSION + '&platform=' + process.platform, @@ -27,6 +26,8 @@ module.exports = { CONFIG_POSTER_PATH: path.join(getConfigPath(), 'Posters'), CONFIG_TORRENT_PATH: path.join(getConfigPath(), 'Torrents'), + DELAYED_INIT: 3000 /* 3 seconds */, + GITHUB_URL: 'https://github.com/feross/webtorrent-desktop', GITHUB_URL_ISSUES: 'https://github.com/feross/webtorrent-desktop/issues', GITHUB_URL_RAW: 'https://raw.githubusercontent.com/feross/webtorrent-desktop/master', diff --git a/main/auto-updater.js b/main/auto-updater.js deleted file mode 100644 index d701c09c..00000000 --- a/main/auto-updater.js +++ /dev/null @@ -1,50 +0,0 @@ -module.exports = { - init -} - -var electron = require('electron') -var get = require('simple-get') - -var config = require('../config') -var log = require('./log') -var windows = require('./windows') - -var autoUpdater = electron.autoUpdater - -function init () { - autoUpdater.on('error', function (err) { - log.error('App update error: ' + err.message || err) - }) - - autoUpdater.setFeedURL(config.AUTO_UPDATE_URL) - - /* - * We always check for updates on app startup. To keep app startup fast, we delay this - * first check so it happens when there is less going on. - */ - setTimeout(checkForUpdates, config.AUTO_UPDATE_CHECK_STARTUP_DELAY) - - autoUpdater.on('checking-for-update', () => log('Checking for app update')) - autoUpdater.on('update-available', () => log('App update available')) - autoUpdater.on('update-not-available', () => log('App update not available')) - autoUpdater.on('update-downloaded', function (e, releaseNotes, releaseName, releaseDate, updateURL) { - log('App update downloaded: ', releaseName, updateURL) - }) -} - -function checkForUpdates () { - // Electron's built-in auto updater only supports Mac and Windows, for now - if (process.platform !== 'linux') { - return autoUpdater.checkForUpdates() - } - - // If we're on Linux, we have to do it ourselves - get.concat(config.AUTO_UPDATE_URL, function (err, res, data) { - if (err) return log('Error checking for app update: ' + err.message) - if (![200, 204].includes(res.statusCode)) return log('Error checking for app update, got HTTP ' + res.statusCode) - if (res.statusCode !== 200) return - - var obj = JSON.parse(data) - windows.main.send('dispatch', 'updateAvailable', obj.version) - }) -} diff --git a/main/index.js b/main/index.js index a496dba1..2f61ba8c 100644 --- a/main/index.js +++ b/main/index.js @@ -5,7 +5,6 @@ var electron = require('electron') var app = electron.app var ipcMain = electron.ipcMain -var autoUpdater = require('./auto-updater') var config = require('../config') var crashReporter = require('../crash-reporter') var handlers = require('./handlers') @@ -14,8 +13,9 @@ var log = require('./log') var menu = require('./menu') var shortcuts = require('./shortcuts') var squirrelWin32 = require('./squirrel-win32') -var windows = require('./windows') var tray = require('./tray') +var updater = require('./updater') +var windows = require('./windows') var shouldQuit = false var argv = sliceArgv(process.argv) @@ -54,16 +54,16 @@ function init () { app.on('will-finish-launching', function () { crashReporter.init() - autoUpdater.init() }) app.on('ready', function () { - menu.init() windows.createMainWindow() windows.createWebTorrentHiddenWindow() + menu.init() shortcuts.init() - tray.init() - handlers.install() + + // To keep app startup fast, some code is delayed. + setTimeout(delayedInit, config.DELAYED_INIT) }) app.on('ipcReady', function () { @@ -87,6 +87,12 @@ function init () { }) } +function delayedInit () { + tray.init() + handlers.install() + updater.init() +} + function onOpen (e, torrentId) { e.preventDefault() diff --git a/main/ipc.js b/main/ipc.js index ed3c59c6..5f322190 100644 --- a/main/ipc.js +++ b/main/ipc.js @@ -6,7 +6,6 @@ var electron = require('electron') var app = electron.app var ipcMain = electron.ipcMain -var powerSaveBlocker = electron.powerSaveBlocker var log = require('./log') var menu = require('./menu') @@ -15,7 +14,7 @@ var shortcuts = require('./shortcuts') var vlc = require('./vlc') // has to be a number, not a boolean, and undefined throws an error -var powerSaveBlockID = 0 +var powerSaveBlockerId = 0 // messages from the main process, to be sent once the WebTorrent process starts var messageQueueMainToWebTorrent = [] @@ -229,13 +228,13 @@ function setProgress (progress) { } function blockPowerSave () { - powerSaveBlockID = powerSaveBlocker.start('prevent-display-sleep') - log('blockPowerSave %d', powerSaveBlockID) + powerSaveBlockerId = electron.powerSaveBlocker.start('prevent-display-sleep') + log('blockPowerSave %d', powerSaveBlockerId) } function unblockPowerSave () { - if (powerSaveBlocker.isStarted(powerSaveBlockID)) { - powerSaveBlocker.stop(powerSaveBlockID) - log('unblockPowerSave %d', powerSaveBlockID) + if (electron.powerSaveBlocker.isStarted(powerSaveBlockerId)) { + electron.powerSaveBlocker.stop(powerSaveBlockerId) + log('unblockPowerSave %d', powerSaveBlockerId) } } diff --git a/main/shortcuts.js b/main/shortcuts.js index 739f72eb..ee21fb2a 100644 --- a/main/shortcuts.js +++ b/main/shortcuts.js @@ -5,16 +5,15 @@ module.exports = { } var electron = require('electron') -var localShortcut = require('electron-localshortcut') - -var globalShortcut = electron.globalShortcut var menu = require('./menu') var windows = require('./windows') function init () { - // Alternate shortcuts. Most shortcuts are registered in menu,js, but Electron does not - // support multiple shortcuts for a single menu item. + var localShortcut = require('electron-localshortcut') + + // Alternate shortcuts. Most shortcuts are registered in menu,js, but Electron + // does not support multiple shortcuts for a single menu item. localShortcut.register('CmdOrCtrl+Shift+F', menu.toggleFullScreen) localShortcut.register('Space', () => windows.main.send('dispatch', 'playPause')) @@ -24,12 +23,12 @@ function init () { function onPlayerOpen () { // Register special "media key" for play/pause, available on some keyboards - globalShortcut.register( + electron.globalShortcut.register( 'MediaPlayPause', () => windows.main.send('dispatch', 'playPause') ) } function onPlayerClose () { - globalShortcut.unregister('MediaPlayPause') + electron.globalShortcut.unregister('MediaPlayPause') } diff --git a/main/tray.js b/main/tray.js index fa81f951..ad7132d5 100644 --- a/main/tray.js +++ b/main/tray.js @@ -8,8 +8,6 @@ var path = require('path') var electron = require('electron') var app = electron.app -var Menu = electron.Menu -var Tray = electron.Tray var windows = require('./windows') @@ -35,7 +33,7 @@ function hasTray () { } function createTrayIcon () { - trayIcon = new Tray(path.join(__dirname, '..', 'static', 'WebTorrentSmall.png')) + trayIcon = new electron.Tray(path.join(__dirname, '..', 'static', 'WebTorrentSmall.png')) // On Windows, left click to open the app, right click for context menu // On Linux, any click (right or left) opens the context menu @@ -66,7 +64,7 @@ function updateTrayMenu () { } else { showHideMenuItem = { label: 'Show', click: showApp } } - var contextMenu = Menu.buildFromTemplate([ + var contextMenu = electron.Menu.buildFromTemplate([ showHideMenuItem, { label: 'Quit', click: () => app.quit() } ]) diff --git a/main/updater.js b/main/updater.js new file mode 100644 index 00000000..073a55b2 --- /dev/null +++ b/main/updater.js @@ -0,0 +1,71 @@ +module.exports = { + init +} + +var electron = require('electron') +var get = require('simple-get') + +var config = require('../config') +var log = require('./log') +var windows = require('./windows') + +function init () { + if (process.platform === 'linux') { + initLinux() + } else { + initDarwinWin32() + } +} + +// The Electron auto-updater does not support Linux yet, so manually check for updates and +// `show the user a modal notification. +function initLinux () { + get.concat(config.AUTO_UPDATE_URL, onResponse) + + function onResponse (err, res, data) { + if (err) return log(`Update error: ${err.message}`) + if (res.statusCode === 200) { + // Update available + try { + data = JSON.parse(data) + } catch (err) { + return log(`Update error: Invalid JSON response: ${err.message}`) + } + windows.main.send('dispatch', 'updateAvailable', data.version) + } else if (res.statusCode === 204) { + // No update available + } else { + // Unexpected status code + log(`Update error: Unexpected status code: ${res.statusCode}`) + } + } +} + +function initDarwinWin32 () { + electron.autoUpdater.on( + 'error', + (err) => log.error(`Update error: ${err.message}`) + ) + + electron.autoUpdater.on( + 'checking-for-update', + () => log('Checking for update') + ) + + electron.autoUpdater.on( + 'update-available', + () => log('Update available') + ) + + electron.autoUpdater.on( + 'update-not-available', + () => log('Update not available') + ) + + electron.autoUpdater.on( + 'update-downloaded', + (e, notes, name, date, url) => log(`Update downloaded: ${name}: ${url}`) + ) + + electron.autoUpdater.setFeedURL(config.AUTO_UPDATE_URL) +} diff --git a/main/vlc.js b/main/vlc.js index b619a047..b2ff659b 100644 --- a/main/vlc.js +++ b/main/vlc.js @@ -12,8 +12,7 @@ function checkForVLC (cb) { vlcCommand((err) => cb(!err)) } -// Finds if VLC is installed on Mac, Windows, or Linux. -// Uses child_process.spawn() to return a ChildProcess object +// Spawns VLC with child_process.spawn() to return a ChildProcess object // Calls back with (err, childProcess) function spawn (args, cb) { vlcCommand(function (err, vlcPath) { diff --git a/main/windows.js b/main/windows.js index 01ad5ad8..ae8e3af3 100644 --- a/main/windows.js +++ b/main/windows.js @@ -9,6 +9,8 @@ var windows = module.exports = { var electron = require('electron') +var app = electron.app + var config = require('../config') var menu = require('./menu') var tray = require('./tray') @@ -68,7 +70,7 @@ function createWebTorrentHiddenWindow () { // Prevent killing the WebTorrent process win.on('close', function (e) { - if (!electron.app.isQuitting) { + if (!app.isQuitting) { e.preventDefault() win.hide() } @@ -116,8 +118,8 @@ function createMainWindow () { win.on('close', function (e) { if (process.platform !== 'darwin' && !tray.hasTray()) { - electron.app.quit() - } else if (!electron.app.isQuitting) { + app.quit() + } else if (!app.isQuitting) { e.preventDefault() win.hide() win.send('dispatch', 'backToList') diff --git a/renderer/index.js b/renderer/index.js index 1e35e07f..46c31a0c 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -1,14 +1,24 @@ console.time('init') +var crashReporter = require('../crash-reporter') +crashReporter.init() + +var electron = require('electron') + +// Electron apps have two processes: a main process (node) runs first and starts +// a renderer process (essentially a Chrome window). We're in the renderer process, +// and this IPC channel receives from and sends messages to the main process +var ipcRenderer = electron.ipcRenderer + +// Listen for messages from the main process +setupIpc() + var appConfig = require('application-config')('WebTorrent') var concat = require('concat-stream') var dragDrop = require('drag-drop') -var electron = require('electron') var fs = require('fs-extra') var mainLoop = require('main-loop') var path = require('path') -var srtToVtt = require('srt-to-vtt') -var LanguageDetect = require('languagedetect') var createElement = require('virtual-dom/create-element') var diff = require('virtual-dom/diff') @@ -16,7 +26,6 @@ var patch = require('virtual-dom/patch') var App = require('./views/app') var config = require('../config') -var crashReporter = require('../crash-reporter') var errors = require('./lib/errors') var sound = require('./lib/sound') var State = require('./state') @@ -28,17 +37,6 @@ setDispatch(dispatch) appConfig.filePath = path.join(config.CONFIG_PATH, 'config.json') -// Electron apps have two processes: a main process (node) runs first and starts -// a renderer process (essentially a Chrome window). We're in the renderer process, -// and this IPC channel receives from and sends messages to the main process -var ipcRenderer = electron.ipcRenderer -var clipboard = electron.clipboard - -var dialog = electron.remote.dialog -var Menu = electron.remote.Menu -var MenuItem = electron.remote.MenuItem -var remote = electron.remote - // This dependency is the slowest-loading, so we lazy load it var Cast = null @@ -47,10 +45,6 @@ var state = global.state = State.getInitialState() var vdomLoop -// Report crashes back to our server. -// Not global JS exceptions, not like Rollbar, handles segfaults/core dumps only -crashReporter.init() - // All state lives in state.js. `state.saved` is read from and written to a file. // All other state is ephemeral. First we load state.saved then initialize the app. loadState(init) @@ -71,7 +65,7 @@ function init () { resumeTorrents() // Lazy-load other stuff, like the AppleTV module, later to keep startup fast - window.setTimeout(delayedInit, 5000) + window.setTimeout(delayedInit, config.DELAYED_INIT) // The UI is built with virtual-dom, a minimalist library extracted from React // The concepts--one way data flow, a pure function that renders state to a @@ -100,9 +94,6 @@ function init () { window.addEventListener('focus', onFocus) window.addEventListener('blur', onBlur) - // Listen for messages from the main process - setupIpc() - // Done! Ideally we want to get here <100ms after the user clicks the app sound.play('STARTUP') @@ -416,7 +407,7 @@ function setVolume (volume) { } function openSubtitles () { - dialog.showOpenDialog({ + electron.remote.dialog.showOpenDialog({ title: 'Select a subtitles file.', filters: [ { name: 'Subtitles', extensions: ['vtt', 'srt'] } ], properties: [ 'openFile' ] @@ -586,6 +577,9 @@ function addTorrent (torrentId) { } function addSubtitle (file) { + var srtToVtt = require('srt-to-vtt') + var LanguageDetect = require('languagedetect') + if (state.playing.type !== 'video') return fs.createReadStream(file.path || file).pipe(srtToVtt()).pipe(concat(function (buf) { // Set the cue text position so it appears above the player controls. @@ -990,34 +984,34 @@ function toggleSelectTorrent (infoHash) { function openTorrentContextMenu (infoHash) { var torrentSummary = getTorrentSummary(infoHash) - var menu = new Menu() + var menu = new electron.remote.Menu() if (torrentSummary.files) { - menu.append(new MenuItem({ + menu.append(new electron.remote.MenuItem({ label: process.platform === 'darwin' ? 'Show in Finder' : 'Show in Folder', click: () => showItemInFolder(torrentSummary) })) - menu.append(new MenuItem({ + menu.append(new electron.remote.MenuItem({ type: 'separator' })) } - menu.append(new MenuItem({ + menu.append(new electron.remote.MenuItem({ label: 'Copy Magnet Link to Clipboard', - click: () => clipboard.writeText(torrentSummary.magnetURI) + click: () => electron.clipboard.writeText(torrentSummary.magnetURI) })) - menu.append(new MenuItem({ + menu.append(new electron.remote.MenuItem({ label: 'Copy Instant.io Link to Clipboard', - click: () => clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`) + click: () => electron.clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`) })) - menu.append(new MenuItem({ + menu.append(new electron.remote.MenuItem({ label: 'Save Torrent File As...', click: () => saveTorrentFileAs(torrentSummary) })) - menu.popup(remote.getCurrentWindow()) + menu.popup(electron.remote.getCurrentWindow()) } function showItemInFolder (torrentSummary) { @@ -1038,7 +1032,7 @@ function saveTorrentFileAs (torrentSummary) { { name: 'All Files', extensions: ['*'] } ] } - dialog.showSaveDialog(remote.getCurrentWindow(), opts, function (savePath) { + electron.remote.dialog.showSaveDialog(electron.remote.getCurrentWindow(), opts, function (savePath) { var torrentPath = TorrentSummary.getTorrentPath(torrentSummary) fs.readFile(torrentPath, function (err, torrentFile) { if (err) return onError(err) @@ -1052,7 +1046,7 @@ function saveTorrentFileAs (torrentSummary) { // Set window dimensions to match video dimensions or fill the screen function setDimensions (dimensions) { // Don't modify the window size if it's already maximized - if (remote.getCurrentWindow().isMaximized()) { + if (electron.remote.getCurrentWindow().isMaximized()) { state.window.bounds = null return } @@ -1064,7 +1058,7 @@ function setDimensions (dimensions) { width: window.outerWidth, height: window.outerHeight } - state.window.wasMaximized = remote.getCurrentWindow().isMaximized + state.window.wasMaximized = electron.remote.getCurrentWindow().isMaximized // Limit window size to screen size var screenWidth = window.screen.width @@ -1144,7 +1138,7 @@ function onWarning (err) { function onPaste (e) { if (e.target.tagName.toLowerCase() === 'input') return - var torrentIds = clipboard.readText().split('\n') + var torrentIds = electron.clipboard.readText().split('\n') torrentIds.forEach(function (torrentId) { torrentId = torrentId.trim() if (torrentId.length === 0) return