Merge branch 'master' into master

This commit is contained in:
suraj rathod
2020-11-20 11:27:33 +05:30
committed by GitHub
64 changed files with 7055 additions and 3258 deletions

View File

@@ -39,6 +39,7 @@
- Nuno Campos (nuno.campos@me.com) - Nuno Campos (nuno.campos@me.com)
- Ebrahim Byagowi (ebrahim@gnu.org) - Ebrahim Byagowi (ebrahim@gnu.org)
- Josip Janzic (josip@jjanzic.com) - Josip Janzic (josip@jjanzic.com)
- Egor Yurtaev (yurtaev.egor@gmail.com)
- Emil Bay (github@tixz.dk) - Emil Bay (github@tixz.dk)
- Borewit (borewit@users.noreply.github.com) - Borewit (borewit@users.noreply.github.com)
- greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com) - greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com)
@@ -55,6 +56,7 @@
- Dan Flettre (flettre@gmail.com) - Dan Flettre (flettre@gmail.com)
- Sibiraj (dev.sibiraj@outlook.com) - Sibiraj (dev.sibiraj@outlook.com)
- clujin (clujin@gmail.com) - clujin (clujin@gmail.com)
- Sharon Grossman (sharong1337@gmail.com)
- Linus Unnebäck (linus@folkdatorn.se) - Linus Unnebäck (linus@folkdatorn.se)
- Adrian Tombu (adrian@otso.fr) - Adrian Tombu (adrian@otso.fr)
- Lucas (5874806+RecoX@users.noreply.github.com) - Lucas (5874806+RecoX@users.noreply.github.com)
@@ -64,10 +66,18 @@
- Recox (5874806+RecoX@users.noreply.github.com) - Recox (5874806+RecoX@users.noreply.github.com)
- greenkeeper[bot] (23040076+greenkeeper[bot]@users.noreply.github.com) - greenkeeper[bot] (23040076+greenkeeper[bot]@users.noreply.github.com)
- hicom150 (hicom150@gmail.com) - hicom150 (hicom150@gmail.com)
- Дамјан Георгиевски (gdamjan@gmail.com)
- Jimmy Wärting (jimmy@warting.se) - Jimmy Wärting (jimmy@warting.se)
- Julen Garcia Leunda (hicom150@gmail.com) - Julen Garcia Leunda (hicom150@gmail.com)
- Feross (feross@feross.org) - Feross (feross@feross.org)
- Daniele Debernardi (drebrez@gmail.com) - Daniele Debernardi (drebrez@gmail.com)
- Chandan Chowdary Bhagam (chandandharana@gmail.com) - Chandan Chowdary Bhagam (chandandharana@gmail.com)
- Pieter Goetschalckx (3.14.e.ter@gmail.com)
- Carey Metcalfe (carey@cmetcalfe.ca)
- Ameet Kaustav (akaustav@users.noreply.github.com)
- gpatarin (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,6 +1,64 @@
# WebTorrent Desktop Version History # WebTorrent Desktop Version History
## v0.21.0 - 2019-08 ## 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
🔒 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
- Add macOS Notarization [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834) ([feross](https://github.com/feross))
### Changed
- Update to Electron 10 beta [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834) ([feross](https://github.com/feross))
## v0.22.0 - 2020-07-15
❤️✨ A new version of WebTorrent Desktop is out! ❤️✨
### Added
- Linux `.rpm` packages and `arm64` builds are now available! [\#1694](https://github.com/webtorrent/webtorrent-desktop/pull/1694) ([hicom150](https://github.com/hicom150))
- Add support for multiple audio tracks [\#1712](https://github.com/webtorrent/webtorrent-desktop/pull/1712) ([hicom150](https://github.com/hicom150))
- Improve codec unsupported detection [\#1711](https://github.com/webtorrent/webtorrent-desktop/pull/1711) ([hicom150](https://github.com/hicom150))
- Report when files are being verified [\#1717](https://github.com/webtorrent/webtorrent-desktop/pull/1717) ([pR0Ps](https://github.com/pR0Ps))
- Support additional audio files: MPEG-Layer-2, Musepack, Matroska audio, WavePack [\#1772](https://github.com/webtorrent/webtorrent-desktop/pull/1772)
### Changed
- 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 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))
### Fixed
- Fix a few type errors [\#1720](https://github.com/webtorrent/webtorrent-desktop/pull/1720) ([mathiasvr](https://github.com/mathiasvr))
- Fix electron SUID sandbox error [\#1707](https://github.com/webtorrent/webtorrent-desktop/pull/1707) ([hicom150](https://github.com/hicom150))
- 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 [\#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 "Save Torrent File As..." [\#1743](https://github.com/webtorrent/webtorrent-desktop/pull/1743) ([gpatarin](https://github.com/gpatarin))
## v0.21.0 - 2019-09-14
### Added ### Added

View File

@@ -46,6 +46,11 @@
``` ```
npm run package -- darwin --sign npm run package -- darwin --sign
```
Move the `.zip` and `.dmg` file somewhere because the next step wipes the `dist/` folder away.
```
npm run package -- linux --sign npm run package -- linux --sign
``` ```
@@ -75,11 +80,23 @@
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. - Smoke test WebTorrent Desktop on each platform. Before a release, check that the following basic use cases work correctly:
See Smoke Tests below for details. Open DevTools 1. Click "Play" to stream a built-in torrent (e.g. Sintel)
on Windows and Mac, and ensure that the auto updater is running. If the auto updater does not - Ensure that seeking to undownloaded region works and plays immediately.
run, users will successfully auto update to this new version, and then be stuck there forever. - 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. (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
@@ -88,25 +105,5 @@
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.
As soon as this PR is merged, Jenkins will automatically redeploy the WebTorrent website, and Once this PR is merged and Feross redeploys the WebTorrent website,
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
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

@@ -0,0 +1,12 @@
<?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>

View File

@@ -8,7 +8,6 @@ const cp = require('child_process')
const electronPackager = require('electron-packager') const electronPackager = require('electron-packager')
const fs = require('fs') const fs = require('fs')
const minimist = require('minimist') const minimist = require('minimist')
const mkdirp = require('mkdirp')
const os = require('os') const os = require('os')
const path = require('path') const path = require('path')
const rimraf = require('rimraf') const rimraf = require('rimraf')
@@ -37,7 +36,7 @@ const argv = minimist(process.argv.slice(2), {
}) })
function build () { function build () {
console.log('Reinstalling node_modules...') console.log('Installing node_modules...')
rimraf.sync(NODE_MODULES_PATH) rimraf.sync(NODE_MODULES_PATH)
cp.execSync('npm ci', { stdio: 'inherit' }) cp.execSync('npm ci', { stdio: 'inherit' })
@@ -267,6 +266,7 @@ 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,16 +282,37 @@ 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',
verbose: true identity: 'Developer ID Application: WebTorrent, LLC (5MAMC8G3L8)',
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.')
console.log('Mac: Notarizing app...')
notarize(notarizeOpts).then(
function () {
console.log('Mac: Notarized app.')
cb(null) cb(null)
},
function (err) {
cb(err)
})
}) })
} }
@@ -457,13 +478,13 @@ function buildWin32 (cb) {
console.log('Windows: Creating portable app...') console.log('Windows: Creating portable app...')
const portablePath = path.join(filesPath, 'Portable Settings') const portablePath = path.join(filesPath, 'Portable Settings')
mkdirp.sync(portablePath) fs.mkdirSync(portablePath, { recursive: true })
const downloadsPath = path.join(portablePath, 'Downloads') const downloadsPath = path.join(portablePath, 'Downloads')
mkdirp.sync(downloadsPath) fs.mkdirSync(downloadsPath, { recursive: true })
const tempPath = path.join(portablePath, 'Temp') const tempPath = path.join(portablePath, 'Temp')
mkdirp.sync(tempPath) fs.mkdirSync(tempPath, { recursive: true })
const inPath = path.join(DIST_PATH, path.basename(filesPath)) const inPath = path.join(DIST_PATH, path.basename(filesPath))
const outPath = path.join(DIST_PATH, BUILD_NAME + '-win.zip') const outPath = path.join(DIST_PATH, BUILD_NAME + '-win.zip')
@@ -524,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(

View File

@@ -13,6 +13,7 @@ while (<>) {
next if /(dc\@DCs-MacBook.local)/; next if /(dc\@DCs-MacBook.local)/;
next if /(rolandoguedes\@gmail.com)/; next if /(rolandoguedes\@gmail.com)/;
next if /(grunjol\@users.noreply.github.com)/; next if /(grunjol\@users.noreply.github.com)/;
next if /(dependabot)/;
$seen{$_} = push @authors, "- ", $_; $seen{$_} = push @authors, "- ", $_;
} }
END { END {

9537
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +1,85 @@
{ {
"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.21.0", "version": "0.24.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.1.1", "arch": "^2.2.0",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"bitfield": "^3.0.0", "bitfield": "^4.0.0",
"capture-frame": "^3.0.0", "capture-frame": "^4.0.0",
"chokidar": "^3.2.2", "chokidar": "^3.4.3",
"chromecasts": "^1.9.1", "chromecasts": "^1.9.1",
"create-torrent": "^4.4.1", "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.0", "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.0", "iso-639-1": "^2.1.4",
"languagedetect": "^2.0.0", "languagedetect": "^2.0.0",
"location-history": "^1.1.1", "location-history": "^1.1.2",
"material-ui": "^0.20.2", "material-ui": "^0.20.2",
"mkdirp": "^0.5.1", "music-metadata": "^7.4.1",
"music-metadata": "6.3.6",
"network-address": "^1.1.2", "network-address": "^1.1.2",
"parse-torrent": "^7.0.1", "parse-torrent": "^8.0.0",
"prettier-bytes": "^1.0.4", "prettier-bytes": "^1.0.4",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^16.11.0", "react": "^17.0.1",
"react-dom": "^16.11.0", "react-dom": "^17.0.1",
"rimraf": "^3.0.0", "rimraf": "^3.0.2",
"run-parallel": "^1.1.9", "run-parallel": "^1.1.10",
"semver": "^7.0.0", "semver": "^7.3.2",
"simple-concat": "^1.0.0", "simple-concat": "^1.0.1",
"simple-get": "^3.1.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.107.16", "webtorrent": ">=0.109.2",
"winreg": "^1.2.4" "winreg": "^1.2.4"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^10.0.3", "@babel/cli": "^7.6.0",
"buble": "^0.19.8", "@babel/core": "^7.6.0",
"cross-zip": "^3.0.0", "@babel/eslint-parser": "^7.12.1",
"depcheck": "^0.9.0", "@babel/plugin-transform-react-jsx": "^7.3.0",
"electron": "~7.1.0", "buble": "^0.20.0",
"electron-osx-sign": "^0.4.14", "cross-zip": "^3.1.0",
"electron-packager": "^14.0.6", "depcheck": "^1.2.0",
"electron-winstaller": "^4.0.0", "electron": "~11.0.0",
"gh-release": "^3.5.0", "electron-notarize": "^1.0.0",
"minimist": "^1.2.0", "electron-osx-sign": "^0.4.17",
"nodemon": "^2.0.0", "electron-packager": "^15.1.0",
"open": "^7.0.0", "electron-winstaller": "^4.0.1",
"gh-release": "^4.0.3",
"minimist": "^1.2.5",
"nodemon": "^2.0.6",
"open": "^7.3.0",
"plist": "^3.0.1", "plist": "^3.0.1",
"pngjs": "^3.4.0", "pngjs": "^6.0.0",
"run-series": "^1.1.8", "run-series": "^1.1.9",
"spectron": "^9.0.0", "spectron": "~12.0.0",
"standard": "*", "standard": "*",
"tape": "^4.11.0", "tape": "^5.0.1",
"walk-sync": "^2.0.2" "walk-sync": "^2.2.0"
}, },
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0"
@@ -86,8 +99,8 @@
"main": "index.js", "main": "index.js",
"optionalDependencies": { "optionalDependencies": {
"appdmg": "^0.6.0", "appdmg": "^0.6.0",
"electron-installer-debian": "^2.0.1", "electron-installer-debian": "^3.1.0",
"electron-installer-redhat": "^2.0.0" "electron-installer-redhat": "^3.2.0"
}, },
"private": true, "private": true,
"productName": "WebTorrent", "productName": "WebTorrent",
@@ -96,19 +109,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 && 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": "@babel/eslint-parser"
} }
} }

View File

@@ -7,8 +7,9 @@ function init () {
const { crashReporter } = require('electron') const { crashReporter } = require('electron')
crashReporter.start({ 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

@@ -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 == null) return vlcCommand(cb) if (!playerPath) return vlcCommand(cb)
process.nextTick(() => cb(null)) process.nextTick(() => cb(null))
} }
function spawn (playerPath, url, title) { function spawn (playerPath, url, title) {
if (playerPath != null) return spawnExternal(playerPath, [url]) if (playerPath) 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

@@ -2,10 +2,13 @@ console.time('init')
const { app, ipcMain } = require('electron') const { app, ipcMain } = require('electron')
// 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')
@@ -106,10 +109,6 @@ 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)
@@ -196,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

@@ -137,9 +137,10 @@ function init () {
* Shell * Shell
*/ */
ipcMain.on('openItem', (e, ...args) => {
ipcMain.on('openPath', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.openItem(...args) shell.openPath(...args)
}) })
ipcMain.on('showItemInFolder', (e, ...args) => { ipcMain.on('showItemInFolder', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')

View File

@@ -1,6 +1,6 @@
module.exports = { module.exports = {
openExternal, openExternal,
openItem, openPath,
showItemInFolder, showItemInFolder,
moveItemToTrash moveItemToTrash
} }
@@ -19,9 +19,10 @@ function openExternal (url) {
/** /**
* Open the given file in the desktops default manner. * Open the given file in the desktops default manner.
*/ */
function openItem (path) {
log(`openItem: ${path}`) function openPath (path) {
shell.openItem(path) log(`openPath: ${path}`)
shell.openPath(path)
} }
/** /**

View File

@@ -2,148 +2,39 @@ module.exports = {
handleEvent handleEvent
} }
const cp = require('child_process')
const { app } = require('electron') const { app } = 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 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')
function handleEvent (cmd) { const run = function (args, done) {
if (cmd === '--squirrel-install') { spawn(UPDATE_EXE, args, { detached: true })
// App was installed. Install desktop/start menu shortcuts. .on('close', done)
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') { function handleEvent (cmd) {
// App was updated. (Called on new version of app) if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
updateShortcuts(function () { run([`--createShortcut=${EXE_NAME}`], app.quit)
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()
// Remove desktop/start menu shortcuts. run([`--removeShortcut=${EXE_NAME}`], app.quit)
// 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

@@ -26,7 +26,9 @@ 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

@@ -42,7 +42,9 @@ 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

View File

@@ -26,7 +26,9 @@ function init () {
useContentSize: true, useContentSize: true,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks' enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true,
backgroundThrottling: false
}, },
width: 150 width: 150
}) })

View File

@@ -59,12 +59,12 @@ module.exports = class PlaybackController {
} }
// Open a file in OS default app. // Open a file in OS default app.
openItem (infoHash, index) { openPath (infoHash, index) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash) const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
const filePath = path.join( const filePath = path.join(
torrentSummary.path, torrentSummary.path,
torrentSummary.files[index].path) torrentSummary.files[index].path)
ipcRenderer.send('openItem', filePath) ipcRenderer.send('openPath', filePath)
} }
// Toggle (play or pause) the currently playing media // Toggle (play or pause) the currently playing media
@@ -156,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
@@ -268,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

@@ -158,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.
@@ -173,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)
}) })

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'], '.ogv', '.webm', '.wmv', '.m2ts'],
image: ['.gif', '.jpg', '.jpeg', '.png'] image: ['.gif', '.jpg', '.jpeg', '.png']
} }

View File

@@ -29,7 +29,7 @@ function run (state) {
if (semver.lt(version, '0.17.0')) migrate_0_17_0(saved) if (semver.lt(version, '0.17.0')) migrate_0_17_0(saved)
if (semver.lt(version, '0.17.2')) migrate_0_17_2(saved) if (semver.lt(version, '0.17.2')) migrate_0_17_2(saved)
if (semver.lt(version, '0.21.0')) migrate_0_21_0(saved) if (semver.lt(version, '0.21.0')) migrate_0_21_0(saved)
if (semver.lt(version, '0.21.1')) migrate_0_21_1(saved) if (semver.lt(version, '0.22.0')) migrate_0_22_0(saved)
// Config is now on the new version // Config is now on the new version
state.saved.version = config.APP_VERSION state.saved.version = config.APP_VERSION
@@ -216,7 +216,7 @@ function migrate_0_21_0 (saved) {
} }
} }
function migrate_0_21_1 (saved) { function migrate_0_22_0 (saved) {
if (saved.prefs.externalPlayerPath == null) { if (saved.prefs.externalPlayerPath == null) {
saved.prefs.externalPlayerPath = '' saved.prefs.externalPlayerPath = ''
} }

View File

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

View File

@@ -14,9 +14,6 @@ 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.
@@ -133,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
@@ -277,10 +277,12 @@ 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),
openItem: (infoHash, index) => controllers.playback().openItem(infoHash, index), openPath: (infoHash, index) => controllers.playback().openPath(infoHash, index),
// Subtitles // Subtitles
openSubtitles: () => controllers.subtitles().openSubtitles(), openSubtitles: () => controllers.subtitles().openSubtitles(),
@@ -524,6 +526,9 @@ 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') 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')
@@ -536,6 +536,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 +549,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 +659,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 +730,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 +781,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 +850,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

@@ -346,7 +346,7 @@ module.exports = class TorrentList extends React.Component {
} else { } else {
icon = 'description' /* file icon, opens in OS default app */ icon = 'description' /* file icon, opens in OS default app */
handleClick = isDone handleClick = isDone
? dispatcher('openItem', infoHash, index) ? dispatcher('openPath', infoHash, index)
: (e) => e.stopPropagation() // noop if file is not ready : (e) => e.stopPropagation() // noop if file is not ready
} }
// TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed // TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed

View File

@@ -7,20 +7,15 @@ const util = require('util')
const defaultAnnounceList = require('create-torrent').announceList const defaultAnnounceList = require('create-torrent').announceList
const { ipcRenderer } = require('electron') const { ipcRenderer } = require('electron')
const fs = require('fs') const fs = require('fs')
const mkdirp = require('mkdirp')
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()
// 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])
@@ -213,7 +208,7 @@ function saveTorrentFile (torrentKey) {
} }
// Otherwise, save the .torrent file, under the app config folder // Otherwise, save the .torrent file, under the app config folder
mkdirp(config.TORRENT_PATH, function (_) { fs.mkdir(config.TORRENT_PATH, { recursive: true }, function (_) {
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)
@@ -230,7 +225,7 @@ function generateTorrentPoster (torrentKey) {
torrentPoster(torrent, function (err, buf, extension) { torrentPoster(torrent, function (err, buf, extension) {
if (err) return console.log('error generating poster: %o', err) if (err) return console.log('error generating poster: %o', err)
// save it for next time // save it for next time
mkdirp(config.POSTER_PATH, function (err) { fs.mkdir(config.POSTER_PATH, { recursive: true }, function (err) {
if (err) return console.log('error creating poster dir: %o', err) if (err) return console.log('error creating poster dir: %o', err)
const posterFileName = torrent.infoHash + extension const posterFileName = torrent.infoHash + extension
const posterFilePath = path.join(config.POSTER_PATH, posterFileName) const posterFilePath = path.join(config.POSTER_PATH, posterFileName)
@@ -345,7 +340,10 @@ function getAudioMetadata (infoHash, index) {
skipCovers: true, skipCovers: true,
fileSize: file.length, fileSize: file.length,
observer: event => { observer: event => {
ipcRenderer.send('wt-audio-metadata', infoHash, index, event.metadata) ipcRenderer.send('wt-audio-metadata', infoHash, index, {
common: metadata.common,
format: metadata.format
})
} }
} }
const onMetadata = file.done const onMetadata = file.done

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 966 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 KiB

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 KiB

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 KiB

After

Width:  |  Height:  |  Size: 746 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1003 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1019 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1003 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 KiB

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 915 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 904 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 905 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 899 KiB

View File

@@ -1,7 +1,6 @@
const Application = require('spectron').Application const Application = require('spectron').Application
const { copyFileSync } = require('fs') const { copyFileSync } = require('fs')
const fs = require('fs') const fs = require('fs')
const mkdirp = require('mkdirp')
const parseTorrent = require('parse-torrent') const parseTorrent = require('parse-torrent')
const path = require('path') const path = require('path')
const PNG = require('pngjs').PNG const PNG = require('pngjs').PNG
@@ -97,9 +96,11 @@ function screenshotCreateOrCompare (app, t, name) {
} catch (err) { } catch (err) {
ssBuf = Buffer.alloc(0) ssBuf = Buffer.alloc(0)
} }
return wait().then(function () {
return app.browserWindow.capturePage() return app.browserWindow.focus()
}).then(function (buffer) { .then(() => wait())
.then(() => app.browserWindow.capturePage())
.then(function (buffer) {
if (ssBuf.length === 0) { if (ssBuf.length === 0) {
console.log('Saving screenshot ' + ssPath) console.log('Saving screenshot ' + ssPath)
fs.writeFileSync(ssPath, buffer) fs.writeFileSync(ssPath, buffer)
@@ -158,8 +159,8 @@ function compareIgnoringTransparency (bufActual, bufExpected) {
function resetTestDataDir () { function resetTestDataDir () {
rimraf.sync(config.TEST_DIR) rimraf.sync(config.TEST_DIR)
// Create TEST_DIR as well as /Downloads and /Desktop // Create TEST_DIR as well as /Downloads and /Desktop
mkdirp.sync(config.TEST_DIR_DOWNLOAD) fs.mkdirSync(config.TEST_DIR_DOWNLOAD, { recursive: true })
mkdirp.sync(config.TEST_DIR_DESKTOP) fs.mkdirSync(config.TEST_DIR_DESKTOP, { recursive: true })
} }
function deleteTestDataDir () { function deleteTestDataDir () {
@@ -178,6 +179,7 @@ function compareDownloadFolder (t, dirname, filenames) {
} }
const expectedSorted = filenames.slice().sort() const expectedSorted = filenames.slice().sort()
const actualSorted = actualFilenames.slice().sort() const actualSorted = actualFilenames.slice().sort()
console.log(actualSorted)
t.deepEqual(actualSorted, expectedSorted, 'download folder contents: ' + dirname) t.deepEqual(actualSorted, expectedSorted, 'download folder contents: ' + dirname)
} catch (err) { } catch (err) {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {
@@ -210,12 +212,14 @@ function compareTorrentFiles (t, pathActual, pathExpected) {
function compareTorrentFile (t, pathActual, fieldsExpected) { function compareTorrentFile (t, pathActual, fieldsExpected) {
const bufActual = fs.readFileSync(pathActual) const bufActual = fs.readFileSync(pathActual)
const fieldsActual = extractImportantFields(parseTorrent(bufActual)) const fieldsActual = extractImportantFields(parseTorrent(bufActual))
if (Array.isArray(fieldsExpected.announce)) fieldsExpected.announce.sort()
t.deepEqual(fieldsActual, fieldsExpected, 'torrent contents: ' + pathActual) t.deepEqual(fieldsActual, fieldsExpected, 'torrent contents: ' + pathActual)
} }
function extractImportantFields (parsedTorrent) { function extractImportantFields (parsedTorrent) {
const { infoHash, name, announce, urlList, comment } = parsedTorrent let { infoHash, name, announce, urlList, comment } = parsedTorrent
const priv = parsedTorrent.private // private is a reserved word in JS const priv = parsedTorrent.private // private is a reserved word in JS
announce = announce.slice().sort()
return { infoHash, name, announce, urlList, comment, private: priv } return { infoHash, name, announce, urlList, comment, private: priv }
} }

View File

@@ -38,18 +38,19 @@ test('create-torrent', function (t) {
const expectedTorrent = { const expectedTorrent = {
announce: [ announce: [
'udp://explodie.org:6969',
'udp://tracker.coppersurfer.tk:6969',
'udp://tracker.empire-js.us:1337',
'udp://tracker.leechers-paradise.org:6969', 'udp://tracker.leechers-paradise.org:6969',
'udp://tracker.coppersurfer.tk:6969',
'udp://tracker.opentrackr.org:1337', 'udp://tracker.opentrackr.org:1337',
'udp://explodie.org:6969',
'udp://tracker.empire-js.us:1337',
'wss://tracker.btorrent.xyz', 'wss://tracker.btorrent.xyz',
'wss://tracker.fastcast.nz',
'wss://tracker.openwebtorrent.com' 'wss://tracker.openwebtorrent.com'
], ],
infoHash: 'b31a80b3dd807c2fdde4c4da1a0db6123fa35883', infoHash: 'b31a80b3dd807c2fdde4c4da1a0db6123fa35883',
name: 'tmp.jpg', name: 'tmp.jpg',
urlList: [] urlList: [],
comment: undefined,
private: undefined
} }
// Set up the files to seed // Set up the files to seed
@@ -77,9 +78,7 @@ test('create-torrent', function (t) {
'dispatch("saveTorrentFileAs", 6)')) 'dispatch("saveTorrentFileAs", 6)'))
.then(() => setup.wait()) .then(() => setup.wait())
// Mock saves to <temp folder>/Desktop/saved.torrent // Mock saves to <temp folder>/Desktop/saved.torrent
.then(() => setup.compareTorrentFile(t, .then(() => setup.compareTorrentFile(t, config.SAVED_TORRENT_FILE, expectedTorrent))
config.SAVED_TORRENT_FILE,
expectedTorrent))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })

View File

@@ -12,7 +12,7 @@ test('audio-streaming', function (t) {
.then(() => app.client.moveToObject('#torrent-wired')) .then(() => app.client.moveToObject('#torrent-wired'))
.then(() => setup.wait()) .then(() => setup.wait())
.then(() => app.client.click('#torrent-wired .icon.play')) .then(() => app.client.click('#torrent-wired .icon.play'))
.then(() => app.client.waitUntilTextExists('.player', 'The Wired CD')) .then(() => app.client.waitUntilTextExists('.player', 'Beastie Boys'))
// Pause. Skip to two seconds in. Wait another two seconds for it to load. // Pause. Skip to two seconds in. Wait another two seconds for it to load.
.then(() => app.webContents.executeJavaScript('dispatch("playPause")')) .then(() => app.webContents.executeJavaScript('dispatch("playPause")'))
.then(() => app.webContents.executeJavaScript('dispatch("skipTo", 2)')) .then(() => app.webContents.executeJavaScript('dispatch("skipTo", 2)'))
@@ -45,6 +45,7 @@ test('audio-streaming', function (t) {
// Back. Return to torrent list // Back. Return to torrent list
.then(() => app.client.click('.back')) .then(() => app.client.click('.back'))
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny')) .then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Seeding', 60e3))
.then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-wired-list')) .then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-wired-list'))
// Forward. Should play again where we left off (should not stay paused) // Forward. Should play again where we left off (should not stay paused)
.then(() => app.client.click('.forward')) .then(() => app.client.click('.forward'))

View File

@@ -11,8 +11,6 @@ test('torrent-list: show download path missing', function (t) {
t.timeoutAfter(20e3) t.timeoutAfter(20e3)
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => app.client.getTitle())
.then((text) => console.log('Title ' + text))
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Download path missing')) .then(() => app.client.waitUntilTextExists('.torrent-list', 'Download path missing'))
.then((err) => t.notOk(err)) .then((err) => t.notOk(err))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-download-path-missing')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-download-path-missing'))
@@ -44,7 +42,7 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt'))
// Click cancel on the resulting confirmation dialog. Should be same as before. // Click cancel on the resulting confirmation dialog. Should be same as before.
.then(() => app.client.click('.control.cancel')) .then(() => app.client.click('.control.cancel'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-2'))
// Click delete on the first torrent again // Click delete on the first torrent again
.then(() => app.client.click('.icon.delete')) .then(() => app.client.click('.icon.delete'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt'))
@@ -72,19 +70,20 @@ test('torrent-list: expand torrent, unselect file', function (t) {
.then(() => app.client.waitUntilTextExists('.torrent-list', '0%')) .then(() => app.client.waitUntilTextExists('.torrent-list', '0%'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-expand-start')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-expand-start'))
// Make sure that it creates all files EXCEPT the deslected one // Make sure that it creates all files EXCEPT the deslected one
.then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', [ // TODO: Disabled test because it stopped working
// TODO: the .gif should NOT be here, since we just deselected it. // .then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', [
// This is a bug. See https://github.com/webtorrent/webtorrent-desktop/issues/719 // // TODO: the .gif should NOT be here, since we just deselected it.
'Cosmos Laundromat - First Cycle (1080p).gif', // // This is a bug. See https://github.com/webtorrent/webtorrent-desktop/issues/719
'Cosmos Laundromat - First Cycle (1080p).mp4', // 'Cosmos Laundromat - First Cycle (1080p).gif',
'Cosmos Laundromat - First Cycle (1080p).ogv', // 'Cosmos Laundromat - First Cycle (1080p).mp4',
'CosmosLaundromat-FirstCycle1080p.en.srt', // 'Cosmos Laundromat - First Cycle (1080p).ogv',
'CosmosLaundromat-FirstCycle1080p.es.srt', // 'CosmosLaundromat-FirstCycle1080p.en.srt',
'CosmosLaundromat-FirstCycle1080p.fr.srt', // 'CosmosLaundromat-FirstCycle1080p.es.srt',
'CosmosLaundromat-FirstCycle1080p.it.srt', // 'CosmosLaundromat-FirstCycle1080p.fr.srt',
'CosmosLaundromatFirstCycle_meta.sqlite', // 'CosmosLaundromat-FirstCycle1080p.it.srt',
'CosmosLaundromatFirstCycle_meta.xml' // 'CosmosLaundromatFirstCycle_meta.sqlite',
])) // 'CosmosLaundromatFirstCycle_meta.xml'
// ]))
// Delete torrent plus data // Delete torrent plus data
// Spectron doesn't have proper support for menu clicks yet... // Spectron doesn't have proper support for menu clicks yet...
.then(() => app.webContents.executeJavaScript( .then(() => app.webContents.executeJavaScript(
@@ -94,7 +93,8 @@ test('torrent-list: expand torrent, unselect file', function (t) {
.then(() => app.client.click('.control.ok')) .then(() => app.client.click('.control.ok'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-deleted')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-deleted'))
// Make sure that all the files are gone // Make sure that all the files are gone
.then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', null)) // TODO: Disabled test because it stopped working
// .then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', null))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })