Compare commits

..

13 Commits

Author SHA1 Message Date
Feross Aboukhadijeh
f5d0692a56 0.18.0 2017-02-03 02:04:28 -08:00
Feross Aboukhadijeh
2f4fe3e521 disable background throttling in all windows 2017-02-03 02:04:17 -08:00
Feross Aboukhadijeh
4937f61199 move Transfers menu 2017-02-03 02:03:47 -08:00
Feross Aboukhadijeh
e74b993ecd updated copyright year 2017-02-03 02:03:12 -08:00
Feross Aboukhadijeh
36be5b2556 changelog for 0.18.0 2017-02-03 02:02:59 -08:00
Feross Aboukhadijeh
6a3aaba01f test: update test for new default trackers 2017-02-03 00:52:09 -08:00
Feross Aboukhadijeh
c50783d058 test: update screenshot for new default trackers 2017-02-03 00:51:58 -08:00
Feross Aboukhadijeh
32b251fa58 remove unneeded package from depcheck 2017-02-03 00:50:08 -08:00
Feross Aboukhadijeh
fcf087810e package: fix deprecation warning 2017-02-03 00:24:58 -08:00
Feross Aboukhadijeh
0124412d3d lock down dev dependencies
Let's be very explicit about updating these. They are very critical to
the build process, so we should always read the changelogs / commit log
before upgrading.
2017-02-03 00:24:41 -08:00
Feross Aboukhadijeh
dd570d9250 electron-osx-sign@0.4 2017-02-03 00:12:16 -08:00
Feross Aboukhadijeh
64add5d68b electron@1.4.15 2017-02-03 00:07:30 -08:00
Feross Aboukhadijeh
36a43b645e buble@0.15 2017-02-02 21:01:28 -08:00
50 changed files with 216 additions and 886 deletions

View File

@@ -34,18 +34,5 @@
- PurgingPanda (t3ch0wn3r@gmail.com)
- Kai Curtis (morecode@kcurtis.com)
- Omri Litov (omrilitov@gmail.com)
- Alexey Romanov (romanalexey@gmail.com)
- Karan Thakkar (karanjthakkar@gmail.com)
- Nuno Campos (nuno.campos@me.com)
- Ebrahim Byagowi (ebrahim@gnu.org)
- Josip Janzic (josip@jjanzic.com)
- Emil Bay (github@tixz.dk)
- Borewit (borewit@users.noreply.github.com)
- greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com)
- Auyer (rafa_auyer@icloud.com)
- SimplyAhmazing (ahmad19526@gmail.com)
- Cezar Carneiro (cezargcarneiro@gmail.com)
- Terry Hau (terryhau@gmail.com)
- Vítor Galvão (info@vitorgalvao.com)
#### Generated by bin/update-authors.sh.

View File

@@ -1,41 +1,6 @@
# WebTorrent Desktop Version History
## v0.20.0 - 2018-04-26
### Added
- Added support for additional audio extensions: 'aiff', 'ape', 'mp2', 'oga', 'opus', 'wma' (#1240)
### Changed
- Displaying filename while music metadata is being downloaded (#1361)
- Improved the poster selection for audio/music based torrents (#1334)
- Launch VLC player without the `--video-on-top` flag (#1286)
### Fixed
- Fix silently failing to open magnets links on Linux (#1367)
## v0.19.0 - 2018-01-26
### Added
- Added watch folder feature: Automatically add new torrent files added to a folder on disk (#1154)
- Added highest playback priority feature: pauses other active torrents when playback starts (#840)
- Add 'Start Speaking' and 'Stop Speaking' menu item (Mac) (#439)
- Add pinch-to-zoom gesture to enter/exit fullscreen (#1148)
### Changed
- [SECURITY] Mitigate Electron protocol handler issue (Windows)
- Moved project from Feross's GitHub account to the WebTorrent GitHub organization
- Updated to electron@1.6.16
- Updated to material-ui@0.17
- Treat .FLAC as playable audio (#1127)
### Fixed
- Fix time and duration so it doesn't bounce in the UI (#1233)
- Fix 'About WebTorrent' menu location on Windows (#1120)
## v0.18.0 - 2017-02-03
## v0.18.0
### Added
- Add a new "Transfers" menu for pausing or resuming all torrents (#1027)
@@ -102,7 +67,7 @@
## v0.16.0 - 2016-09-18
### Added
- **Windows 64-bit support!** (#931)
- **Windows 64-bit support!** ([#931](https://github.com/feross/webtorrent-desktop/pull/931))
- Existing 32-bit users will update to 64-bit automatically in next release
- 64-bit reduces likelihood of out-of-memory errors by increasing the address space
@@ -551,7 +516,7 @@ Windows, and Linux. For now, we're only releasing binaries for OS X.
WebTorrent Desktop is in ALPHA and under very active development  expect lots more polish in
the coming weeks! If you know JavaScript and want to help us out, there's
[lots to do](https://github.com/webtorrent/webtorrent-desktop/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+contribution%22)!
[lots to do](https://github.com/feross/webtorrent-desktop/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+contribution%22)!
### Features

View File

@@ -24,7 +24,7 @@ and to avoid style arguments. `npm test` runs `standard` automatically, so you d
to!
[standard-image]: https://cdn.rawgit.com/feross/standard/master/badge.svg
[standard-url]: https://standardjs.com
[standard-url]: https://github.com/feross/standard
## Project Governance
@@ -36,7 +36,7 @@ standard guarded open source project.
There are a few basic ground-rules for contributors:
1. **No `--force` pushes to master** or modifying history in any way. Rebasing and force pushing your own PR branch is fine.
1. **No `--force` pushes** or modifying the Git history in any way.
2. **Non-master branches** should be used for ongoing work.
3. **Significant modifications** like API changes should be subject to a **pull request**
to solicit feedback from other contributors.
@@ -74,99 +74,6 @@ By making a contribution to this project, I certify that:
including my sign-off) is maintained indefinitely and may be redistributed consistent
with this project or the open source license(s) involved.
## Release Procedure
### 1. Create a new version
- Update `AUTHORS`
```
npm run update-authors
```
Commit if necessary. The commit message should be "authors".
- Write the changelog
You can use `git log --oneline <last version tag>..HEAD` to get a list of changes.
Summarize them concisely in `CHANGELOG.md`. The commit message should be "changelog".
- Update the version
```
npm version [major|minor|patch]
```
This creates both a commit and a git tag.
- Make a PR
Once the PR is reviewed, merge it:
```
git push origin <branch-name>:master
```
This makes it so that the commit hash on master matches the commit hash of the version tag.
Finally, run:
```
git push --tags
```
### 2. Create the release binaries
- On a Mac:
```
npm run package -- darwin --sign
npm run package -- linux --sign
```
- On Windows, or in a Windows VM:
```
npm run package -- win32 --sign
```
- Then, upload the release binaries to Github:
```
npm run gh-release
```
Follow the URL to a newly created Github release page. Manually upload the binaries from
`webtorrent-desktop/dist/`. Open the previous release in another tab, and make sure that you
are uploading the same set of files, no more, no less.
### 3. Test it
**This is the most important part.**
- Manually download the binaries for each platform from Github.
**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
codesigning worked and is valid.
- Smoke test WebTorrent Desktop on each platform.
See Smoke Tests below for details. Open DevTools
on Windows and Mac, and ensure that the auto updater is running. If the auto updater does not
run, users will successfully auto update to this new version, and then be stuck there forever.
### 4. Ship it
- Update the website
Create a pull request in [webtorrent.io](https://github.com/webtorrent/webtorrent.io). Update
`config.js`, updating the desktop app version.
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.**
## Smoke Tests
Before a release, check that the following basic use cases work correctly:

View File

@@ -1,8 +1,6 @@
<h1 align="center">
<br>
<a href="https://webtorrent.io">
<img src="https://webtorrent.io/img/WebTorrent.png" alt="WebTorrent" width="200">
</a>
<a href="https://webtorrent.io"><img src="https://webtorrent.io/img/WebTorrent.png" alt="WebTorrent" width="200"></a>
<br>
WebTorrent Desktop
<br>
@@ -12,21 +10,16 @@
<h4 align="center">The streaming torrent app. For Mac, Windows, and Linux.</h4>
<p align="center">
<a href="https://gitter.im/webtorrent/webtorrent"><img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg" alt="gitter"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/webtorrent/webtorrent-desktop.svg" alt="github release"></a>
<a href="https://travis-ci.org/webtorrent/webtorrent-desktop"><img src="https://img.shields.io/travis/webtorrent/webtorrent-desktop/master.svg" alt="travis"></a>
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Standard - JavaScript Style Guide"></a>
<a href="https://gitter.im/feross/webtorrent"><img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg" alt="Gitter"></a>
<a href="https://travis-ci.org/feross/webtorrent-desktop"><img src="https://img.shields.io/travis/feross/webtorrent-desktop/master.svg" alt="Travis"></a>
<a href="https://github.com/feross/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/feross/webtorrent-desktop.svg" alt="Release"></a>
</p>
## Install
Download the latest version of WebTorrent Desktop from
[the official website](https://webtorrent.io/desktop/) or the
[GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page. Alternatively, you can install it with [Homebrew-Cask](https://github.com/caskroom/homebrew-cask):
```
$ brew cask install webtorrent
```
[GitHub releases](https://github.com/feross/webtorrent-desktop/releases) page.
**WebTorrent Desktop** is under very active development. You can try out the
current (unstable) development version by cloning the Git repo. See the
@@ -44,7 +37,7 @@ instructions below in the ["How to Contribute"](#how-to-contribute) section.
### Get the code
```
$ git clone https://github.com/webtorrent/webtorrent-desktop.git
$ git clone https://github.com/feross/webtorrent-desktop.git
$ cd webtorrent-desktop
$ npm install
```
@@ -75,25 +68,19 @@ $ npm test
$ npm run test-integration
```
The integration tests use Spectron and Tape. They click through the app, taking screenshots and
comparing each one to a reference. Why screenshots?
The integration tests use Spectron and Tape. They click through the app, taking screenshots and comparing each one to a reference. Why screenshots?
* Ad-hoc checking makes the tests a lot more work to write
* Even diffing the whole HTML is not as thorough as screenshot diffing. For example, it wouldn't
catch an bug where hitting ESC from a video doesn't correctly restore window size.
* Even diffing the whole HTML is not as thorough as screenshot diffing. For example, it wouldn't catch an bug where hitting ESC from a video doesn't correctly restore window size.
* Chrome's own integration tests use screenshot diffing iirc
* Small UI changes will break a few tests, but the fix is as easy as deleting the offending
screenshots and running the tests, which will recreate them with the new look.
* The resulting Github PR will then show, pixel by pixel, the exact UI changes that were made! See
https://github.com/blog/817-behold-image-view-modes
* Small UI changes will break a few tests, but the fix is as easy as deleting the offending screenshots and running the tests, which will recreate them with the new look.
* The resulting Github PR will then show, pixel by pixel, the exact UI changes that were made! Ses https://github.com/blog/817-behold-image-view-modes
For MacOS, you'll need a Retina screen for the integration tests to pass. Your screen should have
the same resolution as a 2016 12" Macbook.
For MacOS, you'll need a Retina screen for the integration tests to pass. Your screen should have the same resolution as a 2016 12" Macbook.
For Windows, you'll need Windows 10 with a 1366x768 screen.
When running integration tests, keep the mouse on the edge of the screen and don't touch the mouse
or keyboard while the tests are running.
When running integration tests, keep the mouse on the edge of the screen and don't touch the mouse or keyboard while the tests are running.
### Package the app
@@ -123,7 +110,7 @@ The following optional arguments are available:
- `all` - All platforms (default)
Note: Even with the `--package` option, the auto-update files (.nupkg for Windows,
-darwin.zip for Mac) will always be produced.
*-darwin.zip for Mac) will always be produced.
#### Windows build notes
@@ -158,6 +145,10 @@ Time out? Show a missing codec error?
The app never sends any personally identifying information, nor does it track which
torrents you add.
### Code Style
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
## License
MIT. Copyright (c) [WebTorrent, LLC](https://webtorrent.io).

View File

@@ -1,43 +0,0 @@
# Security Policies and Procedures
This document outlines security procedures and general policies for the WebTorrent
project.
* [Reporting a Bug](#reporting-a-bug)
* [Disclosure Policy](#disclosure-policy)
* [Comments on this Policy](#comments-on-this-policy)
## Reporting a Bug
The WebTorrent team and community take all security bugs in WebTorrent seriously.
Thank you for improving the security of WebTorrent. We appreciate your efforts and
responsible disclosure and will make every effort to acknowledge your
contributions.
Report security bugs by emailing the lead maintainer at feross@feross.org.
The lead maintainer will acknowledge your email within 48 hours, and will send a
more detailed response within 48 hours indicating the next steps in handling
your report. After the initial reply to your report, the security team will
endeavor to keep you informed of the progress towards a fix and full
announcement, and may ask for additional information or guidance.
Report security bugs in third-party modules to the person or team maintaining
the module. You can also report a vulnerability through the
[Node Security Project](https://nodesecurity.io/report).
## Disclosure Policy
When the security team receives a security bug report, they will assign it to a
primary handler. This person will coordinate the fix and release process,
involving the following steps:
* Confirm the problem and determine the affected versions.
* Audit code to find any potential similar problems.
* Prepare fixes for all releases still under maintenance. These fixes will be
released as fast as possible to npm.
## Comments on this Policy
If you have suggestions on how this process could be improved please submit a
pull request.

View File

@@ -25,7 +25,7 @@ let tmpPath
try {
tmpPath = path.join(fs.statSync('/tmp') && '/tmp', 'webtorrent')
} catch (err) {
tmpPath = path.join(os.tmpdir(), 'webtorrent')
tmpPath = path.join(os.tmpDir(), 'webtorrent')
}
rimraf.sync(tmpPath)

View File

@@ -21,7 +21,6 @@ const pkg = require('../package.json')
const BUILD_NAME = config.APP_NAME + '-v' + config.APP_VERSION
const BUILD_PATH = path.join(config.ROOT_PATH, 'build')
const DIST_PATH = path.join(config.ROOT_PATH, 'dist')
const NODE_MODULES_PATH = path.join(config.ROOT_PATH, 'node_modules')
const argv = minimist(process.argv.slice(2), {
boolean: [
@@ -37,12 +36,6 @@ const argv = minimist(process.argv.slice(2), {
})
function build () {
console.log('Reinstalling node_modules...')
rimraf.sync(NODE_MODULES_PATH)
cp.execSync('npm install', { stdio: 'inherit' })
cp.execSync('npm dedupe', { stdio: 'inherit' })
console.log('Nuking dist/ and build/...')
rimraf.sync(DIST_PATH)
rimraf.sync(BUILD_PATH)
@@ -449,51 +442,51 @@ function buildWin32 (cb) {
usePackageJson: false,
version: pkg.version
})
.then(function () {
console.log(`Windows: Created ${destArch} installer.`)
.then(function () {
console.log(`Windows: Created ${destArch} installer.`)
/**
/**
* Delete extraneous Squirrel files (i.e. *.nupkg delta files for older
* versions of the app)
*/
fs.readdirSync(DIST_PATH)
.filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version))
.forEach((filename) => {
fs.unlinkSync(path.join(DIST_PATH, filename))
})
fs.readdirSync(DIST_PATH)
.filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version))
.forEach((filename) => {
fs.unlinkSync(path.join(DIST_PATH, filename))
})
if (destArch === 'ia32') {
console.log('Windows: Renaming ia32 installer files...')
if (destArch === 'ia32') {
console.log('Windows: Renaming ia32 installer files...')
// RELEASES -> RELEASES-ia32
const relPath = path.join(DIST_PATH, 'RELEASES-ia32')
fs.renameSync(
path.join(DIST_PATH, 'RELEASES'),
relPath
)
// RELEASES -> RELEASES-ia32
const relPath = path.join(DIST_PATH, 'RELEASES-ia32')
fs.renameSync(
path.join(DIST_PATH, 'RELEASES'),
relPath
)
// WebTorrent-vX.X.X-full.nupkg -> WebTorrent-vX.X.X-ia32-full.nupkg
fs.renameSync(
path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-full.nupkg`),
path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-ia32-full.nupkg`)
)
// WebTorrent-vX.X.X-full.nupkg -> WebTorrent-vX.X.X-ia32-full.nupkg
fs.renameSync(
path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-full.nupkg`),
path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-ia32-full.nupkg`)
)
// Change file name inside RELEASES-ia32 to match renamed file
const relContent = fs.readFileSync(relPath, 'utf8')
const relContent32 = relContent.replace('full.nupkg', 'ia32-full.nupkg')
fs.writeFileSync(relPath, relContent32)
// Change file name inside RELEASES-ia32 to match renamed file
const relContent = fs.readFileSync(relPath, 'utf8')
const relContent32 = relContent.replace('full.nupkg', 'ia32-full.nupkg')
fs.writeFileSync(relPath, relContent32)
if (relContent === relContent32) {
if (relContent === relContent32) {
// Sanity check
throw new Error('Fixing RELEASES-ia32 failed. Replacement did not modify the file.')
}
console.log('Windows: Renamed ia32 installer files.')
throw new Error('Fixing RELEASES-ia32 failed. Replacement did not modify the file.')
}
cb(null)
})
.catch(cb)
console.log('Windows: Renamed ia32 installer files.')
}
cb(null)
})
.catch(cb)
}
function packagePortable (filesPath, destArch, cb) {

9
bin/release-_post.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
set -e
npm run update-authors
git diff --exit-code
npm run package -- --sign
git push
git push --tags
npm run gh-release

8
bin/release-_pre.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
git pull
rm -rf node_modules/
npm install
npm dedupe
npm test

7
bin/release-major.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
BIN=`dirname $0`
$BIN/release-_pre.sh
npm version major
$BIN/release-_post.sh

7
bin/release-minor.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
BIN=`dirname $0`
$BIN/release-_pre.sh
npm version minor
$BIN/release-_post.sh

7
bin/release-patch.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
BIN=`dirname $0`
$BIN/release-_pre.sh
npm version patch
$BIN/release-_post.sh

View File

@@ -1,14 +1,14 @@
{
"name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.20.0",
"version": "0.18.0",
"author": {
"name": "WebTorrent, LLC",
"email": "feross@webtorrent.io",
"url": "https://webtorrent.io"
},
"bugs": {
"url": "https://github.com/webtorrent/webtorrent-desktop/issues"
"url": "https://github.com/feross/webtorrent-desktop/issues"
},
"dependencies": {
"airplayer": "^2.0.0",
@@ -17,7 +17,6 @@
"auto-launch": "^4.0.1",
"bitfield": "^1.0.2",
"capture-frame": "^1.0.0",
"chokidar": "^1.6.1",
"chromecasts": "^1.8.0",
"cp-file": "^4.0.1",
"create-torrent": "^3.24.5",
@@ -30,14 +29,14 @@
"iso-639-1": "^1.2.1",
"languagedetect": "^1.1.1",
"location-history": "^1.0.0",
"material-ui": "^0.17.0",
"material-ui": "^0.16.0",
"mkdirp": "^0.5.1",
"music-metadata": "^0.9.15",
"musicmetadata": "^2.0.2",
"network-address": "^1.1.0",
"parse-torrent": "^5.7.3",
"prettier-bytes": "^1.0.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react": "^15.2.1",
"react-dom": "^15.2.1",
"react-tap-event-plugin": "^2.0.1",
"rimraf": "^2.5.2",
"run-parallel": "^1.1.6",
@@ -51,10 +50,10 @@
"zero-fill": "^2.2.3"
},
"devDependencies": {
"buble": "^0.19.3",
"buble": "^0.15.2",
"cross-zip": "^2.0.1",
"depcheck": "^0.6.4",
"electron": "1.6.16",
"electron": "1.4.15",
"electron-osx-sign": "0.4.3",
"electron-packager": "~8.5.1",
"electron-winstaller": "~2.5.2",
@@ -94,7 +93,7 @@
"productName": "WebTorrent",
"repository": {
"type": "git",
"url": "git://github.com/webtorrent/webtorrent-desktop.git"
"url": "git://github.com/feross/webtorrent-desktop.git"
},
"scripts": {
"build": "buble src --output build",

View File

@@ -23,7 +23,7 @@ module.exports = {
CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report',
TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry',
APP_COPYRIGHT: 'Copyright © 2014-2018 ' + APP_TEAM,
APP_COPYRIGHT: 'Copyright © 2014-2017 ' + APP_TEAM,
APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'),
APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'),
APP_NAME: APP_NAME,
@@ -70,9 +70,9 @@ module.exports = {
DEFAULT_DOWNLOAD_PATH: getDefaultDownloadPath(),
GITHUB_URL: 'https://github.com/webtorrent/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/webtorrent/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/webtorrent/webtorrent-desktop/master',
GITHUB_URL: 'https://github.com/feross/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/feross/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/feross/webtorrent-desktop/master',
HOME_PAGE_URL: 'https://webtorrent.io',

View File

@@ -32,7 +32,7 @@ function downloadFinished (path) {
*/
function setBadge (count) {
if (process.platform === 'darwin' ||
(process.platform === 'linux' && app.isUnityRunning())) {
process.platform === 'linux' && app.isUnityRunning()) {
log(`setBadge: ${count}`)
app.setBadgeCount(Number(count))
}

View File

@@ -17,8 +17,8 @@ let proc = null
function checkInstall (playerPath, cb) {
// check for VLC if external player has not been specified by the user
// otherwise assume the player is installed
if (playerPath == null) return vlcCommand(cb)
process.nextTick(() => cb(null))
if (playerPath == null) return vlcCommand((err) => cb(!err))
process.nextTick(() => cb(true))
}
function spawn (playerPath, url, title) {
@@ -29,6 +29,7 @@ function spawn (playerPath, url, title) {
if (err) return windows.main.dispatch('externalPlayerNotFound')
const args = [
'--play-and-exit',
'--video-on-top',
'--quiet',
`--meta-title=${JSON.stringify(title)}`,
url

View File

@@ -1,50 +0,0 @@
const chokidar = require('chokidar')
const log = require('./log')
class FolderWatcher {
constructor ({window, state}) {
this.window = window
this.state = state
this.torrentsFolderPath = null
this.watching = false
}
isEnabled () {
return this.state.saved.prefs.autoAddTorrents
}
start () {
// Stop watching previous folder before
// start watching a new one.
if (this.watching) this.stop()
const torrentsFolderPath = this.state.saved.prefs.torrentsFolderPath
this.torrentsFolderPath = torrentsFolderPath
if (!torrentsFolderPath) return
const glob = `${torrentsFolderPath}/**/*.torrent`
log('Folder Watcher: watching: ', glob)
const options = {
ignoreInitial: true,
awaitWriteFinish: true
}
this.watcher = chokidar.watch(glob, options)
this.watcher
.on('add', (path) => {
log('Folder Watcher: added torrent: ', path)
this.window.dispatch('addTorrent', path)
})
this.watching = true
}
stop () {
log('Folder Watcher: stop.')
if (!this.watching) return
this.watcher.close()
this.watching = false
}
}
module.exports = FolderWatcher

View File

@@ -44,7 +44,7 @@ function installDarwin () {
function uninstallDarwin () {}
const EXEC_COMMAND = [ process.execPath, '--' ]
const EXEC_COMMAND = [ process.execPath ]
if (!config.IS_PRODUCTION) {
EXEC_COMMAND.push(config.ROOT_PATH)

View File

@@ -72,16 +72,13 @@ function init () {
if (err) throw err
isReady = true
const state = results.state
windows.main.init(state, {hidden: hidden})
windows.main.init(results.state, {hidden: hidden})
windows.webtorrent.init()
menu.init()
// To keep app startup fast, some code is delayed.
setTimeout(() => {
delayedInit(state)
}, config.DELAYED_INIT)
setTimeout(delayedInit, config.DELAYED_INIT)
// Report uncaught exceptions
process.on('uncaughtException', (err) => {
@@ -124,24 +121,17 @@ function init () {
})
}
function delayedInit (state) {
function delayedInit () {
if (app.isQuitting) return
const announcement = require('./announcement')
const dock = require('./dock')
const updater = require('./updater')
const FolderWatcher = require('./folder-watcher')
const folderWatcher = new FolderWatcher({window: windows.main, state})
announcement.init()
dock.init()
updater.init()
ipc.setModule('folderWatcher', folderWatcher)
if (folderWatcher.isEnabled()) {
folderWatcher.start()
}
if (process.platform === 'win32') {
const userTasks = require('./user-tasks')
userTasks.init()
@@ -188,7 +178,7 @@ function onAppOpen (newArgv) {
function sliceArgv (argv) {
return argv.slice(config.IS_PRODUCTION ? 1
: config.IS_TEST ? 4
: 2)
: 2)
}
function processArgv (argv) {
@@ -208,7 +198,7 @@ function processArgv (argv) {
// Ignore hidden argument, already being handled
} else if (arg.startsWith('-psn')) {
// Ignore Mac launchd "process serial number" argument
// Issue: https://github.com/webtorrent/webtorrent-desktop/issues/214
// Issue: https://github.com/feross/webtorrent-desktop/issues/214
} else if (arg.startsWith('--')) {
// Ignore Spectron flags
} else if (arg === 'data:,') {

View File

@@ -1,6 +1,5 @@
module.exports = {
init,
setModule
init
}
const electron = require('electron')
@@ -14,14 +13,6 @@ const windows = require('./windows')
// Messages from the main process, to be sent once the WebTorrent process starts
const messageQueueMainToWebTorrent = []
// Will hold modules injected from the app that will be used on fired
// IPC events.
const modules = {}
function setModule (name, module) {
modules[name] = module
}
function init () {
const ipc = electron.ipcMain
@@ -67,7 +58,7 @@ function init () {
})
/**
* Player Events
* Events
*/
ipc.on('onPlayerOpen', function () {
@@ -115,28 +106,6 @@ function init () {
thumbar.onPlayerPause()
})
/**
* Folder Watcher Events
*/
ipc.on('startFolderWatcher', function () {
if (!modules['folderWatcher']) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules['folderWatcher'].start()
})
ipc.on('stopFolderWatcher', function () {
if (!modules['folderWatcher']) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules['folderWatcher'].stop()
})
/**
* Shell
*/
@@ -197,8 +166,8 @@ function init () {
ipc.on('checkForExternalPlayer', function (e, path) {
const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, function (err) {
windows.main.send('checkForExternalPlayer', !err)
externalPlayer.checkInstall(path, function (isInstalled) {
windows.main.send('checkForExternalPlayer', isInstalled)
})
})

View File

@@ -324,7 +324,7 @@ function getMenuTemplate () {
]
if (process.platform === 'darwin') {
// WebTorrent menu (Mac)
// Add WebTorrent app menu (Mac)
template.unshift({
label: config.APP_NAME,
submenu: [
@@ -367,25 +367,7 @@ function getMenuTemplate () {
]
})
// Edit menu (Mac)
template[2].submenu.push(
{
type: 'separator'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
}
)
// Window menu (Mac)
// Add Window menu (Mac)
template.splice(6, 0, {
role: 'window',
submenu: [
@@ -426,7 +408,7 @@ function getMenuTemplate () {
})
// Help menu (Windows, Linux)
template[5].submenu.push(
template[4].submenu.push(
{
type: 'separator'
},

View File

@@ -36,8 +36,8 @@ function setWindowFocus (flag) {
}
function initLinux () {
checkLinuxTraySupport(function (err) {
if (!err) createTray()
checkLinuxTraySupport(function (supportsTray) {
if (supportsTray) createTray()
})
}
@@ -55,14 +55,10 @@ function checkLinuxTraySupport (cb) {
// libappindicator1. If WebTorrent was installed from the deb file, we should
// always have it. If it was installed from the zip file, we might not.
cp.exec('dpkg --get-selections libappindicator1', function (err, stdout) {
if (err) return cb(err)
if (err) return cb(false)
// Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed:
if (stdout.endsWith('\tinstall\n')) {
cb(null)
} else {
cb(new Error('debian package not installed'))
}
cb(stdout.endsWith('\tinstall\n'))
})
}

View File

@@ -166,8 +166,8 @@ function setBounds (bounds, maximize) {
if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen
const scr = electron.screen.getDisplayMatching(main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2))
bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2))
bounds.x = Math.round(scr.bounds.x + scr.bounds.width / 2 - bounds.width / 2)
bounds.y = Math.round(scr.bounds.y + scr.bounds.height / 2 - bounds.height / 2)
log('setBounds: centered to ' + JSON.stringify(bounds))
}
// Resize the window's content area (so window border doesn't need to be taken

View File

@@ -23,8 +23,7 @@ module.exports = class UpdateAvailableModal extends React.Component {
)
function handleShow () {
// TODO: use the GitHub urls from config.js
electron.shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases')
electron.shell.openExternal('https://github.com/feross/webtorrent-desktop/releases')
dispatch('exitModal')
}

View File

@@ -1,13 +0,0 @@
const {ipcRenderer} = require('electron')
module.exports = class FolderWatcherController {
start () {
console.log('-- IPC: start folder watcher')
ipcRenderer.send('startFolderWatcher')
}
stop () {
console.log('-- IPC: stop folder watcher')
ipcRenderer.send('stopFolderWatcher')
}
}

View File

@@ -27,8 +27,6 @@ module.exports = class PlaybackController {
// * Stream, if not already fully downloaded
// * If no file index is provided, restore the most recently viewed file or autoplay the first
playFile (infoHash, index /* optional */) {
this.pauseActiveTorrents(infoHash)
const state = this.state
if (state.location.url() === 'player') {
this.updatePlayer(infoHash, index, false, (err) => {
@@ -86,26 +84,15 @@ module.exports = class PlaybackController {
else this.pause()
}
pauseActiveTorrents (infoHash) {
// Playback Priority: pause all active torrents if needed.
if (!this.state.saved.prefs.highestPlaybackPriority) return
// Do not pause active torrents if playing a fully downloaded torrent.
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
if (torrentSummary.status === 'seeding') return
dispatch('prioritizeTorrent', infoHash)
}
// Play next file in list (if any)
nextTrack () {
const state = this.state
if (Playlist.hasNext(state) && state.playing.location !== 'external') {
this.updatePlayer(
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err)
else this.play()
})
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err)
else this.play()
})
}
}
@@ -271,7 +258,7 @@ module.exports = class PlaybackController {
state.playing.fileIndex = index
state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video'
: TorrentPlayer.isAudio(fileSummary) ? 'audio'
: 'other'
: 'other'
// pick up where we left off
let jumpToTime = 0
@@ -354,11 +341,6 @@ module.exports = class PlaybackController {
ipcRenderer.send('onPlayerClose')
// Playback Priority: resume previously paused downloads.
if (this.state.saved.prefs.highestPlaybackPriority) {
dispatch('resumePausedTorrents')
}
this.update()
}
}

View File

@@ -121,10 +121,11 @@ module.exports = class TorrentListController {
torrentSummary.status = 'new'
this.startTorrentingSummary(torrentSummary.torrentKey)
sound.play('ENABLE')
return
} else {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
sound.play('DISABLE')
}
this.pauseTorrent(torrentSummary, true)
}
pauseAllTorrents () {
@@ -148,41 +149,6 @@ module.exports = class TorrentListController {
sound.play('ENABLE')
}
pauseTorrent (torrentSummary, playSound) {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
if (playSound) sound.play('DISABLE')
}
prioritizeTorrent (infoHash) {
this.state.saved.torrents
.filter((torrent) => { // We're interested in active torrents only.
return (['downloading', 'seeding'].indexOf(torrent.status) !== -1)
})
.map((torrent) => { // Pause all active torrents except the one that started playing.
if (infoHash === torrent.infoHash) return
// Pause torrent without playing sounds.
this.pauseTorrent(torrent, false)
this.state.saved.torrentsToResume.push(torrent.infoHash)
})
console.log('Playback Priority: paused torrents: ', this.state.saved.torrentsToResume)
}
resumePausedTorrents () {
console.log('Playback Priority: resuming paused torrents')
if (!this.state.saved.torrentsToResume || !this.state.saved.torrentsToResume.length) return
this.state.saved.torrentsToResume.map((infoHash) => {
this.toggleTorrent(infoHash)
})
// reset paused torrents
this.state.saved.torrentsToResume = []
}
toggleTorrentFile (infoHash, index) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
torrentSummary.selections[index] = !torrentSummary.selections[index]
@@ -315,7 +281,7 @@ module.exports = class TorrentListController {
// Recursively finds {name, path, size} for all files in a folder
// Calls `cb` on success, calls `onError` on failure
function findFilesRecursive (paths, cb_) {
function findFilesRecursive (paths, cb) {
if (paths.length > 1) {
let numComplete = 0
let ret = []
@@ -324,7 +290,7 @@ function findFilesRecursive (paths, cb_) {
ret.push(...fileObjs)
if (++numComplete === paths.length) {
ret.sort((a, b) => a.path < b.path ? -1 : a.path > b.path)
cb_(ret)
cb(ret)
}
})
})
@@ -338,7 +304,7 @@ function findFilesRecursive (paths, cb_) {
// Files: return name, path, and size
if (!stat.isDirectory()) {
const filePath = fileOrFolder
return cb_([{
return cb([{
name: path.basename(filePath),
path: filePath,
size: stat.size
@@ -350,7 +316,7 @@ function findFilesRecursive (paths, cb_) {
fs.readdir(folderPath, function (err, fileNames) {
if (err) return dispatch('error', err)
const paths = fileNames.map((fileName) => path.join(folderPath, fileName))
findFilesRecursive(paths, cb_)
findFilesRecursive(paths, cb)
})
})
}

View File

@@ -78,7 +78,7 @@ function migrate_0_7_0 (saved) {
}
// Fix exception caused by incorrect file ordering.
// https://github.com/webtorrent/webtorrent-desktop/pull/604#issuecomment-222805214
// https://github.com/feross/webtorrent-desktop/pull/604#issuecomment-222805214
delete ts.defaultPlayFileIndex
delete ts.files
delete ts.selections
@@ -111,7 +111,7 @@ function migrate_0_12_0 (saved) {
// Undo a terrible bug where clicking Play on a default torrent on a fresh
// install results in a "path missing" error
// See https://github.com/webtorrent/webtorrent-desktop/pull/806
// See https://github.com/feross/webtorrent-desktop/pull/806
const defaultTorrentFiles = [
'6a9759bffd5c0af65319979fb7832189f4f3c35d.torrent',
'88594aaacbde40ef3e2510c47374ec0aa396c08e.torrent',
@@ -153,7 +153,7 @@ function migrate_0_17_0 (saved) {
function migrate_0_17_2 (saved) {
// Remove the trailing dot (.) from the Wired CD torrent name, since
// folders/files that end in a trailing dot (.) or space are not deletable from
// Windows Explorer. See: https://github.com/webtorrent/webtorrent-desktop/issues/905
// Windows Explorer. See: https://github.com/feross/webtorrent-desktop/issues/905
const cpFile = require('cp-file')
const rimraf = require('rimraf')

View File

@@ -121,13 +121,9 @@ function setupStateSaved (cb) {
isFileHandler: false,
openExternalPlayer: false,
externalPlayerPath: null,
startup: false,
autoAddTorrents: false,
torrentsFolderPath: '',
highestPlaybackPriority: true
startup: false
},
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
torrentsToResume: [],
version: config.APP_VERSION /* make sure we can upgrade gracefully later */
}

View File

@@ -33,18 +33,11 @@ function isVideo (file) {
function isAudio (file) {
return [
'.aac',
'.aiff',
'.ape',
'.ac3',
'.flac',
'.m4a',
'.mp2',
'.mp3',
'.oga',
'.ogg',
'.opus',
'.wav',
'.wma'
'.m4a'
].includes(getFileExtension(file))
}

View File

@@ -3,145 +3,39 @@ module.exports = torrentPoster
const captureFrame = require('capture-frame')
const path = require('path')
const mediaExtensions = {
audio: ['.aac', '.asf', '.flac', '.m2a', '.m4a', '.mp2', '.mp4', '.mp3', '.oga', '.ogg', '.opus',
'.wma', '.wav', '.wv', '.wvp'],
video: ['.mp4', '.m4v', '.webm', '.mov', '.mkv'],
image: ['.gif', '.jpg', '.jpeg', '.png']
}
function torrentPoster (torrent, cb) {
// First, try to use a poster image if available
const posterFile = torrent.files.filter(function (file) {
return /^poster\.(jpg|png|gif)$/.test(file.name)
})[0]
if (posterFile) return extractPoster(posterFile, cb)
if (posterFile) return torrentPosterFromImage(posterFile, torrent, cb)
// 'score' each media type based on total size present in torrent
const bestScore = ['audio', 'video', 'image'].map(mediaType => {
return {
type: mediaType,
size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType])}
}).sort((a, b) => { // sort descending on size
return b.size - a.size
})[0]
// Second, try to use the largest video file
// Filter out file formats that the <video> tag definitely can't play
const videoFile = getLargestFileByExtension(torrent, ['.mp4', '.m4v', '.webm', '.mov', '.mkv'])
if (videoFile) return torrentPosterFromVideo(videoFile, torrent, cb)
if (bestScore.size === 0) {
// Admit defeat, no video, audio or image had a significant presence
return cb(new Error('Cannot generate a poster from any files in the torrent'))
}
// Third, try to use the largest image file
const imgFile = getLargestFileByExtension(torrent, ['.gif', '.jpg', '.jpeg', '.png'])
if (imgFile) return torrentPosterFromImage(imgFile, torrent, cb)
// Based on which media type is dominant we select the corresponding poster function
switch (bestScore.type) {
case 'audio':
return torrentPosterFromAudio(torrent, cb)
case 'image':
return torrentPosterFromImage(torrent, cb)
case 'video':
return torrentPosterFromVideo(torrent, cb)
}
// TODO: generate a waveform from the largest sound file
// Finally, admit defeat
return cb(new Error('Cannot generate a poster from any files in the torrent'))
}
/**
* Calculate the total data size of file matching one of the provided extensions
* @param torrent
* @param extensions List of extension to match
* @returns {number} total size, of matches found (>= 0)
*/
function calculateDataLengthByExtension (torrent, extensions) {
const files = filterOnExtension(torrent, extensions)
if (files.length === 0) return 0
return files
.map(file => file.length)
.reduce((a, b) => {
return a + b
})
}
/**
* Get the largest file of a given torrent, filtered by provided extension
* @param torrent Torrent to search in
* @param extensions Extension whitelist filter
* @returns Torrent file object
*/
function getLargestFileByExtension (torrent, extensions) {
const files = filterOnExtension(torrent, extensions)
const files = torrent.files.filter(function (file) {
const extname = path.extname(file.name).toLowerCase()
return extensions.indexOf(extname) !== -1
})
if (files.length === 0) return undefined
return files.reduce((a, b) => {
return files.reduce(function (a, b) {
return a.length > b.length ? a : b
})
}
/**
* Filter file on a list extension, can be used to find al image files
* @param torrent Torrent to filter files from
* @param extensions File extensions to filter on
* @returns {number} Array of torrent file objects matching one of the given extensions
*/
function filterOnExtension (torrent, extensions) {
return torrent.files.filter(file => {
const extname = path.extname(file.name).toLowerCase()
return extensions.indexOf(extname) !== -1
})
}
/**
* Returns a score how likely the file is suitable as a poster
* @param imgFile File object of an image
* @returns {number} Score, higher score is a better match
*/
function scoreAudioCoverFile (imgFile) {
const fileName = path.basename(imgFile.name, path.extname(imgFile.name)).toLowerCase()
const relevanceScore = {
cover: 80,
folder: 80,
album: 80,
front: 80,
back: 20
}
for (let keyword in relevanceScore) {
if (fileName === keyword) {
return relevanceScore[keyword]
}
if (fileName.indexOf(keyword) !== -1) {
return 0.8 * relevanceScore[keyword]
}
}
return 0
}
function torrentPosterFromAudio (torrent, cb) {
const imageFiles = filterOnExtension(torrent, mediaExtensions.image)
const bestCover = imageFiles.map(file => {
return {
file: file,
score: scoreAudioCoverFile(file)
}
}).reduce((a, b) => {
if (a.score > b.score) {
return a
}
if (b.score > a.score) {
return b
}
// If score is equal, pick the largest file, aiming for highest resolution
if (a.file.length > b.file.length) {
return a
}
return b
})
if (!bestCover) return cb(new Error('Generated poster contains no data'))
const extname = path.extname(bestCover.file.name)
bestCover.file.getBuffer((err, buf) => cb(err, buf, extname))
}
function torrentPosterFromVideo (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.video)
function torrentPosterFromVideo (file, torrent, cb) {
const index = torrent.files.indexOf(file)
const server = torrent.createServer(0)
@@ -183,12 +77,7 @@ function torrentPosterFromVideo (torrent, cb) {
}
}
function torrentPosterFromImage (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.image)
extractPoster(file, cb)
}
function extractPoster (file, cb) {
function torrentPosterFromImage (file, torrent, cb) {
const extname = path.extname(file.name)
file.getBuffer((err, buf) => { return cb(err, buf, extname) })
file.getBuffer((err, buf) => cb(err, buf, extname))
}

View File

@@ -24,7 +24,6 @@ const State = require('./lib/state')
State.load(onState)
const createGetter = require('fn-getter')
const debounce = require('debounce')
const dragDrop = require('drag-drop')
const electron = require('electron')
const fs = require('fs')
@@ -111,10 +110,6 @@ function onState (err, _state) {
update: createGetter(() => {
const UpdateController = require('./controllers/update-controller')
return new UpdateController(state)
}),
folderWatcher: createGetter(() => {
const FolderWatcherController = require('./controllers/folder-watcher-controller')
return new FolderWatcherController()
})
}
@@ -150,18 +145,6 @@ function onState (err, _state) {
// ...same thing if you paste a torrent
document.addEventListener('paste', onPaste)
const debouncedFullscreenToggle = debounce(function () {
dispatch('toggleFullScreen')
}, 1000, true)
document.addEventListener('wheel', function (event) {
// ctrlKey detects pinch to zoom, http://crbug.com/289887
if (event.ctrlKey) {
event.preventDefault()
debouncedFullscreenToggle()
}
})
// ...focus and blur. Needed to show correct dock icon text ('badge') in OSX
window.addEventListener('focus', onFocus)
window.addEventListener('blur', onBlur)
@@ -258,8 +241,6 @@ const dispatchHandlers = {
controllers.torrentList().startTorrentingSummary(torrentKey),
'saveTorrentFileAs': (torrentKey) =>
controllers.torrentList().saveTorrentFileAs(torrentKey),
'prioritizeTorrent': (infoHash) => controllers.torrentList().prioritizeTorrent(infoHash),
'resumePausedTorrents': () => controllers.torrentList().resumePausedTorrents(),
// Playback
'playFile': (infoHash, index) => controllers.playback().playFile(infoHash, index),
@@ -300,8 +281,6 @@ const dispatchHandlers = {
'preferences': () => controllers.prefs().show(),
'updatePreferences': (key, value) => controllers.prefs().update(key, value),
'checkDownloadPath': checkDownloadPath,
'startFolderWatcher': () => controllers.folderWatcher().start(),
'stopFolderWatcher': () => controllers.folderWatcher().stop(),
// Update (check for new versions on Linux, where there's no auto updater)
'updateAvailable': (version) => controllers.update().updateAvailable(version),
@@ -362,7 +341,6 @@ function setupIpc () {
ipcRenderer.on('wt-infohash', (e, ...args) => tc.torrentInfoHash(...args))
ipcRenderer.on('wt-metadata', (e, ...args) => tc.torrentMetadata(...args))
ipcRenderer.on('wt-done', (e, ...args) => tc.torrentDone(...args))
ipcRenderer.on('wt-done', () => controllers.torrentList().resumePausedTorrents())
ipcRenderer.on('wt-warning', (e, ...args) => tc.torrentWarning(...args))
ipcRenderer.on('wt-error', (e, ...args) => tc.torrentError(...args))
@@ -459,13 +437,6 @@ function setDimensions (dimensions) {
function onOpen (files) {
if (!Array.isArray(files)) files = [ files ]
// File API seems to transform "magnet:?foo" in "magnet:///?foo"
// this is a sanitization
files = files.map(file => {
if (typeof file !== 'string') return file
return file.replace(/^magnet:\/+\?/i, 'magnet:?')
})
const url = state.location.url()
const allTorrents = files.every(TorrentPlayer.isTorrent)
const allSubtitles = files.every(controllers.subtitles().isSubtitle)

View File

@@ -160,7 +160,6 @@ function renderMedia (state) {
} else {
// When the last video completes, pause the video instead of looping
state.playing.isPaused = true
if (state.window.isFullScreen) dispatch('toggleFullScreen')
}
}
@@ -207,18 +206,25 @@ function renderOverlay (state) {
function renderAudioMetadata (state) {
const fileSummary = state.getPlayingFileSummary()
if (!fileSummary.audioInfo) return
const common = fileSummary.audioInfo.common || {}
const info = fileSummary.audioInfo
// Get audio track info
const title = common.title ? common.title : fileSummary.name
let title = info.title
if (!title) {
title = fileSummary.name
}
let artist = info.artist && info.artist[0]
let album = info.album
if (album && info.year && !album.includes(info.year)) {
album += ' (' + info.year + ')'
}
let track
if (info.track && info.track.no && info.track.of) {
track = info.track.no + ' of ' + info.track.of
}
// Show a small info box in the middle of the screen with title/album/etc
const elems = []
// Audio metadata: artist(s)
const artist = common.albumartist || common.artist ||
(common.artists && common.artists.filter(function (a) { return a }).join(', ')) ||
'(Unknown Artist)'
if (artist) {
elems.push((
<div key='artist' className='audio-artist'>
@@ -226,44 +232,14 @@ function renderAudioMetadata (state) {
</div>
))
}
// Audio metadata: album
if (common.album) {
if (album) {
elems.push((
<div key='album' className='audio-album'>
<label>Album</label>{common.album}
<label>Album</label>{album}
</div>
))
}
// Audio metadata: year
if (common.year) {
elems.push((
<div key='year' className='audio-year'>
<label>Year</label>{common.year}
</div>
))
}
// Audio metadata: release information (label & catalog-number)
if (common.label || common.catalognumber) {
const releaseInfo = []
if (common.label) {
releaseInfo.push(common.label)
}
if (common.catalognumber) {
releaseInfo.push(common.catalognumber)
}
elems.push((
<div key='release' className='audio-release'>
<label>Release</label>{ releaseInfo.join(' / ') }
</div>
))
}
// Audio metadata: track-number
if (common.track && common.track.no && common.track.of) {
const track = common.track.no + ' of ' + common.track.of
if (track) {
elems.push((
<div key='track' className='audio-track'>
<label>Track</label>{track}
@@ -271,38 +247,6 @@ function renderAudioMetadata (state) {
))
}
// Audio metadata: format
const format = []
fileSummary.audioInfo.format = fileSummary.audioInfo.format || ''
if (fileSummary.audioInfo.format.dataformat) {
format.push(fileSummary.audioInfo.format.dataformat)
}
if (fileSummary.audioInfo.format.bitrate) {
format.push(fileSummary.audioInfo.format.bitrate / 1000 + ' kbps')
}
if (fileSummary.audioInfo.format.sampleRate) {
format.push(fileSummary.audioInfo.format.sampleRate / 1000 + ' kHz')
}
if (fileSummary.audioInfo.format.bitsPerSample) {
format.push(fileSummary.audioInfo.format.bitsPerSample + ' bit')
}
if (format.length > 0) {
elems.push((
<div key='format' className='audio-format'>
<label>Format</label>{ format.join(', ') }
</div>
))
}
// Audio metadata: comments
if (common.comment) {
elems.push((
<div key='comments' className='audio-comments'>
<label>Comments</label>{common.comment.join(' / ')}
</div>
))
}
// Align the title with the other info, if available. Otherwise, center title
const emptyLabel = (<label />)
elems.unshift((
@@ -359,7 +303,7 @@ function renderCastScreen (state) {
isCast = false
} else if (state.playing.location === 'error') {
castIcon = 'error_outline'
castType = 'Unable to Play'
castType = 'Error'
isCast = false
}
@@ -553,9 +497,9 @@ function renderPlayerControls (state) {
const volume = state.playing.volume
const volumeIcon = 'volume_' + (
volume === 0 ? 'off'
: volume < 0.3 ? 'mute'
: volume < 0.6 ? 'down'
: 'up')
: volume < 0.3 ? 'mute'
: volume < 0.6 ? 'down'
: 'up')
const volumeStyle = {
background: '-webkit-gradient(linear, left top, right top, ' +
'color-stop(' + (volume * 100) + '%, #eee), ' +
@@ -579,8 +523,8 @@ function renderPlayerControls (state) {
))
// Show video playback progress
const currentTimeStr = formatTime(state.playing.currentTime, state.playing.duration)
const durationStr = formatTime(state.playing.duration, state.playing.duration)
const currentTimeStr = formatTime(state.playing.currentTime)
const durationStr = formatTime(state.playing.duration)
elements.push((
<span key='time' className='time float-left'>
{currentTimeStr} / {durationStr}
@@ -702,19 +646,17 @@ function cssBackgroundImageDarkGradient () {
'rgba(0,0,0,0.4) 0%, rgba(0,0,0,1) 100%)'
}
function formatTime (time, total) {
function formatTime (time) {
if (typeof time !== 'number' || Number.isNaN(time)) {
return '0:00'
}
let totalHours = Math.floor(total / 3600)
let totalMinutes = Math.floor(total % 3600 / 60)
let hours = Math.floor(time / 3600)
let minutes = Math.floor(time % 3600 / 60)
if (totalMinutes > 9) {
if (hours > 0) {
minutes = zeroFill(2, minutes)
}
let seconds = zeroFill(2, Math.floor(time % 60))
return (totalHours > 0 ? hours + ':' : '') + minutes + ':' + seconds
return (hours > 0 ? hours + ':' : '') + minutes + ':' + seconds
}

View File

@@ -62,24 +62,6 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'openExternalPlayer', !isChecked)
}
highestPlaybackPriorityCheckbox () {
return (
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.highestPlaybackPriority}
label={'Highest Playback Priority'}
onCheck={this.handleHighestPlaybackPriorityChange}
/>
<p>Pauses all active torrents to allow playback to use all of the available bandwidth.</p>
</Preference>
)
}
handleHighestPlaybackPriorityChange (e, isChecked) {
dispatch('updatePreferences', 'highestPlaybackPriority', isChecked)
}
externalPlayerPathSelector () {
const playerPath = this.props.state.unsaved.prefs.externalPlayerPath
const playerName = this.props.state.getExternalPlayerName()
@@ -108,59 +90,6 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'externalPlayerPath', filePath)
}
autoAddTorrentsCheckbox () {
return (
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.autoAddTorrents}
label={'Watch for new .torrent files and add them immediately'}
onCheck={(e, value) => { this.handleAutoAddTorrentsChange(e, value) }}
/>
</Preference>
)
}
handleAutoAddTorrentsChange (e, isChecked) {
const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath
if (isChecked && !torrentsFolderPath) {
alert('Select a torrents folder first.') // eslint-disable-line
e.preventDefault()
return
}
dispatch('updatePreferences', 'autoAddTorrents', isChecked)
if (isChecked) {
dispatch('startFolderWatcher', null)
return
}
dispatch('stopFolderWatcher', null)
}
torrentsFolderPathSelector () {
const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath
return (
<Preference>
<PathSelector
dialog={{
title: 'Select folder to watch for new torrents',
properties: [ 'openDirectory' ]
}}
displayValue={torrentsFolderPath || ''}
onChange={this.handletorrentsFolderPathChange}
title='Folder to watch'
value={torrentsFolderPath ? path.dirname(torrentsFolderPath) : null} />
</Preference>
)
}
handletorrentsFolderPathChange (filePath) {
dispatch('updatePreferences', 'torrentsFolderPath', filePath)
}
setDefaultAppButton () {
const isFileHandler = this.props.state.unsaved.prefs.isFileHandler
if (isFileHandler) {
@@ -216,15 +145,12 @@ class PreferencesPage extends React.Component {
}
return (
<div style={style}>
<PreferencesSection title='Folders'>
<PreferencesSection title='Downloads'>
{this.downloadPathSelector()}
{this.autoAddTorrentsCheckbox()}
{this.torrentsFolderPathSelector()}
</PreferencesSection>
<PreferencesSection title='Playback'>
{this.openExternalPlayerCheckbox()}
{this.externalPlayerPathSelector()}
{this.highestPlaybackPriorityCheckbox()}
</PreferencesSection>
<PreferencesSection title='Default torrent app'>
{this.setDefaultAppButton()}

View File

@@ -216,7 +216,7 @@ module.exports = class TorrentList extends React.Component {
} else { // torrentSummary.status is 'new' or something unexpected
status = ''
}
return (<span key='torrent-status'>{status}</span>)
return (<span>{status}</span>)
}
}

View File

@@ -8,7 +8,7 @@ const defaultAnnounceList = require('create-torrent').announceList
const electron = require('electron')
const fs = require('fs')
const mkdirp = require('mkdirp')
const mm = require('music-metadata')
const musicmetadata = require('musicmetadata')
const networkAddress = require('network-address')
const path = require('path')
const WebTorrent = require('webtorrent')
@@ -98,7 +98,7 @@ function init () {
window.addEventListener('error', (e) =>
ipc.send('wt-uncaught-error', {message: e.error.message, stack: e.error.stack}),
true)
true)
setInterval(updateTorrentProgress, 1000)
console.timeEnd('init')
@@ -128,7 +128,6 @@ function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections)
}
function stopTorrenting (infoHash) {
console.log('--- STOP TORRENTING: ', infoHash)
const torrent = client.get(infoHash)
if (torrent) torrent.destroy()
}
@@ -334,30 +333,16 @@ function stopServer () {
server = null
}
console.log('Initializing...')
function getAudioMetadata (infoHash, index) {
const torrent = client.get(infoHash)
const file = torrent.files[index]
// Set initial matadata to display the filename first.
const metadata = { title: file.name }
ipc.send('wt-audio-metadata', infoHash, index, metadata)
const options = {native: false, skipCovers: true, fileSize: file.length}
const onMetaData = file.done
// If completed; use direct file access
? mm.parseFile(path.join(torrent.path, file.path), options)
// otherwise stream
: mm.parseStream(file.createReadStream(), file.name, options)
onMetaData
.then(function (metadata) {
console.log('got audio metadata for %s (length=%s): %o', file.name, file.length, metadata)
ipc.send('wt-audio-metadata', infoHash, index, metadata)
}).catch(function (err) {
return console.log('error getting audio metadata for ' + infoHash + ':' + index, err)
})
musicmetadata(file.createReadStream(), function (err, info) {
if (err) return console.log('error getting audio metadata for ' + infoHash + ':' + index, err)
const { artist, album, albumartist, title, year, track, disk, genre } = info
const importantInfo = { artist, album, albumartist, title, year, track, disk, genre }
console.log('got audio metadata for %s: %o', file.name, importantInfo)
ipc.send('wt-audio-metadata', infoHash, index, importantInfo)
})
}
function selectFiles (torrentOrInfoHash, selections) {

View File

@@ -615,7 +615,6 @@ body.drag .app::after {
font-size: 13px;
margin: 9px 8px 8px 8px;
opacity: 0.8;
font-variant-numeric: tabular-nums;
}
.player .controls .icon.closed-caption {
@@ -820,17 +819,12 @@ video::-webkit-media-text-track-container {
.audio-metadata label {
display:inline-block;
width: 120px;
width: 100px;
text-align: right;
font-weight: normal;
margin-right: 25px;
}
.audio-metadata .audio-format,
.audio-metadata .audio-comments {
font-weight: normal;
}
/*
* ERRORS
*/

View File

@@ -10,7 +10,7 @@ test('app runs', function (t) {
setup.waitForLoad(app, t)
.then(() => setup.screenshotCreateOrCompare(app, t, 'app-basic'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})
require('./test-torrent-list')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 KiB

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 158 KiB

View File

@@ -122,7 +122,7 @@ function compareIgnoringTransparency (bufActual, bufExpected) {
const de = pngE.data
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const i = ((y * w) + x) * 4
const i = (y * w + x) * 4
if (de[i + 3] === 0) continue // Skip transparent pixels
const ca = (da[i] << 16) | (da[i + 1] << 8) | da[i + 2]
const ce = (de[i] << 16) | (de[i + 1] << 8) | de[i + 2]

View File

@@ -30,7 +30,7 @@ test('add-torrent', function (t) {
.then(() => app.client.moveToObject('.torrent'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-100-percent'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})
test('create-torrent', function (t) {
@@ -84,5 +84,5 @@ test('create-torrent', function (t) {
config.SAVED_TORRENT_FILE,
expectedTorrent))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})

View File

@@ -53,5 +53,5 @@ test('audio-streaming', function (t) {
.then(() => app.webContents.executeJavaScript('dispatch("skipTo", 2)'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-wired-5'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})

View File

@@ -1,25 +0,0 @@
const test = require('tape')
const fs = require('fs')
const path = require('path')
const WebTorrent = require('webtorrent')
const torrentPoster = require('../build/renderer/lib/torrent-poster')
const client = new WebTorrent()
test("get cover from: 'wiredCd.torrent'", (t) => {
const torrentPath = path.join(__dirname, '..', 'static', 'wiredCd.torrent')
const torrentData = fs.readFileSync(torrentPath)
client.add(torrentData, (torrent) => {
torrentPoster(torrent, (err, buf, extension) => {
if (err) {
t.fail(err)
} else {
t.equals(extension, '.jpg')
t.end()
}
})
})
})

View File

@@ -22,7 +22,7 @@ test('torrent-list: show download path missing', function (t) {
.then((windowTitle) => t.equal(windowTitle, 'Preferences', 'window title'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'prefs-basic'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})
test('torrent-list: start, stop, and delete torrents', function (t) {
@@ -54,7 +54,7 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
.then(() => app.client.click('.control.ok'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-deleted'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})
test('torrent-list: expand torrent, unselect file', function (t) {
@@ -79,7 +79,7 @@ test('torrent-list: expand torrent, unselect file', function (t) {
// Make sure that it creates all files EXCEPT the deslected one
.then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', [
// TODO: the .gif should NOT be here, since we just deselected it.
// This is a bug. See https://github.com/webtorrent/webtorrent-desktop/issues/719
// This is a bug. See https://github.com/feross/webtorrent-desktop/issues/719
'Cosmos Laundromat - First Cycle (1080p).gif',
'Cosmos Laundromat - First Cycle (1080p).mp4',
'Cosmos Laundromat - First Cycle (1080p).ogv',
@@ -101,5 +101,5 @@ test('torrent-list: expand torrent, unselect file', function (t) {
// Make sure that all the files are gone
.then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', null))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})

View File

@@ -31,5 +31,5 @@ test('video-streaming', function (t) {
// Take another screenshot to verify that the window resized correctly
.then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-return'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
(err) => setup.endTest(app, t, err || 'error'))
})