Compare commits

...

451 Commits

Author SHA1 Message Date
Borewit
a445c18979 Show error details in case it is not a 'NotSupportedError' error. 2019-10-07 19:03:14 +02:00
hicom150
5280fc76e9 Merge branch 'master' into fix_codec_unsupported_detection 2019-10-07 17:54:41 +02:00
hicom150
d4a8d9a120 Apply review changes 2019-10-07 17:54:36 +02:00
Feross Aboukhadijeh
3fbf33b8ae remove borewit from funding.yml (per his request) 2019-10-06 14:52:23 -07:00
Feross Aboukhadijeh
2c5fcab469 Merge pull request #1717 from pR0Ps/feature/verification-status
Report when files are being verified
2019-10-06 11:04:35 -07:00
Carey Metcalfe
8fc7150559 Report when files are being verified
When starting a torrent that is partially-downloaded, WebTorrent will
now report "Verifying" as it verifies the existing data on disk.

Fixes #1586
2019-10-03 02:22:12 -04:00
Feross Aboukhadijeh
89b53ffe96 Merge pull request #1716 from pR0Ps/master
Floor individual file percentages for display
2019-10-01 06:49:42 -07:00
Feross Aboukhadijeh
e990bc898d mono is needed to create setup.exe (#1715)
mono is needed to create setup.exe
2019-09-30 21:49:09 -07:00
Feross Aboukhadijeh
cb699b5fec readme: clarify windows homebrew instructions 2019-09-30 21:48:56 -07:00
Lucas
8a235c31e0 mono is needed to create setup.exe 2019-10-01 11:05:53 +13:00
Carey Metcalfe
8ee27b8ef4 Floor individual file percentages for display
Fixes #1713
2019-09-26 20:49:17 -04:00
hicom150
5023651e63 Improve codec unsupported detection 2019-09-25 22:24:56 +02:00
Feross Aboukhadijeh
b9bcf747de Set default externalPlayerPath (#1702)
Set default externalPlayerPath
2019-09-19 12:58:42 -07:00
Feross Aboukhadijeh
88e7167b6c Add rpm package creation support (#1694)
Add rpm package creation support
2019-09-19 12:56:14 -07:00
Pieter Goetschalckx
f5ab39be8e Migrate config file 2019-09-19 15:22:49 +02:00
Julen Garcia Leunda
6e69e5d36e Set correct architecture for RedHat based packages 2019-09-18 20:44:14 +02:00
Feross Aboukhadijeh
9c4ca25615 Handle null paths in path selector (#1704)
Handle null paths in path selector
2019-09-18 11:22:33 -07:00
Mathias Rasmussen
d15f40b22d handle null paths 2019-09-18 19:41:26 +02:00
Pieter Goetschalckx
1befac8d0e Set default externalPlayerPath 2019-09-18 13:34:39 +02:00
Julen Garcia Leunda
cfa1ebbea7 Merge branch 'master' into add_package_rpm 2019-09-16 22:13:41 +02:00
Feross Aboukhadijeh
2a679dd6e8 Add linux arm64 build (Fixes for PR #1650) (#1696)
Add linux arm64 build (Fixes for PR #1650)
2019-09-15 14:50:14 -07:00
Feross Aboukhadijeh
fce5f00925 Add buble compiler target chrome version (#1695)
Add buble compiler target chrome version
2019-09-15 12:36:14 -07:00
Feross Aboukhadijeh
39d04c4b2a Add linux arm64 build (Fixes for PR #1650) 2019-09-15 12:30:30 -07:00
Feross Aboukhadijeh
5688e4538c introduce linux arm64 support (#1650)
introduce linux arm64 support
2019-09-15 12:19:15 -07:00
Julen Garcia Leunda
4e18d4091a Add rpm package creation documentation 2019-09-15 20:30:41 +02:00
Julen Garcia Leunda
cfde3ee85d Add buble compiler target chrome version 2019-09-15 19:34:16 +02:00
Julen Garcia Leunda
1c343cb4c1 Add rpm package creation support 2019-09-15 19:16:03 +02:00
Дамјан Георгиевски
e3e490daa5 Merge branch 'master' into master 2019-09-15 18:25:43 +02:00
Feross Aboukhadijeh
d7783749ca Update test-add-torrent.js 2019-09-14 17:06:28 -07:00
Feross Aboukhadijeh
b07fc8285f 0.21.0 2019-09-14 16:44:47 -07:00
Feross Aboukhadijeh
6379356612 authors 2019-09-14 16:44:38 -07:00
Feross Aboukhadijeh
ee3768faf8 Merge branch 'fix-windows' 2019-09-14 16:42:56 -07:00
Feross Aboukhadijeh
8ff65412a3 readme: screenshots: 2018 MacBook Pro 13" 2019-09-14 16:42:40 -07:00
Feross Aboukhadijeh
cb930bef0b test: Update screenshots 2019-09-14 16:42:01 -07:00
Feross Aboukhadijeh
6a76554563 Fix "EPERM: operation not permitted, open 'C:\Users\account\App… (#1693)
Fix "EPERM: operation not permitted, open 'C:\Users\account\AppData\Local
2019-09-14 16:35:12 -07:00
Feross Aboukhadijeh
e72005e239 Remove cp-file, use copyFileSync instead 2019-09-14 16:32:38 -07:00
Feross Aboukhadijeh
54b1832007 remove prepublish script
We don't publish the desktop app to npm anymore
2019-09-14 14:36:52 -07:00
Feross Aboukhadijeh
e714e00223 Fix Windows npm install
Like `appdmg`, `electron-installer-debian` cannot be installed on Windows.
2019-09-14 14:35:05 -07:00
Feross Aboukhadijeh
6989484e7c Add note about homebrew deps for debian build
Fixes: https://github.com/webtorrent/webtorrent-desktop/issues/1689
2019-09-14 14:08:01 -07:00
Feross Aboukhadijeh
53164a058d bump deps 2019-09-12 11:14:33 -07:00
Borewit
f84078c865 Merge pull request #1580 from webtorrent/ignore-intellij-project-files
Ignore JetBrains IntelliJ IDEA project files
2019-09-12 08:30:12 +02:00
Feross Aboukhadijeh
7eda218e82 Store magnet URI once torrent has been parsed (#1686)
Store magnet URI once torrent has been parsed
2019-09-11 19:44:06 -07:00
Feross Aboukhadijeh
54b0c23f11 Replace nobin-debian-installer (#1682)
Replace `nobin-debian-installer`
2019-09-10 14:12:52 -07:00
Feross Aboukhadijeh
b34417b1fc add back removed log statement 2019-09-10 14:12:42 -07:00
Julen Garcia Leunda
40005351b5 Merge branch 'master' into replace_nobin_debian_installer 2019-09-10 23:05:39 +02:00
Julen Garcia Leunda
6255ccd253 Resolve requested changes 2019-09-10 22:54:34 +02:00
Feross Aboukhadijeh
4a10a2dfd1 Merge pull request #1679 from webtorrent/then
Use promise then() instead of catch() with callbacks
2019-09-10 11:05:12 -07:00
Feross Aboukhadijeh
992ab0bf10 electron-osx-sign@0.4.13 2019-09-10 10:33:53 -07:00
Feross Aboukhadijeh
83a2d090ff bump deps 2019-09-10 10:33:09 -07:00
Feross Aboukhadijeh
8133ce7b92 create-torrent@4.4.1 2019-09-10 10:31:32 -07:00
Feross Aboukhadijeh
6a07f705f7 Update package-lock.json 2019-09-10 10:31:29 -07:00
Feross Aboukhadijeh
9455912d98 Update airplayer dependency (#1680)
Update airplayer dependency
2019-09-10 10:27:28 -07:00
Feross Aboukhadijeh
052e0a4a31 Added 'Release notes' and 'follow on twitter' options in help (#1683)
Added 'Release notes' and 'follow on twitter' options in help
2019-09-10 09:59:22 -07:00
Chandan Chowdary Bhagam
b8b1ddc138 Added 'Release notes' and 'follow on twitter' options in help 2019-09-10 09:17:28 +00:00
Feross Aboukhadijeh
a92fee3532 Auto populate 'Magnet Link' field with the address from clipboa… (#1681)
Auto populate 'Magnet Link' field with the address from clipboard
2019-09-09 18:46:29 -07:00
Julen Garcia Leunda
aff402f6e0 Replace nobin-debian-installer 2019-09-09 22:52:26 +02:00
Daniele Debernardi
a39691fdad Auto populate 'Magnet Link' field with the address from clipboard
Co-authored-by: Jon Koops <jonkoops@gmail.com>
2019-09-09 21:41:07 +02:00
hicom150
7eecf709f9 Update airplayer dependency 2019-09-08 11:51:11 +02:00
Feross Aboukhadijeh
dd13de5f65 Use promise then() instead of catch() with callbacks
Addresses my review here: https://github.com/webtorrent/webtorrent-desktop/pull/1419#pullrequestreview-285182318
2019-09-07 11:13:26 -07:00
Feross Aboukhadijeh
647eaf5af9 #1667 rename dependency opn to open (#1677)
#1667 rename dependency opn to open
2019-09-06 12:48:36 -07:00
Borewit
24394a9028 #1667 rename dependency opn to open 2019-09-06 20:46:29 +02:00
Feross Aboukhadijeh
e8835c3054 package: Remove npm dedupe
We don't want to modify the package-lock.json file at packaging time.
2019-09-06 00:12:08 -07:00
Feross Aboukhadijeh
2834cca9fa package: use npm ci, remove npm dedupe
`npm ci` does not modify the package-lock.json file, which is exactly what we want during packaging time. Otherwise, on Windows, npm will attempt to remove the mac-only dependencies like appdmg from package-lock.json.
2019-09-06 00:11:52 -07:00
Feross Aboukhadijeh
744e720c3b Ignore play() when called before init()
If an error occurs during startup, then it's possible that we'll try to call sound.play('ERROR') in the error handler, which will throw an exception. Instead, just don't play sounds in this case.
2019-09-05 23:59:32 -07:00
Feross Aboukhadijeh
a6dfd98717 Depend on webtorrent/nobin-debian-installer fork
Fixes: https://github.com/grunjol/nobin-debian-installer/pull/8
Fixes: https://github.com/grunjol/nobin-debian-installer/pull/7
2019-09-05 23:13:41 -07:00
Feross Aboukhadijeh
4550f09bf1 authors 2019-09-05 22:18:00 -07:00
Feross Aboukhadijeh
7289e32bea Update package-lock.json 2019-09-05 22:18:00 -07:00
Feross Aboukhadijeh
0bdbe66d96 Change electron-packager from callback to promises (#1673)
Change electron-packager from callback to promises
2019-09-05 22:17:42 -07:00
Feross Aboukhadijeh
e58e701e2c appdmg@0.6.0
Fixes: https://github.com/webtorrent/webtorrent-desktop/pull/1593
2019-09-05 22:10:05 -07:00
Feross Aboukhadijeh
ac9b79c8ca Merge pull request #1657 from hicom150/update_spectron
Fix integration test setup

Co-authored-by: null <23040076+greenkeeper[bot]@users.noreply.github.com>
Co-authored-by: Feross Aboukhadijeh <feross@feross.org>
2019-09-05 21:55:27 -07:00
Feross Aboukhadijeh
280fbc1e8d Merge branch 'master' into update_spectron 2019-09-05 21:54:05 -07:00
Feross Aboukhadijeh
a1d4f706f7 Change electron-packager from callback to promises (#1660)
Change electron-packager from callback to promises
2019-09-05 21:47:44 -07:00
Feross Aboukhadijeh
6e2ff6887d Merge pull request #1642 from webtorrent/remove-32
Stop shipping 32-bit binaries
2019-09-05 21:45:52 -07:00
Feross Aboukhadijeh
fe297d4c56 Fixes for PR #1654 2019-09-05 21:42:12 -07:00
Feross Aboukhadijeh
b16971491f Merge branch 'master' of https://github.com/jimmywarting/webtorrent-desktop into jimmywarting 2019-09-05 21:39:19 -07:00
Feross Aboukhadijeh
4b1866d521 electron-winstaller@4 2019-09-05 21:22:47 -07:00
Feross Aboukhadijeh
c539375adc update deps 2019-09-05 21:18:52 -07:00
Feross
ec8beda1ed Merge pull request #1666 from webtorrent/update-deps
Update dependencies (prep for release)
2019-09-05 12:18:55 -07:00
Feross Aboukhadijeh
cd96986445 package.json: bump deps to use latest version
This doesn't actually change package-lock.json in a meaningful way. But I want package.json to list the latest version.
2019-09-04 17:28:15 -07:00
Feross Aboukhadijeh
975e57d57b update castv2 dependency to fix npm audit warning
For: https://github.com/webtorrent/webtorrent-desktop/issues/1599
2019-09-04 16:58:08 -07:00
Feross Aboukhadijeh
08ad4f73f1 music-metadata@4.5.0 2019-09-04 16:54:16 -07:00
Feross Aboukhadijeh
c9f9c868df electron@6.0.7 2019-09-04 16:52:11 -07:00
Feross Aboukhadijeh
e7f8f4ccfc react@16.9.0, react-dom@16.9.0 2019-09-04 16:48:11 -07:00
Feross Aboukhadijeh
e1b0c85778 iso-639-1@2.1.0 2019-09-04 16:44:47 -07:00
Feross Aboukhadijeh
2148d98f3a electron-packager@14.0.5 2019-09-04 16:44:10 -07:00
Feross Aboukhadijeh
d06508fda6 create-torrent@4.3.0 2019-09-04 16:43:17 -07:00
Feross Aboukhadijeh
1f780fc307 npm dedupe 2019-09-04 16:42:52 -07:00
Feross Aboukhadijeh
0178e9dcb8 bitfield@3 2019-09-04 16:41:50 -07:00
Feross Aboukhadijeh
c170c247a9 cross-zip@2.1.6 2019-09-04 16:40:01 -07:00
Feross Aboukhadijeh
4bb3edbf8d deep-equal@1.1.0 2019-09-04 16:39:39 -07:00
Feross Aboukhadijeh
9dbae636ae nodemon@1.19.2 2019-09-04 16:36:46 -07:00
Feross Aboukhadijeh
b6234f7705 vlc-command@1.2.0 2019-09-04 16:36:06 -07:00
Feross Aboukhadijeh
101dd356ae webtorrent@>=0.107.6
To address minor xss vulnerability in http server. See: https://github.com/webtorrent/webtorrent/pull/1714
2019-09-04 16:35:43 -07:00
Feross Aboukhadijeh
f8e9db25cd funding: add borewit 2019-09-04 14:50:14 -07:00
Feross Aboukhadijeh
8e33507839 fixed by bumping babel-eslint 2019-08-28 22:49:36 -07:00
Feross Aboukhadijeh
4c4c3508c2 Merge pull request #1664 from webtorrent/greenkeeper/drag-drop-5.0.1
Greenkeeper/drag drop 5.0.1
2019-08-28 13:14:07 -07:00
greenkeeper[bot]
a7ec8ba04e chore(package): update lockfile package-lock.json 2019-08-28 20:04:57 +00:00
greenkeeper[bot]
b5dce351b0 fix(package): update drag-drop to version 5.0.1
Closes #1663
2019-08-28 20:04:53 +00:00
Julen Garcia Leunda
59fa19d7ad Change electron-packager from callback to promises 2019-08-20 23:22:11 +02:00
hicom150
619e5249ac Fix integration test setup 2019-08-19 16:27:21 +02:00
greenkeeper[bot]
48340010b1 chore(package): update lockfile package-lock.json 2019-08-19 16:27:21 +02:00
greenkeeper[bot]
4599752d9d chore(package): update spectron to version 8.0.0
Closes #1609
2019-08-19 16:27:21 +02:00
Feross Aboukhadijeh
5eec89cd42 Merge pull request #1658 from webtorrent/music-metadata-v4.3
Update dependency music-metadata to v4.3.0
2019-08-18 14:22:10 -07:00
Borewit
dcecab2d9f Update dependency music-metadata to v4.3.0; improve decoding MPEG-4-ADTS/AAC. 2019-08-18 22:09:02 +02:00
Feross Aboukhadijeh
767d31ebd8 Merge pull request #1498 from webtorrent/cleanup-preferences
Cleanup preferences page
2019-08-18 12:30:50 -07:00
Feross Aboukhadijeh
1ece5de579 add discord link 2019-08-18 10:16:20 -07:00
Mathias Rasmussen
f34a79df36 path-selector: display path instead of name 2019-08-17 22:23:05 +02:00
Mathias Rasmussen
f231c54886 clean up preferences-page 2019-08-17 22:23:05 +02:00
Jimmy Wärting
56f0826928 remove zero fill at the final place
supersede #1583
2019-08-16 22:27:37 +02:00
Feross Aboukhadijeh
caceeee238 Merge pull request #1583 from jimmywarting/patch-1
Removed zero-fill
2019-08-16 11:49:38 -07:00
Feross Aboukhadijeh
afd3b84b40 Fixes for PR #1648 2019-08-15 13:10:39 -07:00
Feross Aboukhadijeh
4b927832c2 Merge pull request #1648 from hicom150/pass_title_metadata
Pass title metadata in media url
2019-08-15 13:08:59 -07:00
Feross Aboukhadijeh
c5679d546e chokidar@3
Fixes: https://github.com/webtorrent/webtorrent-desktop/pull/1576
2019-08-15 13:00:40 -07:00
hicom150
097fa9cc7c Add file name to state 2019-08-15 12:06:22 +02:00
Feross Aboukhadijeh
6f1a94359f standard 2019-08-14 17:52:55 -07:00
Feross Aboukhadijeh
44e2f366ce standard 2019-08-14 17:09:07 -07:00
Feross Aboukhadijeh
cf6b46364c standard 2019-08-14 17:07:33 -07:00
Дамјан Георгиевски
b8f7880a78 introduce linux arm64 support 2019-08-15 01:50:47 +02:00
Feross Aboukhadijeh
85c6816c23 standard 2019-08-14 16:29:25 -07:00
Feross Aboukhadijeh
0c1d7d6e0f standard 2019-08-14 16:21:14 -07:00
Feross Aboukhadijeh
3f073e083e standard 2019-08-14 16:13:07 -07:00
Feross Aboukhadijeh
6ce7e662c6 Merge pull request #1649 from webtorrent/greenkeeper/rimraf-3.0.0
Update rimraf to the latest version 🚀
2019-08-14 13:13:12 -07:00
greenkeeper[bot]
1e6c067139 chore(package): update lockfile package-lock.json 2019-08-14 18:18:26 +00:00
greenkeeper[bot]
f53038f6e9 fix(package): update rimraf to version 3.0.0 2019-08-14 18:18:02 +00:00
Feross Aboukhadijeh
c61234dd4c standard 2019-08-13 20:32:42 -07:00
Feross Aboukhadijeh
6336bbd6e3 use org-wide contributing file, move release process to new file 2019-08-13 15:15:43 -07:00
Feross Aboukhadijeh
649ac1900b use org-wide security file 2019-08-13 15:15:43 -07:00
Feross Aboukhadijeh
27e3d33f7d Use org-wide contributing file 2019-08-13 15:15:43 -07:00
hicom150
c29deab728 Pass title metadata in media url 2019-08-13 23:53:37 +02:00
Feross Aboukhadijeh
3a4ba6af37 Merge pull request #1641 from webtorrent/electron-6
Update to Electron 6
2019-08-13 13:05:56 -07:00
Feross Aboukhadijeh
e49d80fb17 Merge pull request #1646 from hicom150/fix_clean
Fix uninstall handlers in Linux
2019-08-12 12:18:52 -07:00
hicom150
a37f00bc10 Fix uninstall handlers in Linux 2019-08-12 10:29:30 +02:00
Feross Aboukhadijeh
925d495b62 remove stray console.log 2019-08-09 15:04:07 -07:00
Feross Aboukhadijeh
809beb4f81 Remove consistent JSX tag closing check
StandardJS is getting this rule in standard v14.
2019-08-09 15:03:06 -07:00
Feross Aboukhadijeh
0359451498 Update to Electron 6 2019-08-09 15:03:06 -07:00
Feross Aboukhadijeh
f6bd2cfcf4 Merge pull request #1643 from webtorrent/electron-5
Update to Electron 5
2019-08-09 15:02:49 -07:00
Feross Aboukhadijeh
9eecc2410c Stop shipping 32-bit binaries
For: https://github.com/webtorrent/webtorrent-desktop/issues/1637
2019-08-07 10:29:41 -07:00
Feross Aboukhadijeh
6cd8af91be Update to Electron 5 2019-08-07 10:24:31 -07:00
Feross Aboukhadijeh
7a3c1f0a10 Merge pull request #1634 from webtorrent/electron-packager
electron-packager@^14.0.4
2019-08-07 10:21:31 -07:00
Feross Aboukhadijeh
f2f3558cc4 Update lock.yml 2019-08-05 16:00:26 -07:00
Feross Aboukhadijeh
0dfaf6c1cf Update lock.yml 2019-08-05 15:58:04 -07:00
Feross Aboukhadijeh
a6ecba8e13 Merge pull request #1635 from webtorrent/changelog-v0.21
Update changelog for v0.21.0
2019-08-04 11:41:46 -07:00
Feross Aboukhadijeh
1a2c507d47 Update config.yml 2019-08-04 10:18:47 -07:00
Feross Aboukhadijeh
95b61826e0 Update stale.yml 2019-08-04 10:16:14 -07:00
Feross Aboukhadijeh
b1da72fde8 Merge pull request #1603 from refreex/original
Changed function name handlesoundNotificationsChange for handleSoundNotificationsChange
2019-08-04 09:37:47 -07:00
Feross Aboukhadijeh
b5e64a390a Merge pull request #1607 from webtorrent/music-metadata-4.1
Update music-metadata to version 4.2
2019-08-04 09:36:58 -07:00
Mathias Rasmussen
da829e0d69 resolve package-lock conflicts 2019-08-04 18:08:47 +02:00
Mathias Rasmussen
fd02908afa Update changelog for v0.21.0 2019-08-04 17:02:52 +02:00
Feross Aboukhadijeh
38092fa055 Update no-response.yml 2019-08-03 22:13:47 -07:00
Feross Aboukhadijeh
24a7e060f2 electron-packager@^14.0.4 2019-08-03 21:54:04 -07:00
Feross Aboukhadijeh
bd962d4d76 Create .npmrc
Helps primarily module developers (like me) who set their local .npmrc to package-lock=false
2019-08-03 21:23:49 -07:00
Feross Aboukhadijeh
a54dbc42e9 Update package-lock.json 2019-08-03 21:23:16 -07:00
Borewit
4c2381db26 Merge pull request #1590 from webtorrent/update-electron
Update to electron 4; it may not be perfect, but we have to move on
2019-07-30 21:44:22 +02:00
Feross Aboukhadijeh
91400ae31c standard 2019-07-27 18:33:46 -07:00
Feross Aboukhadijeh
c8f1e23b1a standard 2019-07-27 16:27:52 -07:00
Borewit
188f1cac8f Merge branch 'master' into update-electron
# Conflicts:
#	package-lock.json
#	package.json
2019-07-21 10:44:11 +02:00
Borewit
b216929dc4 Comply with max 100 chars per line lint rule. 2019-07-21 10:37:16 +02:00
Borewit
08008bb25c #1590 Improve single-instance start-up logic, remove duplicate code. 2019-07-21 10:19:36 +02:00
Feross Aboukhadijeh
c96f212726 Update package-lock.json 2019-07-20 23:12:03 -07:00
Feross Aboukhadijeh
80c1a61c33 Merge pull request #1605 from webtorrent/greenkeeper/babel-eslint-10.0.2
Update babel-eslint to the latest version 🚀
2019-07-20 22:54:41 -07:00
Borewit
7925e04834 Merge remote-tracking branch 'origin/greenkeeper/music-metadata-4.2.0' into music-metadata-4.1
# Conflicts:
#	package.json
2019-07-11 19:11:44 +02:00
Feross Aboukhadijeh
4da9da3d9a Merge pull request #1620 from webtorrent/greenkeeper/walk-sync-2.0.2
Greenkeeper/walk sync 2.0.2
2019-07-10 10:15:09 -07:00
Feross Aboukhadijeh
9916073a98 Merge pull request #1540 from RecoX/patch-1
Recommended readings to start working in the app.
2019-07-10 09:52:03 -07:00
Feross Aboukhadijeh
820776427b Merge pull request #1585 from webtorrent/greenkeeper/depcheck-0.8.0
Update depcheck to the latest version 🚀
2019-07-10 09:51:04 -07:00
greenkeeper[bot]
da76ff53ce chore(package): update lockfile package-lock.json 2019-07-09 22:43:22 +00:00
greenkeeper[bot]
9a657bdc10 chore(package): update walk-sync to version 2.0.2
Closes #1610
2019-07-09 22:43:17 +00:00
Feross Aboukhadijeh
df73ba2512 Merge pull request #1618 from webtorrent/greenkeeper/capture-frame-3.0.0
Update capture-frame to the latest version 🚀
2019-07-08 18:04:45 -07:00
greenkeeper[bot]
450237052c chore(package): update lockfile package-lock.json 2019-07-06 07:02:19 +00:00
greenkeeper[bot]
ffde524c85 fix(package): update capture-frame to version 3.0.0 2019-07-06 07:02:16 +00:00
Feross Aboukhadijeh
2618548fbe Merge pull request #1617 from webtorrent/greenkeeper/parse-torrent-7.0.0
Update parse-torrent to the latest version 🚀
2019-07-05 15:19:11 -07:00
Feross Aboukhadijeh
0471d141c4 standard 2019-07-05 15:06:27 -07:00
greenkeeper[bot]
8b31c0376c chore(package): update lockfile package-lock.json 2019-07-05 21:45:43 +00:00
greenkeeper[bot]
10c23104a1 fix(package): update parse-torrent to version 7.0.0 2019-07-05 21:45:40 +00:00
Feross Aboukhadijeh
9ed3533225 standard 2019-07-05 14:26:38 -07:00
Recox
97342951d0 Merge branch 'master' of https://github.com/webtorrent/webtorrent-desktop into original 2019-07-02 21:06:01 +12:00
Feross Aboukhadijeh
46d50ccda2 Merge pull request #1612 from webtorrent/greenkeeper/create-torrent-4.0.0
Update create-torrent to the latest version 🚀
2019-06-28 17:09:39 -07:00
greenkeeper[bot]
290027b5c5 chore(package): update lockfile package-lock.json 2019-06-28 23:57:54 +00:00
greenkeeper[bot]
f6129444f7 fix(package): update create-torrent to version 4.0.0 2019-06-28 23:57:51 +00:00
greenkeeper[bot]
57189c0d06 fix(package): update music-metadata to version 4.2.0
Closes #1611
2019-06-27 20:29:31 +00:00
Borewit
0aaeea4d3c Update music-metadata to version ^4.1.1 and adapt to API changes 2019-06-18 20:40:59 +02:00
Borewit
f59a54782a Fix processing arguments from second instance 2019-06-17 21:05:37 +02:00
greenkeeper[bot]
4156a3f546 chore(package): update lockfile package-lock.json 2019-06-17 14:22:03 +00:00
greenkeeper[bot]
13edc02440 chore(package): update babel-eslint to version 10.0.2 2019-06-17 14:21:56 +00:00
Borewit
cb4fa201f0 Merge pull request #1597 from dsernst/electron-4-detached-devtools
Electron 4: Migrate .openDevTools() for new 'detach' pattern
2019-06-17 09:36:49 +02:00
Recox
1905f8fec9 Changed function name handleSoundNotificationsChange 2019-06-11 21:32:19 +12:00
Borewit
d45e3f3e7b Merge pull request #1596 from dsernst/electron-4-titlebar
Fix hiddenInset (Mac) titlebar style for electron 4
2019-06-04 07:01:49 +02:00
David Ernst
a039cbf348 Electron 4: Migrate .openDevTools() for new 'detach' pattern 2019-06-03 21:52:06 -07:00
David Ernst
c5a21bddfd Fix hiddenInset (Mac) titlebar style for electron 4 2019-06-03 20:47:05 -07:00
Borewit
c204b7a82c onAppOpen seems to be no longer in use. 2019-05-15 19:54:13 +02:00
Borewit
f475975b40 Adjust single instance call to comply with Electron 4 2019-05-15 19:43:14 +02:00
Borewit
d3031ecff2 Merge branch 'greenkeeper/electron-4.0.0' into update-electron
# Conflicts:
#	package.json
2019-05-15 17:31:37 +02:00
Borewit
9a0a2111d3 Merge pull request #1309 from bilelmoussaoui/patch-2
Fix the desktop file
2019-05-12 20:18:20 +02:00
Borewit
d0053dc2f4 Merge pull request #1536 from adriantombu/feature/sound-option
Toggle the sound notifications
2019-05-12 20:03:14 +02:00
greenkeeper[bot]
60e9bf461c chore(package): update lockfile package-lock.json 2019-05-08 14:07:18 +00:00
greenkeeper[bot]
6add81add8 chore(package): update depcheck to version 0.8.0 2019-05-08 14:07:11 +00:00
Jimmy Wärting
a16b5b5add Removed zero-fill
sets the version the same way webtorrent dose

f8923a66a8/index.js (L27-L29)
2019-05-08 13:55:34 +02:00
Borewit
8b0b5f3738 Merge pull request #1579 from dsernst/playback-hotkeys-hicom150
Add YouTube style hotkeys. Closes #1395
2019-05-06 19:40:07 +02:00
David Ernst
07816f7470 Add f=fullscreen hotkey 2019-05-06 09:58:47 -07:00
Borewit
28bf22a710 Ignore JetBrains IntelliJ IDEA project files 2019-05-05 11:39:25 +02:00
David Ernst
103a5026f9 Add YouTube style hotkeys. Closes #1395 2019-05-04 21:04:41 -07:00
Borewit
c7ef42a65e Merge pull request #1573 from webtorrent/small-updates
Small updates
2019-04-28 21:55:58 +02:00
Alberto Miranda
ccaf73a477 Added missing dots for supported media extensions. 2019-04-28 16:00:43 -03:00
Borewit
dae57b5507 Merge remote-tracking branch 'origin/greenkeeper/opn-6.0.0' into small-updates 2019-04-25 20:05:58 +02:00
Borewit
5dc1ffb70e Merge branch 'pr-1562' into small-updates 2019-04-25 19:55:37 +02:00
Borewit
1dc500d4f3 Merge branch 'pr-1546' into small-updates 2019-04-25 19:54:25 +02:00
Borewit
d9bb625534 Merge remote-tracking branch 'origin/greenkeeper/depcheck-0.7.2' into small-updates
# Conflicts:
#	package-lock.json
2019-04-25 19:51:29 +02:00
Borewit
47cb6b9d5b Merge remote-tracking branch 'origin/greenkeeper/walk-sync-1.1.3' into small-updates 2019-04-25 19:48:34 +02:00
Borewit
b1eed5122a Merge remote-tracking branch 'origin/greenkeeper/semver-6.0.0' into small-updates 2019-04-25 19:47:39 +02:00
Borewit
97801c4fef Merge remote-tracking branch 'origin/greenkeeper/iso-639-1-2.0.5' into small-updates 2019-04-25 19:45:17 +02:00
Borewit
77d2abe04e Merge remote-tracking branch 'origin/greenkeeper/cp-file-7.0.0' into small-updates 2019-04-25 19:43:00 +02:00
Borewit
8b78e29827 Update lock file 2019-04-25 19:42:48 +02:00
Borewit
e6bfc57fc4 Add DSD audio media extensions for Sony DSF (DSD Stream Format) & Philips DSDIFF (Direct Stream Digital Interchange File Format
Related: https://github.com/Borewit/music-metadata/releases/tag/v3.6.0
2019-04-25 08:46:06 +02:00
Borewit
7facf4f6a2 Update music-metadata tp version 3.6.1 2019-04-25 08:45:53 +02:00
Borewit
79c25ffb33 Update lock file 2019-04-24 19:56:57 +02:00
greenkeeper[bot]
24dca39177 chore(package): update lockfile package-lock.json 2019-04-19 13:41:01 +00:00
greenkeeper[bot]
d209c02886 fix(package): update cp-file to version 7.0.0 2019-04-19 13:40:56 +00:00
David Ernst
c3315ab0fc Remove reference to (BETA) in test comments 2019-04-07 00:08:19 -05:00
David Ernst
109803e802 Remove '(BETA)' from app window title 2019-04-06 23:51:29 -05:00
greenkeeper[bot]
43ddbc7fd9 chore(package): update lockfile package-lock.json 2019-04-02 02:15:21 +00:00
greenkeeper[bot]
c8af0d3c1d fix(package): update iso-639-1 to version 2.0.5 2019-04-02 02:15:17 +00:00
greenkeeper[bot]
7db9e920de chore(package): update lockfile package-lock.json 2019-03-27 10:43:08 +00:00
greenkeeper[bot]
c8b726c0d1 fix(package): update semver to version 6.0.0
Closes #1559
2019-03-27 10:43:03 +00:00
greenkeeper[bot]
180acc15cd chore(package): update lockfile package-lock.json 2019-03-27 08:12:50 +00:00
greenkeeper[bot]
c3d261c74a chore(package): update opn to version 6.0.0 2019-03-27 08:12:46 +00:00
Feross Aboukhadijeh
0db89109a5 Remove NSP link 2019-03-25 22:23:43 -07:00
Diego Rodríguez Baquero
6ee0476799 Merge pull request #1531 from webtorrent/music-metadata-3.4.1
Update music-metadata to 3.5.2
2019-03-07 10:56:21 -05:00
Borewit
d86da0b0f7 Update music-metadata to 3.5.2, addressed sinificant performance issue handling MP3's. 2019-03-07 08:35:15 +01:00
greenkeeper[bot]
eb483ccd48 chore(package): update lockfile package-lock.json 2019-02-28 20:55:59 +00:00
greenkeeper[bot]
e7d5af5944 chore(package): update depcheck to version 0.7.2 2019-02-28 20:55:55 +00:00
David Ernst
0e3962e3f6 Update copyright for 2019 2019-02-23 00:09:42 -08:00
greenkeeper[bot]
992de4d80e chore(package): update lockfile package-lock.json 2019-02-05 03:46:35 +00:00
greenkeeper[bot]
85586d2dc5 chore(package): update walk-sync to version 1.1.3 2019-02-05 03:46:30 +00:00
Lucas
21ee5566a7 Update README.md 2019-01-31 11:48:37 +13:00
Lucas
429a376ccf Recommended readings to start working in the app.
As there is no too much information in the readme.md in how is this made, I think put a link of Electron and React to have an idea of the technologies used here is a good starter.
2019-01-31 11:41:31 +13:00
Mathias Rasmussen
a48c09e688 migrate sound option 2019-01-13 22:25:11 +01:00
Adrian Tombu
03de09fcaf Preference page cleaning 2019-01-13 11:17:46 +01:00
Adrian Tombu
97cf38e501 Toggle the sound notifications 2019-01-12 12:54:06 +01:00
Borewit
c24c99de36 Update music-metadata to 3.4.1 2019-01-01 17:33:08 +01:00
greenkeeper[bot]
a1ab889fca chore(package): update electron to version 4.0.0
Closes #1523
2018-12-21 00:36:42 +00:00
Borewit
170fd28c55 Merge pull request #1514 from webtorrent/greenkeeper/music-metadata-3.2.2
Greenkeeper/music metadata 3.3.0
2018-12-17 21:36:59 +01:00
Borewit
cdf5c5364a Merge branch 'master' into greenkeeper/music-metadata-3.2.2
# Conflicts:
#	package-lock.json
2018-12-16 17:15:16 +01:00
Borewit
2e85b4041f Updated music-metadata to v3.3.0. 2018-12-16 16:40:36 +01:00
Diego Rodríguez Baquero
a56e14f0a0 Merge pull request #1483 from webtorrent/update-material-ui
Update React and Material-UI
2018-12-12 14:28:46 -05:00
Mathias Rasmussen
16c3476960 update lockfile 2018-12-12 04:31:43 +01:00
Mathias Rasmussen
982ef3ae97 fix another unique key issue 2018-12-12 04:31:36 +01:00
Mathias Rasmussen
6057751e99 update react 2018-12-12 04:25:59 +01:00
Mathias Rasmussen
4c715111c5 fix react unique keys 2018-12-12 04:25:59 +01:00
Mathias Rasmussen
0d8e9792ff use react prop-types module 2018-12-12 04:25:59 +01:00
Mathias Rasmussen
ba5ccdb5cb update to material-ui 0.20.2
remove react-tap-event-plugin, as it is not needed anymore.
2018-12-12 04:25:59 +01:00
Mathias Rasmussen
579ca908c6 Merge pull request #1485 from webtorrent/update-auto-launch
Update auto-launch
2018-12-12 04:15:46 +01:00
Mathias Rasmussen
c7d7148132 update electron 2018-12-12 03:53:47 +01:00
Mathias Rasmussen
18ad5b0a01 Update dependencies 2018-12-12 03:48:01 +01:00
Mathias Rasmussen
cde3298920 update auto-launch 2018-12-12 03:37:06 +01:00
Borewit
20e3fb6f5f Add aif/aiff, spx (Speex) & mpc (Musepack) as audio file extensions.
Remove clashing .mp4 extension (with video) from audio.
2018-11-20 09:06:02 +01:00
greenkeeper[bot]
f0b87db248 chore(package): update lockfile package-lock.json 2018-11-19 19:50:48 +00:00
greenkeeper[bot]
2d90b29612 chore(package): update music-metadata to version 3.2.2 2018-11-19 19:50:44 +00:00
Borewit
81e6d7f3a7 Merge pull request #1506 from webtorrent/bnjmnt4n/playback-times
Ensure that the minutes field in playback indicator is zero-padded.
2018-11-06 18:37:01 +01:00
Benjamin Tan
a676a20054 Ensure that the minutes field in playback indicator is zero-padded.
The minutes field should be zero-padded as long as the playback media is longer than 9 minutes (including hours).

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

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

Closes #791, #911.

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

* Use fat arrow

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

* Add disk number in addition to track number.

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

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

* #1340 Commented out the metadata event debug output.

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

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

* Return JSX block.

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

* Fixed error when value is undefined.

* Update music-metadata dependency to 2.6.1.

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

Closes #1478

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

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

* Add disk number in addition to track number.

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

* Return JSX block.

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

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

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

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: feross

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

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

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

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

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

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

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

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

4
.gitignore vendored
View File

@@ -2,3 +2,7 @@ node_modules/
build/
dist/
npm-debug.log*
# JetBrains IntelliJ IDEA project files
.idea
*.iml

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=true

View File

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

View File

@@ -38,9 +38,36 @@
- 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)
- Jon Koops (jonkoops@gmail.com)
- Michael George Attard (michaelgeorgeattard@gmail.com)
- SimplyAhmazing (ahmad19526@gmail.com)
- Cezar Carneiro (cezargcarneiro@gmail.com)
- Bilal Elmoussaoui (bil.elmoussaoui@gmail.com)
- Terry Hau (terryhau@gmail.com)
- Vítor Galvão (info@vitorgalvao.com)
- Borewit (Borewit@users.noreply.github.com)
- Diego Rodríguez (diegorbaquero@gmail.com)
- Dan Flettre (flettre@gmail.com)
- Sibiraj (dev.sibiraj@outlook.com)
- clujin (clujin@gmail.com)
- Linus Unnebäck (linus@folkdatorn.se)
- Adrian Tombu (adrian@otso.fr)
- Lucas (5874806+RecoX@users.noreply.github.com)
- David Ernst (dsernst@users.noreply.github.com)
- David Ernst (git@dsernst.com)
- Jimmy Wärting (jimmy@warting.se)
- Recox (5874806+RecoX@users.noreply.github.com)
- greenkeeper[bot] (23040076+greenkeeper[bot]@users.noreply.github.com)
- hicom150 (hicom150@gmail.com)
- Jimmy Wärting (jimmy@warting.se)
- Julen Garcia Leunda (hicom150@gmail.com)
- Feross (feross@feross.org)
- Daniele Debernardi (drebrez@gmail.com)
- Chandan Chowdary Bhagam (chandandharana@gmail.com)
#### Generated by bin/update-authors.sh.

View File

@@ -1,5 +1,50 @@
# WebTorrent Desktop Version History
## v0.21.0 - 2019-08
### Added
- Add YouTube style hotkeys [\#1579](https://github.com/webtorrent/webtorrent-desktop/pull/1579) ([dsernst](https://github.com/dsernst))
- Toggle sound notifications on/off [\#1536](https://github.com/webtorrent/webtorrent-desktop/pull/1536) ([adriantombu](https://github.com/adriantombu))
- Ability to play MPEG-4 Audio Book \(.m4b\) [\#1450](https://github.com/webtorrent/webtorrent-desktop/pull/1450) ([Borewit](https://github.com/Borewit))
- Add support for subtitles on Chromecast [\#1165](https://github.com/webtorrent/webtorrent-desktop/pull/1165) ([janza](https://github.com/janza))
### Changed
- Update to Electron 4 [\#1590](https://github.com/webtorrent/webtorrent-desktop/pull/1590) ([Borewit](https://github.com/Borewit))
- Remove '\(BETA\)' from app window title [\#1562](https://github.com/webtorrent/webtorrent-desktop/pull/1562) ([dsernst](https://github.com/dsernst))
- Update React (v16) and Material-UI (v0.20) [\#1483](https://github.com/webtorrent/webtorrent-desktop/pull/1483) ([mathiasvr](https://github.com/mathiasvr))
- Show audio track and disk number [\#1454](https://github.com/webtorrent/webtorrent-desktop/pull/1454) ([Borewit](https://github.com/Borewit))
- Asynchronous music metadata updates while streaming [\#1449](https://github.com/webtorrent/webtorrent-desktop/pull/1449) ([Borewit](https://github.com/Borewit))
- If torrent is not private, leave private flag unset [\#1411](https://github.com/webtorrent/webtorrent-desktop/pull/1411) ([feross](https://github.com/feross))
- Improve audio poster selection: [\#1368](https://github.com/webtorrent/webtorrent-desktop/pull/1368) ([Borewit](https://github.com/Borewit))
- Save preferences immediately when changed [\#1042](https://github.com/webtorrent/webtorrent-desktop/pull/1042) ([Flet](https://github.com/Flet))
### Fixed
- Ensure that the minutes field in playback indicator is zero-padded. [\#1506](https://github.com/webtorrent/webtorrent-desktop/pull/1506) ([bnjmnt4n](https://github.com/bnjmnt4n))
- Bug Fix: Empty Array Reduce [\#1494](https://github.com/webtorrent/webtorrent-desktop/pull/1494) ([clujin](https://github.com/clujin))
- Fix startup problems [\#1419](https://github.com/webtorrent/webtorrent-desktop/pull/1419) ([Borewit](https://github.com/Borewit))
- Add back loading spinner for player page. [\#1311](https://github.com/webtorrent/webtorrent-desktop/pull/1311) ([bnjmnt4n](https://github.com/bnjmnt4n))
- Fix Linux desktop file [\#1309](https://github.com/webtorrent/webtorrent-desktop/pull/1309) ([bilelmoussaoui](https://github.com/bilelmoussaoui))
## 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
@@ -9,7 +54,7 @@
- Add pinch-to-zoom gesture to enter/exit fullscreen (#1148)
### Changed
- [SECURITY] Mitigate
- [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

View File

@@ -1,188 +0,0 @@
# Contributing Guidelines
Contributions welcome!
**Before spending lots of time on something, ask for feedback on your idea first!**
Please search issues and pull requests before adding something new to avoid duplicating
efforts and conversations.
This project welcomes non-code contributions, too! The following types of contributions
are welcome:
- **Ideas**: participate in an issue thread or start your own to have your voice heard.
- **Writing**: contribute your expertise in an area by helping expand the included docs.
- **Copy editing**: fix typos, clarify language, and improve the quality of the docs.
- **Formatting**: help keep docs easy to read with consistent formatting.
## Code Style
[![standard][standard-image]][standard-url]
This repository uses [`standard`][standard-url] to maintain code style and consistency,
and to avoid style arguments. `npm test` runs `standard` automatically, so you don't have
to!
[standard-image]: https://cdn.rawgit.com/feross/standard/master/badge.svg
[standard-url]: https://standardjs.com
## Project Governance
Individuals making significant and valuable contributions are given commit-access to the
project to contribute as they see fit. This project is more like an open wiki than a
standard guarded open source project.
### Rules
There are a few basic ground-rules for contributors:
1. **No `--force` pushes to master** or modifying history in any way. Rebasing and force pushing your own PR branch is fine.
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.
4. **Pull requests** are *encouraged* for all contributions to solicit feedback, but left to
the discretion of the contributor.
### Releases
Declaring formal releases remains the prerogative of the project maintainer.
### Changes to this arrangement
This is an experiment and feedback is welcome! This document may also be subject to pull-
requests or changes by contributors where you believe you have something valuable to add
or change.
## Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
- (a) The contribution was created in whole or in part by me and I have the right to
submit it under the open source license indicated in the file; or
- (b) The contribution is based upon previous work that, to the best of my knowledge, is
covered under an appropriate open source license and I have the right under that license
to submit that work with modifications, whether created in whole or in part by me, under
the same open source license (unless I am permitted to submit under a different
license), as indicated in the file; or
- (c) The contribution was provided directly to me by some other person who certified
(a), (b) or (c) and I have not modified it.
- (d) I understand and agree that this project and the contribution are public and that a
record of the contribution (including all personal information I submit with it,
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:
1. Click "Play" to stream a built-in torrent (e.g. Sintel)
- Ensure that seeking to undownloaded region works and plays immediately.
- Ensure that sintel.mp4 gets downloaded to `~/Downloads`.
2. Check that the auto-updater works
- Open the console and check for the line "No update available" to indicate
3. Add a new .torrent file via drag-and-drop.
- Ensure that it gets added to the list and starts downloading
4. Remove a torrent from the client
- Ensure that the file is removed from `~/Downloads`
5. Create and seed a new a torrent via drag-and-drop.
- Ensure that the torrent gets created and seeding begins.

View File

@@ -12,21 +12,34 @@
<h4 align="center">The streaming torrent app. For Mac, Windows, and Linux.</h4>
<p align="center">
<a href="https://gitter.im/webtorrent/webtorrent"><img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg" alt="gitter"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/webtorrent/webtorrent-desktop.svg" alt="github release"></a>
<a href="https://discord.gg/cnXkm4Z"><img src="https://img.shields.io/discord/612575111718895616" alt="discord"></a>
<a href="https://travis-ci.org/webtorrent/webtorrent-desktop"><img src="https://img.shields.io/travis/webtorrent/webtorrent-desktop/master.svg" alt="travis"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/webtorrent/webtorrent-desktop.svg" alt="github release version"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/downloads/webtorrent/webtorrent-desktop/total.svg" alt="github release downloads"></a>
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Standard - JavaScript Style Guide"></a>
</p>
## Install
Download the latest version of WebTorrent Desktop from
[the official website](https://webtorrent.io/desktop/) or the
[GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page.
### Recommended Install
**WebTorrent Desktop** is under very active development. You can try out the
current (unstable) development version by cloning the Git repo. See the
instructions below in the ["How to Contribute"](#how-to-contribute) section.
Download the latest version of WebTorrent Desktop from
[the official website](https://webtorrent.io/desktop/):
### [✨ Download WebTorrent Desktop ✨](https://webtorrent.io/desktop/)
### Advanced Install
- Download specific installer files from the [GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page.
- Use [Homebrew-Cask](https://github.com/caskroom/homebrew-cask) to install from the command line:
```
$ brew cask install webtorrent
```
- Try the (unstable) development version by cloning the Git repository. See the
["How to Contribute"](#how-to-contribute) instructions.
## Screenshots
@@ -84,7 +97,7 @@ comparing each one to a reference. Why screenshots?
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.
the same resolution as a 2018 MacBook Pro 13".
For Windows, you'll need Windows 10 with a 1366x768 screen.
@@ -112,6 +125,7 @@ The following optional arguments are available:
- `--sign` - Sign the application (Mac, Windows)
- `--package=[type]` - Package single output type.
- `deb` - Debian package
- `rpm` - RedHat package
- `zip` - Linux zip file
- `dmg` - Mac disk image
- `exe` - Windows installer
@@ -128,11 +142,12 @@ The Windows app can be packaged from **any** platform.
Note: Windows code signing only works from **Windows**, for now.
Note: To package the Windows app from non-Windows platforms,
[Wine](https://www.winehq.org/) needs to be installed. For example on Mac, first
install [XQuartz](http://www.xquartz.org/), then run:
[Wine](https://www.winehq.org/) and [Mono](https://www.mono-project.com/) need
to be installed. For example on Mac, first install
[XQuartz](http://www.xquartz.org/), then run:
```
brew install wine
brew install wine mono
```
(Requires the [Homebrew](http://brew.sh/) package manager.)
@@ -145,6 +160,22 @@ The Mac app can only be packaged from **macOS**.
The Linux app can be packaged from **any** platform.
If packaging from Mac, install system dependencies with Homebrew by running:
```
npm run install-system-deps
```
#### Recommended readings to start working in the app
Electron (Framework to make native apps for Windows, OSX and Linux in Javascript):
https://electronjs.org/docs/tutorial/quick-start
React.js (Framework to work with Frontend UI):
https://reactjs.org/docs/getting-started.html
Material UI (React components that implement Google's Material Design.):
https://material-ui.com/getting-started
### Privacy
WebTorrent Desktop collects some basic usage stats to help us make the app better.

112
RELEASE_PROCESS.md Normal file
View File

@@ -0,0 +1,112 @@
## Release Process
### 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:
1. Click "Play" to stream a built-in torrent (e.g. Sintel)
- Ensure that seeking to undownloaded region works and plays immediately.
- Ensure that sintel.mp4 gets downloaded to `~/Downloads`.
2. Check that the auto-updater works
- Open the console and check for the line "No update available" to indicate
3. Add a new .torrent file via drag-and-drop.
- Ensure that it gets added to the list and starts downloading
4. Remove a torrent from the client
- Ensure that the file is removed from `~/Downloads`
5. Create and seed a new a torrent via drag-and-drop.
- Ensure that the torrent gets created and seeding begins.

View File

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

View File

@@ -7,7 +7,7 @@ const path = require('path')
let hasErrors = false
// Find all Javascript source files
const files = walkSync('src', {globs: ['**/*.js']})
const files = walkSync('src', { globs: ['**/*.js'] })
console.log('Running extra-lint on ' + files.length + ' files...')
// Read each file, line by line
@@ -18,13 +18,6 @@ files.forEach(function (file) {
lines.forEach(function (line, i) {
let error
// Consistent JSX tag closing
if (line.match(/' {2}\/> *$/) ||
line.match('[^ ]/> *$') ||
line.match(' > *$')) {
error = 'JSX tag spacing'
}
// No lines over 100 characters
if (line.length > 100) {
error = 'Line >100 chars'
@@ -35,7 +28,7 @@ files.forEach(function (file) {
}
if (error) {
let name = path.basename(file)
const name = path.basename(file)
console.log('%s:%d - %s:\n%s', name, i + 1, error, line)
hasErrors = true
}

View File

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

View File

@@ -39,8 +39,7 @@ 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' })
cp.execSync('npm ci', { stdio: 'inherit' })
console.log('Nuking dist/ and build/...')
rimraf.sync(DIST_PATH)
@@ -71,11 +70,11 @@ function build () {
const all = {
// The human-readable copyright line for the app. Maps to the `LegalCopyright` metadata
// property on Windows, and `NSHumanReadableCopyright` on Mac.
'app-copyright': config.APP_COPYRIGHT,
appCopyright: config.APP_COPYRIGHT,
// The release version of the application. Maps to the `ProductVersion` metadata
// property on Windows, and `CFBundleShortVersionString` on Mac.
'app-version': pkg.version,
appVersion: pkg.version,
// Package the application's source code into an archive, using Electron's archive
// format. Mitigates issues around long path names on Windows and slightly speeds up
@@ -89,7 +88,7 @@ const all = {
// The build version of the application. Maps to the FileVersion metadata property on
// Windows, and CFBundleVersion on Mac. Note: Windows requires the build version to
// start with a number. We're using the version of the underlying WebTorrent library.
'build-version': require('webtorrent/package.json').version,
buildVersion: require('webtorrent/package.json').version,
// The application source directory.
dir: config.ROOT_PATH,
@@ -119,18 +118,18 @@ const darwin = {
// Build for Mac
platform: 'darwin',
// Build x64 binaries only.
// Build x64 binary only.
arch: 'x64',
// The bundle identifier to use in the application's plist (Mac only).
'app-bundle-id': 'io.webtorrent.webtorrent',
appBundleId: 'io.webtorrent.webtorrent',
// The application category type, as shown in the Finder via "View" -> "Arrange by
// Application Category" when viewing the Applications directory (Mac only).
'app-category-type': 'public.app-category.utilities',
appCategoryType: 'public.app-category.utilities',
// The bundle identifier to use in the application helper's plist (Mac only).
'helper-bundle-id': 'io.webtorrent.webtorrent-helper',
helperBundleId: 'io.webtorrent.webtorrent-helper',
// Application icon.
icon: config.APP_ICON + '.icns'
@@ -140,8 +139,8 @@ const win32 = {
// Build for Windows.
platform: 'win32',
// Build ia32 and x64 binaries.
arch: ['ia32', 'x64'],
// Build x64 binary only.
arch: 'x64',
// Object hash of application metadata to embed into the executable (Windows only)
win32metadata: {
@@ -174,8 +173,8 @@ const linux = {
// Build for Linux.
platform: 'linux',
// Build ia32 and x64 binaries.
arch: ['ia32', 'x64']
// Build x64 and arm64 binaries.
arch: ['x64', 'arm64']
// Note: Application icon for Linux is specified via the BrowserWindow `icon` option.
}
@@ -186,8 +185,7 @@ function buildDarwin (cb) {
const plist = require('plist')
console.log('Mac: Packaging electron...')
electronPackager(Object.assign({}, all, darwin), function (err, buildPath) {
if (err) return cb(err)
electronPackager(Object.assign({}, all, darwin)).then(function (buildPath) {
console.log('Mac: Packaged electron. ' + buildPath)
const appPath = path.join(buildPath[0], config.APP_NAME + '.app')
@@ -198,16 +196,16 @@ function buildDarwin (cb) {
infoPlist.CFBundleDocumentTypes = [
{
CFBundleTypeExtensions: [ 'torrent' ],
CFBundleTypeExtensions: ['torrent'],
CFBundleTypeIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
CFBundleTypeName: 'BitTorrent Document',
CFBundleTypeRole: 'Editor',
LSHandlerRank: 'Owner',
LSItemContentTypes: [ 'org.bittorrent.torrent' ]
LSItemContentTypes: ['org.bittorrent.torrent']
},
{
CFBundleTypeName: 'Any',
CFBundleTypeOSTypes: [ '****' ],
CFBundleTypeOSTypes: ['****'],
CFBundleTypeRole: 'Editor',
LSHandlerRank: 'Owner',
LSTypeIsPackage: false
@@ -219,13 +217,13 @@ function buildDarwin (cb) {
CFBundleTypeRole: 'Editor',
CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
CFBundleURLName: 'BitTorrent Magnet URL',
CFBundleURLSchemes: [ 'magnet' ]
CFBundleURLSchemes: ['magnet']
},
{
CFBundleTypeRole: 'Editor',
CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
CFBundleURLName: 'BitTorrent Stream-Magnet URL',
CFBundleURLSchemes: [ 'stream-magnet' ]
CFBundleURLSchemes: ['stream-magnet']
}
]
@@ -242,7 +240,7 @@ function buildDarwin (cb) {
UTTypeReferenceURL: 'http://www.bittorrent.org/beps/bep_0000.html',
UTTypeTagSpecification: {
'com.apple.ostype': 'TORR',
'public.filename-extension': [ 'torrent' ],
'public.filename-extension': ['torrent'],
'public.mime-type': 'application/x-bittorrent'
}
}
@@ -356,6 +354,8 @@ function buildDarwin (cb) {
cb(null)
})
}
}).catch(function (err) {
cb(err)
})
}
@@ -376,8 +376,7 @@ function buildWin32 (cb) {
CERT_PATH = path.join(os.homedir(), 'Desktop')
}
electronPackager(Object.assign({}, all, win32), function (err, buildPath) {
if (err) return cb(err)
electronPackager(Object.assign({}, all, win32)).then(function (buildPath) {
console.log('Windows: Packaged electron. ' + buildPath)
let signWithParams
@@ -396,21 +395,17 @@ function buildWin32 (cb) {
const tasks = []
buildPath.forEach(function (filesPath) {
const destArch = filesPath.split('-').pop()
if (argv.package === 'exe' || argv.package === 'all') {
tasks.push((cb) => packageInstaller(filesPath, destArch, cb))
tasks.push((cb) => packageInstaller(filesPath, cb))
}
if (argv.package === 'portable' || argv.package === 'all') {
tasks.push((cb) => packagePortable(filesPath, destArch, cb))
tasks.push((cb) => packagePortable(filesPath, cb))
}
})
series(tasks, cb)
function packageInstaller (filesPath, destArch, cb) {
console.log(`Windows: Creating ${destArch} installer...`)
const archStr = destArch === 'ia32' ? '-ia32' : ''
function packageInstaller (filesPath, cb) {
console.log('Windows: Creating installer...')
installer.createWindowsInstaller({
appDirectory: filesPath,
@@ -423,81 +418,43 @@ function buildWin32 (cb) {
noMsi: true,
outputDirectory: DIST_PATH,
productName: config.APP_NAME,
/**
* Only create delta updates for the Windows x64 build because 90% of our
* users have Windows x64 and the delta files take a *very* long time to
* generate. Also, the ia32 files on GitHub have non-standard Squirrel
* names (i.e. RELEASES-ia32 instead of RELEASES) and so Squirrel won't
* find them unless we proxy the requests.
*/
// TODO: Re-enable Windows 64-bit delta updates when we confirm that they
// work correctly in the presence of the "ia32" .nupkg files. I
// (feross) noticed them listed in the 64-bit RELEASES file and
// manually edited them out for the v0.17 release. Shipping only
// full updates for now will work fine, with no ill-effects.
// remoteReleases: destArch === 'x64'
// ? config.GITHUB_URL
// : undefined,
// remoteReleases: config.GITHUB_URL,
/**
* If you hit a "GitHub API rate limit exceeded" error, set this token!
*/
// remoteToken: process.env.WEBTORRENT_GITHUB_API_TOKEN,
setupExe: config.APP_NAME + 'Setup-v' + config.APP_VERSION + archStr + '.exe',
setupExe: config.APP_NAME + 'Setup-v' + config.APP_VERSION + '.exe',
setupIcon: config.APP_ICON + '.ico',
signWithParams: signWithParams,
title: config.APP_NAME,
usePackageJson: false,
version: pkg.version
})
.then(function () {
console.log(`Windows: Created ${destArch} installer.`)
.then(function () {
console.log('Windows: Created 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...')
// 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`)
)
// 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) {
// Sanity check
throw new Error('Fixing RELEASES-ia32 failed. Replacement did not modify the file.')
}
console.log('Windows: Renamed ia32 installer files.')
}
cb(null)
})
.catch(cb)
cb(null)
})
.catch(cb)
}
function packagePortable (filesPath, destArch, cb) {
console.log(`Windows: Creating ${destArch} portable app...`)
function packagePortable (filesPath, cb) {
console.log('Windows: Creating portable app...')
const portablePath = path.join(filesPath, 'Portable Settings')
mkdirp.sync(portablePath)
@@ -508,22 +465,22 @@ function buildWin32 (cb) {
const tempPath = path.join(portablePath, 'Temp')
mkdirp.sync(tempPath)
const archStr = destArch === 'ia32' ? '-ia32' : ''
const inPath = path.join(DIST_PATH, path.basename(filesPath))
const outPath = path.join(DIST_PATH, BUILD_NAME + '-win' + archStr + '.zip')
const outPath = path.join(DIST_PATH, BUILD_NAME + '-win.zip')
zip.zipSync(inPath, outPath)
console.log(`Windows: Created ${destArch} portable app.`)
console.log('Windows: Created portable app.')
cb(null)
}
}).catch(function (err) {
cb(err)
})
}
function buildLinux (cb) {
console.log('Linux: Packaging electron...')
electronPackager(Object.assign({}, all, linux), function (err, buildPath) {
if (err) return cb(err)
electronPackager(Object.assign({}, all, linux)).then(function (buildPath) {
console.log('Linux: Packaged electron. ' + buildPath)
const tasks = []
@@ -533,56 +490,92 @@ function buildLinux (cb) {
if (argv.package === 'deb' || argv.package === 'all') {
tasks.push((cb) => packageDeb(filesPath, destArch, cb))
}
if (argv.package === 'rpm' || argv.package === 'all') {
tasks.push((cb) => packageRpm(filesPath, destArch, cb))
}
if (argv.package === 'zip' || argv.package === 'all') {
tasks.push((cb) => packageZip(filesPath, destArch, cb))
}
})
series(tasks, cb)
}).catch(function (err) {
cb(err)
})
function packageDeb (filesPath, destArch, cb) {
// Linux convention for Debian based 'x64' is 'amd64'
if (destArch === 'x64') {
destArch = 'amd64'
}
// Create .deb file for Debian-based platforms
console.log(`Linux: Creating ${destArch} deb...`)
const deb = require('nobin-debian-installer')()
const destPath = path.join('/opt', pkg.name)
const installer = require('electron-installer-debian')
deb.pack({
package: pkg,
info: {
arch: destArch === 'x64' ? 'amd64' : 'i386',
targetDir: DIST_PATH,
depends: 'gconf2, libgtk2.0-0, libnss3, libxss1',
scripts: {
postinst: path.join(config.STATIC_PATH, 'linux', 'postinst'),
prerm: path.join(config.STATIC_PATH, 'linux', 'prerm')
}
}
}, [{
src: ['./**'],
dest: destPath,
expand: true,
cwd: filesPath
}, {
src: ['./**'],
dest: path.join('/usr', 'share'),
expand: true,
cwd: path.join(config.STATIC_PATH, 'linux', 'share')
}], function (err) {
if (err) return cb(err)
console.log(`Linux: Created ${destArch} deb.`)
cb(null)
})
const options = {
src: filesPath + '/',
dest: DIST_PATH,
arch: destArch,
bin: 'WebTorrent',
icon: {
'48x48': path.join(config.STATIC_PATH, 'linux/share/icons/hicolor/48x48/apps/webtorrent-desktop.png'),
'256x256': path.join(config.STATIC_PATH, 'linux/share/icons/hicolor/256x256/apps/webtorrent-desktop.png')
},
categories: ['Network', 'FileTransfer', 'P2P'],
mimeType: ['application/x-bittorrent', 'x-scheme-handler/magnet', 'x-scheme-handler/stream-magnet'],
desktopTemplate: path.join(config.STATIC_PATH, 'linux/webtorrent-desktop.ejs')
}
installer(options).then(
() => {
console.log(`Linux: Created ${destArch} deb.`)
cb(null)
},
(err) => cb(err)
)
}
function packageRpm (filesPath, destArch, cb) {
// Linux convention for RedHat based 'x64' is 'x86_64'
if (destArch === 'x64') {
destArch = 'x86_64'
}
// Create .rpm file for RedHat-based platforms
console.log(`Linux: Creating ${destArch} rpm...`)
const installer = require('electron-installer-redhat')
const options = {
src: filesPath + '/',
dest: DIST_PATH,
arch: destArch,
bin: 'WebTorrent',
icon: {
'48x48': path.join(config.STATIC_PATH, 'linux/share/icons/hicolor/48x48/apps/webtorrent-desktop.png'),
'256x256': path.join(config.STATIC_PATH, 'linux/share/icons/hicolor/256x256/apps/webtorrent-desktop.png')
},
categories: ['Network', 'FileTransfer', 'P2P'],
mimeType: ['application/x-bittorrent', 'x-scheme-handler/magnet', 'x-scheme-handler/stream-magnet'],
desktopTemplate: path.join(config.STATIC_PATH, 'linux/webtorrent-desktop.ejs')
}
installer(options).then(
() => {
console.log(`Linux: Created ${destArch} rpm.`)
cb(null)
},
(err) => cb(err)
)
}
function packageZip (filesPath, destArch, cb) {
// Create .zip file for Linux
console.log(`Linux: Creating ${destArch} zip...`)
const archStr = destArch === 'ia32' ? '-ia32' : ''
const inPath = path.join(DIST_PATH, path.basename(filesPath))
const outPath = path.join(DIST_PATH, BUILD_NAME + '-linux' + archStr + '.zip')
const outPath = path.join(DIST_PATH, `${BUILD_NAME}-linux-${destArch}.zip`)
zip.zipSync(inPath, outPath)
console.log(`Linux: Created ${destArch} zip.`)

10286
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.19.0",
"version": "0.21.0",
"author": {
"name": "WebTorrent, LLC",
"email": "feross@webtorrent.io",
@@ -11,65 +11,63 @@
"url": "https://github.com/webtorrent/webtorrent-desktop/issues"
},
"dependencies": {
"airplayer": "^2.0.0",
"application-config": "^1.0.0",
"arch": "^2.0.0",
"auto-launch": "^4.0.1",
"bitfield": "^1.0.2",
"capture-frame": "^1.0.0",
"chokidar": "^1.6.1",
"chromecasts": "^1.8.0",
"cp-file": "^4.0.1",
"create-torrent": "^3.24.5",
"debounce": "^1.0.0",
"deep-equal": "^1.0.1",
"airplayer": "github:webtorrent/airplayer#fix-security",
"application-config": "^1.0.1",
"arch": "^2.1.1",
"auto-launch": "^5.0.5",
"bitfield": "^3.0.0",
"capture-frame": "^3.0.0",
"chokidar": "^3.0.2",
"chromecasts": "^1.9.1",
"create-torrent": "^4.4.1",
"debounce": "^1.2.0",
"deep-equal": "^1.1.0",
"dlnacasts": "^0.1.0",
"drag-drop": "^2.12.1",
"es6-error": "^4.0.0",
"drag-drop": "^5.0.1",
"es6-error": "^4.1.1",
"fn-getter": "^1.0.0",
"iso-639-1": "^1.2.1",
"languagedetect": "^1.1.1",
"location-history": "^1.0.0",
"material-ui": "^0.17.0",
"iso-639-1": "^2.1.0",
"languagedetect": "^1.2.0",
"location-history": "^1.1.1",
"material-ui": "^0.20.2",
"mkdirp": "^0.5.1",
"musicmetadata": "^2.0.2",
"network-address": "^1.1.0",
"parse-torrent": "^5.7.3",
"prettier-bytes": "^1.0.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-tap-event-plugin": "^2.0.1",
"rimraf": "^2.5.2",
"run-parallel": "^1.1.6",
"semver": "^5.1.0",
"music-metadata": "^4.5.3",
"network-address": "^1.1.2",
"parse-torrent": "^7.0.1",
"prettier-bytes": "^1.0.4",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"rimraf": "^3.0.0",
"run-parallel": "^1.1.9",
"semver": "^6.3.0",
"simple-concat": "^1.0.0",
"simple-get": "^2.0.0",
"srt-to-vtt": "^1.1.1",
"vlc-command": "^1.0.1",
"webtorrent": "0.x",
"winreg": "^1.2.0",
"zero-fill": "^2.2.3"
"simple-get": "^3.0.3",
"srt-to-vtt": "^1.1.3",
"vlc-command": "^1.2.0",
"webtorrent": ">=0.107.16",
"winreg": "^1.2.4"
},
"devDependencies": {
"buble": "^0.15.2",
"cross-zip": "^2.0.1",
"depcheck": "^0.6.4",
"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",
"babel-eslint": "^10.0.3",
"buble": "^0.19.8",
"cross-zip": "^2.1.6",
"depcheck": "^0.8.3",
"electron": "~6.0.9",
"electron-osx-sign": "^0.4.13",
"electron-packager": "^14.0.6",
"electron-winstaller": "^4.0.0",
"gh-release": "^3.5.0",
"minimist": "^1.2.0",
"nobin-debian-installer": "0.0.10",
"nodemon": "^1.10.2",
"open": "0.0.5",
"plist": "^2.0.1",
"pngjs": "^3.0.0",
"run-series": "^1.1.4",
"spectron": "^3.3.0",
"nodemon": "^1.19.2",
"open": "^6.4.0",
"plist": "^3.0.1",
"pngjs": "^3.4.0",
"run-series": "^1.1.8",
"spectron": "^8.0.0",
"standard": "*",
"tape": "^4.6.0",
"walk-sync": "^0.3.1"
"tape": "^4.11.0",
"walk-sync": "^2.0.2"
},
"engines": {
"node": ">=4.0.0"
@@ -88,7 +86,9 @@
"license": "MIT",
"main": "index.js",
"optionalDependencies": {
"appdmg": "^0.4.3"
"appdmg": "^0.6.0",
"electron-installer-debian": "^2.0.0",
"electron-installer-redhat": "^2.0.0"
},
"private": true,
"productName": "WebTorrent",
@@ -97,16 +97,19 @@
"url": "git://github.com/webtorrent/webtorrent-desktop.git"
},
"scripts": {
"build": "buble src --output build",
"build": "buble src --target chrome:71 --output build",
"clean": "node ./bin/clean.js",
"gh-release": "gh-release",
"install-system-deps": "brew install fakeroot dpkg rpm",
"open-config": "node ./bin/open-config.js",
"package": "node ./bin/package.js",
"prepublish": "npm run build",
"start": "npm run build && electron .",
"test": "standard && depcheck --ignores=buble,nodemon,gh-release --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"test": "standard && depcheck --ignores=standard,babel-eslint --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"test-integration": "npm run build && node ./test",
"update-authors": "./bin/update-authors.sh",
"watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/"
},
"standard": {
"parser": "babel-eslint"
}
}

View File

@@ -23,13 +23,13 @@ module.exports = {
CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report',
TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry',
APP_COPYRIGHT: 'Copyright © 2014-2018 ' + APP_TEAM,
APP_COPYRIGHT: 'Copyright © 2014-2019 ' + APP_TEAM,
APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'),
APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'),
APP_NAME: APP_NAME,
APP_TEAM: APP_TEAM,
APP_VERSION: APP_VERSION,
APP_WINDOW_TITLE: APP_NAME + ' (BETA)',
APP_WINDOW_TITLE: APP_NAME,
CONFIG_PATH: getConfigPath(),
@@ -73,8 +73,10 @@ module.exports = {
GITHUB_URL: 'https://github.com/webtorrent/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/webtorrent/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/webtorrent/webtorrent-desktop/master',
GITHUB_URL_RELEASES: 'https://github.com/webtorrent/webtorrent-desktop/releases',
HOME_PAGE_URL: 'https://webtorrent.io',
TWITTER_PAGE_URL: 'https://twitter.com/WebTorrentApp',
IS_PORTABLE: IS_PORTABLE,
IS_PRODUCTION: IS_PRODUCTION,

View File

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

View File

@@ -19,7 +19,7 @@ function openSeedFile () {
log('openSeedFile')
const opts = {
title: 'Select a file for the torrent.',
properties: [ 'openFile' ]
properties: ['openFile']
}
showOpenSeed(opts)
}
@@ -35,11 +35,11 @@ function openSeedDirectory () {
const opts = process.platform === 'darwin'
? {
title: 'Select a file or folder for the torrent.',
properties: [ 'openFile', 'openDirectory' ]
properties: ['openFile', 'openDirectory']
}
: {
title: 'Select a folder for the torrent.',
properties: [ 'openDirectory' ]
properties: ['openDirectory']
}
showOpenSeed(opts)
}
@@ -54,11 +54,11 @@ function openFiles () {
const opts = process.platform === 'darwin'
? {
title: 'Select a file or folder to add.',
properties: [ 'openFile', 'openDirectory' ]
properties: ['openFile', 'openDirectory']
}
: {
title: 'Select a file to add.',
properties: [ 'openFile' ]
properties: ['openFile']
}
setTitle(opts.title)
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
@@ -77,7 +77,7 @@ function openTorrentFile () {
const opts = {
title: 'Select a .torrent file.',
filters: [{ name: 'Torrent Files', extensions: ['torrent'] }],
properties: [ 'openFile', 'multiSelections' ]
properties: ['openFile', 'multiSelections']
}
setTitle(opts.title)
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {

View File

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

View File

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

View File

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

View File

@@ -7,26 +7,20 @@ const config = require('../config')
const path = require('path')
function install () {
if (process.platform === 'darwin') {
installDarwin()
}
if (process.platform === 'win32') {
installWin32()
}
if (process.platform === 'linux') {
installLinux()
switch (process.platform) {
case 'darwin': installDarwin()
break
case 'win32': installWin32()
break
}
}
function uninstall () {
if (process.platform === 'darwin') {
uninstallDarwin()
}
if (process.platform === 'win32') {
uninstallWin32()
}
if (process.platform === 'linux') {
uninstallLinux()
switch (process.platform) {
case 'darwin': uninstallDarwin()
break
case 'win32': uninstallWin32()
break
}
}
@@ -44,7 +38,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)
@@ -108,37 +102,37 @@ function installWin32 () {
setProtocol()
function setProtocol (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
protocolKey.set('', Registry.REG_SZ, name, setURLProtocol)
}
function setURLProtocol (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
protocolKey.set('URL Protocol', Registry.REG_SZ, '', setIcon)
}
function setIcon (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const iconKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol + '\\DefaultIcon'
key: `\\Software\\Classes\\${protocol}\\DefaultIcon`
})
iconKey.set('', Registry.REG_SZ, icon, setCommand)
}
function setCommand (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const commandKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command'
key: `\\Software\\Classes\\${protocol}\\shell\\open\\command`
})
commandKey.set('', Registry.REG_SZ, `${commandToArgs(command)} "%1"`, done)
}
function done (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
}
}
@@ -163,43 +157,43 @@ function installWin32 () {
function setExt () {
const extKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + ext
key: `\\Software\\Classes\\${ext}`
})
extKey.set('', Registry.REG_SZ, id, setId)
}
function setId (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const idKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id
key: `\\Software\\Classes\\${id}`
})
idKey.set('', Registry.REG_SZ, name, setIcon)
}
function setIcon (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const iconKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id + '\\DefaultIcon'
key: `\\Software\\Classes\\${id}\\DefaultIcon`
})
iconKey.set('', Registry.REG_SZ, icon, setCommand)
}
function setCommand (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
const commandKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id + '\\shell\\open\\command'
key: `\\Software\\Classes\\${id}\\shell\\open\\command`
})
commandKey.set('', Registry.REG_SZ, `${commandToArgs(command)} "%1"`, done)
}
function done (err) {
if (err) log.error(err.message)
if (err) return log.error(err.message)
}
}
}
@@ -217,9 +211,9 @@ function uninstallWin32 () {
function getCommand () {
const commandKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command'
key: `\\Software\\Classes\\${protocol}\\shell\\open\\command`
})
commandKey.get('', function (err, item) {
commandKey.get('', (err, item) => {
if (!err && item.value.indexOf(commandToArgs(command)) >= 0) {
destroyProtocol()
}
@@ -229,9 +223,9 @@ function uninstallWin32 () {
function destroyProtocol () {
const protocolKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol
key: `\\Software\\Classes\\${protocol}`
})
protocolKey.destroy(function () {})
protocolKey.destroy(() => {})
}
}
@@ -241,7 +235,7 @@ function uninstallWin32 () {
function eraseId () {
const idKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + id
key: `\\Software\\Classes\\${id}`
})
idKey.destroy(getExt)
}
@@ -249,9 +243,9 @@ function uninstallWin32 () {
function getExt () {
const extKey = new Registry({
hive: Registry.HKCU,
key: '\\Software\\Classes\\' + ext
key: `\\Software\\Classes\\${ext}`
})
extKey.get('', function (err, item) {
extKey.get('', (err, item) => {
if (!err && item.value === id) {
destroyExt()
}
@@ -261,9 +255,9 @@ function uninstallWin32 () {
function destroyExt () {
const extKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + ext
key: `\\Software\\Classes\\${ext}`
})
extKey.destroy(function () {})
extKey.destroy(() => {})
}
}
}
@@ -271,99 +265,3 @@ function uninstallWin32 () {
function commandToArgs (command) {
return command.map((arg) => `"${arg}"`).join(' ')
}
function installLinux () {
const fs = require('fs')
const os = require('os')
const path = require('path')
const config = require('../config')
const log = require('./log')
// Do not install in user dir if running on system
if (/^\/opt/.test(process.execPath)) return
installDesktopFile()
installIconFile()
function installDesktopFile () {
const templatePath = path.join(
config.STATIC_PATH, 'linux', 'webtorrent-desktop.desktop'
)
fs.readFile(templatePath, 'utf8', writeDesktopFile)
}
function writeDesktopFile (err, desktopFile) {
if (err) return log.error(err.message)
const appPath = config.IS_PRODUCTION
? path.dirname(process.execPath)
: config.ROOT_PATH
desktopFile = desktopFile.replace(/\$APP_NAME/g, config.APP_NAME)
desktopFile = desktopFile.replace(/\$APP_PATH/g, appPath)
desktopFile = desktopFile.replace(/\$EXEC_PATH/g, EXEC_COMMAND.join(' '))
desktopFile = desktopFile.replace(/\$TRY_EXEC_PATH/g, process.execPath)
const desktopFilePath = path.join(
os.homedir(),
'.local',
'share',
'applications',
'webtorrent-desktop.desktop'
)
fs.mkdirp(path.dirname(desktopFilePath))
fs.writeFile(desktopFilePath, desktopFile, function (err) {
if (err) return log.error(err.message)
})
}
function installIconFile () {
const iconStaticPath = path.join(config.STATIC_PATH, 'WebTorrent.png')
fs.readFile(iconStaticPath, writeIconFile)
}
function writeIconFile (err, iconFile) {
if (err) return log.error(err.message)
const mkdirp = require('mkdirp')
const iconFilePath = path.join(
os.homedir(),
'.local',
'share',
'icons',
'webtorrent-desktop.png'
)
mkdirp(path.dirname(iconFilePath), (err) => {
if (err) return log.error(err.message)
fs.writeFile(iconFilePath, iconFile, (err) => {
if (err) log.error(err.message)
})
})
}
}
function uninstallLinux () {
const os = require('os')
const path = require('path')
const rimraf = require('rimraf')
const desktopFilePath = path.join(
os.homedir(),
'.local',
'share',
'applications',
'webtorrent-desktop.desktop'
)
rimraf(desktopFilePath)
const iconFilePath = path.join(
os.homedir(),
'.local',
'share',
'icons',
'webtorrent-desktop.png'
)
rimraf(iconFilePath)
}

View File

@@ -13,9 +13,14 @@ const menu = require('./menu')
const State = require('../renderer/lib/state')
const windows = require('./windows')
const WEBTORRENT_VERSION = require('webtorrent/package.json').version
let shouldQuit = false
let argv = sliceArgv(process.argv)
// allow electron/chromium to play startup sounds (without user interaction)
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required')
// Start the app without showing the main window when auto launching on login
// (On Windows and Linux, we get a flag. On MacOS, we get special API.)
const hidden = argv.includes('--hidden') ||
@@ -38,17 +43,19 @@ if (!shouldQuit && !config.IS_PORTABLE) {
// signal this instance and quit. Note: This feature creates a lock file in
// %APPDATA%\Roaming\WebTorrent so we do not do it for the Portable App since
// we want to be "silent" as well as "portable".
shouldQuit = app.makeSingleInstance(onAppOpen)
if (shouldQuit) {
app.quit()
if (!app.requestSingleInstanceLock()) {
shouldQuit = true
}
}
if (!shouldQuit) {
if (shouldQuit) {
app.quit()
} else {
init()
}
function init () {
app.on('second-instance', (event, commandLine, workingDirectory) => onAppOpen(commandLine))
if (config.IS_PORTABLE) {
const path = require('path')
// Put all user data into the "Portable Settings" folder
@@ -74,7 +81,7 @@ function init () {
isReady = true
const state = results.state
windows.main.init(state, {hidden: hidden})
windows.main.init(state, { hidden: hidden })
windows.webtorrent.init()
menu.init()
@@ -86,11 +93,17 @@ function init () {
// Report uncaught exceptions
process.on('uncaughtException', (err) => {
console.error(err)
const error = {message: err.message, stack: err.stack}
const error = { message: err.message, stack: err.stack }
windows.main.dispatch('uncaughtError', 'main', error)
})
}
// Enable app logging into default directory, i.e. /Library/Logs/WebTorrent
// on Mac, %APPDATA% on Windows, $XDG_CONFIG_HOME or ~/.config on Linux.
app.setAppLogsPath()
app.userAgentFallback = `WebTorrent/${WEBTORRENT_VERSION} (https://webtorrent.io)`
app.on('open-file', onOpen)
app.on('open-url', onOpen)
@@ -131,7 +144,7 @@ function delayedInit (state) {
const dock = require('./dock')
const updater = require('./updater')
const FolderWatcher = require('./folder-watcher')
const folderWatcher = new FolderWatcher({window: windows.main, state})
const folderWatcher = new FolderWatcher({ window: windows.main, state })
announcement.init()
dock.init()
@@ -162,7 +175,7 @@ function onOpen (e, torrentId) {
// Electron issue: https://github.com/atom/electron/issues/4338
setTimeout(() => windows.main.show(), 100)
processArgv([ torrentId ])
processArgv([torrentId])
} else {
argv.push(torrentId)
}
@@ -188,11 +201,11 @@ function onAppOpen (newArgv) {
function sliceArgv (argv) {
return argv.slice(config.IS_PRODUCTION ? 1
: config.IS_TEST ? 4
: 2)
: 2)
}
function processArgv (argv) {
let torrentIds = []
const torrentIds = []
argv.forEach(function (arg) {
if (arg === '-n' || arg === '-o' || arg === '-u') {
// Critical path: Only load the 'dialog' package if it is needed

View File

@@ -120,21 +120,21 @@ function init () {
*/
ipc.on('startFolderWatcher', function () {
if (!modules['folderWatcher']) {
if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules['folderWatcher'].start()
modules.folderWatcher.start()
})
ipc.on('stopFolderWatcher', function () {
if (!modules['folderWatcher']) {
if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules['folderWatcher'].stop()
modules.folderWatcher.stop()
})
/**

View File

@@ -302,6 +302,13 @@ function getMenuTemplate () {
shell.openExternal(config.HOME_PAGE_URL)
}
},
{
label: 'Release Notes',
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_RELEASES)
}
},
{
label: 'Contribute on GitHub',
click: () => {
@@ -318,6 +325,13 @@ function getMenuTemplate () {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_ISSUES)
}
},
{
label: 'Follow us on Twitter',
click: () => {
const shell = require('./shell')
shell.openExternal(config.TWITTER_PAGE_URL)
}
}
]
}

View File

@@ -3,19 +3,11 @@ module.exports = {
uninstall
}
const config = require('../config')
const { APP_NAME } = require('../config')
const AutoLaunch = require('auto-launch')
const { app } = require('electron')
// On Mac, work around a bug in auto-launch where it opens a Terminal window
// See https://github.com/Teamwork/node-auto-launch/issues/28#issuecomment-222194437
const appPath = process.platform === 'darwin'
? app.getPath('exe').replace(/\.app\/Content.*/, '.app')
: undefined // Use the default
const appLauncher = new AutoLaunch({
name: config.APP_NAME,
path: appPath,
name: APP_NAME,
isHidden: true
})

View File

@@ -77,8 +77,8 @@ function onPlayerPlay () {
function onPlayerUpdate (state) {
if (!isEnabled()) return
buttons[PREV].flags = [ state.hasPrevious ? 'enabled' : 'disabled' ]
buttons[NEXT].flags = [ state.hasNext ? 'enabled' : 'disabled' ]
buttons[PREV].flags = [state.hasPrevious ? 'enabled' : 'disabled']
buttons[NEXT].flags = [state.hasNext ? 'enabled' : 'disabled']
update()
}

View File

@@ -24,6 +24,9 @@ function init () {
skipTaskbar: true,
title: 'About ' + config.APP_WINDOW_TITLE,
useContentSize: true,
webPreferences: {
nodeIntegration: true
},
width: 300
})

View File

@@ -40,9 +40,12 @@ function init (state, options) {
minWidth: config.WINDOW_MIN_WIDTH,
show: false,
title: config.APP_WINDOW_TITLE,
titleBarStyle: 'hidden-inset', // Hide title bar (Mac)
titleBarStyle: 'hiddenInset', // Hide title bar (Mac)
useContentSize: true, // Specify web page size without OS chrome
width: initialBounds.width,
webPreferences: {
nodeIntegration: true
},
x: initialBounds.x,
y: initialBounds.y
})
@@ -57,7 +60,7 @@ function init (state, options) {
win.setSheetOffset(config.UI_HEADER_HEIGHT)
}
win.webContents.on('dom-ready', function () {
win.webContents.on('dom-ready', () => {
menu.onToggleFullScreen(main.win.isFullScreen())
})
@@ -73,27 +76,27 @@ function init (state, options) {
win.on('hide', onWindowBlur)
win.on('show', onWindowFocus)
win.on('enter-full-screen', function () {
win.on('enter-full-screen', () => {
menu.onToggleFullScreen(true)
send('fullscreenChanged', true)
win.setMenuBarVisibility(false)
})
win.on('leave-full-screen', function () {
win.on('leave-full-screen', () => {
menu.onToggleFullScreen(false)
send('fullscreenChanged', false)
win.setMenuBarVisibility(true)
})
win.on('move', debounce(function (e) {
win.on('move', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds())
}, 1000))
win.on('resize', debounce(function (e) {
win.on('resize', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds())
}, 1000))
win.on('close', function (e) {
win.on('close', e => {
if (process.platform !== 'darwin') {
const tray = require('../tray')
if (!tray.hasTray()) {
@@ -138,37 +141,29 @@ function setAspectRatio (aspectRatio) {
function setBounds (bounds, maximize) {
// Do nothing in fullscreen
if (!main.win || main.win.isFullScreen()) {
log('setBounds: not setting bounds because we\'re in full screen')
log('setBounds: not setting bounds because already in full screen mode')
return
}
// Maximize or minimize, if the second argument is present
let willBeMaximized
if (maximize === true) {
if (!main.win.isMaximized()) {
log('setBounds: maximizing')
main.win.maximize()
}
willBeMaximized = true
} else if (maximize === false) {
if (main.win.isMaximized()) {
log('setBounds: unmaximizing')
main.win.unmaximize()
}
willBeMaximized = false
} else {
willBeMaximized = main.win.isMaximized()
if (maximize === true && !main.win.isMaximized()) {
log('setBounds: maximizing')
main.win.maximize()
} else if (maximize === false && main.win.isMaximized()) {
log('setBounds: unmaximizing')
main.win.unmaximize()
}
const willBeMaximized = typeof maximize === 'boolean' ? maximize : main.win.isMaximized()
// Assuming we're not maximized or maximizing, set the window size
if (!willBeMaximized) {
log('setBounds: setting bounds to ' + JSON.stringify(bounds))
log(`setBounds: setting bounds to ${JSON.stringify(bounds)}`)
if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen
const scr = electron.screen.getDisplayMatching(main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2))
bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2))
log('setBounds: centered to ' + JSON.stringify(bounds))
log(`setBounds: centered to ${JSON.stringify(bounds)}`)
}
// Resize the window's content area (so window border doesn't need to be taken
// into account)
@@ -217,7 +212,7 @@ function toggleDevTools () {
if (main.win.webContents.isDevToolsOpened()) {
main.win.webContents.closeDevTools()
} else {
main.win.webContents.openDevTools({ detach: true })
main.win.webContents.openDevTools({ mode: 'detach' })
}
}

View File

@@ -25,6 +25,9 @@ function init () {
skipTaskbar: true,
title: 'webtorrent-hidden-window',
useContentSize: true,
webPreferences: {
nodeIntegration: true
},
width: 150
})
@@ -56,6 +59,6 @@ function toggleDevTools () {
webtorrent.win.webContents.closeDevTools()
webtorrent.win.hide()
} else {
webtorrent.win.webContents.openDevTools({ detach: true })
webtorrent.win.webContents.openDevTools({ mode: 'detach' })
}
}

View File

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

View File

@@ -1,27 +1,31 @@
const React = require('react')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher } = require('../lib/dispatcher')
class Header extends React.Component {
render () {
const loc = this.props.state.location
return (
<div className='header'
<div
className='header'
onMouseMove={dispatcher('mediaMouseMoved')}
onMouseEnter={dispatcher('mediaControlsMouseEnter')}
onMouseLeave={dispatcher('mediaControlsMouseLeave')}>
onMouseLeave={dispatcher('mediaControlsMouseLeave')}
>
{this.getTitle()}
<div className='nav left float-left'>
<i
className={'icon back ' + (loc.hasBack() ? '' : 'disabled')}
title='Back'
onClick={dispatcher('back')}>
onClick={dispatcher('back')}
>
chevron_left
</i>
<i
className={'icon forward ' + (loc.hasForward() ? '' : 'disabled')}
title='Forward'
onClick={dispatcher('forward')}>
onClick={dispatcher('forward')}
>
chevron_right
</i>
</div>
@@ -45,7 +49,8 @@ class Header extends React.Component {
<i
className='icon add'
title='Add torrent'
onClick={dispatcher('openFiles')}>
onClick={dispatcher('openFiles')}
>
add
</i>
)

View File

@@ -1,11 +1,12 @@
const React = require('react')
const PropTypes = require('prop-types')
const colors = require('material-ui/styles/colors')
class Heading extends React.Component {
static get propTypes () {
return {
level: React.PropTypes.number
level: PropTypes.number
}
}

View File

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

View File

@@ -1,8 +1,10 @@
const React = require('react')
const TextField = require('material-ui/TextField').default
const { clipboard } = require('electron')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const { isMagnetLink } = require('../lib/torrent-player')
module.exports = class OpenTorrentAddressModal extends React.Component {
render () {
@@ -15,19 +17,27 @@ module.exports = class OpenTorrentAddressModal extends React.Component {
className='control'
ref={(c) => { this.torrentURL = c }}
fullWidth
onKeyDown={handleKeyDown.bind(this)} />
onKeyDown={handleKeyDown.bind(this)}
/>
</div>
<ModalOKCancel
cancelText='CANCEL'
onCancel={dispatcher('exitModal')}
okText='OK'
onOK={handleOK.bind(this)} />
onOK={handleOK.bind(this)}
/>
</div>
)
}
componentDidMount () {
this.torrentURL.input.focus()
const clipboardContent = clipboard.readText()
if (isMagnetLink(clipboardContent)) {
this.torrentURL.input.value = clipboardContent
this.torrentURL.input.select()
}
}
}

View File

@@ -1,6 +1,9 @@
const path = require('path')
const colors = require('material-ui/styles/colors')
const electron = require('electron')
const React = require('react')
const PropTypes = require('prop-types')
const remote = electron.remote
@@ -11,15 +14,14 @@ const TextField = require('material-ui/TextField').default
// Uses the system Open File dialog.
// You can't edit the text field directly.
class PathSelector extends React.Component {
static get propTypes () {
static propTypes () {
return {
className: React.PropTypes.string,
dialog: React.PropTypes.object,
displayValue: React.PropTypes.string,
id: React.PropTypes.string,
onChange: React.PropTypes.func,
title: React.PropTypes.string.isRequired,
value: React.PropTypes.string
className: PropTypes.string,
dialog: PropTypes.object,
id: PropTypes.string,
onChange: PropTypes.func,
title: PropTypes.string.isRequired,
value: PropTypes.string
}
}
@@ -30,8 +32,8 @@ class PathSelector extends React.Component {
handleClick () {
const opts = Object.assign({
defaultPath: this.props.value,
properties: [ 'openFile', 'openDirectory' ]
defaultPath: path.dirname(this.props.value || ''),
properties: ['openFile', 'openDirectory']
}, this.props.dialog)
remote.dialog.showOpenDialog(
@@ -64,7 +66,7 @@ class PathSelector extends React.Component {
const textFieldStyle = {
flex: '1'
}
const text = this.props.displayValue || this.props.value
const text = this.props.value || ''
const buttonStyle = {
marginLeft: 10
}
@@ -74,10 +76,14 @@ class PathSelector extends React.Component {
<div className='label' style={labelStyle}>
{this.props.title}:
</div>
<TextField className='control' disabled id={id} value={text}
inputStyle={textareaStyle} style={textFieldStyle} />
<RaisedButton className='control' label='Change' onClick={this.handleClick}
style={buttonStyle} />
<TextField
className='control' disabled id={id} value={text}
inputStyle={textareaStyle} style={textFieldStyle}
/>
<RaisedButton
className='control' label='Change' onClick={this.handleClick}
style={buttonStyle}
/>
</div>
)
}

View File

@@ -1,7 +1,7 @@
const React = require('react')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
module.exports = class RemoveTorrentModal extends React.Component {
render () {
@@ -18,7 +18,8 @@ module.exports = class RemoveTorrentModal extends React.Component {
cancelText='CANCEL'
onCancel={dispatcher('exitModal')}
okText={buttonText}
onOK={handleRemove} />
onOK={handleRemove}
/>
</div>
)

View File

@@ -1,13 +1,14 @@
const React = require('react')
const PropTypes = require('prop-types')
const RaisedButton = require('material-ui/RaisedButton').default
class ShowMore extends React.Component {
static get propTypes () {
return {
defaultExpanded: React.PropTypes.bool,
hideLabel: React.PropTypes.string,
showLabel: React.PropTypes.string
defaultExpanded: PropTypes.bool,
hideLabel: PropTypes.string,
showLabel: PropTypes.string
}
}
@@ -44,7 +45,8 @@ class ShowMore extends React.Component {
<RaisedButton
className='control'
onClick={this.handleClick}
label={label} />
label={label}
/>
</div>
)
}

View File

@@ -2,7 +2,7 @@ const React = require('react')
const electron = require('electron')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher } = require('../lib/dispatcher')
module.exports = class UnsupportedMediaModal extends React.Component {
render () {
@@ -28,7 +28,8 @@ module.exports = class UnsupportedMediaModal extends React.Component {
cancelText='CANCEL'
onCancel={dispatcher('backToList')}
okText={actionText}
onOK={onAction} />
onOK={onAction}
/>
<p className='error-text'>{errorMessage}</p>
</div>
)

View File

@@ -2,7 +2,7 @@ const React = require('react')
const electron = require('electron')
const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
module.exports = class UpdateAvailableModal extends React.Component {
render () {
@@ -18,7 +18,8 @@ module.exports = class UpdateAvailableModal extends React.Component {
cancelText='SKIP THIS RELEASE'
onCancel={handleSkip}
okText='SHOW DOWNLOAD PAGE'
onOK={handleShow} />
onOK={handleShow}
/>
</div>
)

View File

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

View File

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

View File

@@ -2,9 +2,9 @@ const electron = require('electron')
const path = require('path')
const Cast = require('../lib/cast')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
const telemetry = require('../lib/telemetry')
const {UnplayableFileError, UnplayableTorrentError} = require('../lib/errors')
const { UnplayableFileError, UnplayableTorrentError } = require('../lib/errors')
const sound = require('../lib/sound')
const TorrentPlayer = require('../lib/torrent-player')
const TorrentSummary = require('../lib/torrent-summary')
@@ -102,10 +102,10 @@ module.exports = class PlaybackController {
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()
})
}
}
@@ -269,9 +269,10 @@ module.exports = class PlaybackController {
// update state
state.playing.infoHash = infoHash
state.playing.fileIndex = index
state.playing.fileName = fileSummary.name
state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video'
: TorrentPlayer.isAudio(fileSummary) ? 'audio'
: 'other'
: 'other'
// pick up where we left off
let jumpToTime = 0
@@ -292,7 +293,7 @@ module.exports = class PlaybackController {
}
function getAudioMetadata () {
if (state.playing.type === 'audio' && !fileSummary.audioInfo) {
if (state.playing.type === 'audio') {
ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index)
}
}

View File

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

View File

@@ -5,7 +5,7 @@ const parallel = require('run-parallel')
const remote = electron.remote
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
module.exports = class SubtitlesController {
constructor (state) {
@@ -15,8 +15,8 @@ module.exports = class SubtitlesController {
openSubtitles () {
remote.dialog.showOpenDialog({
title: 'Select a subtitles file.',
filters: [ { name: 'Subtitles', extensions: ['vtt', 'srt'] } ],
properties: [ 'openFile' ]
filters: [{ name: 'Subtitles', extensions: ['vtt', 'srt'] }],
properties: ['openFile']
}, (filenames) => {
if (!Array.isArray(filenames)) return
this.addSubtitles(filenames, true)

View File

@@ -3,14 +3,14 @@ const ipcRenderer = require('electron').ipcRenderer
const TorrentSummary = require('../lib/torrent-summary')
const sound = require('../lib/sound')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
module.exports = class TorrentController {
constructor (state) {
this.state = state
}
torrentInfoHash (torrentKey, infoHash) {
torrentParsed (torrentKey, infoHash, magnetURI) {
let torrentSummary = this.getTorrentSummary(torrentKey)
console.log('got infohash for %s torrent %s',
torrentSummary ? 'existing' : 'new', torrentKey)
@@ -33,6 +33,7 @@ module.exports = class TorrentController {
}
torrentSummary.infoHash = infoHash
torrentSummary.magnetURI = magnetURI
dispatch('update')
}
@@ -62,7 +63,6 @@ module.exports = class TorrentController {
torrentSummary.status = 'downloading'
torrentSummary.name = torrentSummary.displayName || torrentInfo.name
torrentSummary.path = torrentInfo.path
torrentSummary.magnetURI = torrentInfo.magnetURI
// TODO: make torrentInfo immutable, save separately as torrentSummary.info
// For now, check whether torrentSummary.files has already been set:
const hasDetailedFileInfo = torrentSummary.files && torrentSummary.files[0].path

View File

@@ -2,8 +2,8 @@ const fs = require('fs')
const path = require('path')
const electron = require('electron')
const {dispatch} = require('../lib/dispatcher')
const {TorrentKeyNotFoundError} = require('../lib/errors')
const { dispatch } = require('../lib/dispatcher')
const { TorrentKeyNotFoundError } = require('../lib/errors')
const sound = require('../lib/sound')
const TorrentSummary = require('../lib/torrent-summary')
@@ -157,23 +157,24 @@ module.exports = class TorrentListController {
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
.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)
// Pause torrent without playing sounds.
this.pauseTorrent(torrent, false)
this.state.saved.torrentsToResume.push(torrent.infoHash)
})
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)
})
@@ -317,7 +318,7 @@ module.exports = class TorrentListController {
function findFilesRecursive (paths, cb_) {
if (paths.length > 1) {
let numComplete = 0
let ret = []
const ret = []
paths.forEach(function (path) {
findFilesRecursive([path], function (fileObjs) {
ret.push(...fileObjs)

View File

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

View File

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

View File

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

View File

@@ -28,13 +28,15 @@ function run (state) {
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)
if (semver.lt(version, '0.21.0')) migrate_0_21_0(saved)
if (semver.lt(version, '0.21.1')) migrate_0_21_1(saved)
// Config is now on the new version
state.saved.version = config.APP_VERSION
}
function migrate_0_7_0 (saved) {
const cpFile = require('cp-file')
const { copyFileSync } = require('fs')
const path = require('path')
saved.torrents.forEach(function (ts) {
@@ -56,7 +58,7 @@ function migrate_0_7_0 (saved) {
dst = path.join(config.TORRENT_PATH, infoHash + '.torrent')
// Synchronous FS calls aren't ideal, but probably OK in a migration
// that only runs once
if (src !== dst) cpFile.sync(src, dst)
if (src !== dst) copyFileSync(src, dst)
delete ts.torrentPath
ts.torrentFileName = infoHash + '.torrent'
@@ -71,7 +73,7 @@ function migrate_0_7_0 (saved) {
dst = path.join(config.POSTER_PATH, infoHash + extension)
// Synchronous FS calls aren't ideal, but probably OK in a migration
// that only runs once
if (src !== dst) cpFile.sync(src, dst)
if (src !== dst) copyFileSync(src, dst)
delete ts.posterURL
ts.posterFileName = infoHash + extension
@@ -155,7 +157,7 @@ function migrate_0_17_2 (saved) {
// 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 { copyFileSync } = require('fs')
const rimraf = require('rimraf')
const OLD_NAME = 'The WIRED CD - Rip. Sample. Mash. Share.'
@@ -190,7 +192,7 @@ function migrate_0_17_2 (saved) {
ts.posterFileName = NEW_HASH + '.jpg'
rimraf.sync(path.join(config.TORRENT_PATH, ts.torrentFileName))
cpFile.sync(
copyFileSync(
path.join(config.STATIC_PATH, 'wiredCd.torrent'),
path.join(config.TORRENT_PATH, NEW_HASH + '.torrent')
)
@@ -206,3 +208,16 @@ function migrate_0_17_2 (saved) {
} catch (err) {}
}
}
function migrate_0_21_0 (saved) {
if (saved.prefs.soundNotifications == null) {
// The app used to always have sound notifications enabled
saved.prefs.soundNotifications = true
}
}
function migrate_0_21_1 (saved) {
if (saved.prefs.externalPlayerPath == null) {
saved.prefs.externalPlayerPath = ''
}
}

View File

@@ -38,7 +38,8 @@ function getPreviousIndex (state) {
function getCurrentLocalURL (state) {
return state.server
? state.server.localURL + '/' + state.playing.fileIndex
? state.server.localURL + '/' + state.playing.fileIndex + '/' +
encodeURIComponent(state.playing.fileName)
: ''
}

View File

@@ -1,13 +1,17 @@
module.exports = {
init,
play
}
const config = require('../../config')
const {InvalidSoundNameError} = require('./errors')
const { InvalidSoundNameError } = require('./errors')
const path = require('path')
const VOLUME = 0.25
// App state to access the soundNotifications preference
let state
/* Cache of Audio elements, for instant playback */
const cache = {}
@@ -46,7 +50,19 @@ const sounds = {
}
}
function init (appState) {
state = appState
}
function play (name) {
if (state == null) {
return
}
if (!state.saved.prefs.soundNotifications) {
return
}
let audio = cache[name]
if (!audio) {
const sound = sounds[name]

View File

@@ -1,6 +1,6 @@
const appConfig = require('application-config')('WebTorrent')
const path = require('path')
const {EventEmitter} = require('events')
const { EventEmitter } = require('events')
const config = require('../../config')
@@ -88,6 +88,7 @@ function getDefaultPlayState () {
return {
infoHash: null, /* the info hash of the torrent we're playing */
fileIndex: null, /* the zero-based index within the torrent */
fileName: null, /* name of the file that is playing */
location: 'local', /* 'local', 'chromecast', 'airplay' */
type: null, /* 'audio' or 'video', could be 'other' if ever support eg streaming to VLC */
currentTime: 0, /* seconds */
@@ -109,53 +110,49 @@ function getDefaultPlayState () {
}
/* If the saved state file doesn't exist yet, here's what we use instead */
function setupStateSaved (cb) {
const cpFile = require('cp-file')
const fs = require('fs')
function setupStateSaved () {
const { copyFileSync, mkdirSync, readFileSync } = require('fs')
const parseTorrent = require('parse-torrent')
const parallel = require('run-parallel')
const saved = {
prefs: {
downloadPath: config.DEFAULT_DOWNLOAD_PATH,
isFileHandler: false,
openExternalPlayer: false,
externalPlayerPath: null,
externalPlayerPath: '',
startup: false,
soundNotifications: true,
autoAddTorrents: false,
torrentsFolderPath: ''
torrentsFolderPath: '',
highestPlaybackPriority: true
},
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
torrentsToResume: [],
version: config.APP_VERSION /* make sure we can upgrade gracefully later */
}
const tasks = []
// TODO: Doing several sync calls during first startup is not ideal
mkdirSync(config.POSTER_PATH, { recursive: true })
mkdirSync(config.TORRENT_PATH, { recursive: true })
config.DEFAULT_TORRENTS.map(function (t, i) {
config.DEFAULT_TORRENTS.forEach((t, i) => {
const infoHash = saved.torrents[i].infoHash
tasks.push(function (cb) {
cpFile(
path.join(config.STATIC_PATH, t.posterFileName),
path.join(config.POSTER_PATH, infoHash + path.extname(t.posterFileName))
).then(cb).catch(cb)
})
tasks.push(function (cb) {
cpFile(
path.join(config.STATIC_PATH, t.torrentFileName),
path.join(config.TORRENT_PATH, infoHash + '.torrent')
).then(cb).catch(cb)
})
// TODO: Doing several sync calls during first startup is not ideal
copyFileSync(
path.join(config.STATIC_PATH, t.posterFileName),
path.join(config.POSTER_PATH, infoHash + path.extname(t.posterFileName))
)
copyFileSync(
path.join(config.STATIC_PATH, t.torrentFileName),
path.join(config.TORRENT_PATH, infoHash + '.torrent')
)
})
parallel(tasks, function (err) {
if (err) return cb(err)
cb(null, saved)
})
return saved
function createTorrentObject (t) {
// TODO: Doing several fs.readFileSync calls during first startup is not ideal
const torrent = fs.readFileSync(path.join(config.STATIC_PATH, t.torrentFileName))
// TODO: Doing several sync calls during first startup is not ideal
const torrent = readFileSync(path.join(config.STATIC_PATH, t.torrentFileName))
const parsedTorrent = parseTorrent(torrent)
return {
@@ -204,10 +201,14 @@ function load (cb) {
appConfig.read(function (err, saved) {
if (err || !saved.version) {
console.log('Missing config file: Creating new one')
setupStateSaved(onSavedState)
} else {
onSavedState(null, saved)
try {
saved = setupStateSaved()
} catch (err) {
onSavedState(err)
return
}
}
onSavedState(null, saved)
})
function onSavedState (err, saved) {
@@ -237,7 +238,7 @@ function saveImmediate (state, cb) {
.filter((x) => x.infoHash)
.map(function (x) {
const torrent = {}
for (let key in x) {
for (const key in x) {
if (key === 'progress' || key === 'torrentKey') {
continue // Don't save progress info or key for the webtorrent process
}

View File

@@ -74,7 +74,7 @@ function reset () {
// Track screen resolution
function getScreenInfo () {
return electron.screen.getAllDisplays().map((screen) => ({
return electron.remote.screen.getAllDisplays().map((screen) => ({
width: screen.size.width,
height: screen.size.height,
scaleFactor: screen.scaleFactor
@@ -98,7 +98,7 @@ function getSystemInfo () {
function getTorrentStats (state) {
const count = state.saved.torrents.length
let sizeMB = 0
let byStatus = {
const byStatus = {
new: { count: 0, sizeMB: 0 },
downloading: { count: 0, sizeMB: 0 },
seeding: { count: 0, sizeMB: 0 },
@@ -121,7 +121,7 @@ function getTorrentStats (state) {
}
// Then, round all the counts and sums to the nearest power of 2
const ret = roundTorrentStats({count, sizeMB})
const ret = roundTorrentStats({ count, sizeMB })
ret.byStatus = {
new: roundTorrentStats(byStatus.new),
downloading: roundTorrentStats(byStatus.downloading),
@@ -198,7 +198,7 @@ function logUncaughtError (procName, e) {
// Log the app version *at the time of the error*
const version = config.APP_VERSION
telemetry.uncaughtErrors.push({process: procName, message, stack, version})
telemetry.uncaughtErrors.push({ process: procName, message, stack, version })
}
// Turns a DOM element into a string, eg "DIV.my-class.visible"

View File

@@ -3,11 +3,14 @@ module.exports = {
isVideo,
isAudio,
isTorrent,
isMagnetLink,
isPlayableTorrentSummary
}
const path = require('path')
const mediaExtensions = require('./media-extensions')
// Checks whether a fileSummary or file path is audio/video that we can play,
// based on the file extension
function isPlayable (file) {
@@ -16,30 +19,12 @@ function isPlayable (file) {
// Checks whether a fileSummary or file path is playable video
function isVideo (file) {
return [
'.avi',
'.m4v',
'.mkv',
'.mov',
'.mp4',
'.mpg',
'.ogv',
'.webm',
'.wmv'
].includes(getFileExtension(file))
return mediaExtensions.video.includes(getFileExtension(file))
}
// Checks whether a fileSummary or file path is playable audio
function isAudio (file) {
return [
'.aac',
'.ac3',
'.mp3',
'.ogg',
'.wav',
'.flac',
'.m4a'
].includes(getFileExtension(file))
return mediaExtensions.audio.includes(getFileExtension(file))
}
// Checks if the argument is either:
@@ -47,9 +32,15 @@ function isAudio (file) {
// - a file object where obj.name is ends in .torrent
// - a string that's a magnet link (magnet://...)
function isTorrent (file) {
const isTorrentFile = getFileExtension(file) === '.torrent'
const isMagnet = typeof file === 'string' && /^(stream-)?magnet:/.test(file)
return isTorrentFile || isMagnet
return isTorrentFile(file) || isMagnetLink(file)
}
function isTorrentFile (file) {
return getFileExtension(file) === '.torrent'
}
function isMagnetLink (link) {
return typeof link === 'string' && /^(stream-)?magnet:/.test(link)
}
function getFileExtension (file) {

View File

@@ -3,39 +3,144 @@ module.exports = torrentPoster
const captureFrame = require('capture-frame')
const path = require('path')
const mediaExtensions = require('./media-extensions')
const msgNoSuitablePoster = 'Cannot generate a poster from any files in the torrent'
function torrentPoster (torrent, cb) {
// First, try to use a poster image if available
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(msgNoSuitablePoster))
}
// 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,
spectrogram: -80
}
for (const keyword in relevanceScore) {
if (fileName === keyword) {
return relevanceScore[keyword]
}
if (fileName.indexOf(keyword) !== -1) {
return relevanceScore[keyword]
}
}
return 0
}
function torrentPosterFromAudio (torrent, cb) {
const imageFiles = filterOnExtension(torrent, mediaExtensions.image)
if (imageFiles.length === 0) return cb(new Error(msgNoSuitablePoster))
const bestCover = imageFiles.map(file => {
return {
file: file,
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
})
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)
@@ -70,14 +175,19 @@ function torrentPosterFromVideo (file, torrent, cb) {
server.destroy()
if (buf.length === 0) return cb(new Error('Generated poster contains no data'))
if (buf.length === 0) return cb(new Error(msgNoSuitablePoster))
cb(null, buf, '.jpg')
}
}
}
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

@@ -5,7 +5,7 @@
* actually used because auto-prefixing is disabled with
* `darkBaseTheme.userAgent = false`. Return a fake object.
*/
let Module = require('module')
const Module = require('module')
const _require = Module.prototype.require
Module.prototype.require = function (id) {
if (id === 'inline-style-prefixer') return {}
@@ -39,9 +39,6 @@ const TorrentPlayer = require('./lib/torrent-player')
// Perf optimization: Needed immediately, so do not lazy load it below
const TorrentListController = require('./controllers/torrent-list-controller')
// Required by Material UI -- adds `onTouchTap` event
require('react-tap-event-plugin')()
const App = require('./pages/app')
// Electron apps have two processes: a main process (node) runs first and starts
@@ -77,6 +74,7 @@ function onState (err, _state) {
window.dispatch = dispatch
telemetry.init(state)
sound.init(state)
// Log uncaught JS errors
window.addEventListener(
@@ -150,6 +148,9 @@ function onState (err, _state) {
// ...same thing if you paste a torrent
document.addEventListener('paste', onPaste)
// Add YouTube style hotkey shortcuts
window.addEventListener('keydown', onKeydown)
const debouncedFullscreenToggle = debounce(function () {
dispatch('toggleFullScreen')
}, 1000, true)
@@ -234,100 +235,100 @@ function updateElectron () {
const dispatchHandlers = {
// Torrent list: creating, deleting, selecting torrents
'openTorrentFile': () => ipcRenderer.send('openTorrentFile'),
'openFiles': () => ipcRenderer.send('openFiles'), /* shows the open file dialog */
'openTorrentAddress': () => { state.modal = { id: 'open-torrent-address-modal' } },
openTorrentFile: () => ipcRenderer.send('openTorrentFile'),
openFiles: () => ipcRenderer.send('openFiles'), /* shows the open file dialog */
openTorrentAddress: () => { state.modal = { id: 'open-torrent-address-modal' } },
'addTorrent': (torrentId) => controllers.torrentList().addTorrent(torrentId),
'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) =>
addTorrent: (torrentId) => controllers.torrentList().addTorrent(torrentId),
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) =>
confirmDeleteTorrent: (infoHash, deleteData) =>
controllers.torrentList().confirmDeleteTorrent(infoHash, deleteData),
'deleteTorrent': (infoHash, deleteData) =>
deleteTorrent: (infoHash, deleteData) =>
controllers.torrentList().deleteTorrent(infoHash, deleteData),
'toggleSelectTorrent': (infoHash) =>
toggleSelectTorrent: (infoHash) =>
controllers.torrentList().toggleSelectTorrent(infoHash),
'openTorrentContextMenu': (infoHash) =>
openTorrentContextMenu: (infoHash) =>
controllers.torrentList().openTorrentContextMenu(infoHash),
'startTorrentingSummary': (torrentKey) =>
startTorrentingSummary: (torrentKey) =>
controllers.torrentList().startTorrentingSummary(torrentKey),
'saveTorrentFileAs': (torrentKey) =>
saveTorrentFileAs: (torrentKey) =>
controllers.torrentList().saveTorrentFileAs(torrentKey),
'prioritizeTorrent': (infoHash) => controllers.torrentList().prioritizeTorrent(infoHash),
'resumePausedTorrents': () => controllers.torrentList().resumePausedTorrents(),
prioritizeTorrent: (infoHash) => controllers.torrentList().prioritizeTorrent(infoHash),
resumePausedTorrents: () => controllers.torrentList().resumePausedTorrents(),
// Playback
'playFile': (infoHash, index) => controllers.playback().playFile(infoHash, index),
'playPause': () => controllers.playback().playPause(),
'nextTrack': () => controllers.playback().nextTrack(),
'previousTrack': () => controllers.playback().previousTrack(),
'skip': (time) => controllers.playback().skip(time),
'skipTo': (time) => controllers.playback().skipTo(time),
'changePlaybackRate': (dir) => controllers.playback().changePlaybackRate(dir),
'changeVolume': (delta) => controllers.playback().changeVolume(delta),
'setVolume': (vol) => controllers.playback().setVolume(vol),
'openItem': (infoHash, index) => controllers.playback().openItem(infoHash, index),
playFile: (infoHash, index) => controllers.playback().playFile(infoHash, index),
playPause: () => controllers.playback().playPause(),
nextTrack: () => controllers.playback().nextTrack(),
previousTrack: () => controllers.playback().previousTrack(),
skip: (time) => controllers.playback().skip(time),
skipTo: (time) => controllers.playback().skipTo(time),
changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir),
changeVolume: (delta) => controllers.playback().changeVolume(delta),
setVolume: (vol) => controllers.playback().setVolume(vol),
openItem: (infoHash, index) => controllers.playback().openItem(infoHash, index),
// Subtitles
'openSubtitles': () => controllers.subtitles().openSubtitles(),
'selectSubtitle': (index) => controllers.subtitles().selectSubtitle(index),
'toggleSubtitlesMenu': () => controllers.subtitles().toggleSubtitlesMenu(),
'checkForSubtitles': () => controllers.subtitles().checkForSubtitles(),
'addSubtitles': (files, autoSelect) => controllers.subtitles().addSubtitles(files, autoSelect),
openSubtitles: () => controllers.subtitles().openSubtitles(),
selectSubtitle: (index) => controllers.subtitles().selectSubtitle(index),
toggleSubtitlesMenu: () => controllers.subtitles().toggleSubtitlesMenu(),
checkForSubtitles: () => controllers.subtitles().checkForSubtitles(),
addSubtitles: (files, autoSelect) => controllers.subtitles().addSubtitles(files, autoSelect),
// Local media: <video>, <audio>, external players
'mediaStalled': () => controllers.media().mediaStalled(),
'mediaError': (err) => controllers.media().mediaError(err),
'mediaSuccess': () => controllers.media().mediaSuccess(),
'mediaTimeUpdate': () => controllers.media().mediaTimeUpdate(),
'mediaMouseMoved': () => controllers.media().mediaMouseMoved(),
'mediaControlsMouseEnter': () => controllers.media().controlsMouseEnter(),
'mediaControlsMouseLeave': () => controllers.media().controlsMouseLeave(),
'openExternalPlayer': () => controllers.media().openExternalPlayer(),
'externalPlayerNotFound': () => controllers.media().externalPlayerNotFound(),
mediaStalled: () => controllers.media().mediaStalled(),
mediaError: (err) => controllers.media().mediaError(err),
mediaSuccess: () => controllers.media().mediaSuccess(),
mediaTimeUpdate: () => controllers.media().mediaTimeUpdate(),
mediaMouseMoved: () => controllers.media().mediaMouseMoved(),
mediaControlsMouseEnter: () => controllers.media().controlsMouseEnter(),
mediaControlsMouseLeave: () => controllers.media().controlsMouseLeave(),
openExternalPlayer: () => controllers.media().openExternalPlayer(),
externalPlayerNotFound: () => controllers.media().externalPlayerNotFound(),
// Remote casting: Chromecast, Airplay, etc
'toggleCastMenu': (deviceType) => lazyLoadCast().toggleMenu(deviceType),
'selectCastDevice': (index) => lazyLoadCast().selectDevice(index),
'stopCasting': () => lazyLoadCast().stop(),
toggleCastMenu: (deviceType) => lazyLoadCast().toggleMenu(deviceType),
selectCastDevice: (index) => lazyLoadCast().selectDevice(index),
stopCasting: () => lazyLoadCast().stop(),
// Preferences screen
'preferences': () => controllers.prefs().show(),
'updatePreferences': (key, value) => controllers.prefs().update(key, value),
'checkDownloadPath': checkDownloadPath,
'startFolderWatcher': () => controllers.folderWatcher().start(),
'stopFolderWatcher': () => controllers.folderWatcher().stop(),
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),
'skipVersion': (version) => controllers.update().skipVersion(version),
updateAvailable: (version) => controllers.update().updateAvailable(version),
skipVersion: (version) => controllers.update().skipVersion(version),
// Navigation between screens (back, forward, ESC, etc)
'exitModal': () => { state.modal = null },
'backToList': backToList,
'escapeBack': escapeBack,
'back': () => state.location.back(),
'forward': () => state.location.forward(),
'cancel': () => state.location.cancel(),
exitModal: () => { state.modal = null },
backToList: backToList,
escapeBack: escapeBack,
back: () => state.location.back(),
forward: () => state.location.forward(),
cancel: () => state.location.cancel(),
// Controlling the window
'setDimensions': setDimensions,
'toggleFullScreen': (setTo) => ipcRenderer.send('toggleFullScreen', setTo),
'setTitle': (title) => { state.window.title = title },
'resetTitle': () => { state.window.title = config.APP_WINDOW_TITLE },
setDimensions: setDimensions,
toggleFullScreen: (setTo) => ipcRenderer.send('toggleFullScreen', setTo),
setTitle: (title) => { state.window.title = title },
resetTitle: () => { state.window.title = config.APP_WINDOW_TITLE },
// Everything else
'onOpen': onOpen,
'error': onError,
'uncaughtError': (proc, err) => telemetry.logUncaughtError(proc, err),
'stateSave': () => State.save(state),
'stateSaveImmediate': () => State.saveImmediate(state),
'update': () => {} // No-op, just trigger an update
onOpen: onOpen,
error: onError,
uncaughtError: (proc, err) => telemetry.logUncaughtError(proc, err),
stateSave: () => State.save(state),
stateSaveImmediate: () => State.saveImmediate(state),
update: () => {} // No-op, just trigger an update
}
// Events from the UI never modify state directly. Instead they call dispatch()
@@ -359,7 +360,7 @@ function setupIpc () {
ipcRenderer.on('windowBoundsChanged', onWindowBoundsChanged)
const tc = controllers.torrent()
ipcRenderer.on('wt-infohash', (e, ...args) => tc.torrentInfoHash(...args))
ipcRenderer.on('wt-parsed', (e, ...args) => tc.torrentParsed(...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())
@@ -450,14 +451,21 @@ function setDimensions (dimensions) {
)
ipcRenderer.send('setAspectRatio', aspectRatio)
ipcRenderer.send('setBounds', {contentBounds: true, x: null, y: null, width, height})
ipcRenderer.send('setBounds', { contentBounds: true, x: null, y: null, width, height })
state.playing.aspectRatio = aspectRatio
}
// Called when the user adds files (.torrent, files to seed, subtitles) to the app
// via any method (drag-drop, drag to app icon, command line)
function onOpen (files) {
if (!Array.isArray(files)) files = [ 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)
@@ -503,6 +511,34 @@ function onPaste (e) {
update()
}
function onKeydown (e) {
const key = e.key
if (key === 'ArrowLeft') {
dispatch('skip', -5)
} else if (key === 'ArrowRight') {
dispatch('skip', 5)
} else if (key === 'ArrowUp') {
dispatch('changeVolume', 0.1)
} else if (key === 'ArrowDown') {
dispatch('changeVolume', -0.1)
} else if (key === 'j') {
dispatch('skip', -10)
} else if (key === 'l') {
dispatch('skip', 10)
} else if (key === 'k') {
dispatch('playPause')
} else if (key === '>') {
dispatch('changePlaybackRate', 1)
} else if (key === '<') {
dispatch('changePlaybackRate', -1)
} else if (key === 'f') {
dispatch('toggleFullScreen')
}
update()
}
function onFocus (e) {
state.window.isFocused = true
state.dock.badge = 0

View File

@@ -12,10 +12,10 @@ const Header = require('../components/header')
const TorrentListPage = require('./torrent-list-page')
const Views = {
'home': createGetter(() => TorrentListPage),
'player': createGetter(() => require('./player-page')),
home: createGetter(() => TorrentListPage),
player: createGetter(() => require('./player-page')),
'create-torrent': createGetter(() => require('./create-torrent-page')),
'preferences': createGetter(() => require('./preferences-page'))
preferences: createGetter(() => require('./preferences-page'))
}
const Modals = {
@@ -88,8 +88,10 @@ class App extends React.Component {
return (<div key={i} className='error'>{error.message}</div>)
})
return (
<div key='errors'
className={'error-popover ' + (hasErrors ? 'visible' : 'hidden')}>
<div
key='errors'
className={'error-popover ' + (hasErrors ? 'visible' : 'hidden')}
>
<div key='title' className='title'>Error</div>
{errorElems}
</div>

View File

@@ -3,7 +3,7 @@ const path = require('path')
const prettyBytes = require('prettier-bytes')
const React = require('react')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const FlatButton = require('material-ui/FlatButton').default
const RaisedButton = require('material-ui/RaisedButton').default
@@ -35,7 +35,7 @@ class CreateTorrentPage extends React.Component {
// Then, exclude .DS_Store and other dotfiles
const files = info.files
.filter((f) => !containsDots(f.path, pathPrefix))
.map((f) => ({name: f.name, path: f.path, size: f.size}))
.map((f) => ({ name: f.name, path: f.path, size: f.size }))
if (files.length === 0) return (<CreateTorrentErrorPage state={state} />)
// Then, use the name of the base folder (or sole file, for a single file torrent)
@@ -65,9 +65,9 @@ class CreateTorrentPage extends React.Component {
}
// Create React event handlers only once
this.setIsPrivate = (_, isPrivate) => this.setState({isPrivate})
this.setComment = (_, comment) => this.setState({comment})
this.setTrackers = (_, trackers) => this.setState({trackers})
this.handleSetIsPrivate = (_, isPrivate) => this.setState({ isPrivate })
this.handleSetComment = (_, comment) => this.setState({ comment })
this.handleSetTrackers = (_, trackers) => this.setState({ trackers })
this.handleSubmit = handleSubmit.bind(this)
}
@@ -94,7 +94,8 @@ class CreateTorrentPage extends React.Component {
marginBottom: 10
}}
hideLabel='Hide advanced settings...'
showLabel='Show advanced settings...'>
showLabel='Show advanced settings...'
>
{this.renderAdvanced()}
</ShowMore>
<div className='float-right'>
@@ -104,12 +105,14 @@ class CreateTorrentPage extends React.Component {
style={{
marginRight: 10
}}
onClick={dispatcher('cancel')} />
onClick={dispatcher('cancel')}
/>
<RaisedButton
className='control create-torrent-button'
label='Create Torrent'
primary
onClick={this.handleSubmit} />
onClick={this.handleSubmit}
/>
</div>
</div>
)
@@ -141,9 +144,10 @@ class CreateTorrentPage extends React.Component {
<label>Private:</label>
<Checkbox
className='torrent-is-private control'
style={{display: ''}}
style={{ display: '' }}
checked={this.state.isPrivate}
onCheck={this.setIsPrivate} />
onCheck={this.handleSetIsPrivate}
/>
</div>
<div key='trackers' className='torrent-attribute'>
<label>Trackers:</label>
@@ -155,7 +159,8 @@ class CreateTorrentPage extends React.Component {
rows={2}
rowsMax={10}
value={this.state.trackers}
onChange={this.setTrackers} />
onChange={this.handleSetTrackers}
/>
</div>
<div key='comment' className='torrent-attribute'>
<label>Comment:</label>
@@ -168,7 +173,8 @@ class CreateTorrentPage extends React.Component {
rows={2}
rowsMax={10}
value={this.state.comment}
onChange={this.setComment} />
onChange={this.handleSetComment}
/>
</div>
<div key='files' className='torrent-attribute'>
<label>Files:</label>
@@ -191,9 +197,14 @@ function handleSubmit () {
path: this.state.basePath,
files: this.state.files,
announce: announceList,
private: this.state.isPrivate,
comment: this.state.comment.trim()
}
// If torrent is not private, leave private flag unset. This ensures that
// the torrent info hash will match the result generated by other tools,
// including webtorrent-cli.
if (this.state.isPrivate) options.private = true
dispatch('createTorrent', options)
}

View File

@@ -1,11 +1,10 @@
const React = require('react')
const Bitfield = require('bitfield')
const prettyBytes = require('prettier-bytes')
const zeroFill = require('zero-fill')
const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist')
const {dispatch, dispatcher} = require('../lib/dispatcher')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const config = require('../../config')
// Shows a streaming video player. Standard features + Chromecast + Airplay
@@ -20,7 +19,8 @@ module.exports = class Player extends React.Component {
<div
className='player'
onWheel={handleVolumeWheel}
onMouseMove={dispatcher('mediaMouseMoved')}>
onMouseMove={dispatcher('mediaMouseMoved')}
>
{showVideo ? renderMedia(state) : renderCastScreen(state)}
{showControls ? renderPlayerControls(state) : null}
</div>
@@ -54,6 +54,10 @@ function renderMedia (state) {
mediaElement.pause()
} else if (!state.playing.isPaused && mediaElement.paused) {
mediaElement.play()
.then(
() => dispatch('mediaSuccess'),
err => dispatch('mediaError', err.name === 'NotSupportedError' ? 'Codec unsupported' : `${err.name}: ${err.message}`)
)
}
// When the user clicks or drags on the progress bar, jump to that position
if (state.playing.jumpToTime != null) {
@@ -110,7 +114,8 @@ function renderMedia (state) {
default={isSelected ? 'default' : ''}
label={track.label}
type='subtitles'
src={track.buffer} />
src={track.buffer}
/>
)
}
}
@@ -124,10 +129,9 @@ function renderMedia (state) {
onLoadedMetadata={onLoadedMetadata}
onEnded={onEnded}
onStalled={dispatcher('mediaStalled')}
onError={dispatcher('mediaError')}
onTimeUpdate={dispatcher('mediaTimeUpdate')}
onEncrypted={dispatcher('mediaEncrypted')}
onCanPlay={onCanPlay}>
>
{trackTags}
</MediaTagName>
)
@@ -137,7 +141,8 @@ function renderMedia (state) {
<div
key='letterbox'
className='letterbox'
onMouseMove={dispatcher('mediaMouseMoved')}>
onMouseMove={dispatcher('mediaMouseMoved')}
>
{mediaTag}
{renderOverlay(state)}
</div>
@@ -163,19 +168,6 @@ function renderMedia (state) {
if (state.window.isFullScreen) dispatch('toggleFullScreen')
}
}
function onCanPlay (e) {
const elem = e.target
if (state.playing.type === 'video' &&
elem.webkitVideoDecodedByteCount === 0) {
dispatch('mediaError', 'Video codec unsupported')
} else if (elem.webkitAudioDecodedByteCount === 0) {
dispatch('mediaError', 'Audio codec unsupported')
} else {
dispatch('mediaSuccess')
elem.play()
}
}
}
function renderOverlay (state) {
@@ -204,28 +196,41 @@ function renderOverlay (state) {
)
}
/**
* Render track or disk number string
* @param common metadata.common part
* @param key should be either 'track' or 'disk'
* @return track or disk number metadata as JSX block
*/
function renderTrack (common, key) {
// Audio metadata: track-number
if (common[key] && common[key].no) {
let str = `${common[key].no}`
if (common[key].of) {
str += ` of ${common[key].of}`
}
const style = { textTransform: 'capitalize' }
return (
<div className={`audio-${key}`}>
<label style={style}>{key}</label> {str}
</div>
)
}
}
function renderAudioMetadata (state) {
const fileSummary = state.getPlayingFileSummary()
if (!fileSummary.audioInfo) return
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.artist || common.albumartist
if (artist) {
elems.push((
<div key='artist' className='audio-artist'>
@@ -233,17 +238,90 @@ function renderAudioMetadata (state) {
</div>
))
}
if (album) {
// Audio metadata: disk & track-number
const count = ['track', 'disk']
count.forEach(key => {
const nrElem = renderTrack(common, key)
if (nrElem) {
elems.push(nrElem)
}
})
// Audio metadata: album
if (common.album) {
elems.push((
<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='track' className='audio-track'>
<label>Track</label>{track}
<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 && common.catalognumber &&
common.label.length === common.catalognumber.length) {
// Assume labels & catalog-numbers are pairs
for (let n = 0; n < common.label.length; ++n) {
releaseInfo.push(common.label[0] + ' / ' + common.catalognumber[n])
}
} else {
if (common.label) {
releaseInfo.push(...common.label)
}
if (common.catalognumber) {
releaseInfo.push(...common.catalognumber)
}
}
elems.push((
<div key='release' className='audio-release'>
<label>Release</label>{releaseInfo.join(', ')}
</div>
))
}
// Audio metadata: format
const format = []
fileSummary.audioInfo.format = fileSummary.audioInfo.format || ''
if (fileSummary.audioInfo.format.container) {
format.push(fileSummary.audioInfo.format.container)
}
if (fileSummary.audioInfo.format.codec &&
fileSummary.audioInfo.format.container !== fileSummary.audioInfo.format.codec) {
format.push(fileSummary.audioInfo.format.codec)
}
if (fileSummary.audioInfo.format.bitrate) {
format.push(Math.round(fileSummary.audioInfo.format.bitrate / 1000) + ' kbps') // 128 kbps
}
if (fileSummary.audioInfo.format.sampleRate) {
format.push(Math.round(fileSummary.audioInfo.format.sampleRate / 100) / 10 + ' 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>
))
}
@@ -274,9 +352,9 @@ function renderLoadingSpinner (state) {
return (
<div key='loading' className='media-stalled'>
<div key='loading-spinner' className='loading-spinner'>&nbsp;</div>
<div key='loading-spinner' className='loading-spinner' />
<div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded
<span><span className='progress'>{fileProgress}%</span> downloaded</span>
<span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> ↑ {prettyBytes(prog.uploadSpeed || 0)}/s</span>
</div>
@@ -334,7 +412,7 @@ function renderCastScreen (state) {
function renderCastOptions (state) {
if (!state.devices.castMenu) return
const {location, devices} = state.devices.castMenu
const { location, devices } = state.devices.castMenu
const player = state.devices[location]
const items = devices.map(function (device, ix) {
@@ -343,6 +421,7 @@ function renderCastOptions (state) {
return (
<li key={ix} onClick={dispatcher('selectCastDevice', ix)}>
<i className='icon'>{isSelected ? 'radio_button_checked' : 'radio_button_unchecked'}</i>
{' '}
{name}
</li>
)
@@ -399,41 +478,47 @@ function renderPlayerControls (state) {
<div
key='cursor'
className='playback-cursor'
style={playbackCursorStyle} />
style={playbackCursorStyle}
/>
<div
key='scrub-bar'
className='scrub-bar'
draggable='true'
onDragStart={handleDragStart}
onClick={handleScrub}
onDrag={handleScrub} />
onDrag={handleScrub}
/>
</div>,
<i
key='skip-previous'
className={'icon skip-previous float-left ' + prevClass}
onClick={dispatcher('previousTrack')}>
onClick={dispatcher('previousTrack')}
>
skip_previous
</i>,
<i
key='play'
className='icon play-pause float-left'
onClick={dispatcher('playPause')}>
onClick={dispatcher('playPause')}
>
{state.playing.isPaused ? 'play_arrow' : 'pause'}
</i>,
<i
key='skip-next'
className={'icon skip-next float-left ' + nextClass}
onClick={dispatcher('nextTrack')}>
onClick={dispatcher('nextTrack')}
>
skip_next
</i>,
<i
key='fullscreen'
className='icon fullscreen float-right'
onClick={dispatcher('toggleFullScreen')}>
onClick={dispatcher('toggleFullScreen')}
>
{state.window.isFullScreen ? 'fullscreen_exit' : 'fullscreen'}
</i>
]
@@ -444,7 +529,8 @@ function renderPlayerControls (state) {
<i
key='subtitles'
className={'icon closed-caption float-right ' + captionsClass}
onClick={handleSubtitles}>
onClick={handleSubtitles}
>
closed_caption
</i>
))
@@ -457,9 +543,9 @@ function renderPlayerControls (state) {
// Add the cast buttons. Icons for each cast type, connected/disconnected:
const buttonIcons = {
'chromecast': {true: 'cast_connected', false: 'cast'},
'airplay': {true: 'airplay', false: 'airplay'},
'dlna': {true: 'tv', false: 'tv'}
chromecast: { true: 'cast_connected', false: 'cast' },
airplay: { true: 'airplay', false: 'airplay' },
dlna: { true: 'tv', false: 'tv' }
}
castTypes.forEach(function (castType) {
// Do we show this button (eg. the Chromecast button) at all?
@@ -488,7 +574,8 @@ function renderPlayerControls (state) {
<i
key={castType}
className={'icon device float-right ' + buttonClass}
onClick={buttonHandler}>
onClick={buttonHandler}
>
{buttonIcon}
</i>
))
@@ -498,9 +585,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), ' +
@@ -511,7 +598,8 @@ function renderPlayerControls (state) {
<div key='volume' className='volume float-left'>
<i
className='icon volume-icon float-left'
onMouseDown={handleVolumeMute}>
onMouseDown={handleVolumeMute}
>
{volumeIcon}
</i>
<input
@@ -519,7 +607,8 @@ function renderPlayerControls (state) {
type='range' min='0' max='1' step='0.05'
value={volume}
onChange={handleVolumeScrub}
style={volumeStyle} />
style={volumeStyle}
/>
</div>
))
@@ -542,9 +631,11 @@ function renderPlayerControls (state) {
}
return (
<div key='controls' className='controls'
<div
key='controls' className='controls'
onMouseEnter={dispatcher('mediaControlsMouseEnter')}
onMouseLeave={dispatcher('mediaControlsMouseLeave')}>
onMouseLeave={dispatcher('mediaControlsMouseLeave')}
>
{elements}
{renderCastOptions(state)}
{renderSubtitleOptions(state)}
@@ -614,7 +705,7 @@ function renderLoadingBar (state) {
for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) {
const partPresent = Bitfield.prototype.get.call(prog.bitfield, i)
if (partPresent && !lastPiecePresent) {
parts.push({start: i - fileProg.startPiece, count: 1})
parts.push({ start: i - fileProg.startPiece, count: 1 })
} else if (partPresent) {
parts[parts.length - 1].count++
}
@@ -652,14 +743,14 @@ function formatTime (time, total) {
return '0:00'
}
let totalHours = Math.floor(total / 3600)
let totalMinutes = Math.floor(total % 3600 / 60)
let hours = Math.floor(time / 3600)
const totalHours = Math.floor(total / 3600)
const totalMinutes = Math.floor(total / 60)
const hours = Math.floor(time / 3600)
let minutes = Math.floor(time % 3600 / 60)
if (totalMinutes > 9) {
minutes = zeroFill(2, minutes)
if (totalMinutes > 9 && minutes < 10) {
minutes = '0' + minutes
}
let seconds = zeroFill(2, Math.floor(time % 60))
const seconds = `0${Math.floor(time % 60)}`.slice(-2)
return (totalHours > 0 ? hours + ':' : '') + minutes + ':' + seconds
}

View File

@@ -1,5 +1,5 @@
const path = require('path')
const React = require('react')
const PropTypes = require('prop-types')
const colors = require('material-ui/styles/colors')
const Checkbox = require('material-ui/Checkbox').default
@@ -7,7 +7,7 @@ const RaisedButton = require('material-ui/RaisedButton').default
const Heading = require('../components/heading')
const PathSelector = require('../components/path-selector')
const {dispatch} = require('../lib/dispatcher')
const { dispatch } = require('../lib/dispatcher')
const config = require('../../config')
class PreferencesPage extends React.Component {
@@ -25,6 +25,9 @@ class PreferencesPage extends React.Component {
this.handleStartupChange =
this.handleStartupChange.bind(this)
this.handleSoundNotificationsChange =
this.handleSoundNotificationsChange.bind(this)
}
downloadPathSelector () {
@@ -33,11 +36,12 @@ class PreferencesPage extends React.Component {
<PathSelector
dialog={{
title: 'Select download directory',
properties: [ 'openDirectory' ]
properties: ['openDirectory']
}}
onChange={this.handleDownloadPathChange}
title='Download location'
value={this.props.state.unsaved.prefs.downloadPath} />
value={this.props.state.saved.prefs.downloadPath}
/>
</Preference>
)
}
@@ -51,9 +55,10 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={!this.props.state.unsaved.prefs.openExternalPlayer}
label={'Play torrent media files using WebTorrent'}
onCheck={this.handleOpenExternalPlayerChange} />
checked={!this.props.state.saved.prefs.openExternalPlayer}
label='Play torrent media files using WebTorrent'
onCheck={this.handleOpenExternalPlayerChange}
/>
</Preference>
)
}
@@ -67,8 +72,8 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.highestPlaybackPriority}
label={'Highest Playback Priority'}
checked={this.props.state.saved.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>
@@ -81,10 +86,10 @@ class PreferencesPage extends React.Component {
}
externalPlayerPathSelector () {
const playerPath = this.props.state.unsaved.prefs.externalPlayerPath
const playerPath = this.props.state.saved.prefs.externalPlayerPath
const playerName = this.props.state.getExternalPlayerName()
const description = this.props.state.unsaved.prefs.openExternalPlayer
const description = this.props.state.saved.prefs.openExternalPlayer
? `Torrent media files will always play in ${playerName}.`
: `Torrent media files will play in ${playerName} if WebTorrent cannot play them.`
@@ -94,12 +99,12 @@ class PreferencesPage extends React.Component {
<PathSelector
dialog={{
title: 'Select media player app',
properties: [ 'openFile' ]
properties: ['openFile']
}}
displayValue={playerName}
onChange={this.handleExternalPlayerPathChange}
title='External player'
value={playerPath ? path.dirname(playerPath) : null} />
value={playerPath}
/>
</Preference>
)
}
@@ -113,8 +118,8 @@ class PreferencesPage extends React.Component {
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.autoAddTorrents}
label={'Watch for new .torrent files and add them immediately'}
checked={this.props.state.saved.prefs.autoAddTorrents}
label='Watch for new .torrent files and add them immediately'
onCheck={(e, value) => { this.handleAutoAddTorrentsChange(e, value) }}
/>
</Preference>
@@ -122,7 +127,7 @@ class PreferencesPage extends React.Component {
}
handleAutoAddTorrentsChange (e, isChecked) {
const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath
const torrentsFolderPath = this.props.state.saved.prefs.torrentsFolderPath
if (isChecked && !torrentsFolderPath) {
alert('Select a torrents folder first.') // eslint-disable-line
e.preventDefault()
@@ -132,37 +137,37 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'autoAddTorrents', isChecked)
if (isChecked) {
dispatch('startFolderWatcher', null)
dispatch('startFolderWatcher')
return
}
dispatch('stopFolderWatcher', null)
dispatch('stopFolderWatcher')
}
torrentsFolderPathSelector () {
const torrentsFolderPath = this.props.state.unsaved.prefs.torrentsFolderPath
const torrentsFolderPath = this.props.state.saved.prefs.torrentsFolderPath
return (
<Preference>
<PathSelector
dialog={{
title: 'Select folder to watch for new torrents',
properties: [ 'openDirectory' ]
properties: ['openDirectory']
}}
displayValue={torrentsFolderPath || ''}
onChange={this.handletorrentsFolderPathChange}
onChange={this.handleTorrentsFolderPathChange}
title='Folder to watch'
value={torrentsFolderPath ? path.dirname(torrentsFolderPath) : null} />
value={torrentsFolderPath}
/>
</Preference>
)
}
handletorrentsFolderPathChange (filePath) {
handleTorrentsFolderPathChange (filePath) {
dispatch('updatePreferences', 'torrentsFolderPath', filePath)
}
setDefaultAppButton () {
const isFileHandler = this.props.state.unsaved.prefs.isFileHandler
const isFileHandler = this.props.state.saved.prefs.isFileHandler
if (isFileHandler) {
return (
<Preference>
@@ -176,7 +181,8 @@ class PreferencesPage extends React.Component {
<RaisedButton
className='control'
onClick={this.handleSetDefaultApp}
label='Make WebTorrent the default' />
label='Make WebTorrent the default'
/>
</Preference>
)
}
@@ -185,25 +191,40 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'startup', isChecked)
}
setStartupSection () {
setStartupCheckbox () {
if (config.IS_PORTABLE) {
return
}
return (
<PreferencesSection title='Startup'>
<Preference>
<Checkbox
className='control'
checked={this.props.state.unsaved.prefs.startup}
label={'Open WebTorrent on startup.'}
onCheck={this.handleStartupChange}
/>
</Preference>
</PreferencesSection>
<Preference>
<Checkbox
className='control'
checked={this.props.state.saved.prefs.startup}
label='Open WebTorrent on startup'
onCheck={this.handleStartupChange}
/>
</Preference>
)
}
soundNotificationsCheckbox () {
return (
<Preference>
<Checkbox
className='control'
checked={this.props.state.saved.prefs.soundNotifications}
label='Enable sounds'
onCheck={this.handleSoundNotificationsChange}
/>
</Preference>
)
}
handleSoundNotificationsChange (e, isChecked) {
dispatch('updatePreferences', 'soundNotifications', isChecked)
}
handleSetDefaultApp () {
dispatch('updatePreferences', 'isFileHandler', true)
}
@@ -229,7 +250,10 @@ class PreferencesPage extends React.Component {
<PreferencesSection title='Default torrent app'>
{this.setDefaultAppButton()}
</PreferencesSection>
{this.setStartupSection()}
<PreferencesSection title='General'>
{this.setStartupCheckbox()}
{this.soundNotificationsCheckbox()}
</PreferencesSection>
</div>
)
}
@@ -238,7 +262,7 @@ class PreferencesPage extends React.Component {
class PreferencesSection extends React.Component {
static get propTypes () {
return {
title: React.PropTypes.string
title: PropTypes.string
}
}

View File

@@ -6,7 +6,7 @@ const LinearProgress = require('material-ui/LinearProgress').default
const TorrentSummary = require('../lib/torrent-summary')
const TorrentPlayer = require('../lib/torrent-player')
const {dispatcher} = require('../lib/dispatcher')
const { dispatcher } = require('../lib/dispatcher')
module.exports = class TorrentList extends React.Component {
render () {
@@ -67,7 +67,8 @@ module.exports = class TorrentList extends React.Component {
style={style}
className={classes.join(' ')}
onContextMenu={infoHash && dispatcher('openTorrentContextMenu', infoHash)}
onClick={infoHash && dispatcher('toggleSelectTorrent', infoHash)}>
onClick={infoHash && dispatcher('toggleSelectTorrent', infoHash)}
>
{this.renderTorrentMetadata(torrentSummary)}
{infoHash ? this.renderTorrentButtons(torrentSummary) : null}
{isSelected ? this.renderTorrentDetails(torrentSummary) : null}
@@ -130,7 +131,8 @@ module.exports = class TorrentList extends React.Component {
}}
checked={isActive}
onClick={stopPropagation}
onCheck={dispatcher('toggleTorrent', infoHash)} />
onCheck={dispatcher('toggleTorrent', infoHash)}
/>
)
}
@@ -147,7 +149,7 @@ module.exports = class TorrentList extends React.Component {
}
}
return (
<div style={styles.wrapper}>
<div key='progress-bar' style={styles.wrapper}>
<LinearProgress style={styles.progress} mode='determinate' value={progress} />
</div>
)
@@ -200,7 +202,7 @@ module.exports = class TorrentList extends React.Component {
const minutesStr = (hours || minutes) ? minutes + 'm' : ''
const secondsStr = seconds + 's'
return (<span>{hoursStr} {minutesStr} {secondsStr} remaining</span>)
return (<span key='eta'>{hoursStr} {minutesStr} {secondsStr} remaining</span>)
}
function renderTorrentStatus () {
@@ -210,13 +212,15 @@ module.exports = class TorrentList extends React.Component {
else if (torrentSummary.progress.progress === 1) status = 'Not seeding'
else status = 'Paused'
} else if (torrentSummary.status === 'downloading') {
status = 'Downloading'
if (!torrentSummary.progress) status = ''
else if (!torrentSummary.progress.ready) status = 'Verifying'
else status = 'Downloading'
} else if (torrentSummary.status === 'seeding') {
status = 'Seeding'
} else { // torrentSummary.status is 'new' or something unexpected
status = ''
}
return (<span>{status}</span>)
return (<span key='torrent-status'>{status}</span>)
}
}
@@ -232,8 +236,9 @@ module.exports = class TorrentList extends React.Component {
<i
key='play-button'
title='Start streaming'
className={'icon play'}
onClick={dispatcher('playFile', infoHash)}>
className='icon play'
onClick={dispatcher('playFile', infoHash)}
>
play_circle_outline
</i>
)
@@ -246,7 +251,8 @@ module.exports = class TorrentList extends React.Component {
key='delete-button'
className='icon delete'
title='Remove torrent'
onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}>
onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}
>
close
</i>
</div>
@@ -312,7 +318,7 @@ module.exports = class TorrentList extends React.Component {
torrentSummary.progress.files[index]) {
const fileProg = torrentSummary.progress.files[index]
isDone = fileProg.numPiecesPresent === fileProg.numPieces
progress = Math.round(100 * fileProg.numPiecesPresent / fileProg.numPieces) + '%'
progress = Math.floor(100 * fileProg.numPiecesPresent / fileProg.numPieces) + '%'
}
// Second, for media files where we saved our position, show how far we got
@@ -355,8 +361,10 @@ module.exports = class TorrentList extends React.Component {
<td className={'col-size ' + rowClass}>
{prettyBytes(file.length)}
</td>
<td className='col-select'
onClick={dispatcher('toggleTorrentFile', infoHash, index)}>
<td
className='col-select'
onClick={dispatcher('toggleTorrentFile', infoHash, index)}
>
<i className='icon deselect-file'>{isSelected ? 'close' : 'add'}</i>
</td>
</tr>
@@ -365,21 +373,21 @@ module.exports = class TorrentList extends React.Component {
renderRadialProgressBar (fraction, cssClass) {
const rotation = 360 * fraction
const transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'}
const transformFix = {transform: 'rotate(' + rotation + 'deg)'}
const transformFill = { transform: 'rotate(' + (rotation / 2) + 'deg)' }
const transformFix = { transform: 'rotate(' + rotation + 'deg)' }
return (
<div key='radial-progress' className={'radial-progress ' + cssClass}>
<div key='circle' className='circle'>
<div key='mask-full' className='mask full' style={transformFill}>
<div key='fill' className='fill' style={transformFill} />
<div className='circle'>
<div className='mask full' style={transformFill}>
<div className='fill' style={transformFill} />
</div>
<div key='mask-half' className='mask half'>
<div key='fill' className='fill' style={transformFill} />
<div key='fill-fix' className='fill fix' style={transformFix} />
<div className='mask half'>
<div className='fill' style={transformFill} />
<div className='fill fix' style={transformFix} />
</div>
</div>
<div key='inset' className='inset' />
<div className='inset' />
</div>
)
}
@@ -393,7 +401,7 @@ function getErrorMessage (torrentSummary) {
const err = torrentSummary.error
if (err === 'path-missing') {
return (
<span>
<span key='path-missing'>
Path missing.<br />
Fix and restart the app, or delete the torrent.
</span>

View File

@@ -8,15 +8,14 @@ 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')
const zeroFill = require('zero-fill')
const crashReporter = require('../crash-reporter')
const config = require('../config')
const {TorrentKeyNotFoundError} = require('./lib/errors')
const { TorrentKeyNotFoundError } = require('./lib/errors')
const torrentPoster = require('./lib/torrent-poster')
// Report when the process crashes
@@ -41,10 +40,9 @@ const VERSION = require('../../package.json').version
* '0.16.1' -> '0016'
* '1.2.5' -> '0102'
*/
const VERSION_STR = VERSION.match(/([0-9]+)/g)
.slice(0, 2)
.map((v) => zeroFill(2, v))
.join('')
const VERSION_STR = VERSION
.replace(/\d*./g, v => `0${v % 100}`.slice(-2))
.slice(0, 4)
/**
* Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
@@ -97,8 +95,8 @@ function init () {
ipc.send('ipcReadyWebTorrent')
window.addEventListener('error', (e) =>
ipc.send('wt-uncaught-error', {message: e.error.message, stack: e.error.stack}),
true)
ipc.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }),
true)
setInterval(updateTorrentProgress, 1000)
console.timeEnd('init')
@@ -149,7 +147,7 @@ function addTorrentEvents (torrent) {
torrent.on('error', (err) =>
ipc.send('wt-error', torrent.key, err.message))
torrent.on('infoHash', () =>
ipc.send('wt-infohash', torrent.key, torrent.infoHash))
ipc.send('wt-parsed', torrent.key, torrent.infoHash, torrent.magnetURI))
torrent.on('metadata', torrentMetadata)
torrent.on('ready', torrentReady)
torrent.on('done', torrentDone)
@@ -251,7 +249,7 @@ function generateTorrentPoster (torrentKey) {
function updateTorrentProgress () {
const progress = getTorrentProgress()
// TODO: diff torrent-by-torrent, not once for the whole update
if (prevProgress && deepEqual(progress, prevProgress, {strict: true})) {
if (prevProgress && deepEqual(progress, prevProgress, { strict: true })) {
return /* don't send heavy object if it hasn't changed */
}
ipc.send('wt-progress', progress)
@@ -320,7 +318,8 @@ function startServerFromReadyTorrent (torrent, cb) {
const info = {
torrentKey: torrent.key,
localURL: 'http://localhost' + urlSuffix,
networkURL: 'http://' + networkAddress() + urlSuffix
networkURL: 'http://' + networkAddress() + urlSuffix,
networkAddress: networkAddress()
}
ipc.send('wt-server-running', info)
@@ -334,16 +333,40 @@ 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,
observer: event => {
ipc.send('wt-audio-metadata', infoHash, index, event.metadata)
}
}
const onMetaData = file.done
// If completed; use direct file access
? mm.parseFile(path.join(torrent.path, file.path), options)
// otherwise stream
: mm.parseStream(file.createReadStream(), file.name, options)
onMetaData
.then(
() => console.log(`metadata for file='${file.name}' completed.`),
err => {
console.log(
`error getting audio metadata for ${infoHash}:${index}`,
err
)
}
)
}
function selectFiles (torrentOrInfoHash, selections) {

View File

@@ -1,4 +0,0 @@
#!/bin/sh
set -e
chmod +x /opt/webtorrent-desktop/WebTorrent
ln -s -f /opt/webtorrent-desktop/WebTorrent /usr/bin/webtorrent-desktop

View File

@@ -1,3 +0,0 @@
#!/bin/sh
set -e
rm /usr/bin/webtorrent-desktop

View File

@@ -1,33 +0,0 @@
[Desktop Entry]
Name=$APP_NAME
Version=1.0
GenericName=BitTorrent Client
X-GNOME-FullName=$APP_NAME
Comment=Download and share files over BitTorrent
Encoding=UTF-8
Type=Application
Icon=webtorrent-desktop
Terminal=false
Path=$APP_PATH
Exec=$EXEC_PATH %U
TryExec=$TRY_EXEC_PATH
StartupNotify=false
Categories=Network;FileTransfer;P2P;
MimeType=application/x-bittorrent;x-scheme-handler/magnet;x-scheme-handler/stream-magnet;
Actions=CreateNewTorrent;OpenTorrentFile;OpenTorrentAddress;
[Desktop Action CreateNewTorrent]
Name=Create New Torrent...
Exec=$EXEC_PATH -n
Path=$APP_PATH
[Desktop Action OpenTorrentFile]
Name=Open Torrent File...
Exec=$EXEC_PATH -o
Path=$APP_PATH
[Desktop Action OpenTorrentAddress]
Name=Open Torrent Address...
Exec=$EXEC_PATH -u
Path=$APP_PATH

View File

@@ -0,0 +1,26 @@
[Desktop Entry]
Type=Application
<% if (version) { %>Version=<%= version %><% } %>
Name=<%= productName %>
<% if (genericName) { %>GenericName=<%= genericName %><% } %>
<% if (description) { %>Comment=<%= description %><% } %>
Icon=<%= name %>
<% if (name) { %>Exec=<%= name %> %U<% } %>
Terminal=false
Actions=CreateNewTorrent;OpenTorrentFile;OpenTorrentAddress;
<% if (mimeType && mimeType.length) { %>MimeType=<%= mimeType.join(';') %>;<% } %>
<% if (categories && categories.length) { %>Categories=<%= categories.join(';') %>;<% } %>
StartupNotify=true
<% if (name) { %>StartupWMClass=<%= name %> <% } %>
[Desktop Action CreateNewTorrent]
Name=Create New Torrent...
<% if (name) { %>Exec=<%= name %> -n <% } %>
[Desktop Action OpenTorrentFile]
Name=Open Torrent File...
<% if (name) { %>Exec=<%= name %> -o <% } %>
[Desktop Action OpenTorrentAddress]
Name=Open Torrent Address...
<% if (name) { %>Exec=<%= name %> -u <% } %>

View File

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

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.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

After

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 KiB

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 KiB

After

Width:  |  Height:  |  Size: 681 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 844 KiB

After

Width:  |  Height:  |  Size: 873 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.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1024 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Some files were not shown because too many files have changed in this diff Show More