Merge pull request #494 from feross/perf
Improve app startup time by ~350ms
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
|
||||
13
main/ipc.js
13
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
])
|
||||
|
||||
71
main/updater.js
Normal file
71
main/updater.js
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user