Merge branch 'master' of github.com:webtorrent/webtorrent-desktop

This commit is contained in:
BlueManCZ
2021-01-31 10:40:05 +01:00
40 changed files with 4384 additions and 2517 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ node_modules/
build/ build/
dist/ dist/
npm-debug.log* npm-debug.log*
.vscode/
# JetBrains IntelliJ IDEA project files # JetBrains IntelliJ IDEA project files
.idea .idea

View File

@@ -1,3 +1,5 @@
language: node_js language: node_js
node_js: node_js:
- lts/* - lts/*
cache:
npm: false

View File

@@ -19,7 +19,7 @@
## v0.23.0 - 2020-07-15 ## v0.23.0 - 2020-07-15
🔒 This release contains a critical security fix. Please update as soon as possible. 🔒 This release contains a critical security fix. Please update as soon as possible. [\#1837](https://github.com/webtorrent/webtorrent-desktop/issues/1837#issuecomment-729320901)
### Added ### Added

View File

@@ -1,39 +0,0 @@
#!/usr/bin/env node
const walkSync = require('walk-sync')
const fs = require('fs')
const path = require('path')
let hasErrors = false
// Find all Javascript source files
const files = walkSync('src', { globs: ['**/*.js'] })
console.log('Running extra-lint on ' + files.length + ' files...')
// Read each file, line by line
files.forEach(function (file) {
const filepath = path.join('src', file)
const lines = fs.readFileSync(filepath, 'utf8').split('\n')
lines.forEach(function (line, i) {
let error
// No lines over 100 characters
if (line.length > 100) {
error = 'Line >100 chars'
}
if (line.match(/^var /) || line.match(/ var /)) {
error = 'Use const or let'
}
if (error) {
const name = path.basename(file)
console.log('%s:%d - %s:\n%s', name, i + 1, error, line)
hasErrors = true
}
})
})
if (hasErrors) process.exit(1)
else console.log('Looks good!')

View File

@@ -545,7 +545,17 @@ function buildLinux (cb) {
}, },
categories: ['Network', 'FileTransfer', 'P2P'], categories: ['Network', 'FileTransfer', 'P2P'],
mimeType: ['application/x-bittorrent', 'x-scheme-handler/magnet', 'x-scheme-handler/stream-magnet'], mimeType: ['application/x-bittorrent', 'x-scheme-handler/magnet', 'x-scheme-handler/stream-magnet'],
desktopTemplate: path.join(config.STATIC_PATH, 'linux/webtorrent-desktop.ejs') desktopTemplate: path.join(config.STATIC_PATH, 'linux/webtorrent-desktop.ejs'),
lintianOverrides: [
'unstripped-binary-or-object',
'embedded-library',
'missing-dependency-on-libc',
'changelog-file-missing-in-native-package',
'description-synopsis-is-duplicated',
'setuid-binary',
'binary-without-manpage',
'shlib-with-executable-bit'
]
} }
installer(options).then( installer(options).then(

6232
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,66 +7,77 @@
"email": "feross@webtorrent.io", "email": "feross@webtorrent.io",
"url": "https://webtorrent.io" "url": "https://webtorrent.io"
}, },
"babel": {
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"useBuiltIns": true
}
]
]
},
"bugs": { "bugs": {
"url": "https://github.com/webtorrent/webtorrent-desktop/issues" "url": "https://github.com/webtorrent/webtorrent-desktop/issues"
}, },
"dependencies": { "dependencies": {
"airplayer": "github:webtorrent/airplayer#fix-security", "airplayer": "github:webtorrent/airplayer#fix-security",
"application-config": "^2.0.0", "application-config": "^2.0.0",
"arch": "^2.1.2", "arch": "^2.2.0",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"bitfield": "^3.0.0", "bitfield": "^4.0.0",
"capture-frame": "^4.0.0", "capture-frame": "^4.0.0",
"chokidar": "^3.4.2", "chokidar": "^3.4.3",
"chromecasts": "^1.9.1", "chromecasts": "^1.9.1",
"create-torrent": "^4.4.2", "create-torrent": "^4.4.2",
"debounce": "^1.2.0", "debounce": "^1.2.0",
"dlnacasts": "^0.1.0", "dlnacasts": "^0.1.0",
"drag-drop": "^6.0.2", "drag-drop": "^6.1.0",
"es6-error": "^4.1.1", "es6-error": "^4.1.1",
"fn-getter": "^1.0.0", "fn-getter": "^1.0.0",
"iso-639-1": "^2.1.4", "iso-639-1": "^2.1.4",
"languagedetect": "^2.0.0", "languagedetect": "^2.0.0",
"location-history": "^1.1.2", "location-history": "^1.1.2",
"material-ui": "^0.20.2", "material-ui": "^0.20.2",
"music-metadata": "^7.0.2", "music-metadata": "^7.5.0",
"network-address": "^1.1.2", "network-address": "^1.1.2",
"parse-torrent": "^7.1.3", "parse-torrent": "^9.1.0",
"prettier-bytes": "^1.0.4", "prettier-bytes": "^1.0.4",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^16.13.1", "react": "^17.0.1",
"react-dom": "^16.13.1", "react-dom": "^17.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"run-parallel": "^1.1.9", "run-parallel": "^1.1.10",
"semver": "^7.3.2", "semver": "^7.3.2",
"simple-concat": "^1.0.1", "simple-concat": "^1.0.1",
"simple-get": "^4.0.0", "simple-get": "^4.0.0",
"srt-to-vtt": "^1.1.3", "srt-to-vtt": "^1.1.3",
"vlc-command": "^1.2.0", "vlc-command": "^1.2.0",
"webtorrent": ">=0.108.6", "webtorrent": ">=0.111.0",
"winreg": "^1.2.4" "winreg": "^1.2.4"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^10.1.0", "@babel/cli": "^7.12.8",
"buble": "^0.20.0", "@babel/core": "^7.12.9",
"cross-zip": "^3.1.0", "@babel/eslint-parser": "^7.12.1",
"depcheck": "^1.2.0", "@babel/plugin-transform-react-jsx": "^7.12.7",
"electron": "~10.1.0", "cross-zip": "^4.0.0",
"depcheck": "^1.3.1",
"electron": "~11.0.3",
"electron-notarize": "^1.0.0", "electron-notarize": "^1.0.0",
"electron-osx-sign": "^0.4.17", "electron-osx-sign": "^0.5.0",
"electron-packager": "^15.1.0", "electron-packager": "^15.1.0",
"electron-winstaller": "^4.0.1", "electron-winstaller": "^4.0.1",
"gh-release": "^3.5.0", "gh-release": "^4.0.3",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"nodemon": "^2.0.4", "nodemon": "^2.0.6",
"open": "^7.2.1", "open": "^7.3.0",
"plist": "^3.0.1", "plist": "^3.0.1",
"pngjs": "^5.0.0", "pngjs": "^6.0.0",
"run-series": "^1.1.8", "run-series": "^1.1.9",
"spectron": "~11.1.0", "spectron": "~12.0.0",
"standard": "*", "standard": "*",
"tape": "^5.0.1", "tape": "^5.0.1"
"walk-sync": "^2.2.0"
}, },
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0"
@@ -96,19 +107,19 @@
"url": "git://github.com/webtorrent/webtorrent-desktop.git" "url": "git://github.com/webtorrent/webtorrent-desktop.git"
}, },
"scripts": { "scripts": {
"build": "buble src --target chrome:71 --output build", "build": "babel src --out-dir build",
"clean": "node ./bin/clean.js", "clean": "node ./bin/clean.js",
"gh-release": "gh-release", "gh-release": "gh-release",
"install-system-deps": "brew install fakeroot dpkg rpm", "install-system-deps": "brew install fakeroot dpkg rpm",
"open-config": "node ./bin/open-config.js", "open-config": "node ./bin/open-config.js",
"package": "node ./bin/package.js", "package": "node ./bin/package.js",
"start": "npm run build && electron --no-sandbox .", "start": "npm run build && electron --no-sandbox .",
"test": "standard && depcheck --ignores=standard,babel-eslint --ignore-dirs=build,dist && node ./bin/extra-lint.js", "test": "standard && depcheck --ignores=standard,@babel/eslint-parser --ignore-dirs=build,dist",
"test-integration": "npm run build && node ./test", "test-integration": "npm run build && node ./test",
"update-authors": "./bin/update-authors.sh", "update-authors": "./bin/update-authors.sh",
"watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/" "watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/"
}, },
"standard": { "standard": {
"parser": "babel-eslint" "parser": "@babel/eslint-parser"
} }
} }

View File

@@ -1,6 +1,6 @@
const appConfig = require('application-config')('WebTorrent') const appConfig = require('application-config')('WebTorrent')
const path = require('path') const path = require('path')
const electron = require('electron') const { app, remote } = require('electron')
const arch = require('arch') const arch = require('arch')
const APP_NAME = 'WebTorrent' const APP_NAME = 'WebTorrent'
@@ -126,10 +126,10 @@ function getPath (key) {
return '' return ''
} else if (process.type === 'renderer') { } else if (process.type === 'renderer') {
// Electron renderer process // Electron renderer process
return electron.remote.app.getPath(key) return remote.app.getPath(key)
} else { } else {
// Electron main process // Electron main process
return electron.app.getPath(key) return app.getPath(key)
} }
} }

View File

@@ -4,9 +4,9 @@ module.exports = {
function init () { function init () {
const config = require('./config') const config = require('./config')
const electron = require('electron') const { crashReporter } = require('electron')
electron.crashReporter.start({ crashReporter.start({
productName: config.APP_NAME, productName: config.APP_NAME,
submitURL: config.CRASH_REPORT_URL, submitURL: config.CRASH_REPORT_URL,
globalExtra: { _companyName: config.APP_NAME }, globalExtra: { _companyName: config.APP_NAME },

View File

@@ -2,7 +2,7 @@ module.exports = {
init init
} }
const electron = require('electron') const { dialog } = require('electron')
const config = require('../config') const config = require('../config')
const log = require('./log') const log = require('./log')
@@ -44,7 +44,7 @@ function onResponse (err, res, data) {
} }
} }
electron.dialog.showMessageBox({ dialog.showMessageBox({
type: 'info', type: 'info',
buttons: ['OK'], buttons: ['OK'],
title: data.title, title: data.title,

View File

@@ -6,7 +6,7 @@ module.exports = {
openFiles openFiles
} }
const electron = require('electron') const { dialog } = require('electron')
const log = require('./log') const log = require('./log')
const windows = require('./windows') const windows = require('./windows')
@@ -34,13 +34,13 @@ function openSeedDirectory () {
log('openSeedDirectory') log('openSeedDirectory')
const opts = process.platform === 'darwin' const opts = process.platform === 'darwin'
? { ? {
title: 'Select a file or folder for the torrent.', title: 'Select a file or folder for the torrent.',
properties: ['openFile', 'openDirectory'] properties: ['openFile', 'openDirectory']
} }
: { : {
title: 'Select a folder for the torrent.', title: 'Select a folder for the torrent.',
properties: ['openDirectory'] properties: ['openDirectory']
} }
showOpenSeed(opts) showOpenSeed(opts)
} }
@@ -53,15 +53,15 @@ function openFiles () {
log('openFiles') log('openFiles')
const opts = process.platform === 'darwin' const opts = process.platform === 'darwin'
? { ? {
title: 'Select a file or folder to add.', title: 'Select a file or folder to add.',
properties: ['openFile', 'openDirectory'] properties: ['openFile', 'openDirectory']
} }
: { : {
title: 'Select a file to add.', title: 'Select a file to add.',
properties: ['openFile'] properties: ['openFile']
} }
setTitle(opts.title) setTitle(opts.title)
const selectedPaths = electron.dialog.showOpenDialogSync(windows.main.win, opts) const selectedPaths = dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('onOpen', selectedPaths) windows.main.dispatch('onOpen', selectedPaths)
@@ -79,7 +79,7 @@ function openTorrentFile () {
properties: ['openFile', 'multiSelections'] properties: ['openFile', 'multiSelections']
} }
setTitle(opts.title) setTitle(opts.title)
const selectedPaths = electron.dialog.showOpenDialogSync(windows.main.win, opts) const selectedPaths = dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
selectedPaths.forEach(function (selectedPath) { selectedPaths.forEach(function (selectedPath) {
@@ -114,7 +114,7 @@ function resetTitle () {
*/ */
function showOpenSeed (opts) { function showOpenSeed (opts) {
setTitle(opts.title) setTitle(opts.title)
const selectedPaths = electron.dialog.showOpenDialogSync(windows.main.win, opts) const selectedPaths = dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('showCreateTorrent', selectedPaths) windows.main.dispatch('showCreateTorrent', selectedPaths)

View File

@@ -25,8 +25,7 @@ function uninstall () {
} }
function installDarwin () { function installDarwin () {
const electron = require('electron') const { app } = require('electron')
const app = electron.app
// On Mac, only protocols that are listed in `Info.plist` can be set as the // On Mac, only protocols that are listed in `Info.plist` can be set as the
// default handler at runtime. // default handler at runtime.

View File

@@ -1,7 +1,6 @@
console.time('init') console.time('init')
const electron = require('electron') const { app, ipcMain } = require('electron')
const app = electron.app
// Start crash reporter early, so it takes effect for child processes // Start crash reporter early, so it takes effect for child processes
const crashReporter = require('../crash-reporter') const crashReporter = require('../crash-reporter')
@@ -67,8 +66,6 @@ function init () {
app.setPath('temp', path.join(config.CONFIG_PATH, 'Temp')) app.setPath('temp', path.join(config.CONFIG_PATH, 'Temp'))
} }
const ipcMain = electron.ipcMain
let isReady = false // app ready, windows can be created let isReady = false // app ready, windows can be created
app.ipcReady = false // main window has finished loading and IPC is ready app.ipcReady = false // main window has finished loading and IPC is ready
app.isQuitting = false app.isQuitting = false
@@ -198,9 +195,13 @@ function onAppOpen (newArgv) {
// Development: 2 args, eg: electron . // Development: 2 args, eg: electron .
// Test: 4 args, eg: electron -r .../mocks.js . // Test: 4 args, eg: electron -r .../mocks.js .
function sliceArgv (argv) { function sliceArgv (argv) {
return argv.slice(config.IS_PRODUCTION ? 1 return argv.slice(
: config.IS_TEST ? 4 config.IS_PRODUCTION
: 2) ? 1
: config.IS_TEST
? 4
: 2
)
} }
function processArgv (argv) { function processArgv (argv) {

View File

@@ -3,9 +3,7 @@ module.exports = {
setModule setModule
} }
const electron = require('electron') const { app, ipcMain } = require('electron')
const app = electron.app
const log = require('./log') const log = require('./log')
const menu = require('./menu') const menu = require('./menu')
@@ -23,14 +21,12 @@ function setModule (name, module) {
} }
function init () { function init () {
const ipc = electron.ipcMain ipcMain.once('ipcReady', function (e) {
ipc.once('ipcReady', function (e) {
app.ipcReady = true app.ipcReady = true
app.emit('ipcReady') app.emit('ipcReady')
}) })
ipc.once('ipcReadyWebTorrent', function (e) { ipcMain.once('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)
@@ -44,11 +40,11 @@ function init () {
* Dialog * Dialog
*/ */
ipc.on('openTorrentFile', () => { ipcMain.on('openTorrentFile', () => {
const dialog = require('./dialog') const dialog = require('./dialog')
dialog.openTorrentFile() dialog.openTorrentFile()
}) })
ipc.on('openFiles', () => { ipcMain.on('openFiles', () => {
const dialog = require('./dialog') const dialog = require('./dialog')
dialog.openFiles() dialog.openFiles()
}) })
@@ -57,11 +53,11 @@ function init () {
* Dock * Dock
*/ */
ipc.on('setBadge', (e, ...args) => { ipcMain.on('setBadge', (e, ...args) => {
const dock = require('./dock') const dock = require('./dock')
dock.setBadge(...args) dock.setBadge(...args)
}) })
ipc.on('downloadFinished', (e, ...args) => { ipcMain.on('downloadFinished', (e, ...args) => {
const dock = require('./dock') const dock = require('./dock')
dock.downloadFinished(...args) dock.downloadFinished(...args)
}) })
@@ -70,7 +66,7 @@ function init () {
* Player Events * Player Events
*/ */
ipc.on('onPlayerOpen', function () { ipcMain.on('onPlayerOpen', function () {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts') const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -81,14 +77,14 @@ function init () {
thumbar.enable() thumbar.enable()
}) })
ipc.on('onPlayerUpdate', function (e, ...args) { ipcMain.on('onPlayerUpdate', function (e, ...args) {
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
menu.onPlayerUpdate(...args) menu.onPlayerUpdate(...args)
thumbar.onPlayerUpdate(...args) thumbar.onPlayerUpdate(...args)
}) })
ipc.on('onPlayerClose', function () { ipcMain.on('onPlayerClose', function () {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts') const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -99,7 +95,7 @@ function init () {
thumbar.disable() thumbar.disable()
}) })
ipc.on('onPlayerPlay', function () { ipcMain.on('onPlayerPlay', function () {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -107,7 +103,7 @@ function init () {
thumbar.onPlayerPlay() thumbar.onPlayerPlay()
}) })
ipc.on('onPlayerPause', function () { ipcMain.on('onPlayerPause', function () {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -119,7 +115,7 @@ function init () {
* Folder Watcher Events * Folder Watcher Events
*/ */
ipc.on('startFolderWatcher', function () { ipcMain.on('startFolderWatcher', function () {
if (!modules.folderWatcher) { if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.') log('IPC ERR: folderWatcher module is not defined.')
return return
@@ -128,7 +124,7 @@ function init () {
modules.folderWatcher.start() modules.folderWatcher.start()
}) })
ipc.on('stopFolderWatcher', function () { ipcMain.on('stopFolderWatcher', function () {
if (!modules.folderWatcher) { if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.') log('IPC ERR: folderWatcher module is not defined.')
return return
@@ -141,15 +137,15 @@ function init () {
* Shell * Shell
*/ */
ipc.on('openPath', (e, ...args) => { ipcMain.on('openPath', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.openPath(...args) shell.openPath(...args)
}) })
ipc.on('showItemInFolder', (e, ...args) => { ipcMain.on('showItemInFolder', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.showItemInFolder(...args) shell.showItemInFolder(...args)
}) })
ipc.on('moveItemToTrash', (e, ...args) => { ipcMain.on('moveItemToTrash', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.moveItemToTrash(...args) shell.moveItemToTrash(...args)
}) })
@@ -158,7 +154,7 @@ function init () {
* File handlers * File handlers
*/ */
ipc.on('setDefaultFileHandler', (e, flag) => { ipcMain.on('setDefaultFileHandler', (e, flag) => {
const handlers = require('./handlers') const handlers = require('./handlers')
if (flag) handlers.install() if (flag) handlers.install()
@@ -169,7 +165,7 @@ function init () {
* Auto start on login * Auto start on login
*/ */
ipc.on('setStartup', (e, flag) => { ipcMain.on('setStartup', (e, flag) => {
const startup = require('./startup') const startup = require('./startup')
if (flag) startup.install() if (flag) startup.install()
@@ -182,19 +178,19 @@ function init () {
const main = windows.main const main = windows.main
ipc.on('setAspectRatio', (e, ...args) => main.setAspectRatio(...args)) ipcMain.on('setAspectRatio', (e, ...args) => main.setAspectRatio(...args))
ipc.on('setBounds', (e, ...args) => main.setBounds(...args)) ipcMain.on('setBounds', (e, ...args) => main.setBounds(...args))
ipc.on('setProgress', (e, ...args) => main.setProgress(...args)) ipcMain.on('setProgress', (e, ...args) => main.setProgress(...args))
ipc.on('setTitle', (e, ...args) => main.setTitle(...args)) ipcMain.on('setTitle', (e, ...args) => main.setTitle(...args))
ipc.on('show', () => main.show()) ipcMain.on('show', () => main.show())
ipc.on('toggleFullScreen', (e, ...args) => main.toggleFullScreen(...args)) ipcMain.on('toggleFullScreen', (e, ...args) => main.toggleFullScreen(...args))
ipc.on('setAllowNav', (e, ...args) => menu.setAllowNav(...args)) ipcMain.on('setAllowNav', (e, ...args) => menu.setAllowNav(...args))
/** /**
* External Media Player * External Media Player
*/ */
ipc.on('checkForExternalPlayer', function (e, path) { ipcMain.on('checkForExternalPlayer', function (e, path) {
const externalPlayer = require('./external-player') const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, function (err) { externalPlayer.checkInstall(path, function (err) {
@@ -202,7 +198,7 @@ function init () {
}) })
}) })
ipc.on('openExternalPlayer', (e, ...args) => { ipcMain.on('openExternalPlayer', (e, ...args) => {
const externalPlayer = require('./external-player') const externalPlayer = require('./external-player')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -211,7 +207,7 @@ function init () {
externalPlayer.spawn(...args) externalPlayer.spawn(...args)
}) })
ipc.on('quitExternalPlayer', () => { ipcMain.on('quitExternalPlayer', () => {
const externalPlayer = require('./external-player') const externalPlayer = require('./external-player')
externalPlayer.kill() externalPlayer.kill()
}) })
@@ -220,8 +216,8 @@ function init () {
* Message passing * Message passing
*/ */
const oldEmit = ipc.emit const oldEmit = ipcMain.emit
ipc.emit = function (name, e, ...args) { ipcMain.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') {
@@ -244,6 +240,6 @@ function init () {
} }
// Emit all other events normally // Emit all other events normally
oldEmit.call(ipc, name, e, ...args) oldEmit.call(ipcMain, name, e, ...args)
} }
} }

View File

@@ -8,11 +8,9 @@ module.exports.error = error
* where they can be viewed in Developer Tools. * where they can be viewed in Developer Tools.
*/ */
const electron = require('electron') const { app } = require('electron')
const windows = require('./windows') const windows = require('./windows')
const app = electron.app
function log (...args) { function log (...args) {
if (app.ipcReady) { if (app.ipcReady) {
windows.main.send('log', ...args) windows.main.send('log', ...args)

View File

@@ -8,9 +8,7 @@ module.exports = {
onToggleFullScreen onToggleFullScreen
} }
const electron = require('electron') const { app, Menu } = require('electron')
const app = electron.app
const config = require('../config') const config = require('../config')
const windows = require('./windows') const windows = require('./windows')
@@ -18,8 +16,8 @@ const windows = require('./windows')
let menu = null let menu = null
function init () { function init () {
menu = electron.Menu.buildFromTemplate(getMenuTemplate()) menu = Menu.buildFromTemplate(getMenuTemplate())
electron.Menu.setApplicationMenu(menu) Menu.setApplicationMenu(menu)
} }
function togglePlaybackControls (flag) { function togglePlaybackControls (flag) {

View File

@@ -3,7 +3,7 @@ module.exports = {
disable disable
} }
const electron = require('electron') const { powerSaveBlocker } = require('electron')
const log = require('./log') const log = require('./log')
let blockId = 0 let blockId = 0
@@ -13,11 +13,11 @@ let blockId = 0
* display. * display.
*/ */
function enable () { function enable () {
if (electron.powerSaveBlocker.isStarted(blockId)) { if (powerSaveBlocker.isStarted(blockId)) {
// If a power saver block already exists, do nothing. // If a power saver block already exists, do nothing.
return return
} }
blockId = electron.powerSaveBlocker.start('prevent-display-sleep') blockId = powerSaveBlocker.start('prevent-display-sleep')
log(`powerSaveBlocker.enable: ${blockId}`) log(`powerSaveBlocker.enable: ${blockId}`)
} }
@@ -25,10 +25,10 @@ function enable () {
* Stop blocking the system from entering low-power mode. * Stop blocking the system from entering low-power mode.
*/ */
function disable () { function disable () {
if (!electron.powerSaveBlocker.isStarted(blockId)) { if (!powerSaveBlocker.isStarted(blockId)) {
// If a power saver block does not exist, do nothing. // If a power saver block does not exist, do nothing.
return return
} }
electron.powerSaveBlocker.stop(blockId) powerSaveBlocker.stop(blockId)
log(`powerSaveBlocker.disable: ${blockId}`) log(`powerSaveBlocker.disable: ${blockId}`)
} }

View File

@@ -5,7 +5,7 @@ module.exports = {
moveItemToTrash moveItemToTrash
} }
const electron = require('electron') const { shell } = require('electron')
const log = require('./log') const log = require('./log')
/** /**
@@ -13,15 +13,16 @@ const log = require('./log')
*/ */
function openExternal (url) { function openExternal (url) {
log(`openExternal: ${url}`) log(`openExternal: ${url}`)
electron.shell.openExternal(url) shell.openExternal(url)
} }
/** /**
* Open the given file in the desktops default manner. * Open the given file in the desktops default manner.
*/ */
function openPath (path) { function openPath (path) {
log(`openPath: ${path}`) log(`openPath: ${path}`)
electron.shell.openPath(path) shell.openPath(path)
} }
/** /**
@@ -29,7 +30,7 @@ function openPath (path) {
*/ */
function showItemInFolder (path) { function showItemInFolder (path) {
log(`showItemInFolder: ${path}`) log(`showItemInFolder: ${path}`)
electron.shell.showItemInFolder(path) shell.showItemInFolder(path)
} }
/** /**
@@ -37,5 +38,5 @@ function showItemInFolder (path) {
*/ */
function moveItemToTrash (path) { function moveItemToTrash (path) {
log(`moveItemToTrash: ${path}`) log(`moveItemToTrash: ${path}`)
electron.shell.moveItemToTrash(path) shell.moveItemToTrash(path)
} }

View File

@@ -3,20 +3,20 @@ module.exports = {
enable enable
} }
const electron = require('electron') const { globalShortcut } = require('electron')
const windows = require('./windows') const windows = require('./windows')
function enable () { function enable () {
// Register play/pause media key, available on some keyboards. // Register play/pause media key, available on some keyboards.
electron.globalShortcut.register( globalShortcut.register(
'MediaPlayPause', 'MediaPlayPause',
() => windows.main.dispatch('playPause') () => windows.main.dispatch('playPause')
) )
electron.globalShortcut.register( globalShortcut.register(
'MediaNextTrack', 'MediaNextTrack',
() => windows.main.dispatch('nextTrack') () => windows.main.dispatch('nextTrack')
) )
electron.globalShortcut.register( globalShortcut.register(
'MediaPreviousTrack', 'MediaPreviousTrack',
() => windows.main.dispatch('previousTrack') () => windows.main.dispatch('previousTrack')
) )
@@ -24,7 +24,7 @@ function enable () {
function disable () { function disable () {
// Return the media key to the OS, so other apps can use it. // Return the media key to the OS, so other apps can use it.
electron.globalShortcut.unregister('MediaPlayPause') globalShortcut.unregister('MediaPlayPause')
electron.globalShortcut.unregister('MediaNextTrack') globalShortcut.unregister('MediaNextTrack')
electron.globalShortcut.unregister('MediaPreviousTrack') globalShortcut.unregister('MediaPreviousTrack')
} }

View File

@@ -2,11 +2,10 @@ module.exports = {
handleEvent handleEvent
} }
const { app } = require('electron')
const path = require('path') const path = require('path')
const spawn = require('child_process').spawn const spawn = require('child_process').spawn
const electron = require('electron')
const app = electron.app
const handlers = require('./handlers') const handlers = require('./handlers')

View File

@@ -4,9 +4,7 @@ module.exports = {
setWindowFocus setWindowFocus
} }
const electron = require('electron') const { app, Tray, Menu } = require('electron')
const app = electron.app
const config = require('../config') const config = require('../config')
const windows = require('./windows') const windows = require('./windows')
@@ -67,7 +65,7 @@ function checkLinuxTraySupport (cb) {
} }
function createTray () { function createTray () {
tray = new electron.Tray(getIconPath()) tray = new Tray(getIconPath())
// On Windows, left click opens the app, right click opens the context menu. // On Windows, left click opens the app, right click opens the context menu.
// On Linux, any click (left or right) opens the context menu. // On Linux, any click (left or right) opens the context menu.
@@ -78,7 +76,7 @@ function createTray () {
} }
function updateTrayMenu () { function updateTrayMenu () {
const contextMenu = electron.Menu.buildFromTemplate(getMenuTemplate()) const contextMenu = Menu.buildFromTemplate(getMenuTemplate())
tray.setContextMenu(contextMenu) tray.setContextMenu(contextMenu)
} }

View File

@@ -2,7 +2,7 @@ module.exports = {
init init
} }
const electron = require('electron') const { autoUpdater } = require('electron')
const get = require('simple-get') const get = require('simple-get')
const config = require('../config') const config = require('../config')
@@ -47,31 +47,31 @@ function onResponse (err, res, data) {
} }
function initDarwinWin32 () { function initDarwinWin32 () {
electron.autoUpdater.on( autoUpdater.on(
'error', 'error',
(err) => log.error(`Update error: ${err.message}`) (err) => log.error(`Update error: ${err.message}`)
) )
electron.autoUpdater.on( autoUpdater.on(
'checking-for-update', 'checking-for-update',
() => log('Checking for update') () => log('Checking for update')
) )
electron.autoUpdater.on( autoUpdater.on(
'update-available', 'update-available',
() => log('Update available') () => log('Update available')
) )
electron.autoUpdater.on( autoUpdater.on(
'update-not-available', 'update-not-available',
() => log('No update available') () => log('No update available')
) )
electron.autoUpdater.on( autoUpdater.on(
'update-downloaded', 'update-downloaded',
(e, notes, name, date, url) => log(`Update downloaded: ${name}: ${url}`) (e, notes, name, date, url) => log(`Update downloaded: ${name}: ${url}`)
) )
electron.autoUpdater.setFeedURL({ url: AUTO_UPDATE_URL }) autoUpdater.setFeedURL({ url: AUTO_UPDATE_URL })
electron.autoUpdater.checkForUpdates() autoUpdater.checkForUpdates()
} }

View File

@@ -2,9 +2,7 @@ module.exports = {
init init
} }
const electron = require('electron') const { app } = require('electron')
const app = electron.app
/** /**
* Add a user task menu to the app icon on right-click. (Windows) * Add a user task menu to the app icon on right-click. (Windows)

View File

@@ -4,14 +4,14 @@ const about = module.exports = {
} }
const config = require('../../config') const config = require('../../config')
const electron = require('electron') const { BrowserWindow } = require('electron')
function init () { function init () {
if (about.win) { if (about.win) {
return about.win.show() return about.win.show()
} }
const win = about.win = new electron.BrowserWindow({ const win = about.win = new BrowserWindow({
backgroundColor: '#ECECEC', backgroundColor: '#ECECEC',
center: true, center: true,
fullscreen: false, fullscreen: false,
@@ -27,7 +27,8 @@ function init () {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks', enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true enableRemoteModule: true,
backgroundThrottling: false
}, },
width: 300 width: 300
}) })

View File

@@ -14,11 +14,9 @@ const main = module.exports = {
win: null win: null
} }
const electron = require('electron') const { app, BrowserWindow, screen } = require('electron')
const debounce = require('debounce') const debounce = require('debounce')
const app = electron.app
const config = require('../../config') const config = require('../../config')
const log = require('../log') const log = require('../log')
const menu = require('../menu') const menu = require('../menu')
@@ -30,7 +28,7 @@ function init (state, options) {
const initialBounds = Object.assign(config.WINDOW_INITIAL_BOUNDS, state.saved.bounds) const initialBounds = Object.assign(config.WINDOW_INITIAL_BOUNDS, state.saved.bounds)
const win = main.win = new electron.BrowserWindow({ const win = main.win = new BrowserWindow({
backgroundColor: '#282828', backgroundColor: '#282828',
darkTheme: true, // Forces dark theme (GTK+3) darkTheme: true, // Forces dark theme (GTK+3)
height: initialBounds.height, height: initialBounds.height,
@@ -45,7 +43,8 @@ function init (state, options) {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks', enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true enableRemoteModule: true,
backgroundThrottling: false
}, },
x: initialBounds.x, x: initialBounds.x,
y: initialBounds.y y: initialBounds.y
@@ -161,7 +160,7 @@ function setBounds (bounds, maximize) {
log(`setBounds: setting bounds to ${JSON.stringify(bounds)}`) log(`setBounds: setting bounds to ${JSON.stringify(bounds)}`)
if (bounds.x === null && bounds.y === null) { if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen // X and Y not specified? By default, center on current screen
const scr = electron.screen.getDisplayMatching(main.win.getBounds()) const scr = screen.getDisplayMatching(main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2)) 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)) bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2))
log(`setBounds: centered to ${JSON.stringify(bounds)}`) log(`setBounds: centered to ${JSON.stringify(bounds)}`)

View File

@@ -6,12 +6,12 @@ const webtorrent = module.exports = {
win: null win: null
} }
const electron = require('electron') const { app, BrowserWindow } = require('electron')
const config = require('../../config') const config = require('../../config')
function init () { function init () {
const win = webtorrent.win = new electron.BrowserWindow({ const win = webtorrent.win = new BrowserWindow({
backgroundColor: '#1E1E1E', backgroundColor: '#1E1E1E',
center: true, center: true,
fullscreen: false, fullscreen: false,
@@ -27,7 +27,8 @@ function init () {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks', enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true enableRemoteModule: true,
backgroundThrottling: false
}, },
width: 150 width: 150
}) })
@@ -36,7 +37,7 @@ function init () {
// Prevent killing the WebTorrent process // Prevent killing the WebTorrent process
win.on('close', function (e) { win.on('close', function (e) {
if (electron.app.isQuitting) { if (app.isQuitting) {
return return
} }
e.preventDefault() e.preventDefault()

View File

@@ -1,12 +1,10 @@
const path = require('path') const path = require('path')
const colors = require('material-ui/styles/colors') const colors = require('material-ui/styles/colors')
const electron = require('electron') const { remote } = require('electron')
const React = require('react') const React = require('react')
const PropTypes = require('prop-types') const PropTypes = require('prop-types')
const remote = electron.remote
const RaisedButton = require('material-ui/RaisedButton').default const RaisedButton = require('material-ui/RaisedButton').default
const TextField = require('material-ui/TextField').default const TextField = require('material-ui/TextField').default

View File

@@ -1,5 +1,5 @@
const React = require('react') const React = require('react')
const electron = require('electron') const { shell } = require('electron')
const ModalOKCancel = require('./modal-ok-cancel') const ModalOKCancel = require('./modal-ok-cancel')
const { dispatcher } = require('../lib/dispatcher') const { dispatcher } = require('../lib/dispatcher')
@@ -36,7 +36,7 @@ module.exports = class UnsupportedMediaModal extends React.Component {
} }
onInstall () { onInstall () {
electron.shell.openExternal('http://www.videolan.org/vlc/') shell.openExternal('http://www.videolan.org/vlc/')
// TODO: dcposch send a dispatch rather than modifying state directly // TODO: dcposch send a dispatch rather than modifying state directly
const state = this.props.state const state = this.props.state

View File

@@ -1,5 +1,5 @@
const React = require('react') const React = require('react')
const electron = require('electron') const { shell } = require('electron')
const ModalOKCancel = require('./modal-ok-cancel') const ModalOKCancel = require('./modal-ok-cancel')
const { dispatch } = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
@@ -25,7 +25,7 @@ module.exports = class UpdateAvailableModal extends React.Component {
function handleShow () { function handleShow () {
// TODO: use the GitHub urls from config.js // TODO: use the GitHub urls from config.js
electron.shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases') shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases')
dispatch('exitModal') dispatch('exitModal')
} }

View File

@@ -1,4 +1,4 @@
const electron = require('electron') const { ipcRenderer } = require('electron')
const path = require('path') const path = require('path')
const Cast = require('../lib/cast') const Cast = require('../lib/cast')
@@ -11,8 +11,6 @@ const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist') const Playlist = require('../lib/playlist')
const State = require('../lib/state') const State = require('../lib/state')
const ipcRenderer = electron.ipcRenderer
// Controls playback of torrents and files within torrents // Controls playback of torrents and files within torrents
// both local (<video>,<audio>,external player) and remote (cast) // both local (<video>,<audio>,external player) and remote (cast)
module.exports = class PlaybackController { module.exports = class PlaybackController {
@@ -158,6 +156,20 @@ module.exports = class PlaybackController {
else this.state.playing.jumpToTime = time else this.state.playing.jumpToTime = time
} }
// Show video preview
preview (x) {
if (!Number.isFinite(x)) {
console.error('Tried to preview a non-finite position ' + x)
return console.trace()
}
this.state.playing.previewXCoord = x
}
// Hide video preview
clearPreview () {
this.state.playing.previewXCoord = null
}
// Change playback speed. 1 = faster, -1 = slower // Change playback speed. 1 = faster, -1 = slower
// Playback speed ranges from 16 (fast forward) to 1 (normal playback) // Playback speed ranges from 16 (fast forward) to 1 (normal playback)
// to 0.25 (quarter-speed playback), then goes to -0.25, -0.5, -1, -2, etc // to 0.25 (quarter-speed playback), then goes to -0.25, -0.5, -1, -2, etc
@@ -270,8 +282,10 @@ module.exports = class PlaybackController {
state.playing.infoHash = infoHash state.playing.infoHash = infoHash
state.playing.fileIndex = index state.playing.fileIndex = index
state.playing.fileName = fileSummary.name state.playing.fileName = fileSummary.name
state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video' state.playing.type = TorrentPlayer.isVideo(fileSummary)
: TorrentPlayer.isAudio(fileSummary) ? 'audio' ? 'video'
: TorrentPlayer.isAudio(fileSummary)
? 'audio'
: 'other' : 'other'
// pick up where we left off // pick up where we left off

View File

@@ -1,5 +1,5 @@
const { dispatch } = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
const ipcRenderer = require('electron').ipcRenderer const { ipcRenderer } = require('electron')
// Controls the Preferences screen // Controls the Preferences screen
module.exports = class PrefsController { module.exports = class PrefsController {

View File

@@ -1,10 +1,8 @@
const electron = require('electron') const { remote } = require('electron')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const parallel = require('run-parallel') const parallel = require('run-parallel')
const remote = electron.remote
const { dispatch } = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
module.exports = class SubtitlesController { module.exports = class SubtitlesController {

View File

@@ -1,5 +1,5 @@
const path = require('path') const path = require('path')
const ipcRenderer = require('electron').ipcRenderer const { ipcRenderer } = require('electron')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
const sound = require('../lib/sound') const sound = require('../lib/sound')

View File

@@ -1,14 +1,12 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const electron = require('electron') const { ipcRenderer, remote, clipboard } = require('electron')
const { dispatch } = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
const { TorrentKeyNotFoundError } = require('../lib/errors') const { TorrentKeyNotFoundError } = require('../lib/errors')
const sound = require('../lib/sound') const sound = require('../lib/sound')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
const ipcRenderer = electron.ipcRenderer
const instantIoRegex = /^(https:\/\/)?instant\.io\/#/ const instantIoRegex = /^(https:\/\/)?instant\.io\/#/
// Controls the torrent list: creating, adding, deleting, & manipulating torrents // Controls the torrent list: creating, adding, deleting, & manipulating torrents
@@ -160,7 +158,7 @@ module.exports = class TorrentListController {
.filter((torrent) => { // We're interested in active torrents only. .filter((torrent) => { // We're interested in active torrents only.
return (['downloading', 'seeding'].indexOf(torrent.status) !== -1) return (['downloading', 'seeding'].indexOf(torrent.status) !== -1)
}) })
.map((torrent) => { // Pause all active torrents except the one that started playing. .forEach((torrent) => { // Pause all active torrents except the one that started playing.
if (infoHash === torrent.infoHash) return if (infoHash === torrent.infoHash) return
// Pause torrent without playing sounds. // Pause torrent without playing sounds.
@@ -175,7 +173,7 @@ module.exports = class TorrentListController {
resumePausedTorrents () { resumePausedTorrents () {
console.log('Playback Priority: resuming paused torrents') console.log('Playback Priority: resuming paused torrents')
if (!this.state.saved.torrentsToResume || !this.state.saved.torrentsToResume.length) return if (!this.state.saved.torrentsToResume || !this.state.saved.torrentsToResume.length) return
this.state.saved.torrentsToResume.map((infoHash) => { this.state.saved.torrentsToResume.forEach((infoHash) => {
this.toggleTorrent(infoHash) this.toggleTorrent(infoHash)
}) })
@@ -249,59 +247,59 @@ module.exports = class TorrentListController {
openTorrentContextMenu (infoHash) { openTorrentContextMenu (infoHash) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash) const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
const menu = new electron.remote.Menu() const menu = new remote.Menu()
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Remove From List', label: 'Remove From List',
click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, false) click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, false)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Remove Data File', label: 'Remove Data File',
click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, true) click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, true)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
if (torrentSummary.files) { if (torrentSummary.files) {
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: process.platform === 'darwin' ? 'Show in Finder' : 'Show in Folder', label: process.platform === 'darwin' ? 'Show in Finder' : 'Show in Folder',
click: () => showItemInFolder(torrentSummary) click: () => showItemInFolder(torrentSummary)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
} }
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Copy Magnet Link to Clipboard', label: 'Copy Magnet Link to Clipboard',
click: () => electron.clipboard.writeText(torrentSummary.magnetURI) click: () => clipboard.writeText(torrentSummary.magnetURI)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Copy Instant.io Link to Clipboard', label: 'Copy Instant.io Link to Clipboard',
click: () => electron.clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`) click: () => clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Save Torrent File As...', label: 'Save Torrent File As...',
click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey), click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey),
enabled: torrentSummary.torrentFileName != null enabled: torrentSummary.torrentFileName != null
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
const sortedByName = this.state.saved.prefs.sortByName const sortedByName = this.state.saved.prefs.sortByName
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: `${sortedByName ? '✓ ' : ''}Sort by Name`, label: `${sortedByName ? '✓ ' : ''}Sort by Name`,
click: () => dispatch('updatePreferences', 'sortByName', !sortedByName) click: () => dispatch('updatePreferences', 'sortByName', !sortedByName)
})) }))
menu.popup({ window: electron.remote.getCurrentWindow() }) menu.popup({ window: remote.getCurrentWindow() })
} }
// Takes a torrentSummary or torrentKey // Takes a torrentSummary or torrentKey
@@ -311,7 +309,7 @@ module.exports = class TorrentListController {
if (!torrentSummary) throw new TorrentKeyNotFoundError(torrentKey) if (!torrentSummary) throw new TorrentKeyNotFoundError(torrentKey)
const downloadPath = this.state.saved.prefs.downloadPath const downloadPath = this.state.saved.prefs.downloadPath
const newFileName = path.parse(torrentSummary.name).name + '.torrent' const newFileName = path.parse(torrentSummary.name).name + '.torrent'
const win = electron.remote.getCurrentWindow() const win = remote.getCurrentWindow()
const opts = { const opts = {
title: 'Save Torrent File', title: 'Save Torrent File',
defaultPath: path.join(downloadPath, newFileName), defaultPath: path.join(downloadPath, newFileName),
@@ -322,7 +320,7 @@ module.exports = class TorrentListController {
buttonLabel: 'Save' buttonLabel: 'Save'
} }
const savePath = electron.remote.dialog.showSaveDialogSync(win, opts) const savePath = remote.dialog.showSaveDialogSync(win, opts)
if (!savePath) return // They clicked Cancel if (!savePath) return // They clicked Cancel
console.log('Saving torrent ' + torrentKey + ' to ' + savePath) console.log('Saving torrent ' + torrentKey + ' to ' + savePath)

View File

@@ -7,7 +7,7 @@ module.exports = {
logPlayAttempt logPlayAttempt
} }
const electron = require('electron') const { remote } = require('electron')
const config = require('../../config') const config = require('../../config')
@@ -74,7 +74,7 @@ function reset () {
// Track screen resolution // Track screen resolution
function getScreenInfo () { function getScreenInfo () {
return electron.remote.screen.getAllDisplays().map((screen) => ({ return remote.screen.getAllDisplays().map((screen) => ({
width: screen.size.width, width: screen.size.width,
height: screen.size.height, height: screen.size.height,
scaleFactor: screen.scaleFactor scaleFactor: screen.scaleFactor

View File

@@ -130,7 +130,10 @@ function onState (err, _state) {
resumeTorrents() resumeTorrents()
// Initialize ReactDOM // Initialize ReactDOM
app = ReactDOM.render(<App state={state} />, document.querySelector('#body')) ReactDOM.render(
<App state={state} ref={elem => { app = elem }} />,
document.querySelector('#body')
)
// Calling update() updates the UI given the current state // Calling update() updates the UI given the current state
// Do this at least once a second to give every file in every torrentSummary // Do this at least once a second to give every file in every torrentSummary
@@ -274,6 +277,8 @@ const dispatchHandlers = {
previousTrack: () => controllers.playback().previousTrack(), previousTrack: () => controllers.playback().previousTrack(),
skip: (time) => controllers.playback().skip(time), skip: (time) => controllers.playback().skip(time),
skipTo: (time) => controllers.playback().skipTo(time), skipTo: (time) => controllers.playback().skipTo(time),
preview: (x) => controllers.playback().preview(x),
clearPreview: () => controllers.playback().clearPreview(),
changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir), changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir),
changeVolume: (delta) => controllers.playback().changeVolume(delta), changeVolume: (delta) => controllers.playback().changeVolume(delta),
setVolume: (vol) => controllers.playback().setVolume(vol), setVolume: (vol) => controllers.playback().setVolume(vol),

View File

@@ -1,5 +1,5 @@
const React = require('react') const React = require('react')
const Bitfield = require('bitfield') const BitField = require('bitfield').default
const prettyBytes = require('prettier-bytes') const prettyBytes = require('prettier-bytes')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
@@ -427,22 +427,77 @@ function renderCastScreen (state) {
const isStarting = state.playing.location.endsWith('-pending') const isStarting = state.playing.location.endsWith('-pending')
const castName = state.playing.castName const castName = state.playing.castName
const fileName = state.getPlayingFileSummary().name || ''
let castStatus let castStatus
if (isCast && isStarting) castStatus = 'Connecting to ' + castName + '...' if (isCast && isStarting) castStatus = 'Connecting to ' + castName + '...'
else if (isCast && !isStarting) castStatus = 'Connected to ' + castName else if (isCast && !isStarting) castStatus = 'Connected to ' + castName
else castStatus = '' else castStatus = ''
const prog = state.getPlayingTorrentSummary().progress || {}
// Show a nice title image, if possible // Show a nice title image, if possible
const style = { const style = {
backgroundImage: cssBackgroundImagePoster(state) backgroundImage: cssBackgroundImagePoster(state)
} }
function renderEta (total, downloaded) {
const missing = (total || 0) - (downloaded || 0)
const downloadSpeed = prog.downloadSpeed || 0
if (downloadSpeed === 0 || missing === 0) return
const rawEta = missing / downloadSpeed
const hours = Math.floor(rawEta / 3600) % 24
const minutes = Math.floor(rawEta / 60) % 60
const seconds = Math.floor(rawEta % 60)
const hoursStr = hours ? hours + 'h' : ''
const minutesStr = (hours || minutes) ? minutes + 'm' : ''
const secondsStr = seconds + 's'
return (<span>{hoursStr} {minutesStr} {secondsStr} remaining</span>)
}
function renderDownloadProgress () {
if (!prog.files) return
const fileProg = prog.files[state.playing.fileIndex]
const fileProgress = fileProg.numPiecesPresent / fileProg.numPieces
const fileLength = state.getPlayingFileSummary().length
const fileDownloaded = fileProgress * fileLength
const progress = Math.round(100 * fileProgress)
const total = prettyBytes(fileLength)
const completed = prettyBytes(fileDownloaded)
const downloadSpeed = prettyBytes(prog.downloadSpeed || 0)
const uploadSpeed = prettyBytes(prog.uploadSpeed || 0)
let sizes
if (fileProgress < 1) {
sizes = <span> | {completed} / {total}</span>
} else {
sizes = <span> | {completed}</span>
}
return (
<div key='download-progress'>
<span className='progress'>{progress}% downloaded {sizes}</span>
<br />
<span>↓ {downloadSpeed}/s ↑ {uploadSpeed}/s | {prog.numPeers || 0} peer(s)</span>
<br />
{renderEta(fileLength, fileDownloaded)}
</div>
)
}
return ( return (
<div key='cast' className='letterbox' style={style}> <div key='cast' className='letterbox' style={style}>
<div className='cast-screen'> <div className='cast-screen'>
<i className='icon'>{castIcon}</i> <i className='icon'>{castIcon}</i>
<div key='type' className='cast-type'>{castType}</div> <div key='type' className='cast-type'>{castType}</div>
<div key='status' className='cast-status'>{castStatus}</div> <div key='status' className='cast-status'>{castStatus}</div>
<div key='name' className='name'>{fileName}</div>
{renderDownloadProgress()}
</div> </div>
</div> </div>
) )
@@ -536,6 +591,8 @@ function renderPlayerControls (state) {
const nextClass = Playlist.hasNext(state) ? '' : 'disabled' const nextClass = Playlist.hasNext(state) ? '' : 'disabled'
const elements = [ const elements = [
renderPreview(state),
<div key='playback-bar' className='playback-bar'> <div key='playback-bar' className='playback-bar'>
{renderLoadingBar(state)} {renderLoadingBar(state)}
<div <div
@@ -547,6 +604,8 @@ function renderPlayerControls (state) {
key='scrub-bar' key='scrub-bar'
className='scrub-bar' className='scrub-bar'
draggable draggable
onMouseMove={handleScrubPreview}
onMouseOut={clearPreview}
onDragStart={handleDragStart} onDragStart={handleDragStart}
onClick={handleScrub} onClick={handleScrub}
onDrag={handleScrub} onDrag={handleScrub}
@@ -655,10 +714,14 @@ function renderPlayerControls (state) {
// Render volume slider // Render volume slider
const volume = state.playing.volume const volume = state.playing.volume
const volumeIcon = 'volume_' + ( const volumeIcon = 'volume_' + (
volume === 0 ? 'off' volume === 0
: volume < 0.3 ? 'mute' ? 'off'
: volume < 0.6 ? 'down' : volume < 0.3
: 'up') ? 'mute'
: volume < 0.6
? 'down'
: 'up'
)
const volumeStyle = { const volumeStyle = {
background: '-webkit-gradient(linear, left top, right top, ' + background: '-webkit-gradient(linear, left top, right top, ' +
'color-stop(' + (volume * 100) + '%, #eee), ' + 'color-stop(' + (volume * 100) + '%, #eee), ' +
@@ -722,6 +785,19 @@ function renderPlayerControls (state) {
} }
} }
// Handles a scrub hover (preview another position in the video)
function handleScrubPreview (e) {
// Only show for videos
if (!e.clientX || state.playing.type !== 'video') return
dispatch('mediaMouseMoved')
dispatch('preview', e.clientX)
}
function clearPreview (e) {
if (state.playing.type !== 'video') return
dispatch('clearPreview')
}
// Handles a click or drag to scrub (jump to another position in the video) // Handles a click or drag to scrub (jump to another position in the video)
function handleScrub (e) { function handleScrub (e) {
if (!e.clientX) return if (!e.clientX) return
@@ -760,6 +836,56 @@ function renderPlayerControls (state) {
} }
} }
function renderPreview (state) {
const { previewXCoord = null } = state.playing
// Calculate time from x-coord as fraction of track width
const windowWidth = document.querySelector('body').clientWidth
const fraction = previewXCoord / windowWidth
const time = fraction * state.playing.duration /* seconds */
const height = 70
let width = 0
const previewEl = document.querySelector('video#preview')
if (previewEl !== null && previewXCoord !== null) {
previewEl.currentTime = time
// Auto adjust width to maintain video aspect ratio
width = Math.floor((previewEl.videoWidth / previewEl.videoHeight) * height)
}
// Center preview window on mouse cursor,
// while avoiding falling off the left or right edges
const xPos = Math.min(Math.max(previewXCoord - (width / 2), 5), windowWidth - width - 5)
return (
<div
key='preview' style={{
position: 'absolute',
bottom: 50,
left: xPos,
display: previewXCoord == null && 'none' // Hide preview when XCoord unset
}}
>
<div style={{ width, height, backgroundColor: 'black' }}>
<video
src={Playlist.getCurrentLocalURL(state)}
id='preview'
style={{ border: '1px solid lightgrey', borderRadius: 2 }}
/>
</div>
<p
style={{
textAlign: 'center', margin: 5, textShadow: '0 0 2px rgba(0,0,0,.5)', color: '#eee'
}}
>
{formatTime(time, state.playing.duration)}
</p>
</div>
)
}
// Renders the loading bar. Shows which parts of the torrent are loaded, which // Renders the loading bar. Shows which parts of the torrent are loaded, which
// can be 'spongey' / non-contiguous // can be 'spongey' / non-contiguous
function renderLoadingBar (state) { function renderLoadingBar (state) {
@@ -779,7 +905,7 @@ function renderLoadingBar (state) {
const parts = [] const parts = []
let lastPiecePresent = false let lastPiecePresent = false
for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) { for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) {
const partPresent = Bitfield.prototype.get.call(prog.bitfield, i) const partPresent = BitField.prototype.get.call(prog.bitfield, i)
if (partPresent && !lastPiecePresent) { if (partPresent && !lastPiecePresent) {
parts.push({ start: i - fileProg.startPiece, count: 1 }) parts.push({ start: i - fileProg.startPiece, count: 1 })
} else if (partPresent) { } else if (partPresent) {

View File

@@ -5,7 +5,7 @@ console.time('init')
const crypto = require('crypto') const crypto = require('crypto')
const util = require('util') const util = require('util')
const defaultAnnounceList = require('create-torrent').announceList const defaultAnnounceList = require('create-torrent').announceList
const electron = require('electron') const { ipcRenderer } = require('electron')
const fs = require('fs') const fs = require('fs')
const mm = require('music-metadata') const mm = require('music-metadata')
const networkAddress = require('network-address') const networkAddress = require('network-address')
@@ -16,9 +16,6 @@ const config = require('../config')
const { TorrentKeyNotFoundError } = require('./lib/errors') const { TorrentKeyNotFoundError } = require('./lib/errors')
const torrentPoster = require('./lib/torrent-poster') const torrentPoster = require('./lib/torrent-poster')
// Send & receive messages from the main window
const ipc = electron.ipcRenderer
// Force use of webtorrent trackers on all torrents // Force use of webtorrent trackers on all torrents
global.WEBTORRENT_ANNOUNCE = defaultAnnounceList global.WEBTORRENT_ANNOUNCE = defaultAnnounceList
.map((arr) => arr[0]) .map((arr) => arr[0])
@@ -68,29 +65,29 @@ init()
function init () { function init () {
listenToClientEvents() listenToClientEvents()
ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) => ipcRenderer.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) =>
startTorrenting(torrentKey, torrentID, path, fileModtimes, selections)) startTorrenting(torrentKey, torrentID, path, fileModtimes, selections))
ipc.on('wt-stop-torrenting', (e, infoHash) => ipcRenderer.on('wt-stop-torrenting', (e, infoHash) =>
stopTorrenting(infoHash)) stopTorrenting(infoHash))
ipc.on('wt-create-torrent', (e, torrentKey, options) => ipcRenderer.on('wt-create-torrent', (e, torrentKey, options) =>
createTorrent(torrentKey, options)) createTorrent(torrentKey, options))
ipc.on('wt-save-torrent-file', (e, torrentKey) => ipcRenderer.on('wt-save-torrent-file', (e, torrentKey) =>
saveTorrentFile(torrentKey)) saveTorrentFile(torrentKey))
ipc.on('wt-generate-torrent-poster', (e, torrentKey) => ipcRenderer.on('wt-generate-torrent-poster', (e, torrentKey) =>
generateTorrentPoster(torrentKey)) generateTorrentPoster(torrentKey))
ipc.on('wt-get-audio-metadata', (e, infoHash, index) => ipcRenderer.on('wt-get-audio-metadata', (e, infoHash, index) =>
getAudioMetadata(infoHash, index)) getAudioMetadata(infoHash, index))
ipc.on('wt-start-server', (e, infoHash) => ipcRenderer.on('wt-start-server', (e, infoHash) =>
startServer(infoHash)) startServer(infoHash))
ipc.on('wt-stop-server', (e) => ipcRenderer.on('wt-stop-server', (e) =>
stopServer()) stopServer())
ipc.on('wt-select-files', (e, infoHash, selections) => ipcRenderer.on('wt-select-files', (e, infoHash, selections) =>
selectFiles(infoHash, selections)) selectFiles(infoHash, selections))
ipc.send('ipcReadyWebTorrent') ipcRenderer.send('ipcReadyWebTorrent')
window.addEventListener('error', (e) => window.addEventListener('error', (e) =>
ipc.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }), ipcRenderer.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }),
true) true)
setInterval(updateTorrentProgress, 1000) setInterval(updateTorrentProgress, 1000)
@@ -98,8 +95,8 @@ function init () {
} }
function listenToClientEvents () { function listenToClientEvents () {
client.on('warning', (err) => ipc.send('wt-warning', null, err.message)) client.on('warning', (err) => ipcRenderer.send('wt-warning', null, err.message))
client.on('error', (err) => ipc.send('wt-error', null, err.message)) client.on('error', (err) => ipcRenderer.send('wt-error', null, err.message))
} }
// Starts a given TorrentID, which can be an infohash, magnet URI, etc. // Starts a given TorrentID, which can be an infohash, magnet URI, etc.
@@ -133,44 +130,44 @@ function createTorrent (torrentKey, options) {
const torrent = client.seed(paths, options) const torrent = client.seed(paths, options)
torrent.key = torrentKey torrent.key = torrentKey
addTorrentEvents(torrent) addTorrentEvents(torrent)
ipc.send('wt-new-torrent') ipcRenderer.send('wt-new-torrent')
} }
function addTorrentEvents (torrent) { function addTorrentEvents (torrent) {
torrent.on('warning', (err) => torrent.on('warning', (err) =>
ipc.send('wt-warning', torrent.key, err.message)) ipcRenderer.send('wt-warning', torrent.key, err.message))
torrent.on('error', (err) => torrent.on('error', (err) =>
ipc.send('wt-error', torrent.key, err.message)) ipcRenderer.send('wt-error', torrent.key, err.message))
torrent.on('infoHash', () => torrent.on('infoHash', () =>
ipc.send('wt-parsed', torrent.key, torrent.infoHash, torrent.magnetURI)) ipcRenderer.send('wt-parsed', torrent.key, torrent.infoHash, torrent.magnetURI))
torrent.on('metadata', torrentMetadata) torrent.on('metadata', torrentMetadata)
torrent.on('ready', torrentReady) torrent.on('ready', torrentReady)
torrent.on('done', torrentDone) torrent.on('done', torrentDone)
function torrentMetadata () { function torrentMetadata () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipc.send('wt-metadata', torrent.key, info) ipcRenderer.send('wt-metadata', torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
} }
function torrentReady () { function torrentReady () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipc.send('wt-ready', torrent.key, info) ipcRenderer.send('wt-ready', torrent.key, info)
ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info) ipcRenderer.send('wt-ready-' + torrent.infoHash, torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
} }
function torrentDone () { function torrentDone () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipc.send('wt-done', torrent.key, info) ipcRenderer.send('wt-done', torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
torrent.getFileModtimes(function (err, fileModtimes) { torrent.getFileModtimes(function (err, fileModtimes) {
if (err) return onError(err) if (err) return onError(err)
ipc.send('wt-file-modtimes', torrent.key, fileModtimes) ipcRenderer.send('wt-file-modtimes', torrent.key, fileModtimes)
}) })
} }
} }
@@ -207,7 +204,7 @@ function saveTorrentFile (torrentKey) {
const fileName = torrent.infoHash + '.torrent' const fileName = torrent.infoHash + '.torrent'
if (!err) { if (!err) {
// We've already saved the file // We've already saved the file
return ipc.send('wt-file-saved', torrentKey, fileName) return ipcRenderer.send('wt-file-saved', torrentKey, fileName)
} }
// Otherwise, save the .torrent file, under the app config folder // Otherwise, save the .torrent file, under the app config folder
@@ -215,7 +212,7 @@ function saveTorrentFile (torrentKey) {
fs.writeFile(torrentPath, torrent.torrentFile, function (err) { fs.writeFile(torrentPath, torrent.torrentFile, function (err) {
if (err) return console.log('error saving torrent file %s: %o', torrentPath, err) if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
console.log('saved torrent file %s', torrentPath) console.log('saved torrent file %s', torrentPath)
return ipc.send('wt-file-saved', torrentKey, fileName) return ipcRenderer.send('wt-file-saved', torrentKey, fileName)
}) })
}) })
}) })
@@ -235,7 +232,7 @@ function generateTorrentPoster (torrentKey) {
fs.writeFile(posterFilePath, buf, function (err) { fs.writeFile(posterFilePath, buf, function (err) {
if (err) return console.log('error saving poster: %o', err) if (err) return console.log('error saving poster: %o', err)
// show the poster // show the poster
ipc.send('wt-poster', torrentKey, posterFileName) ipcRenderer.send('wt-poster', torrentKey, posterFileName)
}) })
}) })
}) })
@@ -247,7 +244,7 @@ function updateTorrentProgress () {
if (prevProgress && util.isDeepStrictEqual(progress, prevProgress)) { if (prevProgress && util.isDeepStrictEqual(progress, prevProgress)) {
return /* don't send heavy object if it hasn't changed */ return /* don't send heavy object if it hasn't changed */
} }
ipc.send('wt-progress', progress) ipcRenderer.send('wt-progress', progress)
prevProgress = progress prevProgress = progress
} }
@@ -317,8 +314,8 @@ function startServerFromReadyTorrent (torrent, cb) {
networkAddress: networkAddress() networkAddress: networkAddress()
} }
ipc.send('wt-server-running', info) ipcRenderer.send('wt-server-running', info)
ipc.send('wt-server-' + torrent.infoHash, info) ipcRenderer.send('wt-server-' + torrent.infoHash, info)
}) })
} }
@@ -336,14 +333,14 @@ function getAudioMetadata (infoHash, index) {
// Set initial matadata to display the filename first. // Set initial matadata to display the filename first.
const metadata = { title: file.name } const metadata = { title: file.name }
ipc.send('wt-audio-metadata', infoHash, index, metadata) ipcRenderer.send('wt-audio-metadata', infoHash, index, metadata)
const options = { const options = {
native: false, native: false,
skipCovers: true, skipCovers: true,
fileSize: file.length, fileSize: file.length,
observer: event => { observer: event => {
ipc.send('wt-audio-metadata', infoHash, index, { ipcRenderer.send('wt-audio-metadata', infoHash, index, {
common: metadata.common, common: metadata.common,
format: metadata.format format: metadata.format
}) })
@@ -358,7 +355,7 @@ function getAudioMetadata (infoHash, index) {
onMetadata onMetadata
.then( .then(
metadata => { metadata => {
ipc.send('wt-audio-metadata', infoHash, index, metadata) ipcRenderer.send('wt-audio-metadata', infoHash, index, metadata)
console.log(`metadata for file='${file.name}' completed.`) console.log(`metadata for file='${file.name}' completed.`)
}, },
err => { err => {

View File

@@ -10,7 +10,7 @@ Actions=CreateNewTorrent;OpenTorrentFile;OpenTorrentAddress;
<% if (mimeType && mimeType.length) { %>MimeType=<%= mimeType.join(';') %>;<% } %> <% if (mimeType && mimeType.length) { %>MimeType=<%= mimeType.join(';') %>;<% } %>
<% if (categories && categories.length) { %>Categories=<%= categories.join(';') %>;<% } %> <% if (categories && categories.length) { %>Categories=<%= categories.join(';') %>;<% } %>
StartupNotify=true StartupNotify=true
<% if (name) { %>StartupWMClass=<%= name %> <% } %> StartupWMClass=<%= productName %>
[Desktop Action CreateNewTorrent] [Desktop Action CreateNewTorrent]
Name=Create New Torrent... Name=Create New Torrent...

View File

@@ -558,6 +558,13 @@ body.drag .app::after {
width: 100%; width: 100%;
} }
.player .name {
font-size: 20px;
font-weight: bold;
line-height: 1.5em;
margin-bottom: 6px;
}
/* /*
* PLAYER CONTROLS * PLAYER CONTROLS
*/ */