Compare commits

...

166 Commits

Author SHA1 Message Date
Feross Aboukhadijeh
fbb3fa6a27 0.20.0 2018-04-26 17:07:22 -07:00
Feross Aboukhadijeh
e10debed2f authors 2018-04-26 17:05:21 -07:00
Feross Aboukhadijeh
437dd3adb1 changelog 2018-04-26 16:35:32 -07:00
Feross Aboukhadijeh
e48191381e Merge pull request #1246 from webtorrent/greenkeeper/initial
Update dependencies to enable Greenkeeper 🌴
2018-04-26 16:29:14 -07:00
Feross Aboukhadijeh
abb7c72b84 undo most dependency updates 2018-04-26 16:29:04 -07:00
Feross Aboukhadijeh
20d9ea5a00 remove greenkeeper badge 2018-04-26 16:26:05 -07:00
Feross Aboukhadijeh
0da29e4eb2 Merge pull request #1317 from webtorrent/bugfix-1315
Bugfix 1315
2018-04-26 16:22:51 -07:00
Feross Aboukhadijeh
10f92c78eb Merge pull request #1316 from webtorrent/bugfix-1314
Bugfix 1314
2018-04-26 16:16:55 -07:00
Feross Aboukhadijeh
924dbeeebb Merge pull request #1366 from webtorrent/release/v0.20.0
Release/v0.20.0
2018-04-26 16:15:25 -07:00
Feross Aboukhadijeh
3c363595ce Fix for PR #1366 2018-04-26 16:14:56 -07:00
Feross Aboukhadijeh
695154099d Merge pull request #1367 from webtorrent/fix-linux-open-magnet
Fix external calls failing silently to open magnets
2018-04-26 15:56:06 -07:00
Alberto Miranda
21edc05f19 Removed console log. 2018-04-26 15:04:23 -03:00
Alberto Miranda
cf729b0d31 Fix from leodutra: Fix external calls failing silently to open magnets #1168. 2018-04-26 15:00:58 -03:00
Alberto Miranda
74b806737d Removed already released change. 2018-04-26 11:10:13 -03:00
Alberto Miranda
88cbcc2959 0.20.0 2018-04-26 11:01:12 -03:00
Alberto Miranda
eab15876cc Updated changelog. 2018-04-26 11:00:58 -03:00
Alberto Miranda
c6a7b7c1d2 Merge pull request #1334 from Borewit/feature/improve-audio-torrent-poster-selection
Improve the poster selection for audio/music based torrent
2018-04-26 09:46:12 -03:00
Feross Aboukhadijeh
7f2c91b230 Merge pull request #1364 from vitorgalvao/patch-1
README.md: add cask instructions
2018-04-25 19:35:53 -07:00
Vítor Galvão
dab0802a88 README.md: add cask install 2018-04-25 21:01:31 +01:00
Feross Aboukhadijeh
34f06267b8 Merge pull request #1166 from janza/fix-react-key-error
Add missing key to torrent status element
2018-04-24 16:03:30 -07:00
Alberto Miranda
99062175eb Merge pull request #1361 from webtorrent/music-metadata-fix
Displaying filename while music metadata is being downloaded.
2018-04-20 11:22:06 -03:00
Borewit
f47055c51b Adjust the filename score, size is probably more relevant. 2018-04-20 08:07:27 +02:00
Borewit
4752c2e689 Merge branch 'filename' into feature/improve-audio-torrent-poster-selection 2018-04-20 07:44:51 +02:00
Borewit
57c1a97ebf Update music-metadata dependency to v0.9.15. 2018-04-20 07:43:15 +02:00
Borewit
793ccebf0b Merge branch 'master' into feature/improve-audio-torrent-poster-selection 2018-04-20 07:38:53 +02:00
Alberto Miranda
c688313239 Displaying filename while music metadata is being downloaded. 2018-04-19 14:49:17 -03:00
Alberto Miranda
afd7f6ef0a Merge pull request #1240 from Borewit/music-metadata
Fix playing back audio files & reading music metadata
2018-04-19 14:38:39 -03:00
Terry Hau
cc78251846 Remove 'Always on top' from VLC player 2018-04-18 20:51:43 -07:00
Borewit
30693d7d16 Added some function comments.
Simplified code a bit.
2018-03-11 17:33:02 +01:00
Borewit
bd2fbe60db Pass file length parameter to music-metadata parser.
In some cases this is required to calculate the duration.
2018-03-11 17:13:56 +01:00
Borewit
a114a22770 #1332: Add album as likely name for an image containing the cover 2018-03-04 19:04:18 +01:00
Borewit
8d657d93e0 Fix code style 2018-03-04 18:49:59 +01:00
Borewit
5802cd3591 #1332: Add unit test for cover selection algorithm. 2018-03-04 18:44:14 +01:00
Borewit
2d17cfa7e1 #1332: Fix selection of poster.jpg|png|gif image 2018-03-04 18:43:12 +01:00
Borewit
cf2a3b8948 #1332: Improve cover image score algorithm:
- If filename score is equal, pick highest resolution
- Search for keyword occurrence in file name
2018-03-04 16:24:07 +01:00
Borewit
b89a74e6e9 #1332: Improve the poster selection for audio/music based torrent,
aiming to get the cover-art selected as the torrent poster.
2018-03-04 13:48:07 +01:00
Borewit
9b74602119 Fixed the win32 version of the test-integration/test-audio: updated screenshots to match new meta-data.
The format value has been to partially transparent because the bit-rate is not stable.
This is due to the fact these MP3 are encoded with Variable Bit-Rate (VBR),and they are lacking a "Xing" header, providing VBR encoding details.
Related to issue #1320 & #1240.
2018-03-04 10:45:30 +01:00
Borewit
34463d9084 Enforce music-metadata version which is able to deal with bad Ogg files 2018-03-03 14:44:50 +01:00
Borewit
3dcab9e78b Fix audio format metadata 2018-02-24 16:32:12 +01:00
Borewit
fce0003912 Add audio metadata standalone entries for: year & release information 2018-02-24 16:24:56 +01:00
Borewit
c720f65fee Update dependency music-metadata to version 0.9.5 2018-02-24 16:23:08 +01:00
Borewit
a725726e25 Fix formatting according 'standard' 2018-02-19 21:35:50 +01:00
Borewit
e5a64d9550 Add comments to metadata media overlay.
Adjust the label element width to 120px to be able to fit in the text: 'Comments'.
Adjust the font-weight of the comments & format value to 'normal'.
2018-02-19 21:25:22 +01:00
Borewit
0572f8a932 Update music-metadata dependency to 0.9.4. 2018-02-19 20:30:13 +01:00
Borewit
23d37d7da1 Merge branch 'master' into music-metadata 2018-02-19 20:22:22 +01:00
Feross Aboukhadijeh
474654fd16 standard 2018-02-18 15:49:46 -08:00
Borewit
070d3ff242 Update music-metadata dependency to 0.9.2 2018-02-06 19:41:31 +01:00
Borewit
d5a62cb1a7 Add additional audio extensions: 'aiff', 'ape', 'mp2', 'oga', 'opus', 'wma' 2018-02-06 14:17:59 +01:00
Borewit
ba1f82faa0 Use direct file access, if the individual file has completed downloading.
Will allow slightly faster reading of music-metadata.
2018-02-05 20:29:17 +01:00
Borewit
0cce110c52 Fix bug #1320 by update music-metadata.
Bug was caused by an interoperability between then-read-stream and
readable-stream.
2018-02-05 20:20:53 +01:00
Borewit
bc91cde244 Merge branch 'master' into music-metadata 2018-02-05 10:19:17 +01:00
Borewit
30c9934694 Update version of dependency music-metadata to 0.9.0 2018-02-04 15:22:35 +01:00
Borewit
f56f3c62e9 Update version of dependency music-metadata to 0.8.8 2018-02-04 14:33:39 +01:00
Alberto Miranda
ac180a7cc5 1314: Restored package version. 2018-01-29 11:43:03 -03:00
Alberto Miranda
d1f3a16b7f 1315: Restored package version. 2018-01-29 11:42:23 -03:00
Alberto Miranda
e6c495417b 0.19.2 2018-01-29 09:24:38 -03:00
Alberto Miranda
c301608881 0.19.1 2018-01-29 09:24:32 -03:00
Alberto Miranda
7d2290a773 1315: Added initial state for highestPlaybackPriority. 2018-01-29 09:24:27 -03:00
Alberto Miranda
ba0d0b755e 0.19.1 2018-01-29 08:40:05 -03:00
Alberto Miranda
b5e9923469 1314: Fixed issue with undefined torrents to resume when app starts. 2018-01-29 08:40:01 -03:00
Feross Aboukhadijeh
b5bbd9b6a5 changelog 2018-01-26 16:52:39 +08:00
Feross Aboukhadijeh
5bf7618dde 0.19.0 2018-01-26 16:13:05 +08:00
Feross Aboukhadijeh
d02f6c7d18 Rewrite changelog for v0.19.0 2018-01-26 16:10:50 +08:00
Feross Aboukhadijeh
e69a18e548 authors 2018-01-26 15:56:59 +08:00
Feross Aboukhadijeh
991e2d2024 Update electron to 1.6.16 2018-01-26 15:56:59 +08:00
Feross Aboukhadijeh
9f31f30925 Mitigate protocol handler vulnerability 2018-01-26 15:56:59 +08:00
Feross Aboukhadijeh
340658c747 set version back to 0.18 -- will bump it during release 2018-01-26 15:56:09 +08:00
Alberto Miranda
5bbd9cfabb 0.19.0 2018-01-26 15:56:09 +08:00
Alberto Miranda
03a8b40ee9 Updated changelog. 2018-01-26 15:56:09 +08:00
Benjamin Tan
d0cc157003 Merge pull request #1247 from auyer/master
"Error" text on missing Codec error changed to "Unable to Play"
2018-01-21 21:15:11 +08:00
Cezar Carneiro
01971e2a46 toggle fullscreen on end 2018-01-15 13:10:04 -05:00
SimplyAhmazing
08cb82e255 Update copyright a year later 2018-01-15 13:09:18 -05:00
Alberto Miranda
cae40b44e6 Merge pull request #1154 from webtorrent/watch-folder
Merging watch folder feature.
2017-10-16 22:47:03 -03:00
Auyer
ab0bbcea94 Error on missing Codec changed to "Unable to Play"
The error message was harsh, and new users might close the software
imediatly after it fails.
2017-10-03 11:14:44 -03:00
greenkeeper[bot]
06a7eff0ab docs(readme): add Greenkeeper badge 2017-09-29 01:59:08 +00:00
greenkeeper[bot]
341333c287 chore(package): update dependencies 2017-09-29 01:55:26 +00:00
Borewit
0f00985b75 Eliminated mime dependency 2017-09-20 09:14:51 +02:00
Borewit
c11a86d849 Fix max line length 2017-09-17 22:24:16 +02:00
Borewit
52e16c33b4 Fix lint error 2017-09-17 22:19:32 +02:00
Borewit
54e15644b6 Fix max line length 2017-09-17 22:16:01 +02:00
Borewit
6aa3a6c660 Fix lint errors 2017-09-17 22:14:07 +02:00
Borewit
83350b3b57 Add catalognumber in addition of the release label.
Updated to music-metadata in order to recognize MIME-type: audio/x-flac
2017-09-17 22:10:00 +02:00
Borewit
8868128d6e Add audio format information to play screen.
Extended album information with release lebel if available.
Make use of music-metadata module (musicmetadata appears to be no longer maintained)
2017-09-17 20:57:57 +02:00
Diego Rodríguez Baquero
a81156b363 Merge pull request #1233 from emilbayes/nojitter-time
Use tabular nums and fixed width for no jitter on time
2017-09-01 18:02:23 -05:00
Emil Bay
4b501ab90b Fixed width minutes 2017-08-28 19:41:30 +02:00
Emil Bay
90347c72e0 Use tabular nums and fixed width for no jitter on time 2017-08-28 19:07:24 +02:00
Feross Aboukhadijeh
a51ab3573e Add a security policy 2017-07-17 12:45:13 -07:00
Josip Janzic
96f5f855a3 Add missing key to torrent status element
Fixes #1116
2017-04-27 21:29:18 +02:00
Feross Aboukhadijeh
a82498ba16 gitter 2017-04-14 14:40:18 -07:00
Feross Aboukhadijeh
3b05b52e57 feross -> webtorrent 2017-04-14 13:48:39 -07:00
Alberto Miranda
b02d6f3cdb Preferences update
Renamed “Downloads” section to “Folders”; watch folder preference moved
under “Folders”; updated texts for watch folder preference.
2017-04-02 19:37:13 -03:00
Alberto Miranda
6e67cae494 Wait for .torrent files to finish downloading. 2017-03-26 11:04:05 -03:00
Alberto Miranda
23eef1a058 Removed unused module from folderWatcher. 2017-03-24 12:55:10 -03:00
Alberto Miranda
2ad3f6d6e2 Removed extra logging. 2017-03-24 12:54:28 -03:00
Alberto Miranda
996a81ddaa added folder-watcher-controller 2017-03-23 07:14:27 -03:00
Alberto Miranda
4a8ab6e1f6 fixed styling issue 2017-03-23 07:00:45 -03:00
Alberto Miranda
7f817241ed added start / stop events; starting / stopping folder watcher. 2017-03-22 23:21:07 -03:00
Alberto Miranda
a6c53d06d2 fixed styling issues 2017-03-22 22:27:03 -03:00
Alberto Miranda
a70c4d1bf2 Working on watch-folder
Added chokidar to watch for folder changes; added folder-watcher;
passing state to delayedInit on main;  added default values for new
preferences; added “Auto add torrents” preference with its checkbox and
path selector; TODO: start/stop watching on preference change, start
watching on init, add dialog when trying to enable preference without a
torrents folder.
2017-03-22 09:42:17 -03:00
Feross Aboukhadijeh
cdfb907c75 standard 2017-03-20 19:41:44 -07:00
Alberto Miranda
c2abb50e9e Merge pull request #840 from feross/playback-priority
Playback priority
2017-03-15 14:39:26 -03:00
Alberto Miranda
99f4fc96bf Fixed resuming when downloading finishes. 2017-03-14 19:52:29 -03:00
Alberto Miranda
043f81996e fixed styling issues; returning early in pauseActiveTorrents. 2017-03-14 19:50:49 -03:00
Alberto Miranda
213f2f124d Merge branch 'playback-priority' of https://github.com/feross/webtorrent-desktop into playback-priority 2017-03-14 19:44:45 -03:00
Alberto Miranda
96e7e83e20 Merge branch 'master' into playback-priority
# Conflicts:
#	src/renderer/controllers/playback-controller.js
#	src/renderer/controllers/torrent-list-controller.js
#	src/renderer/main.js
2017-03-14 19:43:57 -03:00
Feross Aboukhadijeh
e5fccd93a8 Tweak pinch-to-zoom timing
Toggle fullscreen immediately, then prevent future toggles for 1s.
2017-03-08 15:00:42 -08:00
Feross Aboukhadijeh
78e46f5576 Merge branch 'ebraminio-master' 2017-03-08 14:56:38 -08:00
Ebrahim Byagowi
69ce07fbf7 Toggle fullscreen on pinch to zoom 2017-03-08 23:13:05 +03:30
Feross Aboukhadijeh
827131e136 standard 2017-03-02 00:33:36 -08:00
Feross Aboukhadijeh
c764bf4884 fixes for standard v10 2017-03-01 23:08:29 -08:00
Feross Aboukhadijeh
972203d675 material-ui@0.17
Tested and everything seems to work alright with the new version.
2017-02-20 03:09:56 -08:00
Feross Aboukhadijeh
2c382e14b5 electron@1.6.0
This version of Electron is 3 versions of Chromium newer, so there are
lots of performance improvements.

There is one known issue with this update:
https://github.com/electron/electron/issues/8694

But I think we should merge this anyway so that collaborators can try
out the new Chromium engine before we do a release with it. We might
find other bugs.
2017-02-15 15:31:35 -08:00
Feross Aboukhadijeh
c298950d34 Add 'Start Speaking' and 'Stop Speaking' menu item (Mac)
Probably improves the accessibility situation a little bit: #439
2017-02-15 13:31:28 -08:00
Feross Aboukhadijeh
00f2e5ccd6 Treat .FLAC as playable audio
Fixes: https://github.com/feross/webtorrent-desktop/issues/1124

Note FLAC is not actually playable in Electron, by default. But this
will at least offer to open it in VLC or the user's preferred player.
2017-02-15 13:31:06 -08:00
Feross Aboukhadijeh
8d86cffabd standard 2017-02-08 16:07:25 -08:00
Feross Aboukhadijeh
95d6ec5fdd move release instructions to contributing.md 2017-02-04 18:00:50 -08:00
DC
9fbee6cfeb Replace release scripts with instructions
The release scripts don't really work, and there are a lot of manual steps that weren't documented. They are now.
2017-02-05 02:55:13 +01:00
Feross Aboukhadijeh
9892f88530 Fix 'About WebTorrent' menu location on Windows 2017-02-03 03:27:35 -08:00
Feross Aboukhadijeh
10ad990f97 update AUTHORS 2017-02-03 02:17:04 -08:00
Feross Aboukhadijeh
0ee7e80fd2 0.18.0 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
7d1520f858 disable background throttling in all windows 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
77ba258fbf move Transfers menu 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
06366529cc updated copyright year 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
d1c263f1b0 changelog for 0.18.0 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
d7a031a457 test: update test for new default trackers 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
b7201f424d test: update screenshot for new default trackers 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
89f2785244 remove unneeded package from depcheck 2017-02-03 02:08:59 -08:00
Feross Aboukhadijeh
cffe416f50 package: fix deprecation warning 2017-02-03 02:01:54 -08:00
Feross Aboukhadijeh
d0eea34e9b 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 02:01:54 -08:00
Feross Aboukhadijeh
4e82718788 electron-osx-sign@0.4 2017-02-03 02:01:54 -08:00
Feross Aboukhadijeh
293ca60e72 electron@1.4.15 2017-02-03 02:01:54 -08:00
Feross Aboukhadijeh
af4a6a5960 buble@0.15 2017-02-03 02:01:54 -08:00
Nuno Campos
e01c0b6f43 Add textarea to list of html tags that skip document-wide onPaste 2017-02-02 15:17:10 -08:00
Feross Aboukhadijeh
efc984f1dd Include policy about copyrighted content in issues in issue template 2017-01-20 19:07:42 -08:00
Feross Aboukhadijeh
a046db40d2 Update react-tap-event-plugin 2.0.1
Fixes https://github.com/feross/webtorrent-desktop/issues/1086
2016-12-07 21:01:12 -08:00
Feross Aboukhadijeh
9068909b4d fixes for standard 2016-11-22 19:43:51 -03:00
Mathias Rasmussen
4bb2056bc9 Auto-focus modal 'OK' button to allow keypress 2016-10-24 14:43:19 -05:00
Karan Thakkar
ed0f05abc4 Pause All will now pause seeding torrents too 2016-10-24 13:33:38 -05:00
Karan Thakkar
0c44e10ca7 Move sound.play outisde the for loop 2016-10-24 13:33:38 -05:00
Karan Thakkar
d8c9014471 Add a new Transfers menu to allow pause all and resume all torrents 2016-10-24 13:33:38 -05:00
Feross Aboukhadijeh
7cf1d96a80 Fixes for PR #1050 2016-10-18 12:49:09 +01:00
Adam Gotlib
ffd9fbda57 Disallow saving torrent file until it is generated
Fixes #1031.
2016-10-18 12:46:33 +01:00
Adam Gotlib
d8904aaf6e Log successful attempts to open an external player 2016-10-13 23:31:34 -05:00
Adam Gotlib
de2db211cc Log play attempt separately for each file 2016-10-13 23:31:34 -05:00
Adam Gotlib
e8ab172d1b Remove unused 'timeout' field from telemetry 2016-10-13 23:31:34 -05:00
Alexey Romanov
32f96c03dd #1039: Formatting fixed.
Removed comma and added space after download speed.
2016-10-13 21:24:17 -07:00
Feross Aboukhadijeh
5158606740 Remove lodash.merge require() hack
See:
https://github.com/callemall/material-ui/pull/4380#issuecomment-25089455
2

My PR to material-ui to replace the full `lodash` with just the needed
functions was merged. So we can remove this hack.
2016-10-12 17:53:52 -05:00
Feross Aboukhadijeh
0de80165ed material-ui@0.16 2016-10-12 17:53:52 -05:00
Feross Aboukhadijeh
dcaa99d2bf es6-error@4 2016-10-12 17:53:52 -05:00
Feross Aboukhadijeh
1a1a4cd5d0 electron 1.4.3
Changelog: https://github.com/electron/electron/releases/tag/v1.4.3
2016-10-12 17:53:52 -05:00
Feross Aboukhadijeh
4cbad1fccd 0.17.2 2016-10-10 22:44:08 -07:00
Feross Aboukhadijeh
f818564dd1 package.json: Add {"private": true}
To prevent accidental publishing to npm.
2016-10-10 22:41:17 -07:00
Feross Aboukhadijeh
8be690a26e v0.17.2 changelog 2016-10-10 22:37:24 -07:00
Feross Aboukhadijeh
8081d42477 Attempt to fix "TypeError: Cannot read property 'startPiece' of undefined"
It wasn't clear what was causing this error, and I couldn't reproduce
it.

This PR attempts to resolve the issue by just guarding against the
exception.
2016-10-10 22:31:33 -07:00
Feross Aboukhadijeh
b0b6069fe2 remove 'pug' from nodemon command (we don't use pug) 2016-10-06 00:37:11 -07:00
Feross Aboukhadijeh
994aed9af7 Fix "Cannot read property 'files' of null"
This PR fixes one of our number 2 top error (142 error reports today
alone):

Processes: webtorrent window, platforms: darwin linux win32, versions:
pre-0.12 0.14.0 0.17.0 0.17.1
TypeError: Cannot read property 'files' of null
    at getAudioMetadata (.../build/renderer/webtorrent.js:328:21)
    at EventEmitter.<anonymous> (.../build/renderer/webtorrent.js:84:74)
    at emitThree (events.js:116:13)
    at EventEmitter.emit (events.js:194:7)

This error is reproducible if you start webtorrent for the first time
and click the WIRED CD torrent. This causes the webtorrent process to
get a  'wt-get-audio-metadata' before 'wt-start-torrenting'.

You can reproduce it 100% of the time if you force the race condition
to show itself by slowing down the sending of the 'wt-start-torrenting'
event.

(This same error was showing for an unrelated reason in the past: #891)
2016-10-05 03:00:52 -07:00
Feross Aboukhadijeh
852fc86cbd Remove unecessary return statement 2016-10-05 03:00:52 -07:00
Feross Aboukhadijeh
8801a87a58 Throttle browser-window 'move' and 'resize' events
Fixes: https://github.com/feross/webtorrent-desktop/issues/1011
2016-10-05 03:00:42 -07:00
Feross Aboukhadijeh
1e10f0083c Windows: Fix impossible to delete Wired CS torrent 2016-10-05 03:00:24 -07:00
Feross Aboukhadijeh
bb40f0f11a Update Mac integration test for Sierra 10.12
The font changed slightly on the next version of Mac OS. Let's update
the screenshots to match, since I already updated. (@dcposch - you'll
need to update too if you want the integration tests to pass on your
machine)
2016-10-04 01:53:49 -07:00
Feross Aboukhadijeh
78d7243a08 package: Explicitly specify architectures to build for
Electron now offers a "armv7l" build on Linux. Because we were
specifying "all", we were generating an armv7l binary which was being
included in "WebTorrent-vX.X.X-linux.zip" along with the ia32 build,
which explains why it was 90MB+ in the last release.
2016-10-03 04:40:42 -07:00
Alberto Miranda
44a0f760de Resuming paused torrents when streaming torrent finishes downloading (wt-done). 2016-08-30 12:09:23 -03:00
Alberto Miranda
1e5ac1df9c removed logging 2016-08-27 03:56:08 -03:00
Alberto Miranda
4cdc3085ff not pausing active torrents when playing an already downloaded torrent. 2016-08-27 03:55:23 -03:00
Alberto Miranda
e483263d70 removed logging 2016-08-27 03:36:28 -03:00
Alberto Miranda
bd41bd4db8 added highest playback priority feature; added highest playback priority checkbox on preferences. 2016-08-27 03:35:53 -03:00
85 changed files with 1093 additions and 345 deletions

View File

@@ -1,3 +1,5 @@
<!-- DO NOT POST LINKS OR REFERENCES TO COPYRIGHTED CONTENT IN YOUR ISSUE. -->
**What version of WebTorrent Desktop?** (See the 'About WebTorrent' menu)
**What operating system and version?**

View File

@@ -34,5 +34,18 @@
- 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,5 +1,68 @@
# 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
### Added
- Add a new "Transfers" menu for pausing or resuming all torrents (#1027)
### Changed
- Update Electron to 1.4.15
- Windows 32-bit: App can use 4GB of memory instead of just 2GB
- Fix "Portable App" writing crash reports to "%APPDATA%\Temp" (Windows)
- Updated WebTorrent engine to 0.98.5
- Fix issue where http web seeds would sometimes stall
- Don't send 'completed' event to tracker again if torrent is already complete
- Add more peer ID entropy
- Set user-agent header for tracker http requests
### Fixed
- Fix paste shortcut in tracker list on Create Torrent page (#1112)
- Auto-focus the 'OK' button in modal dialogs (#1058)
- Fix formatting issue in the speed stats on the Player page (#1039)
## v0.17.2 - 2016-10-10
### Fixed
- Windows: Fix impossible-to-delete "Wired CD" default torrent
- Throttle browser-window 'move' and 'resize' events
- Fix crash ("Cannot read property 'files' of null" error)
- Fix crash ("TypeError: Cannot read property 'startPiece' of undefined")
## v0.17.1 - 2016-10-03
### Changed
@@ -39,7 +102,7 @@
## v0.16.0 - 2016-09-18
### Added
- **Windows 64-bit support!** ([#931](https://github.com/feross/webtorrent-desktop/pull/931))
- **Windows 64-bit support!** (#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
@@ -99,16 +162,13 @@
## v0.12.0 - 2016-08-23
### Added
- Custom external media player
- Linux: add system-wide launcher and icons for Debian, including Ubuntu
### Changed
- Telemetry improvements: redact stacktraces, log app version
### Fixed
- Fix playback and download of default torrents ("missing path" error) (#804)
- Fix Delete Torrent + Data for newly added magnet links
- Fix jumpToTime error (#804)
@@ -121,13 +181,11 @@
- Check for missing default download path and torrent folders on start up (#776)
### Changed
- Do not automatically set WebTorrent as the default handler for torrents (#771)
- Torrents can only be created from the home screen (#770)
- Update Electron to 1.3.3 (#772)
### Fixed
- Allow modifying the default tracker list on the Create Torrent page (#775)
- Prevent opening multiple stacked Preference windows or Create Torrent windows (#770)
- Windows: Player window auto-resize does not match video aspect ratio (#565)
@@ -136,13 +194,11 @@
## v0.10.0 - 2016-08-05
### Added
- Drag-and-drop magnet links (selected text) is now supported (#284)
- Windows: Add "User Tasks" shortcuts to app icon in Start Menu (#114)
- Linux: Show badge count for completed torrent downloads
### Changed
- Change WebTorrent Desktop peer ID prefix to 'WD' to distinguish from WebTorrent in the browser, 'WW' (#688)
- Switch UI to React to improve UI rendering speed (#729)
- The primary bottleneck was actually `hyperx`, not `virtual-dom`.
@@ -159,7 +215,6 @@
- Location history abstraction released independently as [`location-history`](https://www.npmjs.com/package/location-history)
### Fixed
- When streaming to VLC, set VLC window title to torrent file name (#746)
- Fix "Cannot read property 'numPiecesPresent' of undefined" exception (#695)
- Fix rare case where config file could not be completely written (#733)
@@ -167,35 +222,29 @@
## v0.9.0 - 2016-07-20
### Added
- Save selected subtitles
- Ask for confirmation before deleting torrents
- Support Debian Jessie
### Changed
- Only send telemetry in production
- Clean up the code. Split main.js, refactor lots of things
### Fixed
- Fix state.playing.jumpToTime behavior
- Remove torrent file and poster image when deleting a torrent
## v0.8.1 - 2016-06-24
### Added
- New URI handler: stream-magnet
### Fixed
- DLNA crashing bug
## v0.8.0 - 2016-06-23
### Added
- Cast menu: choose which Chromecast, Airplay, or DLNA device you want to use
- Telemetry: send basic data, plus stats on how often the play button works
- Make posters from jpeg files, not just jpg
@@ -203,17 +252,14 @@
- Windows thumbnail bar with a play/pause button
### Changed
- Nicer modal styles
### Fixed
- Windows tray icon now stays in the right state
## v0.7.2 - 2016-06-02
### Fixed
- Fix exception that affects users upgrading from v0.5.1 or older
- Ensure `state.saved.prefs` configuration exists
- Fix window title on "About WebTorrent" window
@@ -221,23 +267,19 @@
## v0.7.1 - 2016-06-02
### Changed
- Change "Step Forward" keyboard shortcut to `Alt+Left` (Windows)
- Change "Step Backward" keyboard shortcut to to `Alt+Right` (Windows)
### Fixed
- First time startup bug -- invalid torrent/poster paths
## v0.7.0 - 2016-06-02
### Added
- Improved AirPlay support -- using the new [`airplayer`](https://www.npmjs.com/package/airplayer) package
- Remember volume setting in player, for as long as the app is open
### Changed
- Add (+) button now also accepts non .torrent files and creates a torrent from
those files
- Show prompt text in title bar for open dialogs (OS X)
@@ -247,7 +289,6 @@
- Fix crash reporter not working (Windows)
### Fixed
- Re-enable WebRTC (web peers)! (OS X, Windows)
- Windows support was disabled in v0.6.1 to work around a bug in Electron
- OS X support was disabled in v0.4.0 to work around a 100% CPU bug
@@ -258,7 +299,6 @@
- Fix torrent loading message UI misalignment
### Known issues
- When upgrading to WebTorrent Desktop v0.7.0, some torrent metadata (file list,
selected files, whether torrent is streamable) will be cleared. Just start the
torrent to re-populate the metadata.
@@ -266,7 +306,6 @@
## v0.6.1 - 2016-05-26
### Fixed
- Disable WebRTC to work around Electron crash (Windows)
- Will be re-enabled in the next version of WebTorrent, which will be based on
the next version of Electron, where the bug is fixed.
@@ -278,7 +317,6 @@
## v0.6.0 - 2016-05-24
### Added
- Added Preferences page to set Download folder
- Save video position, resume playback from saved position
- Add additional video player keyboard shortcuts (#275)
@@ -288,7 +326,6 @@
- Add announcement feature
### Changed
- Nicer player UI
- Reduce startup jank, improve startup time (#568)
- Cleanup unsupported codec detection (#569, #570)
@@ -296,7 +333,6 @@
- Improve subtitle positioning (#551)
### Fixed
- Fix Uncaught TypeError: Cannot read property 'update' of undefined (#567)
- Fix bugs in LocationHistory
- When player is active, and magnet link is pasted, go back to list
@@ -307,23 +343,19 @@
## v0.5.1 - 2016-05-18
### Fixed
- Fix auto-updater (OS X, Windows).
## v0.5.0 - 2016-05-17
### Added
- Select/deselect individual files to torrent.
- Automatically include subtitle files (.srt, .vtt) from torrent in the subtitles menu.
- "Add Subtitle File..." menu item.
### Changed
- When manually adding subtitle track(s), always switch to the new track.
### Fixed
- Magnet links throw exception on app launch. (OS X)
- Multi-file torrents would not seed in-place, were copied to Downloads folder.
- Missing 'About WebTorrent' menu item. (Windows)
@@ -332,7 +364,6 @@
## v0.4.0 - 2016-05-13
### Added
- Better Windows support!
- Windows 32-bit build.
- Windows Portable App build.
@@ -355,7 +386,6 @@
- New default torrent on first launch: The WIRED CD.
### Changed
- Improve app startup time by 40%.
- UI tweaks: Reduce font size, reduce torrent list item height.
- Add Playback menu for playback-related functionality.
@@ -366,7 +396,6 @@
- Remove "Add Fake Airplay/Chromecast" menu items.
### Fixed
- Disable WebRTC to fix 100% CPU usage/crashes caused by Chromium issue. This is
temporary. (OS X)
- When fullscreen, make controls use the full window. (OS X)
@@ -391,24 +420,20 @@ to this release!
## v0.3.3 - 2016-04-07
### Fixed
- App icon was incorrect (OS X)
## v0.3.2 - 2016-04-07
### Added
- Register WebTorrent as default handler for magnet links (OS X)
### Changed
- Faster startup time (50ms)
- Update Electron to 0.37.5
- Remove the white flash when loading pages and resizing the window
- Fix crash when sending IPC messages
### Fixed
- Fix installation bugs with .deb file (Linux)
- Pause audio reliably when closing the window
- Enforce minimimum window size when resizing player (for audio-only .mov files, which are 0x0)
@@ -416,17 +441,14 @@ to this release!
## v0.3.1 - 2016-04-06
### Added
- Add crash reporter to torrent engine process
### Fixed
- Fix cast screen background: cover, don't tile
## v0.3.0 - 2016-04-06
### Added
- **Ubuntu/Debian support!** (.deb installer)
- **DLNA streaming support**
- Add "File > Quit" menu item (Linux)
@@ -434,14 +456,12 @@ to this release!
- Crash reporting
### Changed
- On startup, do not re-verify files when timestamps are unchanged
- Moved torrent engine to an independent process, for better UI performance
- Removed media queries (UI resizing based on window width)
- Improved Chromecast icon, when connected
### Fixed
- "Download Complete" notification shows consistently
- Create new torrents and seed them without copying to temporary folder
- Clicking the "Download Complete" notification will always activate app
@@ -460,7 +480,6 @@ Thanks to @dcposch, @grunjol, and @feross for contributing to this release.
## v0.2.0 - 2016-03-29
### Added
- Minimise to tray (Windows, Linux)
- Show spinner and download speed when player is stalled waiting for data
- Highlight window on drag-and-drop
@@ -469,12 +488,10 @@ Thanks to @dcposch, @grunjol, and @feross for contributing to this release.
Linux users need to download new versions manually.
### Changed
- Renamed WebTorrent.app to WebTorrent Desktop
- Add Cosmos Laundromat as a default torrent
### Fixed
- Only capture media keys when player is active
- Update WebTorrent to 0.88.1 for performance improvements
- When seeding, do not proactively connect to new peers
@@ -534,7 +551,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/feross/webtorrent-desktop/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+contribution%22)!
[lots to do](https://github.com/webtorrent/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://github.com/feross/standard
[standard-url]: https://standardjs.com
## Project Governance
@@ -36,7 +36,7 @@ standard guarded open source project.
There are a few basic ground-rules for contributors:
1. **No `--force` pushes** or modifying the Git history in any way.
1. **No `--force` pushes to master** or modifying history in any way. Rebasing and force pushing your own PR branch is fine.
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,6 +74,99 @@ 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,6 +1,8 @@
<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>
@@ -10,16 +12,21 @@
<h4 align="center">The streaming torrent app. For Mac, Windows, and Linux.</h4>
<p align="center">
<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>
<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>
</p>
## Install
Download the latest version of WebTorrent Desktop from
[the official website](https://webtorrent.io/desktop/) or the
[GitHub releases](https://github.com/feross/webtorrent-desktop/releases) page.
[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
```
**WebTorrent Desktop** is under very active development. You can try out the
current (unstable) development version by cloning the Git repo. See the
@@ -37,7 +44,7 @@ instructions below in the ["How to Contribute"](#how-to-contribute) section.
### Get the code
```
$ git clone https://github.com/feross/webtorrent-desktop.git
$ git clone https://github.com/webtorrent/webtorrent-desktop.git
$ cd webtorrent-desktop
$ npm install
```
@@ -68,19 +75,25 @@ $ 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! Ses 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! See
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
@@ -110,7 +123,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
@@ -145,10 +158,6 @@ 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).

43
SECURITY.md Normal file
View File

@@ -0,0 +1,43 @@
# 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,6 +21,7 @@ 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: [
@@ -36,6 +37,12 @@ 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)
@@ -89,7 +96,7 @@ const all = {
// Pattern which specifies which files to ignore when copying files to create the
// package(s).
ignore: /^\/src|^\/dist|\/(appveyor.yml|\.appveyor.yml|\.github|appdmg|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^\/]*|.*\.md|.*\.markdown)$/,
ignore: /^\/src|^\/dist|\/(appveyor.yml|\.appveyor.yml|\.github|appdmg|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^/]*|.*\.md|.*\.markdown)$/,
// The application name.
name: config.APP_NAME,
@@ -104,8 +111,8 @@ const all = {
// "devDependencies" before starting to package the app.
prune: true,
// The Electron version with which the app is built (without the leading 'v')
version: require('electron/package.json').version
// The Electron version that the app is built with (without the leading 'v')
electronVersion: require('electron/package.json').version
}
const darwin = {
@@ -134,7 +141,7 @@ const win32 = {
platform: 'win32',
// Build ia32 and x64 binaries.
arch: 'all',
arch: ['ia32', 'x64'],
// Object hash of application metadata to embed into the executable (Windows only)
win32metadata: {
@@ -168,7 +175,7 @@ const linux = {
platform: 'linux',
// Build ia32 and x64 binaries.
arch: 'all'
arch: ['ia32', 'x64']
// Note: Application icon for Linux is specified via the BrowserWindow `icon` option.
}
@@ -442,51 +449,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.')
throw new Error('Fixing RELEASES-ia32 failed. Replacement did not modify the file.')
}
console.log('Windows: Renamed ia32 installer files.')
}
console.log('Windows: Renamed ia32 installer files.')
}
cb(null)
})
.catch(cb)
cb(null)
})
.catch(cb)
}
function packagePortable (filesPath, destArch, cb) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
#!/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.17.1",
"version": "0.20.0",
"author": {
"name": "WebTorrent, LLC",
"email": "feross@webtorrent.io",
"url": "https://webtorrent.io"
},
"bugs": {
"url": "https://github.com/feross/webtorrent-desktop/issues"
"url": "https://github.com/webtorrent/webtorrent-desktop/issues"
},
"dependencies": {
"airplayer": "^2.0.0",
@@ -17,6 +17,7 @@
"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",
@@ -24,21 +25,20 @@
"deep-equal": "^1.0.1",
"dlnacasts": "^0.1.0",
"drag-drop": "^2.12.1",
"es6-error": "^3.0.1",
"es6-error": "^4.0.0",
"fn-getter": "^1.0.0",
"iso-639-1": "^1.2.1",
"languagedetect": "^1.1.1",
"location-history": "^1.0.0",
"lodash.merge": "^4.6.0",
"material-ui": "^0.15.4",
"material-ui": "^0.17.0",
"mkdirp": "^0.5.1",
"musicmetadata": "^2.0.2",
"music-metadata": "^0.9.15",
"network-address": "^1.1.0",
"parse-torrent": "^5.7.3",
"prettier-bytes": "^1.0.1",
"react": "^15.2.1",
"react-dom": "^15.2.1",
"react-tap-event-plugin": "^1.0.0",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-tap-event-plugin": "^2.0.1",
"rimraf": "^2.5.2",
"run-parallel": "^1.1.6",
"semver": "^5.1.0",
@@ -51,16 +51,16 @@
"zero-fill": "^2.2.3"
},
"devDependencies": {
"buble": "^0.14.0",
"buble": "^0.19.3",
"cross-zip": "^2.0.1",
"depcheck": "^0.6.4",
"electron": "1.4.2",
"electron-osx-sign": "^0.3.0",
"electron-packager": "^8.0.0",
"electron-winstaller": "^2.3.0",
"electron": "1.6.16",
"electron-osx-sign": "0.4.3",
"electron-packager": "~8.5.1",
"electron-winstaller": "~2.5.2",
"gh-release": "^2.0.3",
"minimist": "^1.2.0",
"nobin-debian-installer": "^0.0.10",
"nobin-debian-installer": "0.0.10",
"nodemon": "^1.10.2",
"open": "0.0.5",
"plist": "^2.0.1",
@@ -90,10 +90,11 @@
"optionalDependencies": {
"appdmg": "^0.4.3"
},
"private": true,
"productName": "WebTorrent",
"repository": {
"type": "git",
"url": "git://github.com/feross/webtorrent-desktop.git"
"url": "git://github.com/webtorrent/webtorrent-desktop.git"
},
"scripts": {
"build": "buble src --output build",
@@ -103,9 +104,9 @@
"package": "node ./bin/package.js",
"prepublish": "npm run build",
"start": "npm run build && electron .",
"test": "standard && depcheck --ignores=buble,lodash.merge,nodemon,gh-release --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"test": "standard && depcheck --ignores=buble,nodemon,gh-release --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,pug,css --ignore build/ --ignore dist/"
"watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/"
}
}

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-2016 ' + APP_TEAM,
APP_COPYRIGHT: 'Copyright © 2014-2018 ' + APP_TEAM,
APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'),
APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'),
APP_NAME: APP_NAME,
@@ -60,7 +60,7 @@ module.exports = {
},
{
testID: 'wired',
name: 'The WIRED CD - Rip. Sample. Mash. Share.',
name: 'The WIRED CD - Rip. Sample. Mash. Share',
posterFileName: 'wiredCd.jpg',
torrentFileName: 'wiredCd.torrent'
}
@@ -70,9 +70,9 @@ module.exports = {
DEFAULT_DOWNLOAD_PATH: getDefaultDownloadPath(),
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',
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',
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((err) => cb(!err))
process.nextTick(() => cb(true))
if (playerPath == null) return vlcCommand(cb)
process.nextTick(() => cb(null))
}
function spawn (playerPath, url, title) {
@@ -29,7 +29,6 @@ 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

@@ -0,0 +1,50 @@
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,13 +72,16 @@ function init () {
if (err) throw err
isReady = true
const state = results.state
windows.main.init(results.state, {hidden: hidden})
windows.main.init(state, {hidden: hidden})
windows.webtorrent.init()
menu.init()
// To keep app startup fast, some code is delayed.
setTimeout(delayedInit, config.DELAYED_INIT)
setTimeout(() => {
delayedInit(state)
}, config.DELAYED_INIT)
// Report uncaught exceptions
process.on('uncaughtException', (err) => {
@@ -121,17 +124,24 @@ function init () {
})
}
function delayedInit () {
function delayedInit (state) {
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()
@@ -178,7 +188,7 @@ function onAppOpen (newArgv) {
function sliceArgv (argv) {
return argv.slice(config.IS_PRODUCTION ? 1
: config.IS_TEST ? 4
: 2)
: 2)
}
function processArgv (argv) {
@@ -198,7 +208,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/feross/webtorrent-desktop/issues/214
// Issue: https://github.com/webtorrent/webtorrent-desktop/issues/214
} else if (arg.startsWith('--')) {
// Ignore Spectron flags
} else if (arg === 'data:,') {

View File

@@ -1,5 +1,6 @@
module.exports = {
init
init,
setModule
}
const electron = require('electron')
@@ -13,6 +14,14 @@ 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
@@ -58,7 +67,7 @@ function init () {
})
/**
* Events
* Player Events
*/
ipc.on('onPlayerOpen', function () {
@@ -106,6 +115,28 @@ 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
*/
@@ -166,8 +197,8 @@ function init () {
ipc.on('checkForExternalPlayer', function (e, path) {
const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, function (isInstalled) {
windows.main.send('checkForExternalPlayer', isInstalled)
externalPlayer.checkInstall(path, function (err) {
windows.main.send('checkForExternalPlayer', !err)
})
})

View File

@@ -278,6 +278,19 @@ function getMenuTemplate () {
}
]
},
{
label: 'Transfers',
submenu: [
{
label: 'Pause All',
click: () => windows.main.dispatch('pauseAllTorrents')
},
{
label: 'Resume All',
click: () => windows.main.dispatch('resumeAllTorrents')
}
]
},
{
label: 'Help',
role: 'help',
@@ -311,7 +324,7 @@ function getMenuTemplate () {
]
if (process.platform === 'darwin') {
// Add WebTorrent app menu (Mac)
// WebTorrent menu (Mac)
template.unshift({
label: config.APP_NAME,
submenu: [
@@ -354,8 +367,26 @@ function getMenuTemplate () {
]
})
// Add Window menu (Mac)
template.splice(5, 0, {
// Edit menu (Mac)
template[2].submenu.push(
{
type: 'separator'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
}
)
// Window menu (Mac)
template.splice(6, 0, {
role: 'window',
submenu: [
{
@@ -395,7 +426,7 @@ function getMenuTemplate () {
})
// Help menu (Windows, Linux)
template[4].submenu.push(
template[5].submenu.push(
{
type: 'separator'
},

View File

@@ -36,8 +36,8 @@ function setWindowFocus (flag) {
}
function initLinux () {
checkLinuxTraySupport(function (supportsTray) {
if (supportsTray) createTray()
checkLinuxTraySupport(function (err) {
if (!err) createTray()
})
}
@@ -55,10 +55,14 @@ 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(false)
if (err) return cb(err)
// Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed:
cb(stdout.endsWith('\tinstall\n'))
if (stdout.endsWith('\tinstall\n')) {
cb(null)
} else {
cb(new Error('debian package not installed'))
}
})
}

View File

@@ -15,6 +15,7 @@ const main = module.exports = {
}
const electron = require('electron')
const debounce = require('debounce')
const app = electron.app
@@ -31,16 +32,17 @@ function init (state, options) {
const win = main.win = new electron.BrowserWindow({
backgroundColor: '#282828',
backgroundThrottling: false, // do not throttle animations/timers when page is background
darkTheme: true, // Forces dark theme (GTK+3)
height: initialBounds.height,
icon: getIconPath(), // Window icon (Windows, Linux)
minWidth: config.WINDOW_MIN_WIDTH,
minHeight: config.WINDOW_MIN_HEIGHT,
minWidth: config.WINDOW_MIN_WIDTH,
show: false,
title: config.APP_WINDOW_TITLE,
titleBarStyle: 'hidden-inset', // Hide title bar (Mac)
useContentSize: true, // Specify web page size without OS chrome
show: false,
width: initialBounds.width,
height: initialBounds.height,
x: initialBounds.x,
y: initialBounds.y
})
@@ -83,13 +85,13 @@ function init (state, options) {
win.setMenuBarVisibility(true)
})
win.on('move', function (e) {
win.on('move', debounce(function (e) {
send('windowBoundsChanged', e.sender.getBounds())
})
}, 1000))
win.on('resize', function (e) {
win.on('resize', debounce(function (e) {
send('windowBoundsChanged', e.sender.getBounds())
})
}, 1000))
win.on('close', function (e) {
if (process.platform !== 'darwin') {
@@ -164,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

@@ -13,6 +13,7 @@ const config = require('../../config')
function init () {
const win = webtorrent.win = new electron.BrowserWindow({
backgroundColor: '#1E1E1E',
backgroundThrottling: false, // do not throttle animations/timers when page is background
center: true,
fullscreen: false,
fullscreenable: false,

View File

@@ -17,7 +17,8 @@ module.exports = class ModalOKCancel extends React.Component {
className='control ok'
primary
label={okText}
onClick={onOK} />
onClick={onOK}
autoFocus />
</div>
)
}

View File

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

View File

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

@@ -1,7 +1,5 @@
const electron = require('electron')
const ipcRenderer = electron.ipcRenderer
const {ipcRenderer} = require('electron')
const telemetry = require('../lib/telemetry')
const Playlist = require('../lib/playlist')
// Controls local play back: the <video>/<audio> tag and VLC
@@ -12,7 +10,7 @@ module.exports = class MediaController {
}
mediaSuccess () {
this.state.playing.result = 'success'
telemetry.logPlayAttempt('success')
}
mediaStalled () {
@@ -22,7 +20,7 @@ module.exports = class MediaController {
mediaError (error) {
const state = this.state
if (state.location.url() === 'player') {
state.playing.result = 'error'
telemetry.logPlayAttempt('error')
state.playing.location = 'error'
ipcRenderer.send('checkForExternalPlayer', state.saved.prefs.externalPlayerPath)
ipcRenderer.once('checkForExternalPlayer', function (e, isInstalled) {
@@ -58,7 +56,10 @@ module.exports = class MediaController {
const state = this.state
state.playing.location = 'external'
let open = function () {
const onServerRunning = function () {
state.playing.isReady = true
telemetry.logPlayAttempt('external')
const mediaURL = Playlist.getCurrentLocalURL(state)
ipcRenderer.send('openExternalPlayer',
state.saved.prefs.externalPlayerPath,
@@ -66,8 +67,8 @@ module.exports = class MediaController {
state.window.title)
}
if (state.server != null) open()
else ipcRenderer.once('wt-server-running', open)
if (state.server != null) onServerRunning()
else ipcRenderer.once('wt-server-running', onServerRunning)
}
externalPlayerNotFound () {

View File

@@ -27,6 +27,8 @@ 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) => {
@@ -84,15 +86,26 @@ 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()
})
}
}
@@ -211,6 +224,7 @@ module.exports = class PlaybackController {
const torrentSummary = TorrentSummary.getByKey(state, infoHash)
state.playing.infoHash = torrentSummary.infoHash
state.playing.isReady = false
// update UI to show pending playback
sound.play('PLAY')
@@ -222,6 +236,8 @@ module.exports = class PlaybackController {
// Starts WebTorrent server for media streaming
startServer (torrentSummary) {
const state = this.state
if (torrentSummary.status === 'paused') {
dispatch('startTorrentingSummary', torrentSummary.torrentKey)
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash,
@@ -232,6 +248,7 @@ module.exports = class PlaybackController {
function onTorrentReady () {
ipcRenderer.send('wt-start-server', torrentSummary.infoHash)
ipcRenderer.once('wt-server-running', () => { state.playing.isReady = true })
}
}
@@ -254,7 +271,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
@@ -268,8 +285,16 @@ module.exports = class PlaybackController {
state.playing.jumpToTime = jumpToTime
// if it's audio, parse out the metadata (artist, title, etc)
if (state.playing.type === 'audio' && !fileSummary.audioInfo) {
ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index)
if (torrentSummary.status === 'paused') {
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash, getAudioMetadata)
} else {
getAudioMetadata()
}
function getAudioMetadata () {
if (state.playing.type === 'audio' && !fileSummary.audioInfo) {
ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index)
}
}
// if it's video, check for subtitles files that are done downloading
@@ -312,12 +337,7 @@ module.exports = class PlaybackController {
// Save volume (this session only, not in state.saved)
state.previousVolume = state.playing.volume
// Telemetry: track what happens after the user clicks play
const result = state.playing.result // 'success' or 'error'
if (result === 'success') telemetry.logPlayAttempt('success') // first frame displayed
else if (result === 'error') telemetry.logPlayAttempt('error') // codec missing, etc
else if (result === undefined) telemetry.logPlayAttempt('abandoned') // user gave up waiting
else console.error('Unknown state.playing.result', state.playing.result)
if (!state.playing.isReady) telemetry.logPlayAttempt('abandoned') // user gave up waiting
// Reset the window contents back to the home screen
state.playing = State.getDefaultPlayState()
@@ -334,6 +354,11 @@ 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,11 +121,66 @@ module.exports = class TorrentListController {
torrentSummary.status = 'new'
this.startTorrentingSummary(torrentSummary.torrentKey)
sound.play('ENABLE')
} else {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
sound.play('DISABLE')
return
}
this.pauseTorrent(torrentSummary, true)
}
pauseAllTorrents () {
this.state.saved.torrents.forEach((torrentSummary) => {
if (torrentSummary.status === 'downloading' ||
torrentSummary.status === 'seeding') {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
}
})
sound.play('DISABLE')
}
resumeAllTorrents () {
this.state.saved.torrents.forEach((torrentSummary) => {
if (torrentSummary.status === 'paused') {
torrentSummary.status = 'downloading'
this.startTorrentingSummary(torrentSummary.torrentKey)
}
})
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) {
@@ -220,7 +275,8 @@ module.exports = class TorrentListController {
menu.append(new electron.remote.MenuItem({
label: 'Save Torrent File As...',
click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey)
click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey),
enabled: torrentSummary.torrentFileName != null
}))
menu.popup(electron.remote.getCurrentWindow())
@@ -259,7 +315,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 = []
@@ -268,7 +324,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)
}
})
})
@@ -282,7 +338,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
@@ -294,7 +350,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

@@ -388,7 +388,8 @@ function toggleMenu (location) {
if (state.playing.location !== 'local') {
throw new CastingError(
`You can't connect to ${location} when already connected to another device`
) }
)
}
// Find all cast devices of the given type
const player = getPlayer(location)

View File

@@ -5,6 +5,7 @@ module.exports = {
}
const fs = require('fs')
const path = require('path')
const semver = require('semver')
const config = require('../../config')
@@ -18,30 +19,15 @@ function run (state) {
}
const version = state.saved.version
const saved = state.saved
if (semver.lt(version, '0.7.0')) {
migrate_0_7_0(state.saved)
}
if (semver.lt(version, '0.7.2')) {
migrate_0_7_2(state.saved)
}
if (semver.lt(version, '0.11.0')) {
migrate_0_11_0(state.saved)
}
if (semver.lt(version, '0.12.0')) {
migrate_0_12_0(state.saved)
}
if (semver.lt(version, '0.14.0')) {
migrate_0_14_0(state.saved)
}
if (semver.lt(version, '0.17.0')) {
migrate_0_17_0(state.saved)
}
if (semver.lt(version, '0.7.0')) migrate_0_7_0(saved)
if (semver.lt(version, '0.7.2')) migrate_0_7_2(saved)
if (semver.lt(version, '0.11.0')) migrate_0_11_0(saved)
if (semver.lt(version, '0.12.0')) migrate_0_12_0(saved)
if (semver.lt(version, '0.14.0')) migrate_0_14_0(saved)
if (semver.lt(version, '0.17.0')) migrate_0_17_0(saved)
if (semver.lt(version, '0.17.2')) migrate_0_17_2(saved)
// Config is now on the new version
state.saved.version = config.APP_VERSION
@@ -92,7 +78,7 @@ function migrate_0_7_0 (saved) {
}
// Fix exception caused by incorrect file ordering.
// https://github.com/feross/webtorrent-desktop/pull/604#issuecomment-222805214
// https://github.com/webtorrent/webtorrent-desktop/pull/604#issuecomment-222805214
delete ts.defaultPlayFileIndex
delete ts.files
delete ts.selections
@@ -125,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/feross/webtorrent-desktop/pull/806
// See https://github.com/webtorrent/webtorrent-desktop/pull/806
const defaultTorrentFiles = [
'6a9759bffd5c0af65319979fb7832189f4f3c35d.torrent',
'88594aaacbde40ef3e2510c47374ec0aa396c08e.torrent',
@@ -163,3 +149,60 @@ 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
const cpFile = require('cp-file')
const rimraf = require('rimraf')
const OLD_NAME = 'The WIRED CD - Rip. Sample. Mash. Share.'
const NEW_NAME = 'The WIRED CD - Rip. Sample. Mash. Share'
const OLD_HASH = '3ba219a8634bf7bae3d848192b2da75ae995589d'
const NEW_HASH = 'a88fda5954e89178c372716a6a78b8180ed4dad3'
const ts = saved.torrents.find((ts) => {
return ts.infoHash === OLD_HASH
})
if (!ts) return // Wired CD torrent does not exist
// New versions of WebTorrent ship with a fixed torrent file. Let's fix up the
// name in existing versions of WebTorrent.
ts.name = ts.displayName = NEW_NAME
ts.files.forEach((file) => {
file.path = file.path.replace(OLD_NAME, NEW_NAME)
})
// Changing the torrent name causes the info hash to change
ts.infoHash = NEW_HASH
ts.magnetURI = ts.magnetURI.replace(OLD_HASH, NEW_HASH)
try {
fs.renameSync(
path.join(config.POSTER_PATH, ts.posterFileName),
path.join(config.POSTER_PATH, NEW_HASH + '.jpg')
)
} catch (err) {}
ts.posterFileName = NEW_HASH + '.jpg'
rimraf.sync(path.join(config.TORRENT_PATH, ts.torrentFileName))
cpFile.sync(
path.join(config.STATIC_PATH, 'wiredCd.torrent'),
path.join(config.TORRENT_PATH, NEW_HASH + '.torrent')
)
ts.torrentFileName = NEW_HASH + '.torrent'
if (ts.path) {
// If torrent folder already exists on disk, try to rename it
try {
fs.renameSync(
path.join(ts.path, OLD_NAME),
path.join(ts.path, NEW_NAME)
)
} catch (err) {}
}
}

View File

@@ -92,6 +92,7 @@ function getDefaultPlayState () {
type: null, /* 'audio' or 'video', could be 'other' if ever support eg streaming to VLC */
currentTime: 0, /* seconds */
duration: 1, /* seconds */
isReady: false,
isPaused: true,
isStalled: false,
lastTimeUpdate: 0, /* Unix time in ms */
@@ -120,9 +121,13 @@ function setupStateSaved (cb) {
isFileHandler: false,
openExternalPlayer: false,
externalPlayerPath: null,
startup: false
startup: false,
autoAddTorrents: false,
torrentsFolderPath: '',
highestPlaybackPriority: true
},
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
torrentsToResume: [],
version: config.APP_VERSION /* make sure we can upgrade gracefully later */
}

View File

@@ -66,8 +66,8 @@ function reset () {
minVersion: config.APP_VERSION,
total: 0,
success: 0,
timeout: 0,
error: 0,
external: 0,
abandoned: 0
}
}
@@ -210,10 +210,10 @@ function getElemString (elem) {
return ret
}
// The user pressed play. It either worked, timed out, or showed the
// 'Play in VLC' codec error
// The user pressed play. Did it work, display an error,
// open an external player or did user abandon the attempt?
function logPlayAttempt (result) {
if (!['success', 'timeout', 'error', 'abandoned'].includes(result)) {
if (!['success', 'error', 'external', 'abandoned'].includes(result)) {
return console.error('Unknown play attempt result', result)
}

View File

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

View File

@@ -3,39 +3,145 @@ 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 torrentPosterFromImage(posterFile, torrent, cb)
if (posterFile) return extractPoster(posterFile, cb)
// 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)
// '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]
// Third, try to use the largest image file
const imgFile = getLargestFileByExtension(torrent, ['.gif', '.jpg', '.jpeg', '.png'])
if (imgFile) return torrentPosterFromImage(imgFile, 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'))
}
// 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'))
// 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)
}
}
/**
* 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 = torrent.files.filter(function (file) {
const extname = path.extname(file.name).toLowerCase()
return extensions.indexOf(extname) !== -1
})
const files = filterOnExtension(torrent, extensions)
if (files.length === 0) return undefined
return files.reduce(function (a, b) {
return files.reduce((a, b) => {
return a.length > b.length ? a : b
})
}
function torrentPosterFromVideo (file, torrent, cb) {
/**
* 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)
const index = torrent.files.indexOf(file)
const server = torrent.createServer(0)
@@ -77,7 +183,12 @@ function torrentPosterFromVideo (file, torrent, cb) {
}
}
function torrentPosterFromImage (file, torrent, cb) {
const extname = path.extname(file.name)
file.getBuffer((err, buf) => cb(err, buf, extname))
function torrentPosterFromImage (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.image)
extractPoster(file, cb)
}
function extractPoster (file, cb) {
const extname = path.extname(file.name)
file.getBuffer((err, buf) => { return cb(err, buf, extname) })
}

View File

@@ -1,10 +1,6 @@
/**
* Perf optimization: Hook into require() to modify how certain modules load:
*
* - `lodash/merge` (used by `material-ui`) causes 119 require() calls at startup,
* which take ~100ms. Replace it with `lodash.merge` which is equivalent.
* See: https://github.com/callemall/material-ui/pull/4380#issuecomment-250894552
*
* - `inline-style-prefixer` (used by `material-ui`) takes ~40ms. It is not
* actually used because auto-prefixing is disabled with
* `darkBaseTheme.userAgent = false`. Return a fake object.
@@ -12,7 +8,6 @@
let Module = require('module')
const _require = Module.prototype.require
Module.prototype.require = function (id) {
if (id === 'lodash/merge') id = 'lodash.merge'
if (id === 'inline-style-prefixer') return {}
return _require.apply(this, arguments)
}
@@ -29,6 +24,7 @@ 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')
@@ -115,6 +111,10 @@ 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,6 +150,18 @@ 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)
@@ -230,6 +242,8 @@ const dispatchHandlers = {
'showCreateTorrent': (paths) => controllers.torrentList().showCreateTorrent(paths),
'createTorrent': (options) => controllers.torrentList().createTorrent(options),
'toggleTorrent': (infoHash) => controllers.torrentList().toggleTorrent(infoHash),
'pauseAllTorrents': () => controllers.torrentList().pauseAllTorrents(),
'resumeAllTorrents': () => controllers.torrentList().resumeAllTorrents(),
'toggleTorrentFile': (infoHash, index) =>
controllers.torrentList().toggleTorrentFile(infoHash, index),
'confirmDeleteTorrent': (infoHash, deleteData) =>
@@ -244,6 +258,8 @@ 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),
@@ -284,6 +300,8 @@ 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),
@@ -344,6 +362,7 @@ 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))
@@ -440,6 +459,13 @@ 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)
@@ -475,8 +501,10 @@ function onError (err) {
update()
}
const editableHtmlTags = new Set(['input', 'textarea'])
function onPaste (e) {
if (e.target.tagName.toLowerCase() === 'input') return
if (editableHtmlTags.has(e.target.tagName.toLowerCase())) return
controllers.torrentList().addTorrent(electron.clipboard.readText())
update()

View File

@@ -160,6 +160,7 @@ 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')
}
}
@@ -206,25 +207,18 @@ function renderOverlay (state) {
function renderAudioMetadata (state) {
const fileSummary = state.getPlayingFileSummary()
if (!fileSummary.audioInfo) return
const info = fileSummary.audioInfo
const common = fileSummary.audioInfo.common || {}
// Get audio track info
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
}
const title = common.title ? common.title : fileSummary.name
// 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'>
@@ -232,14 +226,44 @@ function renderAudioMetadata (state) {
</div>
))
}
if (album) {
// Audio metadata: album
if (common.album) {
elems.push((
<div key='album' className='audio-album'>
<label>Album</label>{album}
<label>Album</label>{common.album}
</div>
))
}
if (track) {
// 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
elems.push((
<div key='track' className='audio-track'>
<label>Track</label>{track}
@@ -247,6 +271,38 @@ 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((
@@ -275,9 +331,9 @@ function renderLoadingSpinner (state) {
<div key='loading' className='media-stalled'>
<div key='loading-spinner' className='loading-spinner'>&nbsp;</div>
<div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded,
<span> {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> {prettyBytes(prog.uploadSpeed || 0)}/s</span>
<span className='progress'>{fileProgress}%</span> downloaded
<span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> ↑ {prettyBytes(prog.uploadSpeed || 0)}/s</span>
</div>
</div>
)
@@ -303,7 +359,7 @@ function renderCastScreen (state) {
isCast = false
} else if (state.playing.location === 'error') {
castIcon = 'error_outline'
castType = 'Error'
castType = 'Unable to Play'
isCast = false
}
@@ -497,9 +553,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), ' +
@@ -523,8 +579,8 @@ function renderPlayerControls (state) {
))
// Show video playback progress
const currentTimeStr = formatTime(state.playing.currentTime)
const durationStr = formatTime(state.playing.duration)
const currentTimeStr = formatTime(state.playing.currentTime, state.playing.duration)
const durationStr = formatTime(state.playing.duration, state.playing.duration)
elements.push((
<span key='time' className='time float-left'>
{currentTimeStr} / {durationStr}
@@ -599,12 +655,15 @@ function renderLoadingBar (state) {
const torrentSummary = state.getPlayingTorrentSummary()
if (!torrentSummary.progress) {
return []
return null
}
// Find all contiguous parts of the torrent which are loaded
const prog = torrentSummary.progress
const fileProg = prog.files[state.playing.fileIndex]
if (!fileProg) return null
const parts = []
let lastPiecePresent = false
for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) {
@@ -626,6 +685,7 @@ function renderLoadingBar (state) {
return (<div key={i} className='loading-bar-part' style={style} />)
})
return (<div key='loading-bar' className='loading-bar'>{loadingBarElems}</div>)
}
@@ -642,17 +702,19 @@ function cssBackgroundImageDarkGradient () {
'rgba(0,0,0,0.4) 0%, rgba(0,0,0,1) 100%)'
}
function formatTime (time) {
function formatTime (time, total) {
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 (hours > 0) {
if (totalMinutes > 9) {
minutes = zeroFill(2, minutes)
}
let seconds = zeroFill(2, Math.floor(time % 60))
return (hours > 0 ? hours + ':' : '') + minutes + ':' + seconds
return (totalHours > 0 ? hours + ':' : '') + minutes + ':' + seconds
}

View File

@@ -62,6 +62,24 @@ 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()
@@ -90,6 +108,59 @@ 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) {
@@ -145,12 +216,15 @@ class PreferencesPage extends React.Component {
}
return (
<div style={style}>
<PreferencesSection title='Downloads'>
<PreferencesSection title='Folders'>
{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>{status}</span>)
return (<span key='torrent-status'>{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 musicmetadata = require('musicmetadata')
const mm = require('music-metadata')
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')
@@ -125,11 +125,10 @@ function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections)
// Only download the files the user wants, not necessarily all files
torrent.once('ready', () => selectFiles(torrent, selections))
return torrent
}
function stopTorrenting (infoHash) {
console.log('--- STOP TORRENTING: ', infoHash)
const torrent = client.get(infoHash)
if (torrent) torrent.destroy()
}
@@ -335,16 +334,30 @@ function stopServer () {
server = null
}
console.log('Initializing...')
function getAudioMetadata (infoHash, index) {
const torrent = client.get(infoHash)
const file = torrent.files[index]
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)
})
// 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)
})
}
function selectFiles (torrentOrInfoHash, selections) {

View File

@@ -615,6 +615,7 @@ 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 {
@@ -819,12 +820,17 @@ video::-webkit-media-text-track-container {
.audio-metadata label {
display:inline-block;
width: 100px;
width: 120px;
text-align: right;
font-weight: normal;
margin-right: 25px;
}
.audio-metadata .audio-format,
.audio-metadata .audio-comments {
font-weight: normal;
}
/*
* ERRORS
*/

Binary file not shown.

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: 1.0 MiB

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: 1010 KiB

After

Width:  |  Height:  |  Size: 1011 KiB

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: 139 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 698 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 478 KiB

After

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 480 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: 481 KiB

After

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 94 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: 843 KiB

After

Width:  |  Height:  |  Size: 844 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

After

Width:  |  Height:  |  Size: 1014 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 698 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1024 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 KiB

After

Width:  |  Height:  |  Size: 1014 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: 1015 KiB

After

Width:  |  Height:  |  Size: 1016 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

After

Width:  |  Height:  |  Size: 725 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 239 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) {
@@ -38,11 +38,12 @@ test('create-torrent', function (t) {
const expectedTorrent = {
announce: [
'udp://exodus.desync.com:6969',
'udp://explodie.org:6969',
'udp://tracker.coppersurfer.tk:6969',
'udp://tracker.internetwarriors.net: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'
@@ -83,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

@@ -0,0 +1,25 @@
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/feross/webtorrent-desktop/issues/719
// This is a bug. See https://github.com/webtorrent/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'))
})