Compare commits

..

3 Commits

Author SHA1 Message Date
Feross Aboukhadijeh
b65b186157 0.23.0 2020-07-15 20:08:06 -07:00
Feross Aboukhadijeh
0945901cc0 Update CHANGELOG.md 2020-07-15 20:07:32 -07:00
Feross Aboukhadijeh
e60c8a2144 update dep 2020-07-15 20:07:24 -07:00
52 changed files with 4460 additions and 5654 deletions

1
.gitignore vendored
View File

@@ -2,7 +2,6 @@ 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,5 +1,3 @@
language: node_js language: node_js
node_js: node_js:
- lts/* - lts/*
cache:
npm: false

View File

@@ -77,7 +77,5 @@
- Ameet Kaustav (akaustav@users.noreply.github.com) - Ameet Kaustav (akaustav@users.noreply.github.com)
- gpatarin (gael.patarin@outlook.com) - gpatarin (gael.patarin@outlook.com)
- Gael Patarin (gael.patarin@outlook.com) - Gael Patarin (gael.patarin@outlook.com)
- Subin Siby (mail@subinsb.com)
- Hinara (hinara.turevel@gmail.com)
#### Generated by bin/update-authors.sh. #### Generated by bin/update-authors.sh.

View File

@@ -1,38 +1,15 @@
# WebTorrent Desktop Version History # WebTorrent Desktop Version History
## v0.24.0 - 2020-08-28
### Added
- Support the `.m2ts` video container format ([hicom150](https://github.com/hicom150))
### Changed
- Update to Electron 10.1.0 [\#1864](https://github.com/webtorrent/webtorrent-desktop/pull/1864) ([feross](https://github.com/feross))
- Update the Windows installer loading image [\#1841](https://github.com/webtorrent/webtorrent-desktop/pull/1841) ([alxhotel](https://github.com/alxhotel))
### Fixed
- Fix music metadata not showing up [\#1847](https://github.com/webtorrent/webtorrent-desktop/pull/1847) ([Borewit](https://github.com/Borewit))
- Fix the "Play in VLC" functionality [\#1850](https://github.com/webtorrent/webtorrent-desktop/pull/1850) ([Hinara](https://github.com/Hinara))
- Prevent shortcuts from activating when user input elements are focused [\#1840](https://github.com/webtorrent/webtorrent-desktop/pull/1840) ([subins2000](https://github.com/subins2000))
## 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. [\#1837](https://github.com/webtorrent/webtorrent-desktop/issues/1837#issuecomment-729320901) This release contains a critical security fix.
### Added
- Add macOS Notarization [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834) ([feross](https://github.com/feross))
### Changed ### Changed
- Update to Electron 10 beta [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834) ([feross](https://github.com/feross)) - Update to Electron 10 beta [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834)
## v0.22.0 - 2020-07-15 ## v0.22.0 - 2020-07-15
❤️✨ A new version of WebTorrent Desktop is out! ❤️✨
### Added ### Added
- Linux `.rpm` packages and `arm64` builds are now available! [\#1694](https://github.com/webtorrent/webtorrent-desktop/pull/1694) ([hicom150](https://github.com/hicom150)) - Linux `.rpm` packages and `arm64` builds are now available! [\#1694](https://github.com/webtorrent/webtorrent-desktop/pull/1694) ([hicom150](https://github.com/hicom150))
@@ -46,7 +23,7 @@
- Update to Electron 9 [\#1729](https://github.com/webtorrent/webtorrent-desktop/pull/1729) [\#1832](https://github.com/webtorrent/webtorrent-desktop/issues/1832) - Update to Electron 9 [\#1729](https://github.com/webtorrent/webtorrent-desktop/pull/1729) [\#1832](https://github.com/webtorrent/webtorrent-desktop/issues/1832)
- Update to music-metadata 4.8.0 [\#1719](https://github.com/webtorrent/webtorrent-desktop/pull/1719) ([Borewit](https://github.com/Borewit)) - Update to music-metadata 4.8.0 [\#1719](https://github.com/webtorrent/webtorrent-desktop/pull/1719) ([Borewit](https://github.com/Borewit))
- Update Windows build documentation [\#1715](https://github.com/webtorrent/webtorrent-desktop/pull/1715) ([RecoX](https://github.com/RecoX)) - Update Windows build documentation [\#1715](https://github.com/webtorrent/webtorrent-desktop/pull/1715) ([RecoX](https://github.com/RecoX))
- Remove unneeded dependencies ([feross](https://github.com/feross)) - Remove unneeded dependencies
### Fixed ### Fixed
@@ -55,8 +32,8 @@
- Fix percentage rounding error [\#1716](https://github.com/webtorrent/webtorrent-desktop/pull/1716) ([pR0Ps](https://github.com/pR0Ps)) - Fix percentage rounding error [\#1716](https://github.com/webtorrent/webtorrent-desktop/pull/1716) ([pR0Ps](https://github.com/pR0Ps))
- Fix path-selector in preferences page [\#1702](https://github.com/webtorrent/webtorrent-desktop/pull/1702) ([314eter](https://github.com/314eter)) - Fix path-selector in preferences page [\#1702](https://github.com/webtorrent/webtorrent-desktop/pull/1702) ([314eter](https://github.com/314eter))
- Fix path-selector in preferences page [\#1704](https://github.com/webtorrent/webtorrent-desktop/pull/1702) ([mathiasvr](https://github.com/mathiasvr)) - Fix path-selector in preferences page [\#1704](https://github.com/webtorrent/webtorrent-desktop/pull/1702) ([mathiasvr](https://github.com/mathiasvr))
- Fix: Increase height of 'About' window [\#1737](https://github.com/webtorrent/webtorrent-desktop/pull/1737) ([akaustav](https://github.com/akaustav)) - Fix: Increase height of 'About' window [\#1737](https://github.com/webtorrent/webtorrent-desktop/pull/1737)
- Fix "Save Torrent File As..." [\#1743](https://github.com/webtorrent/webtorrent-desktop/pull/1743) ([gpatarin](https://github.com/gpatarin)) - Fix "Save Torrent File As..." [\#1743](https://github.com/webtorrent/webtorrent-desktop/pull/1743)
## v0.21.0 - 2019-09-14 ## v0.21.0 - 2019-09-14

View File

@@ -74,29 +74,17 @@
**This is the most important part.** **This is the most important part.**
- Manually download the binaries for each platform from Github. - Manually download the binaries for each platform from Github.
**Do not use your locally built binaries.** Modern OSs treat executables differently if they've **Do not use your locally built binaries.** Modern OSs treat executables differently if they've
been downloaded, even though the files are byte for byte identical. This ensures that the been downloaded, even though the files are byte for byte identical. This ensures that the
codesigning worked and is valid. codesigning worked and is valid.
- Smoke test WebTorrent Desktop on each platform. Before a release, check that the following basic use cases work correctly: - Smoke test WebTorrent Desktop on each platform.
1. Click "Play" to stream a built-in torrent (e.g. Sintel) See Smoke Tests below for details. Open DevTools
- Ensure that seeking to undownloaded region works and plays immediately. on Windows and Mac, and ensure that the auto updater is running. If the auto updater does not
- Ensure that sintel.mp4 gets downloaded to `~/Downloads`. run, users will successfully auto update to this new version, and then be stuck there forever.
2. Check that the auto-updater works
- Open the console and check for the line "No update available" to indicate that the auto-updater is working. (If the auto updater does not run, users will successfully auto update to this new version, and then be stuck there forever.)
3. Add a new .torrent file via drag-and-drop.
- Ensure that it gets added to the list and starts downloading.
4. Remove a torrent from the client
- Ensure that the file is removed from `~/Downloads`
5. Create and seed a new a torrent via drag-and-drop.
- Ensure that the torrent gets created and seeding begins.
### 4. Ship it ### 4. Ship it
@@ -105,5 +93,25 @@
Create a pull request in [webtorrent.io](https://github.com/webtorrent/webtorrent.io). Update Create a pull request in [webtorrent.io](https://github.com/webtorrent/webtorrent.io). Update
`config.js`, updating the desktop app version. `config.js`, updating the desktop app version.
Once this PR is merged and Feross redeploys the WebTorrent website, As soon as this PR is merged, Jenkins will automatically redeploy the WebTorrent website, and
hundreds of thousands of users around the world will start auto updating. **Merge with care.** hundreds of thousands of users around the world will start auto updating. **Merge with care.**
## Smoke Tests
Before a release, check that the following basic use cases work correctly:
1. Click "Play" to stream a built-in torrent (e.g. Sintel)
- Ensure that seeking to undownloaded region works and plays immediately.
- Ensure that sintel.mp4 gets downloaded to `~/Downloads`.
2. Check that the auto-updater works
- Open the console and check for the line "No update available" to indicate that the auto-updater is working.
3. Add a new .torrent file via drag-and-drop.
- Ensure that it gets added to the list and starts downloading.
4. Remove a torrent from the client
- Ensure that the file is removed from `~/Downloads`
5. Create and seed a new a torrent via drag-and-drop.
- Ensure that the torrent gets created and seeding begins.

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
</dict>
</plist>

39
bin/extra-lint.js Executable file
View File

@@ -0,0 +1,39 @@
#!/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

@@ -266,7 +266,6 @@ function buildDarwin (cb) {
function signApp (cb) { function signApp (cb) {
const sign = require('electron-osx-sign') const sign = require('electron-osx-sign')
const { notarize } = require('electron-notarize')
/* /*
* Sign the app with Apple Developer ID certificates. We sign the app for 2 reasons: * Sign the app with Apple Developer ID certificates. We sign the app for 2 reasons:
@@ -282,37 +281,16 @@ function buildDarwin (cb) {
* - Membership in the Apple Developer Program * - Membership in the Apple Developer Program
*/ */
const signOpts = { const signOpts = {
verbose: true,
app: appPath, app: appPath,
platform: 'darwin', platform: 'darwin',
identity: 'Developer ID Application: WebTorrent, LLC (5MAMC8G3L8)', verbose: true
hardenedRuntime: true,
entitlements: path.join(config.ROOT_PATH, 'bin', 'darwin-entitlements.plist'),
'entitlements-inherit': path.join(config.ROOT_PATH, 'bin', 'darwin-entitlements.plist'),
'signature-flags': 'library'
}
const notarizeOpts = {
appBundleId: darwin.appBundleId,
appPath,
appleId: 'feross@feross.org',
appleIdPassword: '@keychain:AC_PASSWORD'
} }
console.log('Mac: Signing app...') console.log('Mac: Signing app...')
sign(signOpts, function (err) { sign(signOpts, function (err) {
if (err) return cb(err) if (err) return cb(err)
console.log('Mac: Signed app.') console.log('Mac: Signed app.')
cb(null)
console.log('Mac: Notarizing app...')
notarize(notarizeOpts).then(
function () {
console.log('Mac: Notarized app.')
cb(null)
},
function (err) {
cb(err)
})
}) })
} }
@@ -545,17 +523,7 @@ 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(

9147
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,83 +1,71 @@
{ {
"name": "webtorrent-desktop", "name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.", "description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.24.0", "version": "0.23.0",
"author": { "author": {
"name": "WebTorrent, LLC", "name": "WebTorrent, LLC",
"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.2.0", "arch": "^2.1.2",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"bitfield": "^4.0.0", "bitfield": "^3.0.0",
"capture-frame": "^4.0.0", "capture-frame": "^3.0.2",
"chokidar": "^3.4.3", "chokidar": "^3.4.1",
"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.1.0", "drag-drop": "^6.0.2",
"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.3",
"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.5.0", "music-metadata": "6.3.6",
"network-address": "^1.1.2", "network-address": "^1.1.2",
"parse-torrent": "^9.1.0", "parse-torrent": "^7.1.3",
"prettier-bytes": "^1.0.4", "prettier-bytes": "^1.0.4",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^17.0.1", "react": "^16.13.1",
"react-dom": "^17.0.1", "react-dom": "^16.13.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"run-parallel": "^1.1.10", "run-parallel": "^1.1.9",
"semver": "^7.3.2", "semver": "^7.3.2",
"simple-concat": "^1.0.1", "simple-concat": "^1.0.0",
"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.111.0", "webtorrent": ">=0.108.6",
"winreg": "^1.2.4" "winreg": "^1.2.4"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.8", "babel-eslint": "^10.1.0",
"@babel/core": "^7.12.9", "buble": "^0.20.0",
"@babel/eslint-parser": "^7.12.1", "cross-zip": "^3.1.0",
"@babel/plugin-transform-react-jsx": "^7.12.7", "depcheck": "^1.0.0",
"cross-zip": "^4.0.0", "electron": "~9.1.0",
"depcheck": "^1.3.1", "electron-osx-sign": "^0.4.17",
"electron": "~11.0.3", "electron-packager": "^15.0.0",
"electron-notarize": "^1.0.0",
"electron-osx-sign": "^0.5.0",
"electron-packager": "^15.1.0",
"electron-winstaller": "^4.0.1", "electron-winstaller": "^4.0.1",
"gh-release": "^4.0.3", "gh-release": "^3.5.0",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"nodemon": "^2.0.6", "nodemon": "^2.0.4",
"open": "^7.3.0", "open": "^7.0.4",
"plist": "^3.0.1", "plist": "^3.0.1",
"pngjs": "^6.0.0", "pngjs": "^5.0.0",
"run-series": "^1.1.9", "run-series": "^1.1.8",
"spectron": "~12.0.0", "spectron": "~11.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"
@@ -98,7 +86,7 @@
"optionalDependencies": { "optionalDependencies": {
"appdmg": "^0.6.0", "appdmg": "^0.6.0",
"electron-installer-debian": "^3.1.0", "electron-installer-debian": "^3.1.0",
"electron-installer-redhat": "^3.2.0" "electron-installer-redhat": "^3.1.0"
}, },
"private": true, "private": true,
"productName": "WebTorrent", "productName": "WebTorrent",
@@ -107,19 +95,19 @@
"url": "git://github.com/webtorrent/webtorrent-desktop.git" "url": "git://github.com/webtorrent/webtorrent-desktop.git"
}, },
"scripts": { "scripts": {
"build": "babel src --out-dir build", "build": "buble src --target chrome:71 --output 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-parser --ignore-dirs=build,dist", "test": "standard && depcheck --ignores=standard,babel-eslint --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"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" "parser": "babel-eslint"
} }
} }

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 { app, remote } = require('electron') const electron = 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 remote.app.getPath(key) return electron.remote.app.getPath(key)
} else { } else {
// Electron main process // Electron main process
return app.getPath(key) return electron.app.getPath(key)
} }
} }

View File

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

View File

@@ -2,7 +2,7 @@ module.exports = {
init init
} }
const { dialog } = require('electron') const electron = 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) {
} }
} }
dialog.showMessageBox({ electron.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 { dialog } = require('electron') const electron = 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 = dialog.showOpenDialogSync(windows.main.win, opts) const selectedPaths = electron.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 = dialog.showOpenDialogSync(windows.main.win, opts) const selectedPaths = electron.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 = dialog.showOpenDialogSync(windows.main.win, opts) const selectedPaths = electron.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

@@ -17,12 +17,12 @@ let proc = null
function checkInstall (playerPath, cb) { function checkInstall (playerPath, cb) {
// check for VLC if external player has not been specified by the user // check for VLC if external player has not been specified by the user
// otherwise assume the player is installed // otherwise assume the player is installed
if (!playerPath) return vlcCommand(cb) if (playerPath == null) return vlcCommand(cb)
process.nextTick(() => cb(null)) process.nextTick(() => cb(null))
} }
function spawn (playerPath, url, title) { function spawn (playerPath, url, title) {
if (playerPath) return spawnExternal(playerPath, [url]) if (playerPath != null) return spawnExternal(playerPath, [url])
// Try to find and use VLC if external player is not specified // Try to find and use VLC if external player is not specified
vlcCommand((err, vlcPath) => { vlcCommand((err, vlcPath) => {

View File

@@ -25,7 +25,8 @@ function uninstall () {
} }
function installDarwin () { function installDarwin () {
const { app } = require('electron') const electron = 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,14 +1,12 @@
console.time('init') console.time('init')
const { app, ipcMain } = require('electron') const electron = require('electron')
const app = electron.app
// Start crash reporter early, so it takes effect for child processes
const crashReporter = require('../crash-reporter')
crashReporter.init()
const parallel = require('run-parallel') const parallel = require('run-parallel')
const config = require('../config') const config = require('../config')
const crashReporter = require('../crash-reporter')
const ipc = require('./ipc') const ipc = require('./ipc')
const log = require('./log') const log = require('./log')
const menu = require('./menu') const menu = require('./menu')
@@ -66,6 +64,8 @@ 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
@@ -109,6 +109,10 @@ function init () {
ipc.init() ipc.init()
app.once('will-finish-launching', function () {
crashReporter.init()
})
app.once('ipcReady', function () { app.once('ipcReady', function () {
log('Command line args:', argv) log('Command line args:', argv)
processArgv(argv) processArgv(argv)
@@ -195,13 +199,9 @@ 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( return argv.slice(config.IS_PRODUCTION ? 1
config.IS_PRODUCTION : config.IS_TEST ? 4
? 1 : 2)
: config.IS_TEST
? 4
: 2
)
} }
function processArgv (argv) { function processArgv (argv) {

View File

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

View File

@@ -8,9 +8,11 @@ module.exports.error = error
* where they can be viewed in Developer Tools. * where they can be viewed in Developer Tools.
*/ */
const { app } = require('electron') const electron = 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,7 +8,9 @@ module.exports = {
onToggleFullScreen onToggleFullScreen
} }
const { app, Menu } = require('electron') const electron = require('electron')
const app = electron.app
const config = require('../config') const config = require('../config')
const windows = require('./windows') const windows = require('./windows')
@@ -16,8 +18,8 @@ const windows = require('./windows')
let menu = null let menu = null
function init () { function init () {
menu = Menu.buildFromTemplate(getMenuTemplate()) menu = electron.Menu.buildFromTemplate(getMenuTemplate())
Menu.setApplicationMenu(menu) electron.Menu.setApplicationMenu(menu)
} }
function togglePlaybackControls (flag) { function togglePlaybackControls (flag) {

View File

@@ -3,7 +3,7 @@ module.exports = {
disable disable
} }
const { powerSaveBlocker } = require('electron') const electron = 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 (powerSaveBlocker.isStarted(blockId)) { if (electron.powerSaveBlocker.isStarted(blockId)) {
// If a power saver block already exists, do nothing. // If a power saver block already exists, do nothing.
return return
} }
blockId = powerSaveBlocker.start('prevent-display-sleep') blockId = electron.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 (!powerSaveBlocker.isStarted(blockId)) { if (!electron.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
} }
powerSaveBlocker.stop(blockId) electron.powerSaveBlocker.stop(blockId)
log(`powerSaveBlocker.disable: ${blockId}`) log(`powerSaveBlocker.disable: ${blockId}`)
} }

View File

@@ -5,7 +5,7 @@ module.exports = {
moveItemToTrash moveItemToTrash
} }
const { shell } = require('electron') const electron = require('electron')
const log = require('./log') const log = require('./log')
/** /**
@@ -13,16 +13,15 @@ const log = require('./log')
*/ */
function openExternal (url) { function openExternal (url) {
log(`openExternal: ${url}`) log(`openExternal: ${url}`)
shell.openExternal(url) electron.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}`)
shell.openPath(path) electron.shell.openPath(path)
} }
/** /**
@@ -30,7 +29,7 @@ function openPath (path) {
*/ */
function showItemInFolder (path) { function showItemInFolder (path) {
log(`showItemInFolder: ${path}`) log(`showItemInFolder: ${path}`)
shell.showItemInFolder(path) electron.shell.showItemInFolder(path)
} }
/** /**
@@ -38,5 +37,5 @@ function showItemInFolder (path) {
*/ */
function moveItemToTrash (path) { function moveItemToTrash (path) {
log(`moveItemToTrash: ${path}`) log(`moveItemToTrash: ${path}`)
shell.moveItemToTrash(path) electron.shell.moveItemToTrash(path)
} }

View File

@@ -3,20 +3,20 @@ module.exports = {
enable enable
} }
const { globalShortcut } = require('electron') const electron = 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.
globalShortcut.register( electron.globalShortcut.register(
'MediaPlayPause', 'MediaPlayPause',
() => windows.main.dispatch('playPause') () => windows.main.dispatch('playPause')
) )
globalShortcut.register( electron.globalShortcut.register(
'MediaNextTrack', 'MediaNextTrack',
() => windows.main.dispatch('nextTrack') () => windows.main.dispatch('nextTrack')
) )
globalShortcut.register( electron.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.
globalShortcut.unregister('MediaPlayPause') electron.globalShortcut.unregister('MediaPlayPause')
globalShortcut.unregister('MediaNextTrack') electron.globalShortcut.unregister('MediaNextTrack')
globalShortcut.unregister('MediaPreviousTrack') electron.globalShortcut.unregister('MediaPreviousTrack')
} }

View File

@@ -2,39 +2,150 @@ module.exports = {
handleEvent handleEvent
} }
const { app } = require('electron') const cp = require('child_process')
const electron = require('electron')
const fs = require('fs')
const os = require('os')
const path = require('path') const path = require('path')
const spawn = require('child_process').spawn
const app = electron.app
const handlers = require('./handlers') const handlers = require('./handlers')
const EXE_NAME = path.basename(process.execPath) const EXE_NAME = path.basename(process.execPath)
const UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe') const UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe')
const run = function (args, done) {
spawn(UPDATE_EXE, args, { detached: true })
.on('close', done)
}
function handleEvent (cmd) { function handleEvent (cmd) {
if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') { if (cmd === '--squirrel-install') {
run([`--createShortcut=${EXE_NAME}`], app.quit) // App was installed. Install desktop/start menu shortcuts.
createShortcuts(function () {
// Ensure user sees install splash screen so they realize that Setup.exe actually
// installed an application and isn't the application itself.
setTimeout(function () {
app.quit()
}, 3000)
})
return true
}
if (cmd === '--squirrel-updated') {
// App was updated. (Called on new version of app)
updateShortcuts(function () {
app.quit()
})
return true return true
} }
if (cmd === '--squirrel-uninstall') { if (cmd === '--squirrel-uninstall') {
// App was just uninstalled. Undo anything we did in the --squirrel-install and
// --squirrel-updated handlers
// Uninstall .torrent file and magnet link handlers // Uninstall .torrent file and magnet link handlers
handlers.uninstall() handlers.uninstall()
run([`--removeShortcut=${EXE_NAME}`], app.quit) // Remove desktop/start menu shortcuts.
// HACK: add a callback to handlers.uninstall() so we can remove this setTimeout
setTimeout(function () {
removeShortcuts(function () {
app.quit()
})
}, 1000)
return true return true
} }
if (cmd === '--squirrel-obsolete') { if (cmd === '--squirrel-obsolete') {
// App will be updated. (Called on outgoing version of app)
app.quit() app.quit()
return true return true
} }
if (cmd === '--squirrel-firstrun') {
// App is running for the first time. Do not quit, allow startup to continue.
return false
}
return false return false
} }
/**
* Spawn a command and invoke the callback when it completes with an error and
* the output from standard out.
*/
function spawn (command, args, cb) {
let stdout = ''
let error = null
let child = null
try {
child = cp.spawn(command, args)
} catch (err) {
// Spawn can throw an error
process.nextTick(function () {
cb(error, stdout)
})
return
}
child.stdout.on('data', function (data) {
stdout += data
})
child.on('error', function (processError) {
error = processError
})
child.on('close', function (code, signal) {
if (code !== 0 && !error) error = new Error('Command failed: #{signal || code}')
if (error) error.stdout = stdout
cb(error, stdout)
})
}
/**
* Spawn the Squirrel `Update.exe` command with the given arguments and invoke
* the callback when the command completes.
*/
function spawnUpdate (args, cb) {
spawn(UPDATE_EXE, args, cb)
}
/**
* Create desktop and start menu shortcuts using the Squirrel `Update.exe`
* command.
*/
function createShortcuts (cb) {
spawnUpdate(['--createShortcut', EXE_NAME], cb)
}
/**
* Update desktop and start menu shortcuts using the Squirrel `Update.exe`
* command.
*/
function updateShortcuts (cb) {
const homeDir = os.homedir()
if (homeDir) {
const desktopShortcutPath = path.join(homeDir, 'Desktop', 'WebTorrent.lnk')
// If the desktop shortcut was deleted by the user, then keep it deleted.
fs.access(desktopShortcutPath, function (err) {
const desktopShortcutExists = !err
createShortcuts(function () {
if (desktopShortcutExists) {
cb()
} else {
// Remove the unwanted desktop shortcut that was recreated
fs.unlink(desktopShortcutPath, cb)
}
})
})
} else {
createShortcuts(cb)
}
}
/**
* Remove desktop and start menu shortcuts using the Squirrel `Update.exe`
* command.
*/
function removeShortcuts (cb) {
spawnUpdate(['--removeShortcut', EXE_NAME], cb)
}

View File

@@ -4,7 +4,9 @@ module.exports = {
setWindowFocus setWindowFocus
} }
const { app, Tray, Menu } = require('electron') const electron = require('electron')
const app = electron.app
const config = require('../config') const config = require('../config')
const windows = require('./windows') const windows = require('./windows')
@@ -65,7 +67,7 @@ function checkLinuxTraySupport (cb) {
} }
function createTray () { function createTray () {
tray = new Tray(getIconPath()) tray = new electron.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.
@@ -76,7 +78,7 @@ function createTray () {
} }
function updateTrayMenu () { function updateTrayMenu () {
const contextMenu = Menu.buildFromTemplate(getMenuTemplate()) const contextMenu = electron.Menu.buildFromTemplate(getMenuTemplate())
tray.setContextMenu(contextMenu) tray.setContextMenu(contextMenu)
} }

View File

@@ -2,7 +2,7 @@ module.exports = {
init init
} }
const { autoUpdater } = require('electron') const electron = 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 () {
autoUpdater.on( electron.autoUpdater.on(
'error', 'error',
(err) => log.error(`Update error: ${err.message}`) (err) => log.error(`Update error: ${err.message}`)
) )
autoUpdater.on( electron.autoUpdater.on(
'checking-for-update', 'checking-for-update',
() => log('Checking for update') () => log('Checking for update')
) )
autoUpdater.on( electron.autoUpdater.on(
'update-available', 'update-available',
() => log('Update available') () => log('Update available')
) )
autoUpdater.on( electron.autoUpdater.on(
'update-not-available', 'update-not-available',
() => log('No update available') () => log('No update available')
) )
autoUpdater.on( electron.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}`)
) )
autoUpdater.setFeedURL({ url: AUTO_UPDATE_URL }) electron.autoUpdater.setFeedURL({ url: AUTO_UPDATE_URL })
autoUpdater.checkForUpdates() electron.autoUpdater.checkForUpdates()
} }

View File

@@ -2,7 +2,9 @@ module.exports = {
init init
} }
const { app } = require('electron') const electron = 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 { BrowserWindow } = require('electron') const electron = 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 BrowserWindow({ const win = about.win = new electron.BrowserWindow({
backgroundColor: '#ECECEC', backgroundColor: '#ECECEC',
center: true, center: true,
fullscreen: false, fullscreen: false,
@@ -26,9 +26,7 @@ function init () {
useContentSize: true, useContentSize: true,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks', enableBlinkFeatures: 'AudioVideoTracks'
enableRemoteModule: true,
backgroundThrottling: false
}, },
width: 300 width: 300
}) })

View File

@@ -14,9 +14,11 @@ const main = module.exports = {
win: null win: null
} }
const { app, BrowserWindow, screen } = require('electron') const electron = 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')
@@ -28,7 +30,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 BrowserWindow({ const win = main.win = new electron.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,
@@ -42,9 +44,7 @@ function init (state, options) {
width: initialBounds.width, width: initialBounds.width,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks', enableBlinkFeatures: 'AudioVideoTracks'
enableRemoteModule: true,
backgroundThrottling: false
}, },
x: initialBounds.x, x: initialBounds.x,
y: initialBounds.y y: initialBounds.y
@@ -160,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 = screen.getDisplayMatching(main.win.getBounds()) const scr = electron.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 { app, BrowserWindow } = require('electron') const electron = require('electron')
const config = require('../../config') const config = require('../../config')
function init () { function init () {
const win = webtorrent.win = new BrowserWindow({ const win = webtorrent.win = new electron.BrowserWindow({
backgroundColor: '#1E1E1E', backgroundColor: '#1E1E1E',
center: true, center: true,
fullscreen: false, fullscreen: false,
@@ -26,9 +26,7 @@ function init () {
useContentSize: true, useContentSize: true,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks', enableBlinkFeatures: 'AudioVideoTracks'
enableRemoteModule: true,
backgroundThrottling: false
}, },
width: 150 width: 150
}) })
@@ -37,7 +35,7 @@ function init () {
// Prevent killing the WebTorrent process // Prevent killing the WebTorrent process
win.on('close', function (e) { win.on('close', function (e) {
if (app.isQuitting) { if (electron.app.isQuitting) {
return return
} }
e.preventDefault() e.preventDefault()

View File

@@ -1,10 +1,12 @@
const path = require('path') const path = require('path')
const colors = require('material-ui/styles/colors') const colors = require('material-ui/styles/colors')
const { remote } = require('electron') const electron = 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 { shell } = require('electron') const electron = 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 () {
shell.openExternal('http://www.videolan.org/vlc/') electron.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 { shell } = require('electron') const electron = 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
shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases') electron.shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases')
dispatch('exitModal') dispatch('exitModal')
} }

View File

@@ -1,4 +1,4 @@
const { ipcRenderer } = require('electron') const electron = require('electron')
const path = require('path') const path = require('path')
const Cast = require('../lib/cast') const Cast = require('../lib/cast')
@@ -11,6 +11,8 @@ 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 {
@@ -156,20 +158,6 @@ 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
@@ -282,10 +270,8 @@ 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) state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video'
? 'video' : TorrentPlayer.isAudio(fileSummary) ? 'audio'
: 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') const ipcRenderer = require('electron').ipcRenderer
// Controls the Preferences screen // Controls the Preferences screen
module.exports = class PrefsController { module.exports = class PrefsController {

View File

@@ -1,8 +1,10 @@
const { remote } = require('electron') const electron = 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') const ipcRenderer = require('electron').ipcRenderer
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,12 +1,14 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const { ipcRenderer, remote, clipboard } = require('electron') const electron = 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
@@ -158,7 +160,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)
}) })
.forEach((torrent) => { // Pause all active torrents except the one that started playing. .map((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.
@@ -173,7 +175,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.forEach((infoHash) => { this.state.saved.torrentsToResume.map((infoHash) => {
this.toggleTorrent(infoHash) this.toggleTorrent(infoHash)
}) })
@@ -247,59 +249,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 remote.Menu() const menu = new electron.remote.Menu()
menu.append(new remote.MenuItem({ menu.append(new electron.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 remote.MenuItem({ menu.append(new electron.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 remote.MenuItem({ menu.append(new electron.remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
if (torrentSummary.files) { if (torrentSummary.files) {
menu.append(new remote.MenuItem({ menu.append(new electron.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 remote.MenuItem({ menu.append(new electron.remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
} }
menu.append(new remote.MenuItem({ menu.append(new electron.remote.MenuItem({
label: 'Copy Magnet Link to Clipboard', label: 'Copy Magnet Link to Clipboard',
click: () => clipboard.writeText(torrentSummary.magnetURI) click: () => electron.clipboard.writeText(torrentSummary.magnetURI)
})) }))
menu.append(new remote.MenuItem({ menu.append(new electron.remote.MenuItem({
label: 'Copy Instant.io Link to Clipboard', label: 'Copy Instant.io Link to Clipboard',
click: () => clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`) click: () => electron.clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`)
})) }))
menu.append(new remote.MenuItem({ menu.append(new electron.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 remote.MenuItem({ menu.append(new electron.remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
const sortedByName = this.state.saved.prefs.sortByName const sortedByName = this.state.saved.prefs.sortByName
menu.append(new remote.MenuItem({ menu.append(new electron.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: remote.getCurrentWindow() }) menu.popup({ window: electron.remote.getCurrentWindow() })
} }
// Takes a torrentSummary or torrentKey // Takes a torrentSummary or torrentKey
@@ -309,7 +311,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 = remote.getCurrentWindow() const win = electron.remote.getCurrentWindow()
const opts = { const opts = {
title: 'Save Torrent File', title: 'Save Torrent File',
defaultPath: path.join(downloadPath, newFileName), defaultPath: path.join(downloadPath, newFileName),
@@ -320,7 +322,7 @@ module.exports = class TorrentListController {
buttonLabel: 'Save' buttonLabel: 'Save'
} }
const savePath = remote.dialog.showSaveDialogSync(win, opts) const savePath = electron.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

@@ -5,7 +5,7 @@ const mediaExtensions = {
'.ogg', '.opus', '.spx', '.wma', '.wav', '.wv', '.wvp'], '.ogg', '.opus', '.spx', '.wma', '.wav', '.wv', '.wvp'],
video: [ video: [
'.avi', '.mp4', '.m4v', '.webm', '.mov', '.mkv', '.mpg', '.mpeg', '.avi', '.mp4', '.m4v', '.webm', '.mov', '.mkv', '.mpg', '.mpeg',
'.ogv', '.webm', '.wmv', '.m2ts'], '.ogv', '.webm', '.wmv'],
image: ['.gif', '.jpg', '.jpeg', '.png'] image: ['.gif', '.jpg', '.jpeg', '.png']
} }

View File

@@ -7,7 +7,7 @@ module.exports = {
logPlayAttempt logPlayAttempt
} }
const { remote } = require('electron') const electron = 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 remote.screen.getAllDisplays().map((screen) => ({ return electron.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

@@ -166,8 +166,7 @@ function torrentPosterFromVideo (torrent, cb) {
function onSeeked () { function onSeeked () {
video.removeEventListener('seeked', onSeeked) video.removeEventListener('seeked', onSeeked)
const frame = captureFrame(video) const buf = captureFrame(video)
const buf = frame && frame.image
// unload video element // unload video element
video.pause() video.pause()

View File

@@ -14,6 +14,9 @@ Module.prototype.require = function (id) {
console.time('init') console.time('init')
const crashReporter = require('../crash-reporter')
crashReporter.init()
// Perf optimization: Start asynchronously read on config file before all the // Perf optimization: Start asynchronously read on config file before all the
// blocking require() calls below. // blocking require() calls below.
@@ -130,10 +133,7 @@ function onState (err, _state) {
resumeTorrents() resumeTorrents()
// Initialize ReactDOM // Initialize ReactDOM
ReactDOM.render( app = ReactDOM.render(<App state={state} />, document.querySelector('#body'))
<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
@@ -277,8 +277,6 @@ 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),
@@ -526,9 +524,6 @@ function onPaste (e) {
} }
function onKeydown (e) { function onKeydown (e) {
// prevent event fire on user input elements
if (editableHtmlTags.has(e.target.tagName.toLowerCase())) return
const key = e.key const key = e.key
if (key === 'ArrowLeft') { if (key === 'ArrowLeft') {

View File

@@ -1,5 +1,5 @@
const React = require('react') const React = require('react')
const BitField = require('bitfield').default const Bitfield = require('bitfield')
const prettyBytes = require('prettier-bytes') const prettyBytes = require('prettier-bytes')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
@@ -427,77 +427,22 @@ 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>
) )
@@ -591,8 +536,6 @@ 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
@@ -604,8 +547,6 @@ 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}
@@ -714,14 +655,10 @@ 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 volume === 0 ? 'off'
? 'off' : volume < 0.3 ? 'mute'
: volume < 0.3 : volume < 0.6 ? 'down'
? 'mute' : 'up')
: 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), ' +
@@ -785,19 +722,6 @@ 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
@@ -836,56 +760,6 @@ 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) {
@@ -905,7 +779,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,17 +5,24 @@ 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 { ipcRenderer } = require('electron') const electron = 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')
const path = require('path') const path = require('path')
const WebTorrent = require('webtorrent') const WebTorrent = require('webtorrent')
const crashReporter = require('../crash-reporter')
const config = require('../config') 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')
// Report when the process crashes
crashReporter.init()
// 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])
@@ -52,7 +59,7 @@ const PEER_ID = Buffer.from(VERSION_PREFIX + crypto.randomBytes(9).toString('bas
// Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid // Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid
// client, as explained here: https://webtorrent.io/faq // client, as explained here: https://webtorrent.io/faq
let client = window.client = new WebTorrent({ peerId: PEER_ID, utp: true }) let client = window.client = new WebTorrent({ peerId: PEER_ID })
// WebTorrent-to-HTTP streaming sever // WebTorrent-to-HTTP streaming sever
let server = null let server = null
@@ -65,29 +72,29 @@ init()
function init () { function init () {
listenToClientEvents() listenToClientEvents()
ipcRenderer.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) => ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) =>
startTorrenting(torrentKey, torrentID, path, fileModtimes, selections)) startTorrenting(torrentKey, torrentID, path, fileModtimes, selections))
ipcRenderer.on('wt-stop-torrenting', (e, infoHash) => ipc.on('wt-stop-torrenting', (e, infoHash) =>
stopTorrenting(infoHash)) stopTorrenting(infoHash))
ipcRenderer.on('wt-create-torrent', (e, torrentKey, options) => ipc.on('wt-create-torrent', (e, torrentKey, options) =>
createTorrent(torrentKey, options)) createTorrent(torrentKey, options))
ipcRenderer.on('wt-save-torrent-file', (e, torrentKey) => ipc.on('wt-save-torrent-file', (e, torrentKey) =>
saveTorrentFile(torrentKey)) saveTorrentFile(torrentKey))
ipcRenderer.on('wt-generate-torrent-poster', (e, torrentKey) => ipc.on('wt-generate-torrent-poster', (e, torrentKey) =>
generateTorrentPoster(torrentKey)) generateTorrentPoster(torrentKey))
ipcRenderer.on('wt-get-audio-metadata', (e, infoHash, index) => ipc.on('wt-get-audio-metadata', (e, infoHash, index) =>
getAudioMetadata(infoHash, index)) getAudioMetadata(infoHash, index))
ipcRenderer.on('wt-start-server', (e, infoHash) => ipc.on('wt-start-server', (e, infoHash) =>
startServer(infoHash)) startServer(infoHash))
ipcRenderer.on('wt-stop-server', (e) => ipc.on('wt-stop-server', (e) =>
stopServer()) stopServer())
ipcRenderer.on('wt-select-files', (e, infoHash, selections) => ipc.on('wt-select-files', (e, infoHash, selections) =>
selectFiles(infoHash, selections)) selectFiles(infoHash, selections))
ipcRenderer.send('ipcReadyWebTorrent') ipc.send('ipcReadyWebTorrent')
window.addEventListener('error', (e) => window.addEventListener('error', (e) =>
ipcRenderer.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }), ipc.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }),
true) true)
setInterval(updateTorrentProgress, 1000) setInterval(updateTorrentProgress, 1000)
@@ -95,8 +102,8 @@ function init () {
} }
function listenToClientEvents () { function listenToClientEvents () {
client.on('warning', (err) => ipcRenderer.send('wt-warning', null, err.message)) client.on('warning', (err) => ipc.send('wt-warning', null, err.message))
client.on('error', (err) => ipcRenderer.send('wt-error', null, err.message)) client.on('error', (err) => ipc.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.
@@ -130,44 +137,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)
ipcRenderer.send('wt-new-torrent') ipc.send('wt-new-torrent')
} }
function addTorrentEvents (torrent) { function addTorrentEvents (torrent) {
torrent.on('warning', (err) => torrent.on('warning', (err) =>
ipcRenderer.send('wt-warning', torrent.key, err.message)) ipc.send('wt-warning', torrent.key, err.message))
torrent.on('error', (err) => torrent.on('error', (err) =>
ipcRenderer.send('wt-error', torrent.key, err.message)) ipc.send('wt-error', torrent.key, err.message))
torrent.on('infoHash', () => torrent.on('infoHash', () =>
ipcRenderer.send('wt-parsed', torrent.key, torrent.infoHash, torrent.magnetURI)) ipc.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)
ipcRenderer.send('wt-metadata', torrent.key, info) ipc.send('wt-metadata', torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
} }
function torrentReady () { function torrentReady () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipcRenderer.send('wt-ready', torrent.key, info) ipc.send('wt-ready', torrent.key, info)
ipcRenderer.send('wt-ready-' + torrent.infoHash, torrent.key, info) ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
} }
function torrentDone () { function torrentDone () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipcRenderer.send('wt-done', torrent.key, info) ipc.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)
ipcRenderer.send('wt-file-modtimes', torrent.key, fileModtimes) ipc.send('wt-file-modtimes', torrent.key, fileModtimes)
}) })
} }
} }
@@ -204,7 +211,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 ipcRenderer.send('wt-file-saved', torrentKey, fileName) return ipc.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
@@ -212,7 +219,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 ipcRenderer.send('wt-file-saved', torrentKey, fileName) return ipc.send('wt-file-saved', torrentKey, fileName)
}) })
}) })
}) })
@@ -232,7 +239,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
ipcRenderer.send('wt-poster', torrentKey, posterFileName) ipc.send('wt-poster', torrentKey, posterFileName)
}) })
}) })
}) })
@@ -244,7 +251,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 */
} }
ipcRenderer.send('wt-progress', progress) ipc.send('wt-progress', progress)
prevProgress = progress prevProgress = progress
} }
@@ -314,8 +321,8 @@ function startServerFromReadyTorrent (torrent, cb) {
networkAddress: networkAddress() networkAddress: networkAddress()
} }
ipcRenderer.send('wt-server-running', info) ipc.send('wt-server-running', info)
ipcRenderer.send('wt-server-' + torrent.infoHash, info) ipc.send('wt-server-' + torrent.infoHash, info)
}) })
} }
@@ -333,17 +340,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 }
ipcRenderer.send('wt-audio-metadata', infoHash, index, metadata) ipc.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 => {
ipcRenderer.send('wt-audio-metadata', infoHash, index, { ipc.send('wt-audio-metadata', infoHash, index, event.metadata)
common: metadata.common,
format: metadata.format
})
} }
} }
const onMetadata = file.done const onMetadata = file.done
@@ -355,7 +359,7 @@ function getAudioMetadata (infoHash, index) {
onMetadata onMetadata
.then( .then(
metadata => { metadata => {
ipcRenderer.send('wt-audio-metadata', infoHash, index, metadata) ipc.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 => {

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,6 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
<% if (version) { %>Version=<%= version %><% } %>
Name=<%= productName %> Name=<%= productName %>
<% if (genericName) { %>GenericName=<%= genericName %><% } %> <% if (genericName) { %>GenericName=<%= genericName %><% } %>
<% if (description) { %>Comment=<%= description %><% } %> <% if (description) { %>Comment=<%= description %><% } %>
@@ -10,7 +11,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
StartupWMClass=<%= productName %> <% if (name) { %>StartupWMClass=<%= name %> <% } %>
[Desktop Action CreateNewTorrent] [Desktop Action CreateNewTorrent]
Name=Create New Torrent... Name=Create New Torrent...

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -558,13 +558,6 @@ 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
*/ */

Binary file not shown.

Binary file not shown.

Binary file not shown.