Compare commits

...

119 Commits

Author SHA1 Message Date
Mathias Rasmussen
fb33d569ca add button to re-download torrent data if missing 2018-10-11 01:10:42 +02:00
Borewit
fa7d917d0d Merge pull request #1494 from clujin/fix_empty_array_reduce
#1436: Fix bug empty array reduce in audio poster selection
2018-10-07 20:27:33 +02:00
Borewit
ab20dc42f1 #1426 Consistent error message if no suitable poster image could be found. 2018-10-07 20:15:12 +02:00
clujin
02fefd0a81 remove redundant error check 2018-10-07 10:42:42 -07:00
clujin
788e907502 remove semicolon 2018-10-06 19:14:03 -07:00
clujin
bb4eb523fe initial commit 2018-10-06 18:59:03 -07:00
Borewit
11b42e58c1 Merge pull request #1484 from sibiraj-s/patch-1
fix: replace dependency `open` with `opn`
2018-10-06 00:06:17 +02:00
Sibiraj
8dba9b93b6 chore: regenerate package-lock.json 2018-10-06 02:45:56 +05:30
Sibiraj
d4a564efb5 fix: replace dependency open with opn 2018-10-06 02:45:09 +05:30
Borewit
98ce4bb1fb Merge pull request #1042 from webtorrent/save-prefs
Save preferences immediately when changed
2018-10-05 21:03:49 +02:00
Borewit
0ec8809de1 Merge branch 'save-prefs' of https://github.com/webtorrent/webtorrent-desktop into save-prefs 2018-10-05 21:00:24 +02:00
Mathias Rasmussen
981ac464b5 update: remove unsaved 2018-10-05 20:58:54 +02:00
Dan Flettre
f04017a068 save preferences immediately when changed 2018-10-05 20:58:54 +02:00
Mathias Rasmussen
1a2b18fcbb update: remove unsaved 2018-09-28 17:45:03 +02:00
Dan Flettre
12d05b96a0 save preferences immediately when changed 2018-09-28 17:42:22 +02:00
Mathias Rasmussen
4ed4d8287f fix ci problems 2018-09-28 00:49:37 +02:00
Mathias Rasmussen
1239f7b871 lockfile 2018-09-27 21:47:32 +02:00
Mathias Rasmussen
8c2e29eaa2 update plist dependency 2018-09-27 21:41:17 +02:00
Borewit
9d060a66ec Merge pull request #1311 from bnjmnt4n/loading-spinner
Add back loading spinner for player page.
2018-09-27 19:55:17 +02:00
Borewit
06566d37c2 Asynchronous music-metadata updates while streaming (#1449)
* Ensure that default file/protocol handlers are re-installed after updating.

Previously, they were only installed when the preference was changed.
This caused the handlers to point to non-existing files after updates
occurred and older versions were removed by Squirrel.

Closes #791, #911.

* webtorrent/webtorrent-desktop#1340: Switch to async metadata updates.

* Use fat arrow

* Fix issue track number not displayed if total number of tracks is not defined (common.track.of === null).

* Add disk number in addition to track number.

* Update order of audio properties from: album, track, disk, format to track, disk, album, format

* #1340 Update music-metadata to 2.5.0, enabling async 'per' tag updates
#1452 Fix for playing some '.m4b' files

* #1340 Commented out the metadata event debug output.

* #1340 Remove line comment to get rid of max line length lint error

* Update music-metadata 2.6.0 to fix some async events are getting triggered.

* Return JSX block.

* Get rid of third parameter which is replaced by CSS capitalize

* Fixed error when value is undefined.

* Update music-metadata dependency to 2.6.1.

* fix(package): update music-metadata to version 3.1.0

Closes #1478

* Revert "Ensure that default file/protocol handlers are re-installed after updating."

This reverts commit 39145b2366.
2018-09-26 22:25:32 +02:00
greenkeeper[bot]
6a50fe7ced fix(package): update chokidar to version 2.0.4 (#1428) 2018-09-26 00:19:47 +02:00
Borewit
d40449f2ed Fix audio track number not displayed and adds audio disk (media) number (#1454)
* Fix issue track number not displayed if total number of tracks is not defined (common.track.of === null).

* Add disk number in addition to track number.

* Update order of audio properties from: album, track, disk, format to track, disk, album, format

* Return JSX block.

* Get rid of third parameter which is replaced by CSS capitalize

* Fixed error when value is undefined.
2018-09-25 23:51:45 +02:00
Feross Aboukhadijeh
23e1ab6f21 Merge pull request #1461 from webtorrent/greenkeeper/babel-eslint-9.0.0
Update babel-eslint to the latest version 🚀
2018-08-30 13:48:17 -07:00
Feross Aboukhadijeh
f23e52c699 Merge pull request #1462 from webtorrent/standard-12
standard
2018-08-28 19:41:59 -07:00
Feross Aboukhadijeh
10b4450214 standard 2018-08-28 19:38:25 -07:00
greenkeeper[bot]
4b639ae476 chore(package): update babel-eslint to version 9.0.0 2018-08-28 02:34:25 +00:00
Diego Rodríguez
77c497e984 Merge branch 'master' of github.com:webtorrent/webtorrent-desktop 2018-08-15 16:08:50 -05:00
Diego Rodríguez
a4ee465439 Update electron-winstaller and electron-osx-sign 2018-08-15 16:08:43 -05:00
Diego Rodríguez Baquero
b6890bc780 Merge pull request #1447 from webtorrent/greenkeeper/simple-get-3.0.3
Update simple-get to the latest version 🚀
2018-08-15 16:07:16 -05:00
Diego Rodríguez Baquero
109accdb54 Merge pull request #1450 from webtorrent/reuse-media-extensions
Reuse media extensions
2018-08-15 13:13:45 -05:00
Borewit
87491351cc Merge branch 'master' into reuse-media-extensions
# Conflicts:
#	src/renderer/lib/torrent-poster.js
2018-08-13 21:47:13 +02:00
Borewit
c2a38bd4e0 Ensure consistency between extension-categories, between torrent-player and torrent-poster.
Add additional audio-file-extensions.
2018-08-13 21:35:37 +02:00
greenkeeper[bot]
25282fc44a fix(package): update simple-get to version 3.0.3 2018-08-09 06:44:28 +00:00
Alberto Miranda
cbbc61d1e1 Merge pull request #1440 from webtorrent/Borewit-dependency/update-music-metadata
Small fixes added on top of #1435 by @Borewit.
Merging this one which is already approved and has those fixes.
We can continue making enhancements on top of this :)

Thanks guys!
2018-07-25 10:55:52 -03:00
Alberto Miranda
6ed7ec35a4 Fixed styling. 2018-07-22 16:58:28 -03:00
Alberto Miranda
a5d946fe83 Ensure we get proper audio metadata. 2018-07-22 16:17:34 -03:00
Borewit
cf8c1654e8 Add extension for MP4 audio book. 2018-07-21 14:10:23 +02:00
Borewit
715a7846f7 Bump version to 2.4.2: improve m4a handling. 2018-07-21 14:09:14 +02:00
Borewit
82ea41c1a1 Round bit-rate to kbps & sample rate to 1/10 of kHz.
Update music-metadata to 2.4.0.
2018-07-17 21:02:06 +02:00
Borewit
934f497118 Update music-metadata dependency: v2.0.1 => v2.3.2 2018-07-14 11:15:23 +02:00
Borewit
e0de30baff Merge pull request #1165 from janza/chromecast-subtitles
Add support for subtitles on Chromecast
2018-06-26 21:15:10 +02:00
Alberto Miranda
a1cfe8730b Merge pull request #1419 from Borewit/fix/issue-1417
Fix startup problems (fix for #1417)
2018-06-10 18:47:33 -03:00
Borewit
91fc7cab7c Merge pull request #1418 from webtorrent/greenkeeper/music-metadata-2.0.0
Update music-metadata dependency to version 2.0.1 🚀
2018-06-10 08:38:09 +02:00
Borewit
5cdb3d059c #1417: Simplify code
- use forEach instead of map (because return value was ignored)
- use Promise.all instead of parallel, because tasks are already Promise based. Fewer callbacks.
2018-06-09 14:54:24 +02:00
Borewit
b010dc28d2 Avoid promise resolve results are ending up as error objects in callback handler.
Reference: issue: webtorrent/webtorrent-desktop#1417
2018-06-07 21:53:58 +02:00
Borewit
9b51ba443a Update to version 2.0.1 fixing bug: Borewit/music-metadata#100
pull request: webtorrent/webtorrent-desktop#1418
2018-06-07 20:21:08 +02:00
greenkeeper[bot]
47c08cc8f8 fix(package): update music-metadata to version 2.0.0 2018-06-05 04:53:51 +00:00
Diego Rodríguez Baquero
c1329938f5 Merge pull request #1406 from Borewit/code-style/merge
Code style/merge
2018-06-03 18:46:26 -05:00
Borewit
2c07b1146c Merge remote-tracking branch 'origin/code-style/merge' into code-style/merge 2018-05-28 08:24:50 +02:00
Michael George Attard
fc51b230c4 rename parameter 2018-05-27 22:33:07 +02:00
Michael George Attard
4d5ee7887e Merge branch 'master' into feature/external-player.js-cleanup 2018-05-27 22:32:29 +02:00
Diego Rodríguez Baquero
ad63ec8a7b Merge pull request #1411 from webtorrent/is-private
If torrent is not private, leave private flag unset
2018-05-25 12:15:45 -05:00
Diego Rodríguez Baquero
2b0e2ca0fb Merge pull request #1410 from webtorrent/greenkeeper/parse-torrent-6.0.1
Update parse-torrent to the latest version 🚀
2018-05-25 11:53:02 -05:00
Feross Aboukhadijeh
217ca9a053 If torrent is not private, leave private flag unset
If torrent is not private, leave private flag unset. This ensures that the torrent info hash will match the result generated by other tools, including webtorrent-cli.
2018-05-25 01:10:40 -07:00
Feross Aboukhadijeh
6bf38611e8 fix bug, function needs to be invoked 2018-05-25 00:30:54 -07:00
Feross Aboukhadijeh
7251967ef5 style 2018-05-25 00:28:46 -07:00
Feross Aboukhadijeh
2c9b305721 style 2018-05-25 00:28:14 -07:00
greenkeeper[bot]
6eac7405fd fix(package): update parse-torrent to version 6.0.1 2018-05-25 07:23:55 +00:00
Feross Aboukhadijeh
4b51fb9255 e -> err 2018-05-25 00:20:39 -07:00
Feross Aboukhadijeh
a2e5390d30 ProBot No Response: tweak comment 2018-05-24 23:03:26 -07:00
Feross Aboukhadijeh
03a6ec3466 ProBot Welcome: tweak comments 2018-05-24 23:03:26 -07:00
Feross Aboukhadijeh
228d68b488 ProBot Request Info: tweak comment 2018-05-24 23:03:26 -07:00
Feross Aboukhadijeh
1af4ddb618 ProBot Lock: 90 days 2018-05-24 23:03:26 -07:00
Borewit
a30eedbec9 Update music-metadata to 1.1.0, which I expected to be part of #1396. 2018-05-23 21:00:55 +02:00
Borewit
2bc3ab6971 Merge branch 'code-style/main' into code-style/merge 2018-05-23 20:30:50 +02:00
Borewit
b1f5e7d7ec Merge branch 'code-style/external-player' into code-style/merge 2018-05-23 20:23:33 +02:00
Borewit
84d4e0f013 Issue #92: Merge with master & exclude some of the proposed changes. 2018-05-23 20:19:28 +02:00
Borewit
61648db0fc Merge pull request #1396 from webtorrent/greenkeeper/music-metadata-1.0.0
Update music-metadata to the latest version 🚀
2018-05-23 07:20:44 +02:00
Feross Aboukhadijeh
7b72dfafc2 ProBot Welcome: PR message tweak 2018-05-18 07:38:21 +02:00
Feross Aboukhadijeh
d3da2af8a1 Merge pull request #1325 from webtorrent/download-count-badge
add github download count badge
2018-05-17 11:32:40 +02:00
Borewit
55a60018b5 Merge pull request #1398 from Borewit/update-dependency/music-metadata-1.0.0
Fix code style
2018-05-16 21:41:16 +02:00
Borewit
6df718aa4a Fix code style 2018-05-16 21:31:48 +02:00
Borewit
a3d62de091 Merge pull request #1397 from Borewit/update-dependency/music-metadata-1.0.0
Add required changed to be able to merge music-metadata v1.0.0
2018-05-16 21:28:09 +02:00
Borewit
eb4007aa87 Update dependency music-metadata v1.0.0
Multiplicity of common.label & common.catalog-number number changed.
Added possibility to visualize multiple labels & catalog-number in pairs.
2018-05-16 21:23:34 +02:00
greenkeeper[bot]
4438c9c85f fix(package): update music-metadata to version 1.0.0 2018-05-16 18:25:57 +00:00
Feross Aboukhadijeh
c01380a897 Merge pull request #1394 from webtorrent/greenkeeper/capture-frame-2.0.0
Update capture-frame to the latest version 🚀
2018-05-15 04:28:54 -07:00
greenkeeper[bot]
ff7c053442 fix(package): update capture-frame to version 2.0.0 2018-05-15 11:16:40 +00:00
Feross Aboukhadijeh
4f342bb530 Merge pull request #1390 from webtorrent/greenkeeper/cp-file-6.0.0
Update cp-file to the latest version 🚀
2018-05-15 03:52:15 -07:00
greenkeeper[bot]
99a7c663b3 fix(package): update cp-file to version 6.0.0 2018-05-12 11:06:03 +00:00
Feross Aboukhadijeh
49f2bb2bdf ProBot Stale: never mark 'meta' issues stale 2018-05-10 17:52:28 -07:00
Feross Aboukhadijeh
9cfce5f72b Merge pull request #1386 from webtorrent/greenkeeper/drag-drop-4.1.0
Update drag-drop to the latest version 🚀
2018-05-10 04:01:48 -07:00
Feross Aboukhadijeh
5c8bd3422d travis 2018-05-10 03:18:42 -07:00
greenkeeper[bot]
670d6a0489 fix(package): update drag-drop to version 4.1.0 2018-05-10 10:15:22 +00:00
Feross Aboukhadijeh
45d0d94ab4 ProBot Welcome: tweak message 2018-05-10 00:21:50 -07:00
Feross Aboukhadijeh
4d864465d7 ProBot No Response: tweak message 2018-05-09 20:22:03 -07:00
Feross Aboukhadijeh
56327ca25e Merge pull request #1385 from webtorrent/probot
Add bots!
2018-05-09 20:17:52 -07:00
Feross Aboukhadijeh
0aef294df9 ProBot Stale 2018-05-09 20:15:38 -07:00
Feross Aboukhadijeh
7fb471feb1 ProBot No Response 2018-05-09 20:15:26 -07:00
Feross Aboukhadijeh
e2d2420aa2 ProBot Lock 2018-05-09 20:15:21 -07:00
Feross Aboukhadijeh
bcb3551a0a ProBot WIP 2018-05-09 20:15:15 -07:00
Feross Aboukhadijeh
20faf71338 ProBot Welcome 2018-05-09 20:15:11 -07:00
Feross Aboukhadijeh
63d866a1d2 ProBot Request Info 2018-05-09 20:15:05 -07:00
Feross Aboukhadijeh
6d9874601a travis: test node LTS only 2018-05-08 18:52:58 -07:00
Feross Aboukhadijeh
3cbf33b482 travis: Do full dependency install
I don't care about test time, I'd rather not have to keep this list up-to-date (Easier maintenance, e.g. this caused a failure because I just added babel-eslint and it doesn't get installed on travis and so standard fails)
2018-05-08 18:52:51 -07:00
Feross Aboukhadijeh
4b2a778708 Use babel-eslint with standard 2018-05-08 18:46:26 -07:00
Feross Aboukhadijeh
f5102c3558 Merge pull request #1260 from michaelgeorgeattard/master
Style clean up
2018-05-03 18:23:45 -07:00
Borewit
0fdc6b2d8b Merge pull request #1365 from webtorrent/readme-install
Improve install instructions
2018-05-02 20:28:00 +02:00
Feross Aboukhadijeh
e34c18b751 Merge pull request #1259 from jonkoops/update-chromecasts
Update 'chromecasts' dependency to latest version
2018-04-30 03:16:53 -07:00
Feross Aboukhadijeh
4f41f5968c Merge pull request #1368 from Borewit/feature/improve-audio-torrent-poster-selection
Prevent spectrogram being selected as the poster.
2018-04-29 20:20:11 -07:00
Feross Aboukhadijeh
b48d2b1610 Merge pull request #1371 from webtorrent/greenkeeper/music-metadata-0.11.2
Update music-metadata to the latest version 🚀
2018-04-29 20:18:11 -07:00
Feross Aboukhadijeh
ceb5c3f11c Merge pull request #1372 from webtorrent/greenkeeper/gh-release-3.2.1
Update gh-release to the latest version 🚀
2018-04-28 01:53:59 +02:00
greenkeeper[bot]
0585c515b3 chore(package): update gh-release to version 3.2.1 2018-04-27 17:03:20 +00:00
greenkeeper[bot]
e2074aeb31 fix(package): update music-metadata to version 0.11.2 2018-04-27 11:50:18 +00:00
Borewit
bc3cec1172 webtorrent/webtorrent-desktop#1368: Remove pointless factor. 2018-04-27 09:01:50 +02:00
Feross Aboukhadijeh
83e8bb1bd7 Merge pull request #1369 from webtorrent/new-mac-screenshots
Update mac screenshots for macOS 10.13.4
2018-04-26 19:04:14 -07:00
Feross Aboukhadijeh
88fb6d86bd Fix   showing up in UI 2018-04-26 18:23:12 -07:00
Feross Aboukhadijeh
e8c39f2074 Fix screenshots further 2018-04-26 17:59:32 -07:00
Feross Aboukhadijeh
386b80a66f Update mac screenshots for macOS 10.13.4
It looks like font renderering changed slightly, so screenshots need to be updated
2018-04-26 17:25:32 -07:00
Borewit
8516da8a44 Prevent spectrogram being selected as the poster.
Related issue webtorrent/webtorrent-desktop#1332, as well pull request webtorrent/webtorrent-desktop#1334
2018-04-26 21:25:09 +02:00
Feross Aboukhadijeh
d76cd628cf Improve install instructions
Make the default install method stand out more than other options. Include a large link.
2018-04-25 19:45:22 -07:00
Feross Aboukhadijeh
c0d70e91b0 add github download count badge 2018-02-15 17:47:55 -08:00
Benjamin Tan
386bc950f4 Add back loading spinner for player page.
This appeared to have been removed accidentally when other parts of the CSS was refactored.
2018-01-22 20:05:33 +08:00
Michael George Attard
962c563f2e Clean up handlers.js 2017-10-30 22:49:34 +01:00
Michael George Attard
bd6f2c2faf Clean up of main.js 2017-10-30 22:46:33 +01:00
Michael George Attard
ee8fe1e7ff Cleaned up external-player.js 2017-10-30 22:42:22 +01:00
Michael George Attard
424d171656 Used string template where it made sense. 2017-10-28 00:05:58 +02:00
Michael George Attard
7351133542 Used string template instead of concatenation. 2017-10-26 21:20:30 +02:00
Jon Koops
84deb403eb Update 'chromecasts' dependency to latest version
This should close:
- https://github.com/webtorrent/webtorrent-desktop/issues/1193
- https://github.com/webtorrent/webtorrent-desktop/issues/1202
2017-10-25 23:44:43 +02:00
Josip Janzic
f7ec537cd5 Add support for subtitles on Chromecast 2017-09-17 00:07:57 +02:00
81 changed files with 12527 additions and 308 deletions

19
.github/config.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
# ProBot Request Info (https://probot.github.io/apps/request-info/)
requestInfoReplyComment: >
👋 We would appreciate it if you could provide us with more information about this
issue. <br><br> ![](https://feross.net/s/cats/14.gif)
requestInfoLabelToAdd: 'need more info'
# ProBot Welcome (https://probot.github.io/apps/welcome/)
newPRWelcomeComment: >
🙌 Thanks for opening this pull request! You're awesome.
<br><br> ![](https://feross.net/s/cats/6.gif)
firstPRMergeComment: >
🎉 Congrats on getting your first pull request landed! <br><br>
![](https://feross.net/s/cats/29.gif)
# ProBot WIP (https://probot.github.io/apps/wip/)
# (Requires no configuration)

4
.github/lock.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# ProBot Lock (https://probot.github.io/apps/lock/)
daysUntilLock: 90
lockComment: false

9
.github/no-response.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
# ProBot No Response (https://probot.github.io/apps/no-response/)
daysUntilClose: 7
responseRequiredLabel: 'need more info'
closeComment: >
This issue has been automatically closed because there was no response to a
request for more information from the issue opener. Please leave a comment or
open a new issue if you have additional information related to this issue.
<br><br> ![](https://feross.net/s/cats/7.gif)

14
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# ProBot Stale (https://probot.github.io/apps/stale/)
daysUntilStale: 90
daysUntilClose: 7
exemptLabels:
- accepted
- bug
- enhancement
- meta
staleLabel: stale
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
closeComment: false

View File

@@ -1,4 +1,3 @@
language: node_js
node_js:
- 'node'
install: npm install standard depcheck walk-sync
- lts/*

View File

@@ -13,24 +13,33 @@
<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://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/webtorrent/webtorrent-desktop.svg" alt="github release version"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/downloads/webtorrent/webtorrent-desktop/total.svg" alt="github release downloads"></a>
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Standard - JavaScript Style Guide"></a>
</p>
## Install
### Recommended 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):
[the official website](https://webtorrent.io/desktop/):
```
$ brew cask install webtorrent
```
### [✨ Download WebTorrent Desktop ✨](https://webtorrent.io/desktop/)
**WebTorrent Desktop** is under very active development. You can try out the
current (unstable) development version by cloning the Git repo. See the
instructions below in the ["How to Contribute"](#how-to-contribute) section.
### Advanced Install
- Download specific installer files from the [GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page.
- Use [Homebrew-Cask](https://github.com/caskroom/homebrew-cask) to install from the command line:
```
$ brew cask install webtorrent
```
- Try the (unstable) development version by cloning the Git repository. See the
["How to Contribute"](#how-to-contribute) instructions.
## Screenshots

View File

@@ -7,7 +7,7 @@ const path = require('path')
let hasErrors = false
// Find all Javascript source files
const files = walkSync('src', {globs: ['**/*.js']})
const files = walkSync('src', { globs: ['**/*.js'] })
console.log('Running extra-lint on ' + files.length + ' files...')
// Read each file, line by line

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
const config = require('../src/config')
const open = require('open')
const { CONFIG_PATH } = require('../src/config')
const opn = require('opn')
open(config.CONFIG_PATH)
opn(CONFIG_PATH)

12121
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,15 +16,15 @@
"arch": "^2.0.0",
"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",
"capture-frame": "^2.0.0",
"chokidar": "^2.0.4",
"chromecasts": "^1.9.1",
"cp-file": "^6.0.0",
"create-torrent": "^3.24.5",
"debounce": "^1.0.0",
"deep-equal": "^1.0.1",
"dlnacasts": "^0.1.0",
"drag-drop": "^2.12.1",
"drag-drop": "^4.1.0",
"es6-error": "^4.0.0",
"fn-getter": "^1.0.0",
"iso-639-1": "^1.2.1",
@@ -32,9 +32,9 @@
"location-history": "^1.0.0",
"material-ui": "^0.17.0",
"mkdirp": "^0.5.1",
"music-metadata": "^0.9.15",
"music-metadata": "^3.1.0",
"network-address": "^1.1.0",
"parse-torrent": "^5.7.3",
"parse-torrent": "^6.0.1",
"prettier-bytes": "^1.0.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
@@ -43,7 +43,7 @@
"run-parallel": "^1.1.6",
"semver": "^5.1.0",
"simple-concat": "^1.0.0",
"simple-get": "^2.0.0",
"simple-get": "^3.0.3",
"srt-to-vtt": "^1.1.1",
"vlc-command": "^1.0.1",
"webtorrent": "0.x",
@@ -51,19 +51,20 @@
"zero-fill": "^2.2.3"
},
"devDependencies": {
"babel-eslint": "^9.0.0",
"buble": "^0.19.3",
"cross-zip": "^2.0.1",
"depcheck": "^0.6.4",
"electron": "1.6.16",
"electron-osx-sign": "0.4.3",
"electron-osx-sign": "^0.4.10",
"electron-packager": "~8.5.1",
"electron-winstaller": "~2.5.2",
"gh-release": "^2.0.3",
"electron-winstaller": "^2.6.4",
"gh-release": "^3.2.1",
"minimist": "^1.2.0",
"nobin-debian-installer": "0.0.10",
"nodemon": "^1.10.2",
"open": "0.0.5",
"plist": "^2.0.1",
"opn": "^5.4.0",
"plist": "^3.0.1",
"pngjs": "^3.0.0",
"run-series": "^1.1.4",
"spectron": "^3.3.0",
@@ -104,9 +105,12 @@
"package": "node ./bin/package.js",
"prepublish": "npm run build",
"start": "npm run build && electron .",
"test": "standard && depcheck --ignores=buble,nodemon,gh-release --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"test": "standard && depcheck --ignores=standard,babel-eslint --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"test-integration": "npm run build && node ./test",
"update-authors": "./bin/update-authors.sh",
"watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/"
},
"standard": {
"parser": "babel-eslint"
}
}

View File

@@ -7,9 +7,8 @@ const electron = require('electron')
const config = require('../config')
const log = require('./log')
const ANNOUNCEMENT_URL = config.ANNOUNCEMENT_URL +
'?version=' + config.APP_VERSION +
'&platform=' + process.platform
const ANNOUNCEMENT_URL =
`${config.ANNOUNCEMENT_URL}?version=${config.APP_VERSION}&platform=${process.platform}`
/**
* In certain situations, the WebTorrent team may need to show an announcement to

View File

@@ -4,7 +4,7 @@ module.exports = {
setBadge
}
const {app, Menu} = require('electron')
const { app, Menu } = require('electron')
const dialog = require('./dialog')
const log = require('./log')

View File

@@ -25,7 +25,7 @@ function spawn (playerPath, url, title) {
if (playerPath != null) return spawnExternal(playerPath, [url])
// Try to find and use VLC if external player is not specified
vlcCommand(function (err, vlcPath) {
vlcCommand((err, vlcPath) => {
if (err) return windows.main.dispatch('externalPlayerNotFound')
const args = [
'--play-and-exit',
@@ -39,26 +39,26 @@ function spawn (playerPath, url, title) {
function kill () {
if (!proc) return
log('Killing external player, pid ' + proc.pid)
log(`Killing external player, pid ${proc.pid}`)
proc.kill('SIGKILL') // kill -9
proc = null
}
function spawnExternal (playerPath, args) {
log('Running external media player:', playerPath + ' ' + args.join(' '))
log('Running external media player:', `${playerPath} ${args.join(' ')}`)
if (process.platform === 'darwin' && path.extname(playerPath) === '.app') {
// Mac: Use executable in packaged .app bundle
playerPath += '/Contents/MacOS/' + path.basename(playerPath, '.app')
playerPath += `/Contents/MacOS/${path.basename(playerPath, '.app')}`
}
proc = cp.spawn(playerPath, args, {stdio: 'ignore'})
proc = cp.spawn(playerPath, args, { stdio: 'ignore' })
// If it works, close the modal after a second
const closeModalTimeout = setTimeout(() =>
windows.main.dispatch('exitModal'), 1000)
proc.on('close', function (code) {
proc.on('close', code => {
clearTimeout(closeModalTimeout)
if (!proc) return // Killed
log('External player exited with code ', code)
@@ -70,7 +70,7 @@ function spawnExternal (playerPath, args) {
proc = null
})
proc.on('error', function (e) {
log('External player error', e)
proc.on('error', err => {
log('External player error', err)
})
}

View File

@@ -2,7 +2,7 @@ const chokidar = require('chokidar')
const log = require('./log')
class FolderWatcher {
constructor ({window, state}) {
constructor ({ window, state }) {
this.window = window
this.state = state
this.torrentsFolderPath = null

View File

@@ -7,26 +7,24 @@ const config = require('../config')
const path = require('path')
function install () {
if (process.platform === 'darwin') {
installDarwin()
}
if (process.platform === 'win32') {
installWin32()
}
if (process.platform === 'linux') {
installLinux()
switch (process.platform) {
case 'darwin': installDarwin()
break
case 'win32': installWin32()
break
case 'linux': installLinux()
break
}
}
function uninstall () {
if (process.platform === 'darwin') {
uninstallDarwin()
}
if (process.platform === 'win32') {
uninstallWin32()
}
if (process.platform === 'linux') {
uninstallLinux()
switch (process.platform) {
case 'darwin': uninstallDarwin()
break
case 'win32': uninstallWin32()
break
case 'linux': uninstallLinux()
break
}
}
@@ -108,37 +106,37 @@ function installWin32 () {
setProtocol()
function setProtocol (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
protocolKey.set('', Registry.REG_SZ, name, setURLProtocol)
}
function setURLProtocol (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
protocolKey.set('URL Protocol', Registry.REG_SZ, '', setIcon)
}
function setIcon (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const iconKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol + '\\DefaultIcon'
key: `\\Software\\Classes\\${protocol}\\DefaultIcon`
})
iconKey.set('', Registry.REG_SZ, icon, setCommand)
}
function setCommand (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const commandKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command'
key: `\\Software\\Classes\\${protocol}\\shell\\open\\command`
})
commandKey.set('', Registry.REG_SZ, `${commandToArgs(command)} "%1"`, done)
}
function done (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
}
}
@@ -163,43 +161,43 @@ function installWin32 () {
function setExt () {
const extKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + ext
key: `\\Software\\Classes\\${ext}`
})
extKey.set('', Registry.REG_SZ, id, setId)
}
function setId (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const idKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id
key: `\\Software\\Classes\\${id}`
})
idKey.set('', Registry.REG_SZ, name, setIcon)
}
function setIcon (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const iconKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id + '\\DefaultIcon'
key: `\\Software\\Classes\\${id}\\DefaultIcon`
})
iconKey.set('', Registry.REG_SZ, icon, setCommand)
}
function setCommand (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const commandKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id + '\\shell\\open\\command'
key: `\\Software\\Classes\\${id}\\shell\\open\\command`
})
commandKey.set('', Registry.REG_SZ, `${commandToArgs(command)} "%1"`, done)
}
function done (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
}
}
}
@@ -217,9 +215,9 @@ function uninstallWin32 () {
function getCommand () {
const commandKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command'
key: `\\Software\\Classes\\${protocol}\\shell\\open\\command`
})
commandKey.get('', function (err, item) {
commandKey.get('', (err, item) => {
if (!err && item.value.indexOf(commandToArgs(command)) >= 0) {
destroyProtocol()
}
@@ -229,9 +227,9 @@ function uninstallWin32 () {
function destroyProtocol () {
const protocolKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol
key: `\\Software\\Classes\\${protocol}`
})
protocolKey.destroy(function () {})
protocolKey.destroy(() => {})
}
}
@@ -241,7 +239,7 @@ function uninstallWin32 () {
function eraseId () {
const idKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + id
key: `\\Software\\Classes\\${id}`
})
idKey.destroy(getExt)
}
@@ -249,9 +247,9 @@ function uninstallWin32 () {
function getExt () {
const extKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + ext
key: `\\Software\\Classes\\${ext}`
})
extKey.get('', function (err, item) {
extKey.get('', (err, item) => {
if (!err && item.value === id) {
destroyExt()
}
@@ -261,9 +259,9 @@ function uninstallWin32 () {
function destroyExt () {
const extKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + ext
key: `\\Software\\Classes\\${ext}`
})
extKey.destroy(function () {})
extKey.destroy(() => {})
}
}
}
@@ -300,10 +298,11 @@ function installLinux () {
? path.dirname(process.execPath)
: config.ROOT_PATH
desktopFile = desktopFile.replace(/\$APP_NAME/g, config.APP_NAME)
desktopFile = desktopFile.replace(/\$APP_PATH/g, appPath)
desktopFile = desktopFile.replace(/\$EXEC_PATH/g, EXEC_COMMAND.join(' '))
desktopFile = desktopFile.replace(/\$TRY_EXEC_PATH/g, process.execPath)
desktopFile = desktopFile
.replace(/\$APP_NAME/g, config.APP_NAME)
.replace(/\$APP_PATH/g, appPath)
.replace(/\$EXEC_PATH/g, EXEC_COMMAND.join(' '))
.replace(/\$TRY_EXEC_PATH/g, process.execPath)
const desktopFilePath = path.join(
os.homedir(),
@@ -313,7 +312,7 @@ function installLinux () {
'webtorrent-desktop.desktop'
)
fs.mkdirp(path.dirname(desktopFilePath))
fs.writeFile(desktopFilePath, desktopFile, function (err) {
fs.writeFile(desktopFilePath, desktopFile, err => {
if (err) return log.error(err.message)
})
}
@@ -335,9 +334,9 @@ function installLinux () {
'icons',
'webtorrent-desktop.png'
)
mkdirp(path.dirname(iconFilePath), (err) => {
mkdirp(path.dirname(iconFilePath), err => {
if (err) return log.error(err.message)
fs.writeFile(iconFilePath, iconFile, (err) => {
fs.writeFile(iconFilePath, iconFile, err => {
if (err) log.error(err.message)
})
})

View File

@@ -74,7 +74,7 @@ function init () {
isReady = true
const state = results.state
windows.main.init(state, {hidden: hidden})
windows.main.init(state, { hidden: hidden })
windows.webtorrent.init()
menu.init()
@@ -86,7 +86,7 @@ function init () {
// Report uncaught exceptions
process.on('uncaughtException', (err) => {
console.error(err)
const error = {message: err.message, stack: err.stack}
const error = { message: err.message, stack: err.stack }
windows.main.dispatch('uncaughtError', 'main', error)
})
}
@@ -131,7 +131,7 @@ function delayedInit (state) {
const dock = require('./dock')
const updater = require('./updater')
const FolderWatcher = require('./folder-watcher')
const folderWatcher = new FolderWatcher({window: windows.main, state})
const folderWatcher = new FolderWatcher({ window: windows.main, state })
announcement.init()
dock.init()

View File

@@ -57,7 +57,7 @@ function init (state, options) {
win.setSheetOffset(config.UI_HEADER_HEIGHT)
}
win.webContents.on('dom-ready', function () {
win.webContents.on('dom-ready', () => {
menu.onToggleFullScreen(main.win.isFullScreen())
})
@@ -73,27 +73,27 @@ function init (state, options) {
win.on('hide', onWindowBlur)
win.on('show', onWindowFocus)
win.on('enter-full-screen', function () {
win.on('enter-full-screen', () => {
menu.onToggleFullScreen(true)
send('fullscreenChanged', true)
win.setMenuBarVisibility(false)
})
win.on('leave-full-screen', function () {
win.on('leave-full-screen', () => {
menu.onToggleFullScreen(false)
send('fullscreenChanged', false)
win.setMenuBarVisibility(true)
})
win.on('move', debounce(function (e) {
win.on('move', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds())
}, 1000))
win.on('resize', debounce(function (e) {
win.on('resize', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds())
}, 1000))
win.on('close', function (e) {
win.on('close', e => {
if (process.platform !== 'darwin') {
const tray = require('../tray')
if (!tray.hasTray()) {
@@ -138,37 +138,29 @@ function setAspectRatio (aspectRatio) {
function setBounds (bounds, maximize) {
// Do nothing in fullscreen
if (!main.win || main.win.isFullScreen()) {
log('setBounds: not setting bounds because we\'re in full screen')
log(`setBounds: not setting bounds because we're in full screen`)
return
}
// Maximize or minimize, if the second argument is present
let willBeMaximized
if (maximize === true) {
if (!main.win.isMaximized()) {
log('setBounds: maximizing')
main.win.maximize()
}
willBeMaximized = true
} else if (maximize === false) {
if (main.win.isMaximized()) {
log('setBounds: unmaximizing')
main.win.unmaximize()
}
willBeMaximized = false
} else {
willBeMaximized = main.win.isMaximized()
if (maximize === true && !main.win.isMaximized()) {
log('setBounds: maximizing')
main.win.maximize()
} else if (maximize === false && main.win.isMaximized()) {
log('setBounds: unmaximizing')
main.win.unmaximize()
}
const willBeMaximized = typeof maximize === 'boolean' ? maximize : main.win.isMaximized()
// Assuming we're not maximized or maximizing, set the window size
if (!willBeMaximized) {
log('setBounds: setting bounds to ' + JSON.stringify(bounds))
log(`setBounds: setting bounds to ${JSON.stringify(bounds)}`)
if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen
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))
log('setBounds: centered to ' + JSON.stringify(bounds))
log(`setBounds: centered to ${JSON.stringify(bounds)}`)
}
// Resize the window's content area (so window border doesn't need to be taken
// into account)

View File

@@ -1,6 +1,6 @@
const React = require('react')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher } = require('../lib/dispatcher')
module.exports = class CreateTorrentErrorPage extends React.Component {
render () {

View File

@@ -1,6 +1,6 @@
const React = require('react')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher } = require('../lib/dispatcher')
class Header extends React.Component {
render () {

View File

@@ -5,7 +5,7 @@ const RaisedButton = require('material-ui/RaisedButton').default
module.exports = class ModalOKCancel extends React.Component {
render () {
const cancelStyle = { marginRight: 10, color: 'black' }
const {cancelText, onCancel, okText, onOK} = this.props
const { cancelText, onCancel, okText, onOK } = this.props
return (
<div className='float-right'>
<FlatButton

View File

@@ -2,7 +2,7 @@ const React = require('react')
const TextField = require('material-ui/TextField').default
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
module.exports = class OpenTorrentAddressModal extends React.Component {
render () {

View File

@@ -1,7 +1,7 @@
const React = require('react')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
module.exports = class RemoveTorrentModal extends React.Component {
render () {

View File

@@ -2,7 +2,7 @@ const React = require('react')
const electron = require('electron')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher } = require('../lib/dispatcher')
module.exports = class UnsupportedMediaModal extends React.Component {
render () {

View File

@@ -2,7 +2,7 @@ const React = require('react')
const electron = require('electron')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
module.exports = class UpdateAvailableModal extends React.Component {
render () {

View File

@@ -1,4 +1,4 @@
const {ipcRenderer} = require('electron')
const { ipcRenderer } = require('electron')
module.exports = class FolderWatcherController {
start () {

View File

@@ -1,4 +1,4 @@
const {ipcRenderer} = require('electron')
const { ipcRenderer } = require('electron')
const telemetry = require('../lib/telemetry')
const Playlist = require('../lib/playlist')

View File

@@ -2,9 +2,9 @@ const electron = require('electron')
const path = require('path')
const Cast = require('../lib/cast')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
const telemetry = require('../lib/telemetry')
const {UnplayableFileError, UnplayableTorrentError} = require('../lib/errors')
const { UnplayableFileError, UnplayableTorrentError } = require('../lib/errors')
const sound = require('../lib/sound')
const TorrentPlayer = require('../lib/torrent-player')
const TorrentSummary = require('../lib/torrent-summary')
@@ -292,7 +292,7 @@ module.exports = class PlaybackController {
}
function getAudioMetadata () {
if (state.playing.type === 'audio' && !fileSummary.audioInfo) {
if (state.playing.type === 'audio') {
ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index)
}
}

View File

@@ -1,4 +1,4 @@
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
const ipcRenderer = require('electron').ipcRenderer
// Controls the Preferences screen
@@ -16,45 +16,22 @@ module.exports = class PrefsController {
setup: function (cb) {
// initialize preferences
state.window.title = 'Preferences'
state.unsaved = Object.assign(state.unsaved || {}, {
prefs: Object.assign({}, state.saved.prefs)
})
ipcRenderer.send('setAllowNav', false)
cb()
},
destroy: () => {
ipcRenderer.send('setAllowNav', true)
this.save()
}
})
}
// Updates a single property in the UNSAVED prefs
// For example: updatePreferences('foo.bar', 'baz')
// Call save() to save to config.json
// Updates a single property in the saved prefs
// For example: updatePreferences('isFileHandler', true)
update (property, value) {
const path = property.split('.')
let obj = this.state.unsaved.prefs
let i
for (i = 0; i < path.length - 1; i++) {
if (typeof obj[path[i]] === 'undefined') {
obj[path[i]] = {}
}
obj = obj[path[i]]
}
obj[path[i]] = value
}
if (property === 'isFileHandler') ipcRenderer.send('setDefaultFileHandler', value)
else if (property === 'startup') ipcRenderer.send('setStartup', value)
// All unsaved prefs take effect atomically, and are saved to config.json
save () {
const state = this.state
if (state.unsaved.prefs.isFileHandler !== state.saved.prefs.isFileHandler) {
ipcRenderer.send('setDefaultFileHandler', state.unsaved.prefs.isFileHandler)
}
if (state.unsaved.prefs.startup !== state.saved.prefs.startup) {
ipcRenderer.send('setStartup', state.unsaved.prefs.startup)
}
state.saved.prefs = Object.assign(state.saved.prefs || {}, state.unsaved.prefs)
this.state.saved.prefs[property] = value
dispatch('stateSaveImmediate')
dispatch('checkDownloadPath')
}

View File

@@ -5,7 +5,7 @@ const parallel = require('run-parallel')
const remote = electron.remote
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
module.exports = class SubtitlesController {
constructor (state) {

View File

@@ -3,7 +3,7 @@ const ipcRenderer = require('electron').ipcRenderer
const TorrentSummary = require('../lib/torrent-summary')
const sound = require('../lib/sound')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
module.exports = class TorrentController {
constructor (state) {

View File

@@ -2,8 +2,8 @@ const fs = require('fs')
const path = require('path')
const electron = require('electron')
const {dispatch} = require('../lib/dispatcher')
const {TorrentKeyNotFoundError} = require('../lib/errors')
const { dispatch } = require('../lib/dispatcher')
const { TorrentKeyNotFoundError } = require('../lib/errors')
const sound = require('../lib/sound')
const TorrentSummary = require('../lib/torrent-summary')

View File

@@ -1,4 +1,4 @@
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
// Controls the UI checking for new versions of the app, prompting install
module.exports = class UpdateController {

View File

@@ -13,8 +13,10 @@ module.exports = {
setRate
}
const http = require('http')
const config = require('../../config')
const {CastingError} = require('./errors')
const { CastingError } = require('./errors')
// Lazy load these for a ~300ms improvement in startup time
let airplayer, chromecasts, dlnacasts
@@ -81,7 +83,7 @@ function testPlayer (type) {
}
function getDevices () {
return [{name: type + '-1'}, {name: type + '-2'}]
return [{ name: type + '-1' }, { name: type + '-2' }]
}
function open () {}
@@ -130,22 +132,47 @@ function chromecastPlayer () {
})
}
function serveSubtitles (callback) {
const subtitles = state.playing.subtitles
const selectedSubtitle = subtitles.tracks[subtitles.selectedIndex]
if (!selectedSubtitle) {
callback()
} else {
ret.subServer = http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/vtt;charset=utf-8',
'Access-Control-Allow-Origin': '*',
'Transfer-Encoding': 'chunked'
})
res.end(Buffer.from(selectedSubtitle.buffer.substr(21), 'base64'))
}).listen(0, function () {
const port = ret.subServer.address().port
const subtitlesUrl = 'http://' + state.server.networkAddress + ':' + port + '/'
callback(subtitlesUrl)
})
}
}
function open () {
const torrentSummary = state.saved.torrents.find((x) => x.infoHash === state.playing.infoHash)
ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, {
type: 'video/mp4',
title: config.APP_NAME + ' - ' + torrentSummary.name
}, function (err) {
if (err) {
state.playing.location = 'local'
state.errors.push({
time: new Date().getTime(),
message: 'Could not connect to Chromecast. ' + err.message
})
} else {
state.playing.location = 'chromecast'
}
update()
serveSubtitles(function (subtitlesUrl) {
ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, {
type: 'video/mp4',
title: config.APP_NAME + ' - ' + torrentSummary.name,
subtitles: subtitlesUrl ? [subtitlesUrl] : [],
autoSubtitles: !!subtitlesUrl
}, function (err) {
if (err) {
state.playing.location = 'local'
state.errors.push({
time: new Date().getTime(),
message: 'Could not connect to Chromecast. ' + err.message
})
} else {
state.playing.location = 'chromecast'
}
update()
})
})
}
@@ -159,6 +186,9 @@ function chromecastPlayer () {
function stop (callback) {
ret.device.stop(callback)
if (ret.subServer) {
ret.subServer.close()
}
}
function status () {
@@ -399,11 +429,11 @@ function toggleMenu (location) {
}
// Show a menu
state.devices.castMenu = {location, devices}
state.devices.castMenu = { location, devices }
}
function selectDevice (index) {
const {location, devices} = state.devices.castMenu
const { location, devices } = state.devices.castMenu
// Start casting
const player = getPlayer(location)

View File

@@ -0,0 +1,11 @@
const mediaExtensions = {
audio: [
'.aac', '.asf', '.flac', '.m2a', '.m4a', '.m4b', '.mp2', '.mp4',
'.mp3', '.oga', '.ogg', '.opus', '.wma', '.wav', '.wv', '.wvp'],
video: [
'.avi', '.mp4', '.m4v', '.webm', '.mov', '.mkv', 'mpg', 'mpeg',
'.ogv', '.webm', '.wmv'],
image: ['.gif', '.jpg', '.jpeg', '.png']
}
module.exports = mediaExtensions

View File

@@ -3,7 +3,7 @@ module.exports = {
}
const config = require('../../config')
const {InvalidSoundNameError} = require('./errors')
const { InvalidSoundNameError } = require('./errors')
const path = require('path')
const VOLUME = 0.25

View File

@@ -1,6 +1,6 @@
const appConfig = require('application-config')('WebTorrent')
const path = require('path')
const {EventEmitter} = require('events')
const { EventEmitter } = require('events')
const config = require('../../config')
@@ -113,7 +113,6 @@ function setupStateSaved (cb) {
const cpFile = require('cp-file')
const fs = require('fs')
const parseTorrent = require('parse-torrent')
const parallel = require('run-parallel')
const saved = {
prefs: {
@@ -133,26 +132,25 @@ function setupStateSaved (cb) {
const tasks = []
config.DEFAULT_TORRENTS.map(function (t, i) {
config.DEFAULT_TORRENTS.forEach((t, i) => {
const infoHash = saved.torrents[i].infoHash
tasks.push(function (cb) {
tasks.push(
cpFile(
path.join(config.STATIC_PATH, t.posterFileName),
path.join(config.POSTER_PATH, infoHash + path.extname(t.posterFileName))
).then(cb).catch(cb)
})
tasks.push(function (cb) {
)
)
tasks.push(
cpFile(
path.join(config.STATIC_PATH, t.torrentFileName),
path.join(config.TORRENT_PATH, infoHash + '.torrent')
).then(cb).catch(cb)
})
)
)
})
parallel(tasks, function (err) {
if (err) return cb(err)
cb(null, saved)
})
Promise.all(tasks)
.then(() => cb(null, saved))
.catch(err => cb(err))
function createTorrentObject (t) {
// TODO: Doing several fs.readFileSync calls during first startup is not ideal

View File

@@ -121,7 +121,7 @@ function getTorrentStats (state) {
}
// Then, round all the counts and sums to the nearest power of 2
const ret = roundTorrentStats({count, sizeMB})
const ret = roundTorrentStats({ count, sizeMB })
ret.byStatus = {
new: roundTorrentStats(byStatus.new),
downloading: roundTorrentStats(byStatus.downloading),
@@ -198,7 +198,7 @@ function logUncaughtError (procName, e) {
// Log the app version *at the time of the error*
const version = config.APP_VERSION
telemetry.uncaughtErrors.push({process: procName, message, stack, version})
telemetry.uncaughtErrors.push({ process: procName, message, stack, version })
}
// Turns a DOM element into a string, eg "DIV.my-class.visible"

View File

@@ -8,6 +8,8 @@ module.exports = {
const path = require('path')
const mediaExtensions = require('./media-extensions')
// Checks whether a fileSummary or file path is audio/video that we can play,
// based on the file extension
function isPlayable (file) {
@@ -16,36 +18,12 @@ function isPlayable (file) {
// Checks whether a fileSummary or file path is playable video
function isVideo (file) {
return [
'.avi',
'.m4v',
'.mkv',
'.mov',
'.mp4',
'.mpg',
'.ogv',
'.webm',
'.wmv'
].includes(getFileExtension(file))
return mediaExtensions.video.includes(getFileExtension(file))
}
// Checks whether a fileSummary or file path is playable audio
function isAudio (file) {
return [
'.aac',
'.aiff',
'.ape',
'.ac3',
'.flac',
'.m4a',
'.mp2',
'.mp3',
'.oga',
'.ogg',
'.opus',
'.wav',
'.wma'
].includes(getFileExtension(file))
return mediaExtensions.audio.includes(getFileExtension(file))
}
// Checks if the argument is either:

View File

@@ -3,12 +3,9 @@ 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']
}
const mediaExtensions = require('./media-extensions')
const msgNoSuitablePoster = 'Cannot generate a poster from any files in the torrent'
function torrentPoster (torrent, cb) {
// First, try to use a poster image if available
@@ -21,14 +18,14 @@ function torrentPoster (torrent, cb) {
const bestScore = ['audio', 'video', 'image'].map(mediaType => {
return {
type: mediaType,
size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType])}
size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType]) }
}).sort((a, b) => { // sort descending on size
return b.size - a.size
})[0]
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'))
return cb(new Error(msgNoSuitablePoster))
}
// Based on which media type is dominant we select the corresponding poster function
@@ -97,7 +94,8 @@ function scoreAudioCoverFile (imgFile) {
folder: 80,
album: 80,
front: 80,
back: 20
back: 20,
spectrogram: -80
}
for (let keyword in relevanceScore) {
@@ -105,7 +103,7 @@ function scoreAudioCoverFile (imgFile) {
return relevanceScore[keyword]
}
if (fileName.indexOf(keyword) !== -1) {
return 0.8 * relevanceScore[keyword]
return relevanceScore[keyword]
}
}
return 0
@@ -114,6 +112,8 @@ function scoreAudioCoverFile (imgFile) {
function torrentPosterFromAudio (torrent, cb) {
const imageFiles = filterOnExtension(torrent, mediaExtensions.image)
if (imageFiles.length === 0) return cb(new Error(msgNoSuitablePoster))
const bestCover = imageFiles.map(file => {
return {
file: file,
@@ -133,8 +133,6 @@ function torrentPosterFromAudio (torrent, cb) {
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))
}
@@ -176,7 +174,7 @@ function torrentPosterFromVideo (torrent, cb) {
server.destroy()
if (buf.length === 0) return cb(new Error('Generated poster contains no data'))
if (buf.length === 0) return cb(new Error(msgNoSuitablePoster))
cb(null, buf, '.jpg')
}

View File

@@ -450,7 +450,7 @@ function setDimensions (dimensions) {
)
ipcRenderer.send('setAspectRatio', aspectRatio)
ipcRenderer.send('setBounds', {contentBounds: true, x: null, y: null, width, height})
ipcRenderer.send('setBounds', { contentBounds: true, x: null, y: null, width, height })
state.playing.aspectRatio = aspectRatio
}

View File

@@ -3,7 +3,7 @@ const path = require('path')
const prettyBytes = require('prettier-bytes')
const React = require('react')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const FlatButton = require('material-ui/FlatButton').default
const RaisedButton = require('material-ui/RaisedButton').default
@@ -35,7 +35,7 @@ class CreateTorrentPage extends React.Component {
// Then, exclude .DS_Store and other dotfiles
const files = info.files
.filter((f) => !containsDots(f.path, pathPrefix))
.map((f) => ({name: f.name, path: f.path, size: f.size}))
.map((f) => ({ name: f.name, path: f.path, size: f.size }))
if (files.length === 0) return (<CreateTorrentErrorPage state={state} />)
// Then, use the name of the base folder (or sole file, for a single file torrent)
@@ -65,9 +65,9 @@ class CreateTorrentPage extends React.Component {
}
// Create React event handlers only once
this.setIsPrivate = (_, isPrivate) => this.setState({isPrivate})
this.setComment = (_, comment) => this.setState({comment})
this.setTrackers = (_, trackers) => this.setState({trackers})
this.setIsPrivate = (_, isPrivate) => this.setState({ isPrivate })
this.setComment = (_, comment) => this.setState({ comment })
this.setTrackers = (_, trackers) => this.setState({ trackers })
this.handleSubmit = handleSubmit.bind(this)
}
@@ -141,7 +141,7 @@ class CreateTorrentPage extends React.Component {
<label>Private:</label>
<Checkbox
className='torrent-is-private control'
style={{display: ''}}
style={{ display: '' }}
checked={this.state.isPrivate}
onCheck={this.setIsPrivate} />
</div>
@@ -191,9 +191,14 @@ function handleSubmit () {
path: this.state.basePath,
files: this.state.files,
announce: announceList,
private: this.state.isPrivate,
comment: this.state.comment.trim()
}
// If torrent is not private, leave private flag unset. This ensures that
// the torrent info hash will match the result generated by other tools,
// including webtorrent-cli.
if (this.state.isPrivate) options.private = true
dispatch('createTorrent', options)
}

View File

@@ -5,7 +5,7 @@ const zeroFill = require('zero-fill')
const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const config = require('../../config')
// Shows a streaming video player. Standard features + Chromecast + Airplay
@@ -204,6 +204,28 @@ function renderOverlay (state) {
)
}
/**
* Render track or disk number string
* @param common metadata.common part
* @param key should be either 'track' or 'disk'
* @return track or disk number metadata as JSX block
*/
function renderTrack (common, key) {
// Audio metadata: track-number
if (common[key] && common[key].no) {
let str = `${common[key].no}`
if (common[key].of) {
str += ` of ${common[key].of}`
}
const style = { textTransform: 'capitalize' }
return (
<div className={`audio-${key}`}>
<label style={style}>{key}</label> {str}
</div>
)
}
}
function renderAudioMetadata (state) {
const fileSummary = state.getPlayingFileSummary()
if (!fileSummary.audioInfo) return
@@ -216,9 +238,7 @@ function renderAudioMetadata (state) {
const elems = []
// Audio metadata: artist(s)
const artist = common.albumartist || common.artist ||
(common.artists && common.artists.filter(function (a) { return a }).join(', ')) ||
'(Unknown Artist)'
const artist = common.artist || common.albumartist
if (artist) {
elems.push((
<div key='artist' className='audio-artist'>
@@ -227,6 +247,15 @@ function renderAudioMetadata (state) {
))
}
// Audio metadata: disk & track-number
const count = ['track', 'disk']
count.forEach(key => {
const nrElem = renderTrack(common, key)
if (nrElem) {
elems.push(nrElem)
}
})
// Audio metadata: album
if (common.album) {
elems.push((
@@ -248,25 +277,23 @@ function renderAudioMetadata (state) {
// 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)
if (common.label && common.catalognumber &&
common.label.length === common.catalognumber.length) {
// Assume labels & catalog-numbers are pairs
for (let n = 0; n < common.label.length; ++n) {
releaseInfo.push(common.label[0] + ' / ' + common.catalognumber[n])
}
} else {
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
elems.push((
<div key='track' className='audio-track'>
<label>Track</label>{track}
<label>Release</label>{ releaseInfo.join(', ') }
</div>
))
}
@@ -278,10 +305,10 @@ function renderAudioMetadata (state) {
format.push(fileSummary.audioInfo.format.dataformat)
}
if (fileSummary.audioInfo.format.bitrate) {
format.push(fileSummary.audioInfo.format.bitrate / 1000 + ' kbps')
format.push(Math.round(fileSummary.audioInfo.format.bitrate / 1000) + ' kbps') // 128 kbps
}
if (fileSummary.audioInfo.format.sampleRate) {
format.push(fileSummary.audioInfo.format.sampleRate / 1000 + ' kHz')
format.push(Math.round(fileSummary.audioInfo.format.sampleRate / 100) / 10 + ' kHz') // 44.1 kHz
}
if (fileSummary.audioInfo.format.bitsPerSample) {
format.push(fileSummary.audioInfo.format.bitsPerSample + ' bit')
@@ -329,7 +356,7 @@ function renderLoadingSpinner (state) {
return (
<div key='loading' className='media-stalled'>
<div key='loading-spinner' className='loading-spinner'>&nbsp;</div>
<div key='loading-spinner' className='loading-spinner' />
<div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded
<span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span>
@@ -389,7 +416,7 @@ function renderCastScreen (state) {
function renderCastOptions (state) {
if (!state.devices.castMenu) return
const {location, devices} = state.devices.castMenu
const { location, devices } = state.devices.castMenu
const player = state.devices[location]
const items = devices.map(function (device, ix) {
@@ -512,9 +539,9 @@ function renderPlayerControls (state) {
// Add the cast buttons. Icons for each cast type, connected/disconnected:
const buttonIcons = {
'chromecast': {true: 'cast_connected', false: 'cast'},
'airplay': {true: 'airplay', false: 'airplay'},
'dlna': {true: 'tv', false: 'tv'}
'chromecast': { true: 'cast_connected', false: 'cast' },
'airplay': { true: 'airplay', false: 'airplay' },
'dlna': { true: 'tv', false: 'tv' }
}
castTypes.forEach(function (castType) {
// Do we show this button (eg. the Chromecast button) at all?
@@ -669,7 +696,7 @@ function renderLoadingBar (state) {
for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) {
const partPresent = Bitfield.prototype.get.call(prog.bitfield, i)
if (partPresent && !lastPiecePresent) {
parts.push({start: i - fileProg.startPiece, count: 1})
parts.push({ start: i - fileProg.startPiece, count: 1 })
} else if (partPresent) {
parts[parts.length - 1].count++
}

View File

@@ -7,7 +7,7 @@ const RaisedButton = require('material-ui/RaisedButton').default
const Heading = require('../components/heading')
const PathSelector = require('../components/path-selector')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
const config = require('../../config')
class PreferencesPage extends React.Component {
@@ -37,7 +37,7 @@ class PreferencesPage extends React.Component {
}}
onChange={this.handleDownloadPathChange}
title='Download location'
value={this.props.state.unsaved.prefs.downloadPath} />
value={this.props.state.saved.prefs.downloadPath} />
</Preference>
)
}
@@ -51,7 +51,7 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={!this.props.state.unsaved.prefs.openExternalPlayer}
checked={!this.props.state.saved.prefs.openExternalPlayer}
label={'Play torrent media files using WebTorrent'}
onCheck={this.handleOpenExternalPlayerChange} />
</Preference>
@@ -67,7 +67,7 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.highestPlaybackPriority}
checked={this.props.state.saved.prefs.highestPlaybackPriority}
label={'Highest Playback Priority'}
onCheck={this.handleHighestPlaybackPriorityChange}
/>
@@ -81,10 +81,10 @@ class PreferencesPage extends React.Component {
}
externalPlayerPathSelector () {
const playerPath = this.props.state.unsaved.prefs.externalPlayerPath
const playerPath = this.props.state.saved.prefs.externalPlayerPath
const playerName = this.props.state.getExternalPlayerName()
const description = this.props.state.unsaved.prefs.openExternalPlayer
const description = this.props.state.saved.prefs.openExternalPlayer
? `Torrent media files will always play in ${playerName}.`
: `Torrent media files will play in ${playerName} if WebTorrent cannot play them.`
@@ -113,7 +113,7 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.autoAddTorrents}
checked={this.props.state.saved.prefs.autoAddTorrents}
label={'Watch for new .torrent files and add them immediately'}
onCheck={(e, value) => { this.handleAutoAddTorrentsChange(e, value) }}
/>
@@ -122,7 +122,7 @@ class PreferencesPage extends React.Component {
}
handleAutoAddTorrentsChange (e, isChecked) {
const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath
const torrentsFolderPath = this.props.state.saved.prefs.torrentsFolderPath
if (isChecked && !torrentsFolderPath) {
alert('Select a torrents folder first.') // eslint-disable-line
e.preventDefault()
@@ -140,7 +140,7 @@ class PreferencesPage extends React.Component {
}
torrentsFolderPathSelector () {
const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath
const torrentsFolderPath = this.props.state.saved.prefs.torrentsFolderPath
return (
<Preference>
@@ -162,7 +162,7 @@ class PreferencesPage extends React.Component {
}
setDefaultAppButton () {
const isFileHandler = this.props.state.unsaved.prefs.isFileHandler
const isFileHandler = this.props.state.saved.prefs.isFileHandler
if (isFileHandler) {
return (
<Preference>
@@ -195,7 +195,7 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.startup}
checked={this.props.state.saved.prefs.startup}
label={'Open WebTorrent on startup.'}
onCheck={this.handleStartupChange}
/>

View File

@@ -6,7 +6,7 @@ const LinearProgress = require('material-ui/LinearProgress').default
const TorrentSummary = require('../lib/torrent-summary')
const TorrentPlayer = require('../lib/torrent-player')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher, dispatch } = require('../lib/dispatcher')
module.exports = class TorrentList extends React.Component {
render () {
@@ -365,8 +365,8 @@ module.exports = class TorrentList extends React.Component {
renderRadialProgressBar (fraction, cssClass) {
const rotation = 360 * fraction
const transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'}
const transformFix = {transform: 'rotate(' + rotation + 'deg)'}
const transformFill = { transform: 'rotate(' + (rotation / 2) + 'deg)' }
const transformFix = { transform: 'rotate(' + rotation + 'deg)' }
return (
<div key='radial-progress' className={'radial-progress ' + cssClass}>
@@ -394,10 +394,20 @@ function getErrorMessage (torrentSummary) {
if (err === 'path-missing') {
return (
<span>
Path missing.<br />
Fix and restart the app, or delete the torrent.
Torrent data missing.<br />
Fix or click to
<a onClick={(e) => {
e.stopPropagation()
restartTorrent(torrentSummary)
}}>re-download torrent</a>.
</span>
)
}
return 'Error'
}
function restartTorrent (summary) {
delete summary.path
delete summary.error
dispatch('startTorrentingSummary', summary.torrentKey)
}

View File

@@ -16,7 +16,7 @@ const zeroFill = require('zero-fill')
const crashReporter = require('../crash-reporter')
const config = require('../config')
const {TorrentKeyNotFoundError} = require('./lib/errors')
const { TorrentKeyNotFoundError } = require('./lib/errors')
const torrentPoster = require('./lib/torrent-poster')
// Report when the process crashes
@@ -97,7 +97,7 @@ function init () {
ipc.send('ipcReadyWebTorrent')
window.addEventListener('error', (e) =>
ipc.send('wt-uncaught-error', {message: e.error.message, stack: e.error.stack}),
ipc.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }),
true)
setInterval(updateTorrentProgress, 1000)
@@ -251,7 +251,7 @@ function generateTorrentPoster (torrentKey) {
function updateTorrentProgress () {
const progress = getTorrentProgress()
// TODO: diff torrent-by-torrent, not once for the whole update
if (prevProgress && deepEqual(progress, prevProgress, {strict: true})) {
if (prevProgress && deepEqual(progress, prevProgress, { strict: true })) {
return /* don't send heavy object if it hasn't changed */
}
ipc.send('wt-progress', progress)
@@ -320,7 +320,8 @@ function startServerFromReadyTorrent (torrent, cb) {
const info = {
torrentKey: torrent.key,
localURL: 'http://localhost' + urlSuffix,
networkURL: 'http://' + networkAddress() + urlSuffix
networkURL: 'http://' + networkAddress() + urlSuffix,
networkAddress: networkAddress()
}
ipc.send('wt-server-running', info)
@@ -344,7 +345,12 @@ function getAudioMetadata (infoHash, index) {
const metadata = { title: file.name }
ipc.send('wt-audio-metadata', infoHash, index, metadata)
const options = {native: false, skipCovers: true, fileSize: file.length}
const options = { native: false,
skipCovers: true,
fileSize: file.length,
observer: event => {
ipc.send('wt-audio-metadata', infoHash, index, event.metadata)
} }
const onMetaData = file.done
// If completed; use direct file access
? mm.parseFile(path.join(torrent.path, file.path), options)
@@ -352,9 +358,8 @@ function getAudioMetadata (infoHash, index) {
: 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)
.then(() => {
console.log(`metadata for file='${file.name}' completed.`)
}).catch(function (err) {
return console.log('error getting audio metadata for ' + infoHash + ':' + index, err)
})

View File

@@ -801,6 +801,23 @@ video::-webkit-media-text-track-container {
width: 40px;
height: 40px;
margin: 40px auto;
border-top: 6px solid rgba(255, 255, 255, 0.2);
border-right: 6px solid rgba(255, 255, 255, 0.2);
border-bottom: 6px solid rgba(255, 255, 255, 0.2);
border-left: 6px solid #ffffff;
border-radius: 50%;
color: transparent;
animation: spinning 1.1s infinite linear;
}
@keyframes spinning {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.media-stalled .loading-status {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 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: 1011 KiB

After

Width:  |  Height:  |  Size: 1.0 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: 143 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

After

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 KiB

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 KiB

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 844 KiB

After

Width:  |  Height:  |  Size: 870 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1024 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1016 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -32,7 +32,7 @@ function createApp (t) {
path: path.join(__dirname, '..', 'node_modules', '.bin',
'electron' + (process.platform === 'win32' ? '.cmd' : '')),
args: ['-r', path.join(__dirname, 'mocks.js'), path.join(__dirname, '..')],
env: {NODE_ENV: 'test'},
env: { NODE_ENV: 'test' },
waitTimeout: 10e3
})
}

View File

@@ -40,10 +40,9 @@ test('create-torrent', function (t) {
announce: [
'udp://explodie.org:6969',
'udp://tracker.coppersurfer.tk:6969',
'udp://tracker.empire-js.us:1337',
'udp://tracker.leechers-paradise.org:6969',
'udp://tracker.openbittorrent.com:80',
'udp://tracker.opentrackr.org:1337',
'udp://zer0day.ch:1337',
'wss://tracker.btorrent.xyz',
'wss://tracker.fastcast.nz',
'wss://tracker.openwebtorrent.com'

View File

@@ -6,7 +6,7 @@ test('audio-streaming', function (t) {
t.timeoutAfter(60e3)
const app = setup.createApp()
setup.waitForLoad(app, t, {online: true})
setup.waitForLoad(app, t, { online: true })
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Play Wired CD. Wait for it to start streaming.
.then(() => app.client.moveToObject('#torrent-wired'))

View File

@@ -31,22 +31,20 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
const app = setup.createApp()
setup.waitForLoad(app, t)
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Mouse over the first torrent
.then(() => app.client.moveToObject('.torrent'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list'))
// Click download on the first torrent, start downloading
.then(() => app.client.click('.download input'))
.then(() => app.client.waitUntilTextExists('.torrent-list', '276 MB'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-start-download'))
// Click download on the first torrent again, stop downloading
.then(() => app.client.click('.download input'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover-download'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-download'))
// Click delete on the first torrent
.then(() => app.client.click('.icon.delete'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt'))
// Click cancel on the resulting confirmation dialog. Should be same as before.
.then(() => app.client.click('.control.cancel'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list'))
// Click delete on the first torrent again
.then(() => app.client.click('.icon.delete'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt'))
@@ -63,9 +61,6 @@ test('torrent-list: expand torrent, unselect file', function (t) {
const app = setup.createApp()
setup.waitForLoad(app, t)
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Mouse over the torrent
.then(() => app.client.moveToObject('#torrent-cosmos'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-hover'))
// Click on the torrent, expand
.then(() => app.client.click('#torrent-cosmos'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-expand'))

View File

@@ -6,7 +6,7 @@ test('video-streaming', function (t) {
t.timeoutAfter(30e3)
const app = setup.createApp()
setup.waitForLoad(app, t, {online: true})
setup.waitForLoad(app, t, { online: true })
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Play Big Buck Bunny. Wait for it to start streaming.
.then(() => app.client.moveToObject('.torrent'))