@@ -3,49 +3,52 @@
|
|||||||
var fs = require('fs')
|
var fs = require('fs')
|
||||||
var cp = require('child_process')
|
var cp = require('child_process')
|
||||||
|
|
||||||
var BUILT_IN_DEPS = ['child_process', 'electron', 'fs', 'os', 'path', 'screen']
|
var BUILT_IN_DEPS = ['child_process', 'electron', 'fs', 'os', 'path']
|
||||||
var EXECUTABLE_DEPS = ['gh-release', 'standard']
|
var EXECUTABLE_DEPS = ['gh-release', 'standard']
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
// Scans our codebase and package.json for missing or unused dependencies
|
// Scans codebase for missing or unused dependencies. Exits with code 0 on success.
|
||||||
// Process returns 0 on success, prints a message and returns 1 on failure
|
|
||||||
function main () {
|
function main () {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
console.log('Sorry, check-deps only works on Mac and Linux')
|
console.error('Sorry, check-deps only works on Mac and Linux')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsDeps = findJSDeps()
|
var usedDeps = findUsedDeps()
|
||||||
var packageDeps = findPackageDeps()
|
var packageDeps = findPackageDeps()
|
||||||
|
|
||||||
var missingDeps = jsDeps.filter((dep) =>
|
var missingDeps = usedDeps.filter(
|
||||||
packageDeps.indexOf(dep) < 0 &&
|
(dep) => !packageDeps.includes(dep) && !BUILT_IN_DEPS.includes(dep)
|
||||||
BUILT_IN_DEPS.indexOf(dep) < 0)
|
)
|
||||||
var unusedDeps = packageDeps.filter((dep) =>
|
var unusedDeps = packageDeps.filter(
|
||||||
jsDeps.indexOf(dep) < 0 &&
|
(dep) => !usedDeps.includes(dep) && !EXECUTABLE_DEPS.includes(dep)
|
||||||
EXECUTABLE_DEPS.indexOf(dep) < 0)
|
)
|
||||||
|
|
||||||
if (missingDeps.length > 0) console.log('Missing package dependencies: ' + missingDeps)
|
if (missingDeps.length > 0) {
|
||||||
if (unusedDeps.length > 0) console.log('Unused package dependencies: ' + unusedDeps)
|
console.error('Missing package dependencies: ' + missingDeps)
|
||||||
|
}
|
||||||
if (missingDeps.length + unusedDeps.length > 0) process.exit(1)
|
if (unusedDeps.length > 0) {
|
||||||
|
console.error('Unused package dependencies: ' + unusedDeps)
|
||||||
console.log('Lookin good!')
|
}
|
||||||
|
if (missingDeps.length + unusedDeps.length > 0) {
|
||||||
|
process.exitCode = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds all dependencies, required, optional, or dev, in package.json
|
// Finds all dependencies specified in `package.json`
|
||||||
function findPackageDeps () {
|
function findPackageDeps () {
|
||||||
var pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
var pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
||||||
var requiredDeps = Object.keys(pkg.dependencies)
|
|
||||||
|
var deps = Object.keys(pkg.dependencies)
|
||||||
var devDeps = Object.keys(pkg.devDependencies)
|
var devDeps = Object.keys(pkg.devDependencies)
|
||||||
var optionalDeps = Object.keys(pkg.optionalDependencies)
|
var optionalDeps = Object.keys(pkg.optionalDependencies)
|
||||||
|
|
||||||
return [].concat(requiredDeps, devDeps, optionalDeps)
|
return [].concat(deps, devDeps, optionalDeps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds all dependencies required() in the code
|
// Finds all dependencies that used with `require()`
|
||||||
function findJSDeps () {
|
function findUsedDeps () {
|
||||||
var stdout = cp.execSync('./bin/list-deps.sh')
|
var stdout = cp.execSync('./bin/list-deps.sh')
|
||||||
return stdout.toString().trim().split('\n')
|
return stdout.toString().trim().split('\n')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ var path = require('path')
|
|||||||
|
|
||||||
var child = cp.spawn(electron, [path.join(__dirname, '..')], {stdio: 'inherit'})
|
var child = cp.spawn(electron, [path.join(__dirname, '..')], {stdio: 'inherit'})
|
||||||
child.on('close', function (code) {
|
child.on('close', function (code) {
|
||||||
process.exit(code)
|
process.exitCode = code
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
var get = require('simple-get')
|
|
||||||
|
|
||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
var log = require('./log')
|
var log = require('./log')
|
||||||
@@ -12,27 +11,47 @@ var ANNOUNCEMENT_URL = config.ANNOUNCEMENT_URL +
|
|||||||
'?version=' + config.APP_VERSION +
|
'?version=' + config.APP_VERSION +
|
||||||
'&platform=' + process.platform
|
'&platform=' + process.platform
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In certain situations, the WebTorrent team may need to show an announcement to
|
||||||
|
* all WebTorrent Desktop users. For example: a security notice, or an update
|
||||||
|
* notification (if the auto-updater stops working).
|
||||||
|
*
|
||||||
|
* When there is an announcement, the `ANNOUNCEMENT_URL` endpoint should return an
|
||||||
|
* HTTP 200 status code with a JSON object like this:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "title": "WebTorrent Desktop Announcement",
|
||||||
|
* "message": "Security Issue in v0.xx",
|
||||||
|
* "detail": "Please update to v0.xx as soon as possible..."
|
||||||
|
* }
|
||||||
|
*/
|
||||||
function init () {
|
function init () {
|
||||||
get.concat(ANNOUNCEMENT_URL, function (err, res, data) {
|
var get = require('simple-get')
|
||||||
if (err) return log('failed to retrieve remote message')
|
get.concat(ANNOUNCEMENT_URL, onResponse)
|
||||||
if (res.statusCode !== 200) return log('no remote message')
|
|
||||||
|
|
||||||
try {
|
|
||||||
data = JSON.parse(data.toString())
|
|
||||||
} catch (err) {
|
|
||||||
data = {
|
|
||||||
title: 'WebTorrent Desktop Announcement',
|
|
||||||
message: 'WebTorrent Desktop Announcement',
|
|
||||||
detail: data.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
electron.dialog.showMessageBox({
|
|
||||||
type: 'info',
|
|
||||||
buttons: ['OK'],
|
|
||||||
title: data.title,
|
|
||||||
message: data.message,
|
|
||||||
detail: data.detail
|
|
||||||
}, function () {})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onResponse (err, res, data) {
|
||||||
|
if (err) return log(`Failed to retrieve announcement: ${err.message}`)
|
||||||
|
if (res.statusCode !== 200) return log('No announcement exists')
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(data.toString())
|
||||||
|
} catch (err) {
|
||||||
|
// Support plaintext announcement messages, using a default title.
|
||||||
|
data = {
|
||||||
|
title: 'WebTorrent Desktop Announcement',
|
||||||
|
message: data.toString(),
|
||||||
|
detail: data.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
electron.dialog.showMessageBox({
|
||||||
|
type: 'info',
|
||||||
|
buttons: ['OK'],
|
||||||
|
title: data.title,
|
||||||
|
message: data.message,
|
||||||
|
detail: data.detail
|
||||||
|
}, noop)
|
||||||
|
}
|
||||||
|
|
||||||
|
function noop () {}
|
||||||
|
|||||||
97
main/dialog.js
Normal file
97
main/dialog.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
module.exports = {
|
||||||
|
openSeedFile,
|
||||||
|
openSeedDirectory,
|
||||||
|
openTorrentFile,
|
||||||
|
openTorrentAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
var electron = require('electron')
|
||||||
|
|
||||||
|
var config = require('../config')
|
||||||
|
var log = require('./log')
|
||||||
|
var windows = require('./windows')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show open dialog to create a single-file torrent.
|
||||||
|
*/
|
||||||
|
function openSeedFile () {
|
||||||
|
if (!windows.main.win) return
|
||||||
|
log('openSeedFile')
|
||||||
|
var opts = {
|
||||||
|
title: 'Select a file for the torrent.',
|
||||||
|
properties: [ 'openFile' ]
|
||||||
|
}
|
||||||
|
setTitle(opts.title)
|
||||||
|
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
|
||||||
|
resetTitle()
|
||||||
|
if (!Array.isArray(selectedPaths)) return
|
||||||
|
windows.main.dispatch('showCreateTorrent', selectedPaths)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Show open dialog to create a single-file or single-directory torrent. On
|
||||||
|
* Windows and Linux, open dialogs are for files *or* directories only, not both,
|
||||||
|
* so this function shows a directory dialog on those platforms.
|
||||||
|
*/
|
||||||
|
function openSeedDirectory () {
|
||||||
|
if (!windows.main.win) return
|
||||||
|
log('openSeedDirectory')
|
||||||
|
var opts = process.platform === 'darwin'
|
||||||
|
? {
|
||||||
|
title: 'Select a file or folder for the torrent.',
|
||||||
|
properties: [ 'openFile', 'openDirectory' ]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Select a folder for the torrent.',
|
||||||
|
properties: [ 'openDirectory' ]
|
||||||
|
}
|
||||||
|
setTitle(opts.title)
|
||||||
|
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
|
||||||
|
resetTitle()
|
||||||
|
if (!Array.isArray(selectedPaths)) return
|
||||||
|
windows.main.dispatch('showCreateTorrent', selectedPaths)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Show open dialog to open a .torrent file.
|
||||||
|
*/
|
||||||
|
function openTorrentFile () {
|
||||||
|
if (!windows.main.win) return
|
||||||
|
log('openTorrentFile')
|
||||||
|
var opts = {
|
||||||
|
title: 'Select a .torrent file to open.',
|
||||||
|
filters: [{ name: 'Torrent Files', extensions: ['torrent'] }],
|
||||||
|
properties: [ 'openFile', 'multiSelections' ]
|
||||||
|
}
|
||||||
|
setTitle(opts.title)
|
||||||
|
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
|
||||||
|
resetTitle()
|
||||||
|
if (!Array.isArray(selectedPaths)) return
|
||||||
|
selectedPaths.forEach(function (selectedPath) {
|
||||||
|
windows.main.dispatch('addTorrent', selectedPath)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Show modal dialog to open a torrent URL (magnet uri, http torrent link, etc.)
|
||||||
|
*/
|
||||||
|
function openTorrentAddress () {
|
||||||
|
log('openTorrentAddress')
|
||||||
|
windows.main.dispatch('openTorrentAddress')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialogs on do not show a title on OS X, so the window title is used instead.
|
||||||
|
*/
|
||||||
|
function setTitle (title) {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
windows.main.dispatch('setTitle', title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTitle () {
|
||||||
|
setTitle(config.APP_WINDOW_TITLE)
|
||||||
|
}
|
||||||
59
main/dock.js
Normal file
59
main/dock.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
module.exports = {
|
||||||
|
downloadFinished,
|
||||||
|
init,
|
||||||
|
setBadge
|
||||||
|
}
|
||||||
|
|
||||||
|
var electron = require('electron')
|
||||||
|
|
||||||
|
var app = electron.app
|
||||||
|
|
||||||
|
var dialog = require('./dialog')
|
||||||
|
var log = require('./log')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a right-click menu to the dock icon. (OS X)
|
||||||
|
*/
|
||||||
|
function init () {
|
||||||
|
if (!app.dock) return
|
||||||
|
var menu = electron.Menu.buildFromTemplate(getMenuTemplate())
|
||||||
|
app.dock.setMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bounce the Downloads stack if `path` is inside the Downloads folder. (OS X)
|
||||||
|
*/
|
||||||
|
function downloadFinished (path) {
|
||||||
|
if (!app.dock) return
|
||||||
|
log(`downloadFinished: ${path}`)
|
||||||
|
app.dock.downloadFinished(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display string in dock badging area. (OS X)
|
||||||
|
*/
|
||||||
|
function setBadge (text) {
|
||||||
|
if (!app.dock) return
|
||||||
|
log(`setBadge: ${text}`)
|
||||||
|
app.dock.setBadge(String(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMenuTemplate () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Create New Torrent...',
|
||||||
|
accelerator: 'CmdOrCtrl+N',
|
||||||
|
click: () => dialog.openSeedDirectory()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Open Torrent File...',
|
||||||
|
accelerator: 'CmdOrCtrl+O',
|
||||||
|
click: () => dialog.openTorrentFile()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Open Torrent Address...',
|
||||||
|
accelerator: 'CmdOrCtrl+U',
|
||||||
|
click: () => dialog.openTorrentAddress()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -3,9 +3,8 @@ module.exports = {
|
|||||||
uninstall
|
uninstall
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = require('path')
|
|
||||||
|
|
||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
|
var path = require('path')
|
||||||
|
|
||||||
function install () {
|
function install () {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
@@ -35,11 +34,11 @@ function installDarwin () {
|
|||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
var app = electron.app
|
var app = electron.app
|
||||||
|
|
||||||
// On OS X, only protocols that are listed in Info.plist can be set as the default
|
// On OS X, only protocols that are listed in `Info.plist` can be set as the
|
||||||
// handler at runtime.
|
// default handler at runtime.
|
||||||
app.setAsDefaultProtocolClient('magnet')
|
app.setAsDefaultProtocolClient('magnet')
|
||||||
|
|
||||||
// File handlers are registered in the Info.plist.
|
// File handlers are defined in `Info.plist`.
|
||||||
}
|
}
|
||||||
|
|
||||||
function uninstallDarwin () {}
|
function uninstallDarwin () {}
|
||||||
@@ -55,10 +54,22 @@ function installWin32 () {
|
|||||||
|
|
||||||
var log = require('./log')
|
var log = require('./log')
|
||||||
|
|
||||||
var iconPath = path.join(process.resourcesPath, 'app.asar.unpacked', 'static', 'WebTorrentFile.ico')
|
var iconPath = path.join(
|
||||||
|
process.resourcesPath, 'app.asar.unpacked', 'static', 'WebTorrentFile.ico'
|
||||||
registerProtocolHandlerWin32('magnet', 'URL:BitTorrent Magnet URL', iconPath, EXEC_COMMAND)
|
)
|
||||||
registerFileHandlerWin32('.torrent', 'io.webtorrent.torrent', 'BitTorrent Document', iconPath, EXEC_COMMAND)
|
registerProtocolHandlerWin32(
|
||||||
|
'magnet',
|
||||||
|
'URL:BitTorrent Magnet URL',
|
||||||
|
iconPath,
|
||||||
|
EXEC_COMMAND
|
||||||
|
)
|
||||||
|
registerFileHandlerWin32(
|
||||||
|
'.torrent',
|
||||||
|
'io.webtorrent.torrent',
|
||||||
|
'BitTorrent Document',
|
||||||
|
iconPath,
|
||||||
|
EXEC_COMMAND
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To add a protocol handler, the following keys must be added to the Windows registry:
|
* To add a protocol handler, the following keys must be added to the Windows registry:
|
||||||
@@ -265,7 +276,9 @@ function installLinux () {
|
|||||||
installIconFile()
|
installIconFile()
|
||||||
|
|
||||||
function installDesktopFile () {
|
function installDesktopFile () {
|
||||||
var templatePath = path.join(config.STATIC_PATH, 'linux', 'webtorrent-desktop.desktop')
|
var templatePath = path.join(
|
||||||
|
config.STATIC_PATH, 'linux', 'webtorrent-desktop.desktop'
|
||||||
|
)
|
||||||
fs.readFile(templatePath, 'utf8', writeDesktopFile)
|
fs.readFile(templatePath, 'utf8', writeDesktopFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ var ipcMain = electron.ipcMain
|
|||||||
var announcement = require('./announcement')
|
var announcement = require('./announcement')
|
||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
var crashReporter = require('../crash-reporter')
|
var crashReporter = require('../crash-reporter')
|
||||||
|
var dialog = require('./dialog')
|
||||||
|
var dock = require('./dock')
|
||||||
var handlers = require('./handlers')
|
var handlers = require('./handlers')
|
||||||
var ipc = require('./ipc')
|
var ipc = require('./ipc')
|
||||||
var log = require('./log')
|
var log = require('./log')
|
||||||
@@ -60,8 +62,8 @@ function init () {
|
|||||||
app.on('ready', function () {
|
app.on('ready', function () {
|
||||||
isReady = true
|
isReady = true
|
||||||
|
|
||||||
windows.createMainWindow()
|
windows.main.init()
|
||||||
windows.createWebTorrentHiddenWindow()
|
windows.webtorrent.init()
|
||||||
menu.init()
|
menu.init()
|
||||||
|
|
||||||
// To keep app startup fast, some code is delayed.
|
// To keep app startup fast, some code is delayed.
|
||||||
@@ -79,20 +81,21 @@ function init () {
|
|||||||
|
|
||||||
app.isQuitting = true
|
app.isQuitting = true
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
windows.main.send('dispatch', 'saveState') /* try to save state on exit */
|
windows.main.dispatch('saveState') // try to save state on exit
|
||||||
ipcMain.once('savedState', () => app.quit())
|
ipcMain.once('savedState', () => app.quit())
|
||||||
setTimeout(() => app.quit(), 2000) /* quit after 2 secs, at most */
|
setTimeout(() => app.quit(), 2000) // quit after 2 secs, at most
|
||||||
})
|
})
|
||||||
|
|
||||||
app.on('activate', function () {
|
app.on('activate', function () {
|
||||||
if (isReady) windows.createMainWindow()
|
if (isReady) windows.main.show()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function delayedInit () {
|
function delayedInit () {
|
||||||
announcement.init()
|
announcement.init()
|
||||||
tray.init()
|
dock.init()
|
||||||
handlers.install()
|
handlers.install()
|
||||||
|
tray.init()
|
||||||
updater.init()
|
updater.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,12 +103,12 @@ function onOpen (e, torrentId) {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (app.ipcReady) {
|
if (app.ipcReady) {
|
||||||
windows.main.send('dispatch', 'onOpen', torrentId)
|
windows.main.dispatch('onOpen', torrentId)
|
||||||
// Magnet links opened from Chrome won't focus the app without a setTimeout. The
|
// Magnet links opened from Chrome won't focus the app without a setTimeout.
|
||||||
// confirmation dialog Chrome shows causes Chrome to steal back the focus.
|
// The confirmation dialog Chrome shows causes Chrome to steal back the focus.
|
||||||
// Electron issue: https://github.com/atom/electron/issues/4338
|
// Electron issue: https://github.com/atom/electron/issues/4338
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
windows.focusWindow(windows.main)
|
windows.main.show()
|
||||||
}, 100)
|
}, 100)
|
||||||
} else {
|
} else {
|
||||||
argv.push(torrentId)
|
argv.push(torrentId)
|
||||||
@@ -114,10 +117,11 @@ function onOpen (e, torrentId) {
|
|||||||
|
|
||||||
function onAppOpen (newArgv) {
|
function onAppOpen (newArgv) {
|
||||||
newArgv = sliceArgv(newArgv)
|
newArgv = sliceArgv(newArgv)
|
||||||
|
console.log(newArgv)
|
||||||
|
|
||||||
if (app.ipcReady) {
|
if (app.ipcReady) {
|
||||||
log('Second app instance opened, but was prevented:', newArgv)
|
log('Second app instance opened, but was prevented:', newArgv)
|
||||||
windows.focusWindow(windows.main)
|
windows.main.show()
|
||||||
|
|
||||||
processArgv(newArgv)
|
processArgv(newArgv)
|
||||||
} else {
|
} else {
|
||||||
@@ -130,27 +134,22 @@ function sliceArgv (argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processArgv (argv) {
|
function processArgv (argv) {
|
||||||
var pathsToOpen = []
|
var paths = []
|
||||||
argv.forEach(function (arg) {
|
argv.forEach(function (arg) {
|
||||||
if (arg === '-n') {
|
if (arg === '-n') {
|
||||||
menu.showOpenSeedFiles()
|
dialog.openSeedDirectory()
|
||||||
} else if (arg === '-o') {
|
} else if (arg === '-o') {
|
||||||
menu.showOpenTorrentFile()
|
dialog.openTorrentFile()
|
||||||
} else if (arg === '-u') {
|
} else if (arg === '-u') {
|
||||||
menu.showOpenTorrentAddress()
|
dialog.openTorrentAddress()
|
||||||
} else if (arg.startsWith('-psn')) {
|
} else if (arg.startsWith('-psn')) {
|
||||||
// Ignore OS X launchd "process serial number" argument
|
// Ignore OS X launchd "process serial number" argument
|
||||||
// More: https://github.com/feross/webtorrent-desktop/issues/214
|
// Issue: https://github.com/feross/webtorrent-desktop/issues/214
|
||||||
} else {
|
} else {
|
||||||
pathsToOpen.push(arg)
|
paths.push(arg)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (pathsToOpen.length > 0) openFilePaths(pathsToOpen)
|
if (paths.length > 0) {
|
||||||
}
|
windows.main.dispatch('onOpen', paths)
|
||||||
|
}
|
||||||
// Send files to the renderer process
|
|
||||||
// Opening files means either adding torrents, creating and seeding a torrent
|
|
||||||
// from files, or adding subtitles
|
|
||||||
function openFilePaths (paths) {
|
|
||||||
windows.main.send('dispatch', 'onOpen', paths)
|
|
||||||
}
|
}
|
||||||
|
|||||||
212
main/ipc.js
212
main/ipc.js
@@ -5,31 +5,33 @@ module.exports = {
|
|||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
|
|
||||||
var app = electron.app
|
var app = electron.app
|
||||||
var ipcMain = electron.ipcMain
|
|
||||||
|
|
||||||
|
var dialog = require('./dialog')
|
||||||
|
var dock = require('./dock')
|
||||||
var log = require('./log')
|
var log = require('./log')
|
||||||
var menu = require('./menu')
|
var menu = require('./menu')
|
||||||
var windows = require('./windows')
|
var powerSaveBlocker = require('./power-save-blocker')
|
||||||
|
var shell = require('./shell')
|
||||||
var shortcuts = require('./shortcuts')
|
var shortcuts = require('./shortcuts')
|
||||||
var vlc = require('./vlc')
|
var vlc = require('./vlc')
|
||||||
|
var windows = require('./windows')
|
||||||
|
|
||||||
// has to be a number, not a boolean, and undefined throws an error
|
// Messages from the main process, to be sent once the WebTorrent process starts
|
||||||
var powerSaveBlockerId = 0
|
|
||||||
|
|
||||||
// messages from the main process, to be sent once the WebTorrent process starts
|
|
||||||
var messageQueueMainToWebTorrent = []
|
var messageQueueMainToWebTorrent = []
|
||||||
|
|
||||||
// holds a ChildProcess while we're playing a video in VLC, null otherwise
|
// holds a ChildProcess while we're playing a video in VLC, null otherwise
|
||||||
var vlcProcess
|
var vlcProcess
|
||||||
|
|
||||||
function init () {
|
function init () {
|
||||||
ipcMain.on('ipcReady', function (e) {
|
var ipc = electron.ipcMain
|
||||||
|
|
||||||
|
ipc.on('ipcReady', function (e) {
|
||||||
windows.main.show()
|
windows.main.show()
|
||||||
app.ipcReady = true
|
app.ipcReady = true
|
||||||
app.emit('ipcReady')
|
app.emit('ipcReady')
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ipcReadyWebTorrent', function (e) {
|
ipc.on('ipcReadyWebTorrent', function (e) {
|
||||||
app.ipcReadyWebTorrent = true
|
app.ipcReadyWebTorrent = true
|
||||||
log('sending %d queued messages from the main win to the webtorrent window',
|
log('sending %d queued messages from the main win to the webtorrent window',
|
||||||
messageQueueMainToWebTorrent.length)
|
messageQueueMainToWebTorrent.length)
|
||||||
@@ -39,113 +41,111 @@ function init () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('showOpenTorrentFile', menu.showOpenTorrentFile)
|
/**
|
||||||
|
* Dialog
|
||||||
|
*/
|
||||||
|
|
||||||
ipcMain.on('setBounds', function (e, bounds, maximize) {
|
ipc.on('openTorrentFile', () => dialog.openTorrentFile())
|
||||||
setBounds(bounds, maximize)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('setAspectRatio', function (e, aspectRatio) {
|
/**
|
||||||
setAspectRatio(aspectRatio)
|
* Dock
|
||||||
})
|
*/
|
||||||
|
|
||||||
ipcMain.on('setBadge', function (e, text) {
|
ipc.on('setBadge', (e, ...args) => dock.setBadge(...args))
|
||||||
setBadge(text)
|
ipc.on('downloadFinished', (e, ...args) => dock.downloadFinished(...args))
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('setProgress', function (e, progress) {
|
/**
|
||||||
setProgress(progress)
|
* Events
|
||||||
})
|
*/
|
||||||
|
|
||||||
ipcMain.on('toggleFullScreen', function (e, flag) {
|
ipc.on('onPlayerOpen', function () {
|
||||||
menu.toggleFullScreen(flag)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('setTitle', function (e, title) {
|
|
||||||
windows.main.setTitle(title)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('openItem', function (e, path) {
|
|
||||||
log('open item: ' + path)
|
|
||||||
electron.shell.openItem(path)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('showItemInFolder', function (e, path) {
|
|
||||||
log('show item in folder: ' + path)
|
|
||||||
electron.shell.showItemInFolder(path)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('blockPowerSave', blockPowerSave)
|
|
||||||
|
|
||||||
ipcMain.on('unblockPowerSave', unblockPowerSave)
|
|
||||||
|
|
||||||
ipcMain.on('onPlayerOpen', function () {
|
|
||||||
menu.onPlayerOpen()
|
menu.onPlayerOpen()
|
||||||
shortcuts.onPlayerOpen()
|
shortcuts.onPlayerOpen()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('onPlayerClose', function () {
|
ipc.on('onPlayerClose', function () {
|
||||||
menu.onPlayerClose()
|
menu.onPlayerClose()
|
||||||
shortcuts.onPlayerOpen()
|
shortcuts.onPlayerOpen()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('focusWindow', function (e, windowName) {
|
/**
|
||||||
windows.focusWindow(windows[windowName])
|
* Power Save Blocker
|
||||||
})
|
*/
|
||||||
|
|
||||||
ipcMain.on('downloadFinished', function (e, filePath) {
|
ipc.on('blockPowerSave', () => powerSaveBlocker.start())
|
||||||
if (app.dock) {
|
ipc.on('unblockPowerSave', () => powerSaveBlocker.stop())
|
||||||
// Bounces the Downloads stack if the filePath is inside the Downloads folder.
|
|
||||||
app.dock.downloadFinished(filePath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('checkForVLC', function (e) {
|
/**
|
||||||
|
* Shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
ipc.on('openItem', (e, ...args) => shell.openItem(...args))
|
||||||
|
ipc.on('showItemInFolder', (e, ...args) => shell.showItemInFolder(...args))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Windows: Main
|
||||||
|
*/
|
||||||
|
|
||||||
|
var main = windows.main
|
||||||
|
|
||||||
|
ipc.on('setAspectRatio', (e, ...args) => main.setAspectRatio(...args))
|
||||||
|
ipc.on('setBounds', (e, ...args) => main.setBounds(...args))
|
||||||
|
ipc.on('setProgress', (e, ...args) => main.setProgress(...args))
|
||||||
|
ipc.on('setTitle', (e, ...args) => main.setTitle(...args))
|
||||||
|
ipc.on('show', () => main.show())
|
||||||
|
ipc.on('toggleFullScreen', (e, ...args) => main.toggleFullScreen(...args))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VLC
|
||||||
|
* TODO: Move most of this code to vlc.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
ipc.on('checkForVLC', function (e) {
|
||||||
vlc.checkForVLC(function (isInstalled) {
|
vlc.checkForVLC(function (isInstalled) {
|
||||||
windows.main.send('checkForVLC', isInstalled)
|
windows.main.send('checkForVLC', isInstalled)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('vlcPlay', function (e, url) {
|
ipc.on('vlcPlay', function (e, url) {
|
||||||
var args = ['--play-and-exit', '--video-on-top', '--no-video-title-show', '--quiet', url]
|
var args = ['--play-and-exit', '--video-on-top', '--no-video-title-show', '--quiet', url]
|
||||||
console.log('Running vlc ' + args.join(' '))
|
log('Running vlc ' + args.join(' '))
|
||||||
|
|
||||||
vlc.spawn(args, function (err, proc) {
|
vlc.spawn(args, function (err, proc) {
|
||||||
if (err) return windows.main.send('dispatch', 'vlcNotFound')
|
if (err) return windows.main.dispatch('vlcNotFound')
|
||||||
vlcProcess = proc
|
vlcProcess = proc
|
||||||
|
|
||||||
// If it works, close the modal after a second
|
// If it works, close the modal after a second
|
||||||
var closeModalTimeout = setTimeout(() =>
|
var closeModalTimeout = setTimeout(() =>
|
||||||
windows.main.send('dispatch', 'exitModal'), 1000)
|
windows.main.dispatch('exitModal'), 1000)
|
||||||
|
|
||||||
vlcProcess.on('close', function (code) {
|
vlcProcess.on('close', function (code) {
|
||||||
clearTimeout(closeModalTimeout)
|
clearTimeout(closeModalTimeout)
|
||||||
if (!vlcProcess) return // Killed
|
if (!vlcProcess) return // Killed
|
||||||
console.log('VLC exited with code ', code)
|
log('VLC exited with code ', code)
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
windows.main.send('dispatch', 'backToList')
|
windows.main.dispatch('backToList')
|
||||||
} else {
|
} else {
|
||||||
windows.main.send('dispatch', 'vlcNotFound')
|
windows.main.dispatch('vlcNotFound')
|
||||||
}
|
}
|
||||||
vlcProcess = null
|
vlcProcess = null
|
||||||
})
|
})
|
||||||
|
|
||||||
vlcProcess.on('error', function (e) {
|
vlcProcess.on('error', function (e) {
|
||||||
console.log('VLC error', e)
|
log('VLC error', e)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('vlcQuit', function () {
|
ipc.on('vlcQuit', function () {
|
||||||
if (!vlcProcess) return
|
if (!vlcProcess) return
|
||||||
console.log('Killing VLC, pid ' + vlcProcess.pid)
|
log('Killing VLC, pid ' + vlcProcess.pid)
|
||||||
vlcProcess.kill('SIGKILL') // kill -9
|
vlcProcess.kill('SIGKILL') // kill -9
|
||||||
vlcProcess = null
|
vlcProcess = null
|
||||||
})
|
})
|
||||||
|
|
||||||
// Capture all events
|
// Capture all events
|
||||||
var oldEmit = ipcMain.emit
|
var oldEmit = ipc.emit
|
||||||
ipcMain.emit = function (name, e, ...args) {
|
ipc.emit = function (name, e, ...args) {
|
||||||
// Relay messages between the main window and the WebTorrent hidden window
|
// Relay messages between the main window and the WebTorrent hidden window
|
||||||
if (name.startsWith('wt-') && !app.isQuitting) {
|
if (name.startsWith('wt-') && !app.isQuitting) {
|
||||||
if (e.sender.browserWindowOptions.title === 'webtorrent-hidden-window') {
|
if (e.sender.browserWindowOptions.title === 'webtorrent-hidden-window') {
|
||||||
@@ -168,82 +168,6 @@ function init () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Emit all other events normally
|
// Emit all other events normally
|
||||||
oldEmit.call(ipcMain, name, e, ...args)
|
oldEmit.call(ipc, name, e, ...args)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBounds (bounds, maximize) {
|
|
||||||
// Do nothing in fullscreen
|
|
||||||
if (!windows.main || windows.main.isFullScreen()) {
|
|
||||||
log('setBounds: not setting bounds because we\'re in full screen')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maximize or minimize, if the second argument is present
|
|
||||||
var willBeMaximized
|
|
||||||
if (maximize === true) {
|
|
||||||
if (!windows.main.isMaximized()) {
|
|
||||||
log('setBounds: maximizing')
|
|
||||||
windows.main.maximize()
|
|
||||||
}
|
|
||||||
willBeMaximized = true
|
|
||||||
} else if (maximize === false) {
|
|
||||||
if (windows.main.isMaximized()) {
|
|
||||||
log('setBounds: unmaximizing')
|
|
||||||
windows.main.unmaximize()
|
|
||||||
}
|
|
||||||
willBeMaximized = false
|
|
||||||
} else {
|
|
||||||
willBeMaximized = windows.main.isMaximized()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assuming we're not maximized or maximizing, set the window size
|
|
||||||
if (!willBeMaximized) {
|
|
||||||
log('setBounds: setting bounds to ' + JSON.stringify(bounds))
|
|
||||||
if (bounds.x === null && bounds.y === null) {
|
|
||||||
// X and Y not specified? By default, center on current screen
|
|
||||||
var scr = electron.screen.getDisplayMatching(windows.main.getBounds())
|
|
||||||
bounds.x = Math.round(scr.bounds.x + scr.bounds.width / 2 - bounds.width / 2)
|
|
||||||
bounds.y = Math.round(scr.bounds.y + scr.bounds.height / 2 - bounds.height / 2)
|
|
||||||
log('setBounds: centered to ' + JSON.stringify(bounds))
|
|
||||||
}
|
|
||||||
windows.main.setBounds(bounds, true)
|
|
||||||
} else {
|
|
||||||
log('setBounds: not setting bounds because of window maximization')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAspectRatio (aspectRatio) {
|
|
||||||
log('setAspectRatio %o', aspectRatio)
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.setAspectRatio(aspectRatio)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display string in dock badging area (OS X)
|
|
||||||
function setBadge (text) {
|
|
||||||
log('setBadge %s', text)
|
|
||||||
if (app.dock) {
|
|
||||||
app.dock.setBadge(String(text))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show progress bar. Valid range is [0, 1]. Remove when < 0; indeterminate when > 1.
|
|
||||||
function setProgress (progress) {
|
|
||||||
log('setProgress %s', progress)
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.setProgressBar(progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function blockPowerSave () {
|
|
||||||
powerSaveBlockerId = electron.powerSaveBlocker.start('prevent-display-sleep')
|
|
||||||
log('blockPowerSave %d', powerSaveBlockerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
function unblockPowerSave () {
|
|
||||||
if (electron.powerSaveBlocker.isStarted(powerSaveBlockerId)) {
|
|
||||||
electron.powerSaveBlocker.stop(powerSaveBlockerId)
|
|
||||||
log('unblockPowerSave %d', powerSaveBlockerId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,16 @@ module.exports.error = error
|
|||||||
|
|
||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
|
|
||||||
|
var config = require('../config')
|
||||||
var windows = require('./windows')
|
var windows = require('./windows')
|
||||||
|
|
||||||
var app = electron.app
|
var app = electron.app
|
||||||
|
|
||||||
function log (...args) {
|
function log (...args) {
|
||||||
|
if (!config.IS_PRODUCTION) {
|
||||||
|
// In development, also log to the console
|
||||||
|
console.log(...args)
|
||||||
|
}
|
||||||
if (app.ipcReady) {
|
if (app.ipcReady) {
|
||||||
windows.main.send('log', ...args)
|
windows.main.send('log', ...args)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
316
main/menu.js
316
main/menu.js
@@ -2,15 +2,10 @@ module.exports = {
|
|||||||
init,
|
init,
|
||||||
onPlayerClose,
|
onPlayerClose,
|
||||||
onPlayerOpen,
|
onPlayerOpen,
|
||||||
|
onToggleAlwaysOnTop,
|
||||||
onToggleFullScreen,
|
onToggleFullScreen,
|
||||||
onWindowHide,
|
onWindowBlur,
|
||||||
onWindowShow,
|
onWindowFocus
|
||||||
|
|
||||||
// TODO: move these out of menu.js -- they don't belong here
|
|
||||||
showOpenSeedFiles,
|
|
||||||
showOpenTorrentAddress,
|
|
||||||
showOpenTorrentFile,
|
|
||||||
toggleFullScreen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
@@ -18,213 +13,67 @@ var electron = require('electron')
|
|||||||
var app = electron.app
|
var app = electron.app
|
||||||
|
|
||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
var log = require('./log')
|
var dialog = require('./dialog')
|
||||||
|
var shell = require('./shell')
|
||||||
var windows = require('./windows')
|
var windows = require('./windows')
|
||||||
|
|
||||||
var appMenu
|
var menu
|
||||||
|
|
||||||
function init () {
|
function init () {
|
||||||
appMenu = electron.Menu.buildFromTemplate(getAppMenuTemplate())
|
menu = electron.Menu.buildFromTemplate(getMenuTemplate())
|
||||||
electron.Menu.setApplicationMenu(appMenu)
|
electron.Menu.setApplicationMenu(menu)
|
||||||
|
|
||||||
if (app.dock) {
|
|
||||||
var dockMenu = electron.Menu.buildFromTemplate(getDockMenuTemplate())
|
|
||||||
app.dock.setMenu(dockMenu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleFullScreen (flag) {
|
|
||||||
log('toggleFullScreen %s', flag)
|
|
||||||
if (windows.main && windows.main.isVisible()) {
|
|
||||||
flag = flag != null ? flag : !windows.main.isFullScreen()
|
|
||||||
if (flag) {
|
|
||||||
// Allows the window to use the full screen in fullscreen mode (OS X).
|
|
||||||
windows.main.setAspectRatio(0)
|
|
||||||
}
|
|
||||||
windows.main.setFullScreen(flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets whether the window should always show on top of other windows
|
|
||||||
function toggleFloatOnTop (flag) {
|
|
||||||
log('toggleFloatOnTop %s', flag)
|
|
||||||
if (windows.main) {
|
|
||||||
flag = flag != null ? flag : !windows.main.isAlwaysOnTop()
|
|
||||||
windows.main.setAlwaysOnTop(flag)
|
|
||||||
getMenuItem('Float on Top').checked = flag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleDevTools () {
|
|
||||||
log('toggleDevTools')
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.toggleDevTools()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showWebTorrentWindow () {
|
|
||||||
log('showWebTorrentWindow')
|
|
||||||
windows.webtorrent.show()
|
|
||||||
windows.webtorrent.webContents.openDevTools({ detach: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
function playPause () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'playPause')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseVolume () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'changeVolume', 0.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreaseVolume () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'changeVolume', -0.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openSubtitles () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'openSubtitles')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function skipForward () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'skip', 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function skipBack () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'skip', -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function increasePlaybackRate () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'changePlaybackRate', 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreasePlaybackRate () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'changePlaybackRate', -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the preferences window
|
|
||||||
function showPreferences () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'preferences')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeBack () {
|
|
||||||
if (windows.main) {
|
|
||||||
windows.main.send('dispatch', 'escapeBack')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWindowShow () {
|
|
||||||
log('onWindowShow')
|
|
||||||
getMenuItem('Full Screen').enabled = true
|
|
||||||
getMenuItem('Float on Top').enabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWindowHide () {
|
|
||||||
log('onWindowHide')
|
|
||||||
getMenuItem('Full Screen').enabled = false
|
|
||||||
getMenuItem('Float on Top').enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPlayerOpen () {
|
|
||||||
log('onPlayerOpen')
|
|
||||||
getMenuItem('Play/Pause').enabled = true
|
|
||||||
getMenuItem('Increase Volume').enabled = true
|
|
||||||
getMenuItem('Decrease Volume').enabled = true
|
|
||||||
getMenuItem('Add Subtitles File...').enabled = true
|
|
||||||
getMenuItem('Step Forward').enabled = true
|
|
||||||
getMenuItem('Step Backward').enabled = true
|
|
||||||
getMenuItem('Increase Speed').enabled = true
|
|
||||||
getMenuItem('Decrease Speed').enabled = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPlayerClose () {
|
function onPlayerClose () {
|
||||||
log('onPlayerClose')
|
|
||||||
getMenuItem('Play/Pause').enabled = false
|
getMenuItem('Play/Pause').enabled = false
|
||||||
getMenuItem('Increase Volume').enabled = false
|
getMenuItem('Increase Volume').enabled = false
|
||||||
getMenuItem('Decrease Volume').enabled = false
|
getMenuItem('Decrease Volume').enabled = false
|
||||||
getMenuItem('Add Subtitles File...').enabled = false
|
|
||||||
getMenuItem('Step Forward').enabled = false
|
getMenuItem('Step Forward').enabled = false
|
||||||
getMenuItem('Step Backward').enabled = false
|
getMenuItem('Step Backward').enabled = false
|
||||||
getMenuItem('Increase Speed').enabled = false
|
getMenuItem('Increase Speed').enabled = false
|
||||||
getMenuItem('Decrease Speed').enabled = false
|
getMenuItem('Decrease Speed').enabled = false
|
||||||
|
getMenuItem('Add Subtitles File...').enabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function onToggleFullScreen (isFullScreen) {
|
function onPlayerOpen () {
|
||||||
isFullScreen = isFullScreen != null ? isFullScreen : windows.main.isFullScreen()
|
getMenuItem('Play/Pause').enabled = true
|
||||||
windows.main.setMenuBarVisibility(!isFullScreen)
|
getMenuItem('Increase Volume').enabled = true
|
||||||
getMenuItem('Full Screen').checked = isFullScreen
|
getMenuItem('Decrease Volume').enabled = true
|
||||||
windows.main.send('fullscreenChanged', isFullScreen)
|
getMenuItem('Step Forward').enabled = true
|
||||||
|
getMenuItem('Step Backward').enabled = true
|
||||||
|
getMenuItem('Increase Speed').enabled = true
|
||||||
|
getMenuItem('Decrease Speed').enabled = true
|
||||||
|
getMenuItem('Add Subtitles File...').enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function onToggleAlwaysOnTop (flag) {
|
||||||
|
getMenuItem('Float on Top').checked = flag
|
||||||
|
}
|
||||||
|
|
||||||
|
function onToggleFullScreen (flag) {
|
||||||
|
getMenuItem('Full Screen').checked = flag
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWindowBlur () {
|
||||||
|
getMenuItem('Full Screen').enabled = false
|
||||||
|
getMenuItem('Float on Top').enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWindowFocus () {
|
||||||
|
getMenuItem('Full Screen').enabled = true
|
||||||
|
getMenuItem('Float on Top').enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMenuItem (label) {
|
function getMenuItem (label) {
|
||||||
for (var i = 0; i < appMenu.items.length; i++) {
|
for (var i = 0; i < menu.items.length; i++) {
|
||||||
var menuItem = appMenu.items[i].submenu.items.find(function (item) {
|
var menuItem = menu.items[i].submenu.items.find(function (item) {
|
||||||
return item.label === label
|
return item.label === label
|
||||||
})
|
})
|
||||||
if (menuItem) return menuItem
|
if (menuItem) return menuItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompts the user for a file, then creates a torrent. Only allows a single file
|
function getMenuTemplate () {
|
||||||
// selection.
|
|
||||||
function showOpenSeedFile () {
|
|
||||||
electron.dialog.showOpenDialog({
|
|
||||||
title: 'Select a file for the torrent file.',
|
|
||||||
properties: [ 'openFile' ]
|
|
||||||
}, function (selectedPaths) {
|
|
||||||
if (!Array.isArray(selectedPaths)) return
|
|
||||||
windows.main.send('dispatch', 'showCreateTorrent', selectedPaths)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompts the user for a file or directory, then creates a torrent. Only allows a single
|
|
||||||
// selection. To create a multi-file torrent, the user must select a directory.
|
|
||||||
function showOpenSeedFiles () {
|
|
||||||
electron.dialog.showOpenDialog({
|
|
||||||
title: 'Select a file or folder for the torrent file.',
|
|
||||||
properties: [ 'openFile', 'openDirectory' ]
|
|
||||||
}, function (selectedPaths) {
|
|
||||||
if (!Array.isArray(selectedPaths)) return
|
|
||||||
windows.main.send('dispatch', 'showCreateTorrent', selectedPaths)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompts the user to choose a torrent file, then adds it to the app
|
|
||||||
function showOpenTorrentFile () {
|
|
||||||
electron.dialog.showOpenDialog(windows.main, {
|
|
||||||
title: 'Select a .torrent file to open.',
|
|
||||||
filters: [{ name: 'Torrent Files', extensions: ['torrent'] }],
|
|
||||||
properties: [ 'openFile', 'multiSelections' ]
|
|
||||||
}, function (selectedPaths) {
|
|
||||||
if (!Array.isArray(selectedPaths)) return
|
|
||||||
selectedPaths.forEach(function (selectedPath) {
|
|
||||||
windows.main.send('dispatch', 'addTorrent', selectedPath)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompts the user for the URL of a torrent file, then downloads and adds it
|
|
||||||
function showOpenTorrentAddress () {
|
|
||||||
windows.main.send('showOpenTorrentAddress')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAppMenuTemplate () {
|
|
||||||
var template = [
|
var template = [
|
||||||
{
|
{
|
||||||
label: 'File',
|
label: 'File',
|
||||||
@@ -234,17 +83,17 @@ function getAppMenuTemplate () {
|
|||||||
? 'Create New Torrent...'
|
? 'Create New Torrent...'
|
||||||
: 'Create New Torrent from Folder...',
|
: 'Create New Torrent from Folder...',
|
||||||
accelerator: 'CmdOrCtrl+N',
|
accelerator: 'CmdOrCtrl+N',
|
||||||
click: showOpenSeedFiles
|
click: () => dialog.openSeedDirectory()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Open Torrent File...',
|
label: 'Open Torrent File...',
|
||||||
accelerator: 'CmdOrCtrl+O',
|
accelerator: 'CmdOrCtrl+O',
|
||||||
click: showOpenTorrentFile
|
click: () => dialog.openTorrentFile()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Open Torrent Address...',
|
label: 'Open Torrent Address...',
|
||||||
accelerator: 'CmdOrCtrl+U',
|
accelerator: 'CmdOrCtrl+U',
|
||||||
click: showOpenTorrentAddress
|
click: () => dialog.openTorrentAddress()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
@@ -287,7 +136,7 @@ function getAppMenuTemplate () {
|
|||||||
{
|
{
|
||||||
label: 'Preferences',
|
label: 'Preferences',
|
||||||
accelerator: 'CmdOrCtrl+,',
|
accelerator: 'CmdOrCtrl+,',
|
||||||
click: () => showPreferences()
|
click: () => windows.main.dispatch('preferences')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -300,12 +149,20 @@ function getAppMenuTemplate () {
|
|||||||
accelerator: process.platform === 'darwin'
|
accelerator: process.platform === 'darwin'
|
||||||
? 'Ctrl+Command+F'
|
? 'Ctrl+Command+F'
|
||||||
: 'F11',
|
: 'F11',
|
||||||
click: () => toggleFullScreen()
|
click: () => windows.toggleFullScreen()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Float on Top',
|
label: 'Float on Top',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
click: () => toggleFloatOnTop()
|
click: () => windows.toggleAlwaysOnTop()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Go Back',
|
||||||
|
accelerator: 'Esc',
|
||||||
|
click: () => windows.main.dispatch('escapeBack')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
@@ -318,24 +175,16 @@ function getAppMenuTemplate () {
|
|||||||
accelerator: process.platform === 'darwin'
|
accelerator: process.platform === 'darwin'
|
||||||
? 'Alt+Command+I'
|
? 'Alt+Command+I'
|
||||||
: 'Ctrl+Shift+I',
|
: 'Ctrl+Shift+I',
|
||||||
click: toggleDevTools
|
click: () => windows.main.toggleDevTools()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Show WebTorrent Process',
|
label: 'Show WebTorrent Process',
|
||||||
accelerator: process.platform === 'darwin'
|
accelerator: process.platform === 'darwin'
|
||||||
? 'Alt+Command+P'
|
? 'Alt+Command+P'
|
||||||
: 'Ctrl+Shift+P',
|
: 'Ctrl+Shift+P',
|
||||||
click: showWebTorrentWindow
|
click: () => windows.webtorrent.toggleDevTools()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'separator'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Go Back',
|
|
||||||
accelerator: 'Esc',
|
|
||||||
click: escapeBack
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -345,7 +194,7 @@ function getAppMenuTemplate () {
|
|||||||
{
|
{
|
||||||
label: 'Play/Pause',
|
label: 'Play/Pause',
|
||||||
accelerator: 'Space',
|
accelerator: 'Space',
|
||||||
click: playPause,
|
click: () => windows.main.dispatch('playPause'),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -354,13 +203,13 @@ function getAppMenuTemplate () {
|
|||||||
{
|
{
|
||||||
label: 'Increase Volume',
|
label: 'Increase Volume',
|
||||||
accelerator: 'CmdOrCtrl+Up',
|
accelerator: 'CmdOrCtrl+Up',
|
||||||
click: increaseVolume,
|
click: () => windows.main.dispatch('changeVolume', 0.1),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Decrease Volume',
|
label: 'Decrease Volume',
|
||||||
accelerator: 'CmdOrCtrl+Down',
|
accelerator: 'CmdOrCtrl+Down',
|
||||||
click: decreaseVolume,
|
click: () => windows.main.dispatch('changeVolume', -0.1),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -369,13 +218,13 @@ function getAppMenuTemplate () {
|
|||||||
{
|
{
|
||||||
label: 'Step Forward',
|
label: 'Step Forward',
|
||||||
accelerator: 'CmdOrCtrl+Alt+Right',
|
accelerator: 'CmdOrCtrl+Alt+Right',
|
||||||
click: skipForward,
|
click: () => windows.main.dispatch('skip', 1),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Step Backward',
|
label: 'Step Backward',
|
||||||
accelerator: 'CmdOrCtrl+Alt+Left',
|
accelerator: 'CmdOrCtrl+Alt+Left',
|
||||||
click: skipBack,
|
click: () => windows.main.dispatch('skip', -1),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -384,13 +233,13 @@ function getAppMenuTemplate () {
|
|||||||
{
|
{
|
||||||
label: 'Increase Speed',
|
label: 'Increase Speed',
|
||||||
accelerator: 'CmdOrCtrl+=',
|
accelerator: 'CmdOrCtrl+=',
|
||||||
click: increasePlaybackRate,
|
click: () => windows.main.dispatch('changePlaybackRate', 1),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Decrease Speed',
|
label: 'Decrease Speed',
|
||||||
accelerator: 'CmdOrCtrl+-',
|
accelerator: 'CmdOrCtrl+-',
|
||||||
click: decreasePlaybackRate,
|
click: () => windows.main.dispatch('changePlaybackRate', -1),
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -398,7 +247,7 @@ function getAppMenuTemplate () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Add Subtitles File...',
|
label: 'Add Subtitles File...',
|
||||||
click: openSubtitles,
|
click: () => windows.main.dispatch('openSubtitles'),
|
||||||
enabled: false
|
enabled: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -409,18 +258,18 @@ function getAppMenuTemplate () {
|
|||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Learn more about ' + config.APP_NAME,
|
label: 'Learn more about ' + config.APP_NAME,
|
||||||
click: () => electron.shell.openExternal(config.HOME_PAGE_URL)
|
click: () => shell.openExternal(config.HOME_PAGE_URL)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Contribute on GitHub',
|
label: 'Contribute on GitHub',
|
||||||
click: () => electron.shell.openExternal(config.GITHUB_URL)
|
click: () => shell.openExternal(config.GITHUB_URL)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Report an Issue...',
|
label: 'Report an Issue...',
|
||||||
click: () => electron.shell.openExternal(config.GITHUB_URL_ISSUES)
|
click: () => shell.openExternal(config.GITHUB_URL_ISSUES)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -441,7 +290,7 @@ function getAppMenuTemplate () {
|
|||||||
{
|
{
|
||||||
label: 'Preferences',
|
label: 'Preferences',
|
||||||
accelerator: 'Cmd+,',
|
accelerator: 'Cmd+,',
|
||||||
click: () => showPreferences()
|
click: () => windows.main.dispatch('preferences')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
@@ -500,12 +349,13 @@ function getAppMenuTemplate () {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// In Linux and Windows it is not possible to open both folders and files
|
// On Windows and Linux, open dialogs do not support selecting both files and
|
||||||
|
// folders and files, so add an extra menu item so there is one for each type.
|
||||||
if (process.platform === 'linux' || process.platform === 'win32') {
|
if (process.platform === 'linux' || process.platform === 'win32') {
|
||||||
// File menu (Windows, Linux)
|
// File menu (Windows, Linux)
|
||||||
template[0].submenu.unshift({
|
template[0].submenu.unshift({
|
||||||
label: 'Create New Torrent from File...',
|
label: 'Create New Torrent from File...',
|
||||||
click: showOpenSeedFile
|
click: () => dialog.openSeedFile()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Help menu (Windows, Linux)
|
// Help menu (Windows, Linux)
|
||||||
@@ -515,12 +365,12 @@ function getAppMenuTemplate () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'About ' + config.APP_NAME,
|
label: 'About ' + config.APP_NAME,
|
||||||
click: windows.createAboutWindow
|
click: () => windows.about.init()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Add "File > Quit" menu item so Linux distros where the system tray icon is missing
|
// Add "File > Quit" menu item so Linux distros where the system tray icon is
|
||||||
// will have a way to quit the app.
|
// missing will have a way to quit the app.
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
// File menu (Linux)
|
// File menu (Linux)
|
||||||
template[0].submenu.push({
|
template[0].submenu.push({
|
||||||
@@ -531,23 +381,3 @@ function getAppMenuTemplate () {
|
|||||||
|
|
||||||
return template
|
return template
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDockMenuTemplate () {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: 'Create New Torrent...',
|
|
||||||
accelerator: 'CmdOrCtrl+N',
|
|
||||||
click: showOpenSeedFiles
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Open Torrent File...',
|
|
||||||
accelerator: 'CmdOrCtrl+O',
|
|
||||||
click: showOpenTorrentFile
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Open Torrent Address...',
|
|
||||||
accelerator: 'CmdOrCtrl+U',
|
|
||||||
click: showOpenTorrentAddress
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
30
main/power-save-blocker.js
Normal file
30
main/power-save-blocker.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
module.exports = {
|
||||||
|
start,
|
||||||
|
stop
|
||||||
|
}
|
||||||
|
|
||||||
|
var electron = require('electron')
|
||||||
|
var log = require('./log')
|
||||||
|
|
||||||
|
var blockId = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the system from entering low-power (sleep) mode or turning off the
|
||||||
|
* display.
|
||||||
|
*/
|
||||||
|
function start () {
|
||||||
|
stop() // Stop the previous power saver block, if one exists.
|
||||||
|
blockId = electron.powerSaveBlocker.start('prevent-display-sleep')
|
||||||
|
log(`powerSaveBlocker.start: ${blockId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop blocking the system from entering low-power mode.
|
||||||
|
*/
|
||||||
|
function stop () {
|
||||||
|
if (!electron.powerSaveBlocker.isStarted(blockId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
electron.powerSaveBlocker.stop(blockId)
|
||||||
|
log(`powerSaveBlocker.stop: ${blockId}`)
|
||||||
|
}
|
||||||
32
main/shell.js
Normal file
32
main/shell.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
module.exports = {
|
||||||
|
openExternal,
|
||||||
|
openItem,
|
||||||
|
showItemInFolder
|
||||||
|
}
|
||||||
|
|
||||||
|
var electron = require('electron')
|
||||||
|
var log = require('./log')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the given external protocol URL in the desktop’s default manner.
|
||||||
|
*/
|
||||||
|
function openExternal (url) {
|
||||||
|
log(`openExternal: ${url}`)
|
||||||
|
electron.shell.openExternal(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the given file in the desktop’s default manner.
|
||||||
|
*/
|
||||||
|
function openItem (path) {
|
||||||
|
log(`openItem: ${path}`)
|
||||||
|
electron.shell.openItem(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the given file in a file manager. If possible, select the file.
|
||||||
|
*/
|
||||||
|
function showItemInFolder (path) {
|
||||||
|
log(`showItemInFolder: ${path}`)
|
||||||
|
electron.shell.showItemInFolder(path)
|
||||||
|
}
|
||||||
@@ -7,13 +7,14 @@ var electron = require('electron')
|
|||||||
var windows = require('./windows')
|
var windows = require('./windows')
|
||||||
|
|
||||||
function onPlayerOpen () {
|
function onPlayerOpen () {
|
||||||
// Register special "media key" for play/pause, available on some keyboards
|
// Register play/pause media key, available on some keyboards.
|
||||||
electron.globalShortcut.register(
|
electron.globalShortcut.register(
|
||||||
'MediaPlayPause',
|
'MediaPlayPause',
|
||||||
() => windows.main.send('dispatch', 'playPause')
|
() => windows.main.dispatch('playPause')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPlayerClose () {
|
function onPlayerClose () {
|
||||||
|
// Return the media key to the OS, so other apps can use it.
|
||||||
electron.globalShortcut.unregister('MediaPlayPause')
|
electron.globalShortcut.unregister('MediaPlayPause')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ var app = electron.app
|
|||||||
|
|
||||||
var handlers = require('./handlers')
|
var handlers = require('./handlers')
|
||||||
|
|
||||||
var exeName = path.basename(process.execPath)
|
var EXE_NAME = path.basename(process.execPath)
|
||||||
var updateDotExe = path.join(process.execPath, '..', '..', 'Update.exe')
|
var UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe')
|
||||||
|
|
||||||
function handleEvent (cmd) {
|
function handleEvent (cmd) {
|
||||||
if (cmd === '--squirrel-install') {
|
if (cmd === '--squirrel-install') {
|
||||||
@@ -102,12 +102,12 @@ function spawn (command, args, cb) {
|
|||||||
// Spawn Squirrel's Update.exe with the given arguments and invoke the callback when the
|
// Spawn Squirrel's Update.exe with the given arguments and invoke the callback when the
|
||||||
// command completes.
|
// command completes.
|
||||||
function spawnUpdate (args, cb) {
|
function spawnUpdate (args, cb) {
|
||||||
spawn(updateDotExe, args, cb)
|
spawn(UPDATE_EXE, args, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create desktop/start menu shortcuts using the Squirrel Update.exe command line API
|
// Create desktop/start menu shortcuts using the Squirrel Update.exe command line API
|
||||||
function createShortcuts (cb) {
|
function createShortcuts (cb) {
|
||||||
spawnUpdate(['--createShortcut', exeName], cb)
|
spawnUpdate(['--createShortcut', EXE_NAME], cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update desktop/start menu shortcuts using the Squirrel Update.exe command line API
|
// Update desktop/start menu shortcuts using the Squirrel Update.exe command line API
|
||||||
@@ -135,5 +135,5 @@ function updateShortcuts (cb) {
|
|||||||
|
|
||||||
// Remove desktop/start menu shortcuts using the Squirrel Update.exe command line API
|
// Remove desktop/start menu shortcuts using the Squirrel Update.exe command line API
|
||||||
function removeShortcuts (cb) {
|
function removeShortcuts (cb) {
|
||||||
spawnUpdate(['--removeShortcut', exeName], cb)
|
spawnUpdate(['--removeShortcut', EXE_NAME], cb)
|
||||||
}
|
}
|
||||||
|
|||||||
114
main/tray.js
114
main/tray.js
@@ -1,9 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
hasTray,
|
||||||
init,
|
init,
|
||||||
hasTray
|
onWindowBlur,
|
||||||
|
onWindowFocus
|
||||||
}
|
}
|
||||||
|
|
||||||
var cp = require('child_process')
|
|
||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
|
|
||||||
var app = electron.app
|
var app = electron.app
|
||||||
@@ -11,41 +12,51 @@ var app = electron.app
|
|||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
var windows = require('./windows')
|
var windows = require('./windows')
|
||||||
|
|
||||||
var trayIcon
|
var tray
|
||||||
|
|
||||||
function init () {
|
function init () {
|
||||||
// OS X has no tray icon
|
|
||||||
if (process.platform === 'darwin') return
|
|
||||||
|
|
||||||
// On Linux, asynchronously check for libappindicator1
|
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
checkLinuxTraySupport(function (supportsTray) {
|
initLinux()
|
||||||
if (supportsTray) createTrayIcon()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
if (process.platform === 'win32') {
|
||||||
// Windows always supports minimize-to-tray
|
initWin32()
|
||||||
if (process.platform === 'win32') createTrayIcon()
|
}
|
||||||
|
// OS X apps generally do not have menu bar icons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there a tray icon is active.
|
||||||
|
*/
|
||||||
function hasTray () {
|
function hasTray () {
|
||||||
return !!trayIcon
|
return !!tray
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTrayIcon () {
|
function onWindowBlur () {
|
||||||
trayIcon = new electron.Tray(getIconPath())
|
if (!tray) return
|
||||||
|
|
||||||
// On Windows, left click to open the app, right click for context menu
|
|
||||||
// On Linux, any click (right or left) opens the context menu
|
|
||||||
trayIcon.on('click', showApp)
|
|
||||||
|
|
||||||
// Show the tray context menu, and keep the available commands up to date
|
|
||||||
updateTrayMenu()
|
updateTrayMenu()
|
||||||
windows.main.on('show', updateTrayMenu)
|
|
||||||
windows.main.on('hide', updateTrayMenu)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onWindowFocus () {
|
||||||
|
if (!tray) return
|
||||||
|
updateTrayMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
function initLinux () {
|
||||||
|
checkLinuxTraySupport(function (supportsTray) {
|
||||||
|
if (supportsTray) createTray()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function initWin32 () {
|
||||||
|
createTray()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for libappindicator1 support before creating tray icon
|
||||||
|
*/
|
||||||
function checkLinuxTraySupport (cb) {
|
function checkLinuxTraySupport (cb) {
|
||||||
|
var cp = require('child_process')
|
||||||
|
|
||||||
// Check that we're on Ubuntu (or another debian system) and that we have
|
// Check that we're on Ubuntu (or another debian system) and that we have
|
||||||
// libappindicator1. If WebTorrent was installed from the deb file, we should
|
// libappindicator1. If WebTorrent was installed from the deb file, we should
|
||||||
// always have it. If it was installed from the zip file, we might not.
|
// always have it. If it was installed from the zip file, we might not.
|
||||||
@@ -57,27 +68,44 @@ function checkLinuxTraySupport (cb) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTray () {
|
||||||
|
tray = new electron.Tray(getIconPath())
|
||||||
|
|
||||||
|
// On Windows, left click opens the app, right click opens the context menu.
|
||||||
|
// On Linux, any click (left or right) opens the context menu.
|
||||||
|
tray.on('click', () => windows.main.show())
|
||||||
|
|
||||||
|
// Show the tray context menu, and keep the available commands up to date
|
||||||
|
updateTrayMenu()
|
||||||
|
}
|
||||||
|
|
||||||
function updateTrayMenu () {
|
function updateTrayMenu () {
|
||||||
var showHideMenuItem
|
var contextMenu = electron.Menu.buildFromTemplate(getMenuTemplate)
|
||||||
if (windows.main.isVisible()) {
|
tray.setContextMenu(contextMenu)
|
||||||
showHideMenuItem = { label: 'Hide to tray', click: hideApp }
|
}
|
||||||
} else {
|
|
||||||
showHideMenuItem = { label: 'Show', click: showApp }
|
function getMenuTemplate () {
|
||||||
|
return [
|
||||||
|
getToggleItem(),
|
||||||
|
{
|
||||||
|
label: 'Quit',
|
||||||
|
click: () => app.quit()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
function getToggleItem () {
|
||||||
|
if (windows.main.win.isVisible()) {
|
||||||
|
return {
|
||||||
|
label: 'Hide to tray',
|
||||||
|
click: () => windows.main.hide()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
label: 'Show WebTorrent',
|
||||||
|
click: () => windows.main.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var contextMenu = electron.Menu.buildFromTemplate([
|
|
||||||
showHideMenuItem,
|
|
||||||
{ label: 'Quit', click: () => app.quit() }
|
|
||||||
])
|
|
||||||
trayIcon.setContextMenu(contextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
function showApp () {
|
|
||||||
windows.main.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideApp () {
|
|
||||||
windows.main.hide()
|
|
||||||
windows.main.send('dispatch', 'backToList')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIconPath () {
|
function getIconPath () {
|
||||||
|
|||||||
@@ -21,27 +21,27 @@ function init () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Electron auto-updater does not support Linux yet, so manually check for updates and
|
// The Electron auto-updater does not support Linux yet, so manually check for
|
||||||
// `show the user a modal notification.
|
// updates and show the user a modal notification.
|
||||||
function initLinux () {
|
function initLinux () {
|
||||||
get.concat(AUTO_UPDATE_URL, onResponse)
|
get.concat(AUTO_UPDATE_URL, onResponse)
|
||||||
|
}
|
||||||
|
|
||||||
function onResponse (err, res, data) {
|
function onResponse (err, res, data) {
|
||||||
if (err) return log(`Update error: ${err.message}`)
|
if (err) return log(`Update error: ${err.message}`)
|
||||||
if (res.statusCode === 200) {
|
if (res.statusCode === 200) {
|
||||||
// Update available
|
// Update available
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data)
|
data = JSON.parse(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return log(`Update error: Invalid JSON response: ${err.message}`)
|
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}`)
|
|
||||||
}
|
}
|
||||||
|
windows.main.dispatch('updateAvailable', data.version)
|
||||||
|
} else if (res.statusCode === 204) {
|
||||||
|
// No update available
|
||||||
|
} else {
|
||||||
|
// Unexpected status code
|
||||||
|
log(`Update error: Unexpected status code: ${res.statusCode}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
145
main/windows.js
145
main/windows.js
@@ -1,145 +0,0 @@
|
|||||||
var windows = module.exports = {
|
|
||||||
about: null,
|
|
||||||
main: null,
|
|
||||||
createAboutWindow,
|
|
||||||
createWebTorrentHiddenWindow,
|
|
||||||
createMainWindow,
|
|
||||||
focusWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
var electron = require('electron')
|
|
||||||
|
|
||||||
var app = electron.app
|
|
||||||
|
|
||||||
var config = require('../config')
|
|
||||||
var menu = require('./menu')
|
|
||||||
var tray = require('./tray')
|
|
||||||
|
|
||||||
function createAboutWindow () {
|
|
||||||
if (windows.about) {
|
|
||||||
return focusWindow(windows.about)
|
|
||||||
}
|
|
||||||
var win = windows.about = new electron.BrowserWindow({
|
|
||||||
backgroundColor: '#ECECEC',
|
|
||||||
show: false,
|
|
||||||
center: true,
|
|
||||||
resizable: false,
|
|
||||||
icon: getIconPath(),
|
|
||||||
title: process.platform !== 'darwin'
|
|
||||||
? 'About ' + config.APP_WINDOW_TITLE
|
|
||||||
: '',
|
|
||||||
useContentSize: true, // Specify web page size without OS chrome
|
|
||||||
width: 300,
|
|
||||||
height: 170,
|
|
||||||
minimizable: false,
|
|
||||||
maximizable: false,
|
|
||||||
fullscreen: false,
|
|
||||||
skipTaskbar: true
|
|
||||||
})
|
|
||||||
win.loadURL(config.WINDOW_ABOUT)
|
|
||||||
|
|
||||||
// No window menu
|
|
||||||
win.setMenu(null)
|
|
||||||
|
|
||||||
win.webContents.on('did-finish-load', function () {
|
|
||||||
win.show()
|
|
||||||
})
|
|
||||||
|
|
||||||
win.once('closed', function () {
|
|
||||||
windows.about = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWebTorrentHiddenWindow () {
|
|
||||||
var win = windows.webtorrent = new electron.BrowserWindow({
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
show: false,
|
|
||||||
center: true,
|
|
||||||
title: 'webtorrent-hidden-window',
|
|
||||||
useContentSize: true,
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
minimizable: false,
|
|
||||||
maximizable: false,
|
|
||||||
resizable: false,
|
|
||||||
fullscreenable: false,
|
|
||||||
fullscreen: false,
|
|
||||||
skipTaskbar: true
|
|
||||||
})
|
|
||||||
win.loadURL(config.WINDOW_WEBTORRENT)
|
|
||||||
|
|
||||||
// Prevent killing the WebTorrent process
|
|
||||||
win.on('close', function (e) {
|
|
||||||
if (!app.isQuitting) {
|
|
||||||
e.preventDefault()
|
|
||||||
win.hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
win.once('closed', function () {
|
|
||||||
windows.webtorrent = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var HEADER_HEIGHT = 37
|
|
||||||
var TORRENT_HEIGHT = 100
|
|
||||||
|
|
||||||
function createMainWindow () {
|
|
||||||
if (windows.main) {
|
|
||||||
return focusWindow(windows.main)
|
|
||||||
}
|
|
||||||
var win = windows.main = new electron.BrowserWindow({
|
|
||||||
backgroundColor: '#1E1E1E',
|
|
||||||
darkTheme: true, // Forces dark theme (GTK+3)
|
|
||||||
icon: getIconPath(), // Window icon (Windows, Linux)
|
|
||||||
minWidth: config.WINDOW_MIN_WIDTH,
|
|
||||||
minHeight: config.WINDOW_MIN_HEIGHT,
|
|
||||||
show: false, // Hide window until renderer sends 'ipcReady' event
|
|
||||||
title: config.APP_WINDOW_TITLE,
|
|
||||||
titleBarStyle: 'hidden-inset', // Hide OS chrome, except traffic light buttons (OS X)
|
|
||||||
useContentSize: true, // Specify web page size without OS chrome
|
|
||||||
width: 500,
|
|
||||||
height: HEADER_HEIGHT + (TORRENT_HEIGHT * 6) // header height + 5 torrents
|
|
||||||
})
|
|
||||||
win.loadURL(config.WINDOW_MAIN)
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
win.setSheetOffset(HEADER_HEIGHT)
|
|
||||||
}
|
|
||||||
|
|
||||||
win.webContents.on('dom-ready', function () {
|
|
||||||
menu.onToggleFullScreen()
|
|
||||||
})
|
|
||||||
|
|
||||||
win.on('blur', menu.onWindowHide)
|
|
||||||
win.on('focus', menu.onWindowShow)
|
|
||||||
|
|
||||||
win.on('enter-full-screen', () => menu.onToggleFullScreen(true))
|
|
||||||
win.on('leave-full-screen', () => menu.onToggleFullScreen(false))
|
|
||||||
|
|
||||||
win.on('close', function (e) {
|
|
||||||
if (process.platform !== 'darwin' && !tray.hasTray()) {
|
|
||||||
app.quit()
|
|
||||||
} else if (!app.isQuitting) {
|
|
||||||
e.preventDefault()
|
|
||||||
win.hide()
|
|
||||||
win.send('dispatch', 'backToList')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
win.once('closed', function () {
|
|
||||||
windows.main = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function focusWindow (win) {
|
|
||||||
if (win.isMinimized()) {
|
|
||||||
win.restore()
|
|
||||||
}
|
|
||||||
win.show() // shows and gives focus
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIconPath () {
|
|
||||||
return process.platform === 'win32'
|
|
||||||
? config.APP_ICON + '.ico'
|
|
||||||
: config.APP_ICON + '.png'
|
|
||||||
}
|
|
||||||
48
main/windows/about.js
Normal file
48
main/windows/about.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
var about = module.exports = {
|
||||||
|
init,
|
||||||
|
win: null
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = require('../../config')
|
||||||
|
var electron = require('electron')
|
||||||
|
|
||||||
|
function init () {
|
||||||
|
if (about.win) {
|
||||||
|
return about.win.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
var win = about.win = new electron.BrowserWindow({
|
||||||
|
backgroundColor: '#ECECEC',
|
||||||
|
center: true,
|
||||||
|
fullscreen: false,
|
||||||
|
height: 170,
|
||||||
|
icon: getIconPath(),
|
||||||
|
maximizable: false,
|
||||||
|
minimizable: false,
|
||||||
|
resizable: false,
|
||||||
|
show: false,
|
||||||
|
skipTaskbar: true,
|
||||||
|
useContentSize: true,
|
||||||
|
width: 300
|
||||||
|
})
|
||||||
|
|
||||||
|
win.loadURL(config.WINDOW_ABOUT)
|
||||||
|
|
||||||
|
// No menu on the About window
|
||||||
|
win.setMenu(null)
|
||||||
|
|
||||||
|
// TODO: can this be removed?
|
||||||
|
win.webContents.on('did-finish-load', function () {
|
||||||
|
win.show()
|
||||||
|
})
|
||||||
|
|
||||||
|
win.once('closed', function () {
|
||||||
|
about.win = null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIconPath () {
|
||||||
|
return process.platform === 'win32'
|
||||||
|
? config.APP_ICON + '.ico'
|
||||||
|
: config.APP_ICON + '.png'
|
||||||
|
}
|
||||||
3
main/windows/index.js
Normal file
3
main/windows/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
exports.about = require('./about')
|
||||||
|
exports.main = require('./main')
|
||||||
|
exports.webtorrent = require('./webtorrent')
|
||||||
215
main/windows/main.js
Normal file
215
main/windows/main.js
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
var main = module.exports = {
|
||||||
|
dispatch,
|
||||||
|
hide,
|
||||||
|
init,
|
||||||
|
send,
|
||||||
|
setAspectRatio,
|
||||||
|
setBounds,
|
||||||
|
setProgress,
|
||||||
|
setTitle,
|
||||||
|
show,
|
||||||
|
toggleAlwaysOnTop,
|
||||||
|
toggleDevTools,
|
||||||
|
toggleFullScreen,
|
||||||
|
win: null
|
||||||
|
}
|
||||||
|
|
||||||
|
var electron = require('electron')
|
||||||
|
|
||||||
|
var app = electron.app
|
||||||
|
|
||||||
|
var config = require('../../config')
|
||||||
|
var log = require('../log')
|
||||||
|
var menu = require('../menu')
|
||||||
|
var tray = require('../tray')
|
||||||
|
|
||||||
|
var HEADER_HEIGHT = 37
|
||||||
|
var TORRENT_HEIGHT = 100
|
||||||
|
|
||||||
|
function init () {
|
||||||
|
if (main.win) {
|
||||||
|
return main.win.show()
|
||||||
|
}
|
||||||
|
var win = main.win = new electron.BrowserWindow({
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
darkTheme: true, // Forces dark theme (GTK+3)
|
||||||
|
icon: getIconPath(), // Window icon (Windows, Linux)
|
||||||
|
minWidth: config.WINDOW_MIN_WIDTH,
|
||||||
|
minHeight: config.WINDOW_MIN_HEIGHT,
|
||||||
|
show: false, // Hide window until renderer sends 'ipcReady'
|
||||||
|
title: config.APP_WINDOW_TITLE,
|
||||||
|
titleBarStyle: 'hidden-inset', // Hide title bar (OS X)
|
||||||
|
useContentSize: true, // Specify web page size without OS chrome
|
||||||
|
width: 500,
|
||||||
|
height: HEADER_HEIGHT + (TORRENT_HEIGHT * 6) // header height + 5 torrents
|
||||||
|
})
|
||||||
|
|
||||||
|
win.loadURL(config.WINDOW_MAIN)
|
||||||
|
|
||||||
|
if (win.setSheetOffset) win.setSheetOffset(HEADER_HEIGHT)
|
||||||
|
|
||||||
|
win.webContents.on('dom-ready', function () {
|
||||||
|
menu.onToggleFullScreen(main.win.isFullScreen())
|
||||||
|
})
|
||||||
|
|
||||||
|
win.on('blur', function () {
|
||||||
|
menu.onWindowBlur()
|
||||||
|
tray.onWindowBlur()
|
||||||
|
})
|
||||||
|
|
||||||
|
win.on('focus', function () {
|
||||||
|
menu.onWindowFocus()
|
||||||
|
tray.onWindowFocus()
|
||||||
|
})
|
||||||
|
|
||||||
|
win.on('enter-full-screen', function () {
|
||||||
|
menu.onToggleFullScreen(true)
|
||||||
|
send('fullscreenChanged', true)
|
||||||
|
win.setMenuBarVisibility(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
win.on('leave-full-screen', function () {
|
||||||
|
menu.onToggleFullScreen(false)
|
||||||
|
send('fullscreenChanged', false)
|
||||||
|
win.setMenuBarVisibility(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
win.on('close', function (e) {
|
||||||
|
if (process.platform !== 'darwin' && !tray.hasTray()) {
|
||||||
|
app.quit()
|
||||||
|
} else if (!app.isQuitting) {
|
||||||
|
e.preventDefault()
|
||||||
|
win.hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatch (...args) {
|
||||||
|
send('dispatch', ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide () {
|
||||||
|
if (!main.win) return
|
||||||
|
main.win.send('dispatch', 'backToList')
|
||||||
|
main.win.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
function send (...args) {
|
||||||
|
if (!main.win) return
|
||||||
|
main.win.send(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforce window aspect ratio. Remove with 0. (OS X)
|
||||||
|
*/
|
||||||
|
function setAspectRatio (aspectRatio) {
|
||||||
|
if (!main.win) return
|
||||||
|
main.win.setAspectRatio(aspectRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the size of the window.
|
||||||
|
* TODO: Clean this up? Seems overly complicated.
|
||||||
|
*/
|
||||||
|
function setBounds (bounds, maximize) {
|
||||||
|
// Do nothing in fullscreen
|
||||||
|
if (!main.win || main.win.isFullScreen()) {
|
||||||
|
log('setBounds: not setting bounds because we\'re in full screen')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximize or minimize, if the second argument is present
|
||||||
|
var willBeMaximized
|
||||||
|
if (maximize === true) {
|
||||||
|
if (!main.win.isMaximized()) {
|
||||||
|
log('setBounds: maximizing')
|
||||||
|
main.win.maximize()
|
||||||
|
}
|
||||||
|
willBeMaximized = true
|
||||||
|
} else if (maximize === false) {
|
||||||
|
if (main.win.isMaximized()) {
|
||||||
|
log('setBounds: unmaximizing')
|
||||||
|
main.win.unmaximize()
|
||||||
|
}
|
||||||
|
willBeMaximized = false
|
||||||
|
} else {
|
||||||
|
willBeMaximized = main.win.isMaximized()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assuming we're not maximized or maximizing, set the window size
|
||||||
|
if (!willBeMaximized) {
|
||||||
|
log('setBounds: setting bounds to ' + JSON.stringify(bounds))
|
||||||
|
if (bounds.x === null && bounds.y === null) {
|
||||||
|
// X and Y not specified? By default, center on current screen
|
||||||
|
var scr = electron.screen.getDisplayMatching(main.win.getBounds())
|
||||||
|
bounds.x = Math.round(scr.bounds.x + scr.bounds.width / 2 - bounds.width / 2)
|
||||||
|
bounds.y = Math.round(scr.bounds.y + scr.bounds.height / 2 - bounds.height / 2)
|
||||||
|
log('setBounds: centered to ' + JSON.stringify(bounds))
|
||||||
|
}
|
||||||
|
main.win.setBounds(bounds, true)
|
||||||
|
} else {
|
||||||
|
log('setBounds: not setting bounds because of window maximization')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set progress bar to [0, 1]. Indeterminate when > 1. Remove with < 0.
|
||||||
|
*/
|
||||||
|
function setProgress (progress) {
|
||||||
|
if (!main.win) return
|
||||||
|
main.win.setProgressBar(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTitle (title) {
|
||||||
|
if (!main.win) return
|
||||||
|
main.win.setTitle(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
function show () {
|
||||||
|
if (!main.win) return
|
||||||
|
main.win.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets whether the window should always show on top of other windows
|
||||||
|
function toggleAlwaysOnTop (flag) {
|
||||||
|
if (!main.win) return
|
||||||
|
if (flag == null) {
|
||||||
|
flag = !main.isAlwaysOnTop()
|
||||||
|
}
|
||||||
|
log(`toggleAlwaysOnTop ${flag}`)
|
||||||
|
main.setAlwaysOnTop(flag)
|
||||||
|
menu.onToggleAlwaysOnTop(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDevTools () {
|
||||||
|
if (!main.win) return
|
||||||
|
log('toggleDevTools')
|
||||||
|
if (main.win.webContents.isDevToolsOpened()) {
|
||||||
|
main.win.webContents.closeDevTools()
|
||||||
|
} else {
|
||||||
|
main.win.webContents.openDevTools({ detach: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFullScreen (flag) {
|
||||||
|
if (!main.win || !main.win.isVisible()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag == null) flag = !main.win.isFullScreen()
|
||||||
|
|
||||||
|
log(`toggleFullScreen ${flag}`)
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
// Fullscreen and aspect ratio do not play well together. (OS X)
|
||||||
|
main.win.setAspectRatio(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
main.win.setFullScreen(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIconPath () {
|
||||||
|
return process.platform === 'win32'
|
||||||
|
? config.APP_ICON + '.ico'
|
||||||
|
: config.APP_ICON + '.png'
|
||||||
|
}
|
||||||
62
main/windows/webtorrent.js
Normal file
62
main/windows/webtorrent.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
var webtorrent = module.exports = {
|
||||||
|
init,
|
||||||
|
send,
|
||||||
|
show,
|
||||||
|
toggleDevTools,
|
||||||
|
win: null
|
||||||
|
}
|
||||||
|
|
||||||
|
var electron = require('electron')
|
||||||
|
|
||||||
|
var config = require('../../config')
|
||||||
|
var log = require('../log')
|
||||||
|
|
||||||
|
function init () {
|
||||||
|
var win = webtorrent.win = new electron.BrowserWindow({
|
||||||
|
backgroundColor: '#1E1E1E',
|
||||||
|
center: true,
|
||||||
|
fullscreen: false,
|
||||||
|
fullscreenable: false,
|
||||||
|
height: 150,
|
||||||
|
maximizable: false,
|
||||||
|
minimizable: false,
|
||||||
|
resizable: false,
|
||||||
|
show: false,
|
||||||
|
skipTaskbar: true,
|
||||||
|
title: 'webtorrent-hidden-window',
|
||||||
|
useContentSize: true,
|
||||||
|
width: 150
|
||||||
|
})
|
||||||
|
|
||||||
|
win.loadURL(config.WINDOW_WEBTORRENT)
|
||||||
|
|
||||||
|
// Prevent killing the WebTorrent process
|
||||||
|
win.on('close', function (e) {
|
||||||
|
if (electron.app.isQuitting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
win.hide()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function show () {
|
||||||
|
if (!webtorrent.win) return
|
||||||
|
webtorrent.win.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
function send (...args) {
|
||||||
|
if (!webtorrent.win) return
|
||||||
|
webtorrent.win.send(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDevTools () {
|
||||||
|
if (!webtorrent.win) return
|
||||||
|
log('toggleDevTools')
|
||||||
|
if (webtorrent.win.webContents.isDevToolsOpened()) {
|
||||||
|
webtorrent.win.webContents.closeDevTools()
|
||||||
|
webtorrent.win.hide()
|
||||||
|
} else {
|
||||||
|
webtorrent.win.webContents.openDevTools({ detach: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +1,39 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
setDispatch,
|
|
||||||
dispatch,
|
dispatch,
|
||||||
dispatcher
|
dispatcher,
|
||||||
|
setDispatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memoize most of our event handlers, which are functions in the form
|
var dispatchers = {}
|
||||||
// () => dispatch(<args>)
|
var _dispatch = function () {}
|
||||||
// ... this prevents virtual-dom from updating every listener on every update()
|
|
||||||
var _dispatchers = {}
|
|
||||||
var _dispatch = () => {}
|
|
||||||
|
|
||||||
function setDispatch (dispatch) {
|
function setDispatch (dispatch) {
|
||||||
_dispatch = dispatch
|
_dispatch = dispatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a _memoized event handler that calls dispatch()
|
function dispatch (...args) {
|
||||||
// All args must be JSON-able
|
_dispatch(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most DOM event handlers are trivial functions like `() => dispatch(<args>)`.
|
||||||
|
// For these, `dispatcher(<args>)` is preferred because it memoizes the handler
|
||||||
|
// function. This prevents virtual-dom from updating the listener functions on
|
||||||
|
// each update().
|
||||||
function dispatcher (...args) {
|
function dispatcher (...args) {
|
||||||
var json = JSON.stringify(args)
|
var str = JSON.stringify(args)
|
||||||
var handler = _dispatchers[json]
|
var handler = dispatchers[str]
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
handler = _dispatchers[json] = (e) => {
|
handler = dispatchers[str] = function (e) {
|
||||||
// Don't click on whatever is below the button
|
// Do not propagate click to elements below the button
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
// Don't regisiter clicks on disabled buttons
|
|
||||||
if (e.currentTarget.classList.contains('disabled')) return
|
if (e.currentTarget.classList.contains('disabled')) {
|
||||||
_dispatch.apply(null, args)
|
// Ignore clicks on disabled elements
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatch (...args) {
|
|
||||||
_dispatch.apply(null, args)
|
|
||||||
}
|
|
||||||
|
|||||||
5
renderer/lib/hx.js
Normal file
5
renderer/lib/hx.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
var h = require('virtual-dom/h')
|
||||||
|
var hyperx = require('hyperx')
|
||||||
|
var hx = hyperx(h)
|
||||||
|
|
||||||
|
module.exports = hx
|
||||||
@@ -3,8 +3,8 @@ var path = require('path')
|
|||||||
|
|
||||||
var remote = electron.remote
|
var remote = electron.remote
|
||||||
|
|
||||||
var config = require('../config')
|
var config = require('../../config')
|
||||||
var LocationHistory = require('./lib/location-history')
|
var LocationHistory = require('./location-history')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getInitialState,
|
getInitialState,
|
||||||
@@ -280,36 +280,36 @@ table {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page {
|
.create-torrent {
|
||||||
padding: 10px 25px;
|
padding: 10px 25px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page .torrent-attribute {
|
.create-torrent .torrent-attribute {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page .torrent-attribute>* {
|
.create-torrent .torrent-attribute>* {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page .torrent-attribute label {
|
.create-torrent .torrent-attribute label {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page .torrent-attribute>div {
|
.create-torrent .torrent-attribute>div {
|
||||||
width: calc(100% - 90px);
|
width: calc(100% - 90px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page .torrent-attribute div {
|
.create-torrent .torrent-attribute div {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page .torrent-attribute textarea {
|
.create-torrent .torrent-attribute textarea {
|
||||||
width: calc(100% - 80px);
|
width: calc(100% - 80px);
|
||||||
height: 80px;
|
height: 80px;
|
||||||
color: #eee;
|
color: #eee;
|
||||||
@@ -321,11 +321,11 @@ table {
|
|||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page textarea.torrent-trackers {
|
.create-torrent textarea.torrent-trackers {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent-page input.torrent-is-private {
|
.create-torrent input.torrent-is-private {
|
||||||
width: initial;
|
width: initial;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="index.css" charset="utf-8">
|
<link rel="stylesheet" href="main.css" charset="utf-8">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script async src="index.js"></script>
|
<script async src="main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ var App = require('./views/app')
|
|||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
var errors = require('./lib/errors')
|
var errors = require('./lib/errors')
|
||||||
var sound = require('./lib/sound')
|
var sound = require('./lib/sound')
|
||||||
var State = require('./state')
|
var State = require('./lib/state')
|
||||||
var TorrentPlayer = require('./lib/torrent-player')
|
var TorrentPlayer = require('./lib/torrent-player')
|
||||||
var TorrentSummary = require('./lib/torrent-summary')
|
var TorrentSummary = require('./lib/torrent-summary')
|
||||||
|
|
||||||
@@ -224,12 +224,16 @@ function dispatch (action, ...args) {
|
|||||||
if (action === 'addTorrent') {
|
if (action === 'addTorrent') {
|
||||||
addTorrent(args[0] /* torrent */)
|
addTorrent(args[0] /* torrent */)
|
||||||
}
|
}
|
||||||
if (action === 'showOpenTorrentFile') {
|
if (action === 'openTorrentFile') {
|
||||||
ipcRenderer.send('showOpenTorrentFile') /* open torrent file */
|
ipcRenderer.send('openTorrentFile') /* open torrent file */
|
||||||
}
|
}
|
||||||
if (action === 'showCreateTorrent') {
|
if (action === 'showCreateTorrent') {
|
||||||
showCreateTorrent(args[0] /* paths */)
|
showCreateTorrent(args[0] /* paths */)
|
||||||
}
|
}
|
||||||
|
if (action === 'openTorrentAddress') {
|
||||||
|
state.modal = { id: 'open-torrent-address-modal' }
|
||||||
|
update()
|
||||||
|
}
|
||||||
if (action === 'createTorrent') {
|
if (action === 'createTorrent') {
|
||||||
createTorrent(args[0] /* options */)
|
createTorrent(args[0] /* options */)
|
||||||
}
|
}
|
||||||
@@ -377,6 +381,9 @@ function dispatch (action, ...args) {
|
|||||||
if (action === 'saveState') {
|
if (action === 'saveState') {
|
||||||
saveState()
|
saveState()
|
||||||
}
|
}
|
||||||
|
if (action === 'setTitle') {
|
||||||
|
state.window.title = args[0] /* title */
|
||||||
|
}
|
||||||
|
|
||||||
// Update the virtual-dom, unless it's just a mouse move event
|
// Update the virtual-dom, unless it's just a mouse move event
|
||||||
if (action !== 'mediaMouseMoved' || showOrHidePlayerControls()) {
|
if (action !== 'mediaMouseMoved' || showOrHidePlayerControls()) {
|
||||||
@@ -508,11 +515,6 @@ function setupIpc () {
|
|||||||
|
|
||||||
ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args))
|
ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args))
|
||||||
|
|
||||||
ipcRenderer.on('showOpenTorrentAddress', function (e) {
|
|
||||||
state.modal = { id: 'open-torrent-address-modal' }
|
|
||||||
update()
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) {
|
ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) {
|
||||||
state.window.isFullScreen = isFullScreen
|
state.window.isFullScreen = isFullScreen
|
||||||
if (!isFullScreen) {
|
if (!isFullScreen) {
|
||||||
@@ -606,7 +608,8 @@ function saveState () {
|
|||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the user drag-drops files onto the app
|
// Called when the user adds files (.torrent, files to seed, subtitles) to the app
|
||||||
|
// via any method (drag-drop, drag to app icon, command line)
|
||||||
function onOpen (files) {
|
function onOpen (files) {
|
||||||
if (!Array.isArray(files)) files = [ files ]
|
if (!Array.isArray(files)) files = [ files ]
|
||||||
|
|
||||||
@@ -674,12 +677,13 @@ function addTorrent (torrentId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addSubtitles (files, autoSelect) {
|
function addSubtitles (files, autoSelect) {
|
||||||
// Subtitles are only supported while playing video
|
// Subtitles are only supported when playing video files
|
||||||
if (state.playing.type !== 'video') return
|
if (state.playing.type !== 'video') return
|
||||||
|
if (files.length === 0) return
|
||||||
|
|
||||||
// Read the files concurrently, then add all resulting subtitle tracks
|
// Read the files concurrently, then add all resulting subtitle tracks
|
||||||
var jobs = files.map((file) => (cb) => loadSubtitle(file, cb))
|
var tasks = files.map((file) => (cb) => loadSubtitle(file, cb))
|
||||||
parallel(jobs, function (err, tracks) {
|
parallel(tasks, function (err, tracks) {
|
||||||
if (err) return onError(err)
|
if (err) return onError(err)
|
||||||
|
|
||||||
for (var i = 0; i < tracks.length; i++) {
|
for (var i = 0; i < tracks.length; i++) {
|
||||||
@@ -974,7 +978,10 @@ function torrentProgress (progressInfo) {
|
|||||||
torrentSummary.progress = p
|
torrentSummary.progress = p
|
||||||
})
|
})
|
||||||
|
|
||||||
checkForSubtitles()
|
// TODO: Find an efficient way to re-enable this line, which allows subtitle
|
||||||
|
// files which are completed after a video starts to play to be added
|
||||||
|
// dynamically to the list of subtitles.
|
||||||
|
// checkForSubtitles()
|
||||||
|
|
||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
@@ -1312,7 +1319,7 @@ function showDoneNotification (torrent) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
notif.onclick = function () {
|
notif.onclick = function () {
|
||||||
ipcRenderer.send('focusWindow', 'main')
|
ipcRenderer.send('show')
|
||||||
}
|
}
|
||||||
|
|
||||||
sound.play('DONE')
|
sound.play('DONE')
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
module.exports = App
|
module.exports = App
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
var hx = require('../lib/hx')
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var Header = require('./header')
|
var Header = require('./header')
|
||||||
|
|
||||||
var Views = {
|
var Views = {
|
||||||
'home': require('./torrent-list'),
|
'home': require('./home'),
|
||||||
'player': require('./player'),
|
'player': require('./player'),
|
||||||
'create-torrent': require('./create-torrent-page'),
|
'create-torrent': require('./create-torrent'),
|
||||||
'preferences': require('./preferences')
|
'preferences': require('./preferences')
|
||||||
}
|
}
|
||||||
|
|
||||||
var Modals = {
|
var Modals = {
|
||||||
'open-torrent-address-modal': require('./open-torrent-address-modal'),
|
'open-torrent-address-modal': require('./open-torrent-address-modal'),
|
||||||
'update-available-modal': require('./update-available-modal'),
|
'update-available-modal': require('./update-available-modal'),
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
module.exports = CreateTorrentPage
|
module.exports = CreateTorrentPage
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var createTorrent = require('create-torrent')
|
var createTorrent = require('create-torrent')
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
var prettyBytes = require('prettier-bytes')
|
var prettyBytes = require('prettier-bytes')
|
||||||
|
|
||||||
var {dispatch, dispatcher} = require('../lib/dispatcher')
|
var {dispatch, dispatcher} = require('../lib/dispatcher')
|
||||||
|
var hx = require('../lib/hx')
|
||||||
|
|
||||||
function CreateTorrentPage (state) {
|
function CreateTorrentPage (state) {
|
||||||
var info = state.location.current()
|
var info = state.location.current()
|
||||||
@@ -59,7 +56,7 @@ function CreateTorrentPage (state) {
|
|||||||
var collapsedClass = info.showAdvanced ? 'expanded' : 'collapsed'
|
var collapsedClass = info.showAdvanced ? 'expanded' : 'collapsed'
|
||||||
|
|
||||||
return hx`
|
return hx`
|
||||||
<div class='create-torrent-page'>
|
<div class='create-torrent'>
|
||||||
<h2>Create torrent ${defaultName}</h2>
|
<h2>Create torrent ${defaultName}</h2>
|
||||||
<p class="torrent-info">
|
<p class="torrent-info">
|
||||||
${torrentInfo}
|
${torrentInfo}
|
||||||
@@ -132,7 +129,7 @@ function CreateTorrentPage (state) {
|
|||||||
|
|
||||||
function CreateTorrentErrorPage () {
|
function CreateTorrentErrorPage () {
|
||||||
return hx`
|
return hx`
|
||||||
<div class='create-torrent-page'>
|
<div class='create-torrent'>
|
||||||
<h2>Create torrent</h2>
|
<h2>Create torrent</h2>
|
||||||
<p class="torrent-info">
|
<p class="torrent-info">
|
||||||
<p>
|
<p>
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
module.exports = Header
|
module.exports = Header
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var {dispatcher} = require('../lib/dispatcher')
|
var {dispatcher} = require('../lib/dispatcher')
|
||||||
|
var hx = require('../lib/hx')
|
||||||
|
|
||||||
function Header (state) {
|
function Header (state) {
|
||||||
return hx`
|
return hx`
|
||||||
@@ -42,7 +39,7 @@ function Header (state) {
|
|||||||
<i
|
<i
|
||||||
class='icon add'
|
class='icon add'
|
||||||
title='Add torrent'
|
title='Add torrent'
|
||||||
onclick=${dispatcher('showOpenTorrentFile')}>
|
onclick=${dispatcher('openTorrentFile')}>
|
||||||
add
|
add
|
||||||
</i>
|
</i>
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
module.exports = TorrentList
|
module.exports = TorrentList
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
var prettyBytes = require('prettier-bytes')
|
var prettyBytes = require('prettier-bytes')
|
||||||
|
|
||||||
|
var hx = require('../lib/hx')
|
||||||
var TorrentSummary = require('../lib/torrent-summary')
|
var TorrentSummary = require('../lib/torrent-summary')
|
||||||
var TorrentPlayer = require('../lib/torrent-player')
|
var TorrentPlayer = require('../lib/torrent-player')
|
||||||
var {dispatcher} = require('../lib/dispatcher')
|
var {dispatcher} = require('../lib/dispatcher')
|
||||||
|
|
||||||
function TorrentList (state) {
|
function TorrentList (state) {
|
||||||
var torrentRows = state.saved.torrents.map(
|
var torrentRows = state.saved.torrents.map(
|
||||||
(torrentSummary) => renderTorrent(torrentSummary))
|
(torrentSummary) => renderTorrent(torrentSummary)
|
||||||
|
)
|
||||||
|
|
||||||
return hx`
|
return hx`
|
||||||
<div class='torrent-list'>
|
<div class='torrent-list'>
|
||||||
${torrentRows}
|
${torrentRows}
|
||||||
@@ -20,11 +20,7 @@ function TorrentList (state) {
|
|||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
// Renders a torrent in the torrent list
|
|
||||||
// Includes name, download status, play button, background image
|
|
||||||
// May be expanded for additional info, including the list of files inside
|
|
||||||
function renderTorrent (torrentSummary) {
|
function renderTorrent (torrentSummary) {
|
||||||
// Get ephemeral data (like progress %) directly from the WebTorrent handle
|
|
||||||
var infoHash = torrentSummary.infoHash
|
var infoHash = torrentSummary.infoHash
|
||||||
var isSelected = infoHash && state.selectedInfoHash === infoHash
|
var isSelected = infoHash && state.selectedInfoHash === infoHash
|
||||||
|
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
module.exports = OpenTorrentAddressModal
|
module.exports = OpenTorrentAddressModal
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var {dispatch} = require('../lib/dispatcher')
|
var {dispatch} = require('../lib/dispatcher')
|
||||||
|
var hx = require('../lib/hx')
|
||||||
|
|
||||||
function OpenTorrentAddressModal (state) {
|
function OpenTorrentAddressModal (state) {
|
||||||
return hx`
|
return hx`
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
module.exports = Player
|
module.exports = Player
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var Bitfield = require('bitfield')
|
var Bitfield = require('bitfield')
|
||||||
var prettyBytes = require('prettier-bytes')
|
var prettyBytes = require('prettier-bytes')
|
||||||
var zeroFill = require('zero-fill')
|
var zeroFill = require('zero-fill')
|
||||||
|
|
||||||
|
var hx = require('../lib/hx')
|
||||||
var TorrentSummary = require('../lib/torrent-summary')
|
var TorrentSummary = require('../lib/torrent-summary')
|
||||||
var {dispatch, dispatcher} = require('../lib/dispatcher')
|
var {dispatch, dispatcher} = require('../lib/dispatcher')
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
module.exports = Preferences
|
module.exports = Preferences
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
var hx = require('../lib/hx')
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
var {dispatch} = require('../lib/dispatcher')
|
var {dispatch} = require('../lib/dispatcher')
|
||||||
|
|
||||||
var remote = require('electron').remote
|
var remote = require('electron').remote
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
module.exports = UnsupportedMediaModal
|
module.exports = UnsupportedMediaModal
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
|
|
||||||
var {dispatch, dispatcher} = require('../lib/dispatcher')
|
var {dispatch, dispatcher} = require('../lib/dispatcher')
|
||||||
|
var hx = require('../lib/hx')
|
||||||
|
|
||||||
function UnsupportedMediaModal (state) {
|
function UnsupportedMediaModal (state) {
|
||||||
var err = state.modal.error
|
var err = state.modal.error
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
module.exports = UpdateAvailableModal
|
module.exports = UpdateAvailableModal
|
||||||
|
|
||||||
var h = require('virtual-dom/h')
|
|
||||||
var hyperx = require('hyperx')
|
|
||||||
var hx = hyperx(h)
|
|
||||||
|
|
||||||
var electron = require('electron')
|
var electron = require('electron')
|
||||||
|
|
||||||
var {dispatch} = require('../lib/dispatcher')
|
var {dispatch} = require('../lib/dispatcher')
|
||||||
|
var hx = require('../lib/hx')
|
||||||
|
|
||||||
function UpdateAvailableModal (state) {
|
function UpdateAvailableModal (state) {
|
||||||
return hx`
|
return hx`
|
||||||
|
|||||||
Reference in New Issue
Block a user