Compare commits

...

658 Commits

Author SHA1 Message Date
Feross Aboukhadijeh
b65b186157 0.23.0 2020-07-15 20:08:06 -07:00
Feross Aboukhadijeh
0945901cc0 Update CHANGELOG.md 2020-07-15 20:07:32 -07:00
Feross Aboukhadijeh
e60c8a2144 update dep 2020-07-15 20:07:24 -07:00
Feross Aboukhadijeh
ed3f3f11f6 update tape, pngjs deps 2020-07-15 19:24:08 -07:00
Feross Aboukhadijeh
0572997a44 Update RELEASE_PROCESS.md 2020-07-15 19:15:47 -07:00
Feross Aboukhadijeh
4ce10859e7 0.22.0 changelog formatting 2020-07-15 18:17:03 -07:00
Feross Aboukhadijeh
eb5a291c8b Update RELEASE_PROCESS.md 2020-07-15 18:02:30 -07:00
Feross Aboukhadijeh
4b6394d0d5 0.22.0 2020-07-15 17:55:28 -07:00
Feross Aboukhadijeh
6b075ebe83 update integration tests to pass 2020-07-15 17:55:02 -07:00
Feross Aboukhadijeh
9b4dea29f4 lock spectron to 11.0.0
11.1.0 contains breaking changes. See: https://github.com/electron-userland/spectron/issues/647
2020-07-15 16:38:55 -07:00
Feross Aboukhadijeh
a2539b15d5 Prep for 0.22.0 release
Supercedes: https://github.com/webtorrent/webtorrent-desktop/pull/1730

Thanks @hicom150 :)
2020-07-15 15:39:39 -07:00
Feross Aboukhadijeh
a0da514f33 Update package.js 2020-07-15 15:22:01 -07:00
Feross Aboukhadijeh
ff5ae7a054 authors 2020-07-15 14:47:28 -07:00
Feross Aboukhadijeh
27bbdcc465 fixes for Electron 9 2020-07-15 14:35:03 -07:00
Feross Aboukhadijeh
1f19ed9a2d update electron to 9; update related deps 2020-07-15 14:34:27 -07:00
Feross Aboukhadijeh
06ce97bf6c Remove mkdirp
Fixes: https://github.com/webtorrent/webtorrent-desktop/issues/1831
2020-07-15 14:03:27 -07:00
Feross Aboukhadijeh
5c67a561c1 update easy deps
Update deps

from ~70 down to 34 vulnerabilities (33 low, 1 moderate)
2020-07-15 14:03:26 -07:00
Feross Aboukhadijeh
1ee49318fc Merge pull request #1830 from webtorrent/dependabot/npm_and_yarn/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19
2020-07-15 13:47:25 -07:00
dependabot[bot]
cc06379ce8 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-15 20:38:26 +00:00
Feross Aboukhadijeh
66ac79c709 Merge pull request #1828 from webtorrent/dependabot/npm_and_yarn/electron-7.2.4 2020-07-15 13:37:32 -07:00
dependabot[bot]
ad252c5fc6 Bump electron from 7.1.0 to 7.2.4
Bumps [electron](https://github.com/electron/electron) from 7.1.0 to 7.2.4.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v7.1.0...v7.2.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-07 00:30:18 +00:00
Feross Aboukhadijeh
3d66d32597 Merge pull request #1806 from LinusU/object-shorthand 2020-05-17 12:43:29 -07:00
Feross Aboukhadijeh
e4962fca52 Merge pull request #1776 from webtorrent/greenkeeper/cross-zip-3.0.0 2020-05-10 11:34:04 -07:00
Linus Unnebäck
81b3d64f42 Use object shorthand for properties 2020-04-26 15:14:36 +01:00
Borewit
cf0cf79440 Merge pull request #1795 from webtorrent/greenkeeper/music-metadata-6.3.6
Greenkeeper/music metadata 6.3.6
2020-03-16 18:11:00 +01:00
greenkeeper[bot]
a8e6796002 chore(package): update lockfile package-lock.json 2020-03-11 07:37:52 +00:00
greenkeeper[bot]
f77aff6adf fix(package): update music-metadata to version 6.3.6
Closes #1774
2020-03-11 07:37:48 +00:00
Borewit
7fd028e5fa Merge pull request #1787 from dsernst/dynamic-copyright-year
Automatically keep the copyright year up-to-date
2020-02-09 14:15:31 +01:00
David Ernst
74f1be5452 Remove unnecessary parentheses 2020-02-08 12:37:48 -08:00
Borewit
77317aadc4 Merge pull request #1582 from dsernst/feature/sort-file-alphanumerically-1185
Context menu option to sort files by name
2020-02-08 12:01:55 +01:00
David Ernst
8eb2fb8d5d Automatically keep the copyright year up-to-date 2020-02-05 16:15:17 -08:00
David Ernst
58de27c484 Merge branch 'master' into feature/sort-file-alphanumerically-1185 2020-02-05 09:22:51 -08:00
David Ernst
185f5ec30b Remove unnecessary natsort dep 2020-02-05 09:17:58 -08:00
greenkeeper[bot]
351e890db5 chore(package): update lockfile package-lock.json 2020-01-16 00:28:55 +00:00
greenkeeper[bot]
6f2a00c785 chore(package): update cross-zip to version 3.0.0 2020-01-16 00:28:51 +00:00
Borewit
6f2aa86229 Merge pull request #1772 from webtorrent/greenkeeper/music-metadata-6.1.0
Update music metadata 6.1.0
2020-01-08 20:02:17 +01:00
Borewit
38aef81476 Add additional audio file extensions: MPEG-Layer-2, Musepack, Matroska audio, WavePack 2020-01-08 19:58:48 +01:00
Borewit
9dc3bb4f03 Pass FileInfo into music-metadata.parseStream() according API v6.
Update metadata if music-metadata parse method resolves.
2020-01-06 12:01:38 +01:00
Borewit
9291449737 Sync lock file 2020-01-06 11:01:04 +01:00
greenkeeper[bot]
98ee69d696 chore(package): update lockfile package-lock.json 2020-01-05 16:41:17 +00:00
greenkeeper[bot]
c5d20d56ec fix(package): update music-metadata to version 6.1.0
Closes #1749
2020-01-05 16:41:12 +00:00
Julen Garcia Leunda
e341367583 Merge pull request #1239 from SharonGrossman/feat/remove-all-torrents
feat(delete-all-torrents): added remove all torrents capability to the Transfers menu
2020-01-05 16:29:01 +01:00
Diego Rodríguez Baquero
f92a4c3e7d Merge pull request #1764 from hicom150/remove_deep_equal
Remove deep-equal dependency
2020-01-04 13:07:57 -05:00
Julen Garcia Leunda
5b894df71b fix lint errors 2019-12-19 18:34:35 +01:00
Julen Garcia Leunda
4577670fa8 Merge branch 'master' into feat/remove-all-torrents 2019-12-19 18:26:29 +01:00
Julen Garcia Leunda
6cac1f84ec Merge pull request #1757 from webtorrent/greenkeeper/semver-7.0.0
Update semver to the latest version 🚀
2019-12-19 17:46:05 +01:00
Julen Garcia Leunda
3c6b189ae6 Remove deep-equal dependency 2019-12-19 17:23:17 +01:00
greenkeeper[bot]
570145b2a4 chore(package): update lockfile package-lock.json 2019-12-14 20:06:27 +00:00
greenkeeper[bot]
95f2356b4c fix(package): update semver to version 7.0.0 2019-12-14 20:06:23 +00:00
Julen Garcia Leunda
3b7a9f9830 Merge pull request #1751 from hicom150/update_to_dialog_sync_functions
Fix deprecated dialog callback functions
2019-12-12 22:09:58 +01:00
Julen Garcia Leunda
61b92efcda Merge pull request #1746 from webtorrent/greenkeeper/application-config-2.0.0
Update application-config to the latest version 🚀
2019-12-06 17:17:25 +01:00
hicom150
b3026dbd87 Merge remote-tracking branch 'upstream/master' into update_application_config 2019-12-06 16:58:15 +01:00
hicom150
2eea86d8eb fix application-config update 2019-12-06 16:48:53 +01:00
Julen Garcia Leunda
46fb391d6c Merge pull request #1752 from hicom150/architectures
Show all supported architectures
2019-11-28 18:47:35 +01:00
Julen Garcia Leunda
79b3597637 Merge pull request #1740 from webtorrent/greenkeeper/electron-7.1.0
Update electron to the latest version 🚀
2019-11-28 17:23:45 +01:00
Julen Garcia Leunda
32dc268891 Merge pull request #1735 from akaustav/master
Fixed Material UI Getting Started link
2019-11-28 17:23:01 +01:00
Julen Garcia Leunda
02975e6b0e Merge branch 'master' into greenkeeper/electron-7.1.0 2019-11-28 17:21:23 +01:00
Julen Garcia Leunda
83dd5e35a7 Fix integration tests 2019-11-28 17:13:37 +01:00
Julen Garcia Leunda
1d6054f78b Merge pull request #1750 from webtorrent/greenkeeper/nodemon-2.0.0
Update nodemon to the latest version 🚀
2019-11-28 16:39:56 +01:00
Julen Garcia Leunda
8a947a2f80 Merge pull request #1748 from webtorrent/update-music-metadata
Update music-metadata to 4.9.2
2019-11-28 16:35:29 +01:00
Julen Garcia Leunda
6c22f5f595 show all supported architectures 2019-11-28 16:22:23 +01:00
Julen Garcia Leunda
bea86b2a67 Merge pull request #1743 from gpatarin/issue-1741-savetorrentas
Fix for issue #1741 (Save Torrent File As...)
2019-11-26 17:11:25 +01:00
Julen Garcia Leunda
82f873270e Merge pull request #1737 from akaustav/fix-about-window
Fix: Increase height of 'About' window
2019-11-26 17:01:50 +01:00
Julen Garcia Leunda
2b8fa8b6fe fix review comments 2019-11-26 16:59:10 +01:00
Julen Garcia Leunda
38390405dc Merge remote-tracking branch 'upstream/master' into fix-about-window 2019-11-26 16:55:44 +01:00
Gael Patarin
f30343b821 Added buttonLabel as Save in the save dialog 2019-11-26 14:33:47 +01:00
hicom150
d738021285 Fix deprecated dialog callback functions 2019-11-23 00:03:20 +01:00
greenkeeper[bot]
08e32dde7e chore(package): update lockfile package-lock.json 2019-11-20 16:03:51 +00:00
greenkeeper[bot]
229321c6c5 chore(package): update nodemon to version 2.0.0 2019-11-20 16:03:40 +00:00
Borewit
3b65f2a4a7 Update music-metadata to 4.9.2 2019-11-19 21:13:56 +01:00
Diego Rodríguez Baquero
93b0e42609 Merge pull request #1739 from webtorrent/greenkeeper/depcheck-0.9.0
Update depcheck to the latest version 🚀
2019-11-18 12:36:00 -05:00
Diego Rodríguez Baquero
633cdb4a82 Merge pull request #1742 from webtorrent/greenkeeper/languagedetect-2.0.0
Update languagedetect to the latest version 🚀
2019-11-18 12:33:47 -05:00
greenkeeper[bot]
eda0e0f964 chore(package): update lockfile package-lock.json 2019-11-16 18:01:35 +00:00
greenkeeper[bot]
a850cf6635 fix(package): update application-config to version 2.0.0 2019-11-16 18:01:31 +00:00
Feross Aboukhadijeh
af8b9cf1f2 Update drag-drop to the latest version 🚀 (#1744)
Update drag-drop to the latest version 🚀
2019-11-12 16:13:31 -08:00
greenkeeper[bot]
cc6cc41ad3 chore(package): update lockfile package-lock.json 2019-11-13 00:07:46 +00:00
greenkeeper[bot]
8228036936 fix(package): update drag-drop to version 6.0.0 2019-11-13 00:07:40 +00:00
gpatarin
600646b6ce Making the save file dialog sync to ensure working properly on MacOS and others 2019-11-09 23:58:43 +01:00
greenkeeper[bot]
fc73d2e6fd chore(package): update lockfile package-lock.json 2019-11-05 23:41:49 +00:00
greenkeeper[bot]
616cef87cb fix(package): update languagedetect to version 2.0.0 2019-11-05 23:41:45 +00:00
greenkeeper[bot]
fdc65d6e51 chore(package): update lockfile package-lock.json 2019-11-05 14:45:08 +00:00
greenkeeper[bot]
be922ef7c1 chore(package): update electron to version 7.1.0 2019-11-05 14:45:03 +00:00
greenkeeper[bot]
7dd5385f15 chore(package): update lockfile package-lock.json 2019-11-01 22:46:37 +00:00
greenkeeper[bot]
a5a199b63c chore(package): update depcheck to version 0.9.0 2019-11-01 22:46:32 +00:00
Ameet Kaustav
ab129fa233 Fix: Increase Height of 'About' window even more 2019-10-31 01:17:09 -07:00
Mathias Rasmussen
35717e9dfb Fix config copyright path in about window 2019-10-30 12:58:30 +01:00
Ameet Kaustav
a881668b38 Fix: Increase height of 'About' window 2019-10-29 22:28:07 -07:00
Ameet Kaustav
7e92647360 Fixed Material UI Getting Started link 2019-10-29 19:54:03 -07:00
Feross Aboukhadijeh
0198d7493d spectron@9 2019-10-23 13:23:34 -07:00
Feross Aboukhadijeh
04d9bc8752 Bump versions 2019-10-23 13:21:49 -07:00
Feross Aboukhadijeh
3e5fdcf6b9 Fixes for Electron 7 2019-10-23 13:19:24 -07:00
Feross Aboukhadijeh
d538a23ad0 Update electron to the latest version 🚀 (#1729)
Update electron to the latest version 🚀
2019-10-23 13:19:12 -07:00
Feross Aboukhadijeh
8de77e3d11 use org .github repo 2019-10-23 11:37:03 -07:00
greenkeeper[bot]
985def222e chore(package): update lockfile package-lock.json 2019-10-22 06:04:33 +00:00
greenkeeper[bot]
9bf4576a50 chore(package): update electron to version 7.0.0 2019-10-22 06:04:22 +00:00
Feross Aboukhadijeh
a5c41c8313 Update open to the latest version 🚀 (#1724)
Update open to the latest version 🚀
2019-10-19 12:56:42 -07:00
Julen Garcia Leunda
11d04d52c1 Merge pull request #1720 from webtorrent/type-issues
Fix a few type errors
2019-10-18 11:26:25 +02:00
greenkeeper[bot]
cb5af1a396 chore(package): update lockfile package-lock.json 2019-10-15 18:45:03 +00:00
greenkeeper[bot]
8a487cff9e chore(package): update open to version 7.0.0 2019-10-15 18:44:44 +00:00
Julen Garcia Leunda
45920e065e Merge pull request #1712 from hicom150/change_audio_tracks
Add support for multiple audio tracks
2019-10-13 22:54:59 +02:00
hicom150
2d7b6c01d0 Merge branch 'master' into change_audio_tracks 2019-10-13 22:38:50 +02:00
Julen Garcia Leunda
c33acd0746 Merge pull request #1711 from hicom150/fix_codec_unsupported_detection
Improve codec unsupported detection
2019-10-13 22:12:56 +02:00
hicom150
f2c531baa4 use videoTracks and audioTracks to check unsupported codecs 2019-10-12 17:44:13 +02:00
hicom150
287bcfd703 Merge branch 'master' into fix_codec_unsupported_detection 2019-10-11 16:56:34 +02:00
Mathias Rasmussen
e71555e664 Fix type errors 2019-10-09 19:34:30 +02:00
Borewit
bc921c889e Merge pull request #1719 from webtorrent/update-music-metadata
Update music-metadata to v4.8.0
2019-10-08 10:54:08 +02:00
Julen Garcia Leunda
59ebe132db Merge pull request #1707 from hicom150/no_sandbox
Add --no-sandbox option to desktop files
2019-10-07 22:57:04 +02:00
hicom150
b47d335e86 Merge branch 'fix_codec_unsupported_detection' of github.com:hicom150/webtorrent-desktop into fix_codec_unsupported_detection 2019-10-07 22:08:46 +02:00
hicom150
f5e186dc67 Apply review changes 2019-10-07 22:06:50 +02:00
hicom150
3d85803df9 Improve codec unsupported detection 2019-10-07 22:06:50 +02:00
Borewit
5c64fb5d35 Update music-metadata to v4.8.0 2019-10-07 20:01:19 +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
hicom150
0d27223f16 Add support for multiple audio tracks 2019-10-06 22:10:35 +02: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
Julen Garcia Leunda
f65fc68179 Add --no-sandbox option to desktop files 2019-09-19 22:41:34 +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
David Ernst
aab918ff1a Fix lint error (Line >100 chars) 2019-05-06 16:23:50 -07:00
David Ernst
ca97413b76 Update package-lock.json for natsort 2019-05-06 16:00:23 -07: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
2466b2e0a6 Add context menu option to toggle Sort by Name 2019-05-04 22:39:27 -07:00
David Ernst
2dc2953831 Merge branch 'master' into feature/sort-file-alphanumerically-1185 2019-05-04 22:03:59 -07: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
Mathias Rasmussen
60862ec01d refactor torrent-list-controller 2018-10-08 01:52:21 +02:00
Sharon Grossman
b7fedb1983 feat(delete-all-torrents): added remove all torrents capability to the Transfers menu 2018-10-08 01:52:21 +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
Feross Aboukhadijeh
5bf7618dde 0.19.0 2018-01-26 16:13:05 +08:00
Feross Aboukhadijeh
d02f6c7d18 Rewrite changelog for v0.19.0 2018-01-26 16:10:50 +08:00
Feross Aboukhadijeh
e69a18e548 authors 2018-01-26 15:56:59 +08:00
Feross Aboukhadijeh
991e2d2024 Update electron to 1.6.16 2018-01-26 15:56:59 +08:00
Feross Aboukhadijeh
9f31f30925 Mitigate protocol handler vulnerability 2018-01-26 15:56:59 +08:00
Feross Aboukhadijeh
340658c747 set version back to 0.18 -- will bump it during release 2018-01-26 15:56:09 +08:00
Alberto Miranda
5bbd9cfabb 0.19.0 2018-01-26 15:56:09 +08:00
Alberto Miranda
03a8b40ee9 Updated changelog. 2018-01-26 15:56:09 +08:00
Benjamin Tan
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
Benjamin Tan
d0cc157003 Merge pull request #1247 from auyer/master
"Error" text on missing Codec error changed to "Unable to Play"
2018-01-21 21:15:11 +08:00
Bilal Elmoussaoui
c990738c57 Fix the desktop file 2018-01-20 10:19:23 +01:00
Cezar Carneiro
01971e2a46 toggle fullscreen on end 2018-01-15 13:10:04 -05:00
SimplyAhmazing
08cb82e255 Update copyright a year later 2018-01-15 13:09:18 -05:00
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
Alberto Miranda
cae40b44e6 Merge pull request #1154 from webtorrent/watch-folder
Merging watch folder feature.
2017-10-16 22:47:03 -03:00
Auyer
ab0bbcea94 Error on missing Codec changed to "Unable to Play"
The error message was harsh, and new users might close the software
imediatly after it fails.
2017-10-03 11:14:44 -03:00
greenkeeper[bot]
06a7eff0ab docs(readme): add Greenkeeper badge 2017-09-29 01:59:08 +00:00
greenkeeper[bot]
341333c287 chore(package): update dependencies 2017-09-29 01:55:26 +00:00
Borewit
0f00985b75 Eliminated mime dependency 2017-09-20 09:14:51 +02:00
Borewit
c11a86d849 Fix max line length 2017-09-17 22:24:16 +02:00
Borewit
52e16c33b4 Fix lint error 2017-09-17 22:19:32 +02:00
Borewit
54e15644b6 Fix max line length 2017-09-17 22:16:01 +02:00
Borewit
6aa3a6c660 Fix lint errors 2017-09-17 22:14:07 +02:00
Borewit
83350b3b57 Add catalognumber in addition of the release label.
Updated to music-metadata in order to recognize MIME-type: audio/x-flac
2017-09-17 22:10:00 +02:00
Borewit
8868128d6e Add audio format information to play screen.
Extended album information with release lebel if available.
Make use of music-metadata module (musicmetadata appears to be no longer maintained)
2017-09-17 20:57:57 +02:00
Josip Janzic
f7ec537cd5 Add support for subtitles on Chromecast 2017-09-17 00:07:57 +02:00
Diego Rodríguez Baquero
a81156b363 Merge pull request #1233 from emilbayes/nojitter-time
Use tabular nums and fixed width for no jitter on time
2017-09-01 18:02:23 -05:00
Emil Bay
4b501ab90b Fixed width minutes 2017-08-28 19:41:30 +02:00
Emil Bay
90347c72e0 Use tabular nums and fixed width for no jitter on time 2017-08-28 19:07:24 +02:00
Feross Aboukhadijeh
a51ab3573e Add a security policy 2017-07-17 12:45:13 -07:00
Egor Yurtaev
2aa2f0de19 Sort files alphanumerically #1185 2017-06-24 22:09:42 +06:00
Josip Janzic
96f5f855a3 Add missing key to torrent status element
Fixes #1116
2017-04-27 21:29:18 +02:00
Feross Aboukhadijeh
a82498ba16 gitter 2017-04-14 14:40:18 -07:00
Feross Aboukhadijeh
3b05b52e57 feross -> webtorrent 2017-04-14 13:48:39 -07:00
Alberto Miranda
b02d6f3cdb Preferences update
Renamed “Downloads” section to “Folders”; watch folder preference moved
under “Folders”; updated texts for watch folder preference.
2017-04-02 19:37:13 -03:00
Alberto Miranda
6e67cae494 Wait for .torrent files to finish downloading. 2017-03-26 11:04:05 -03:00
Alberto Miranda
23eef1a058 Removed unused module from folderWatcher. 2017-03-24 12:55:10 -03:00
Alberto Miranda
2ad3f6d6e2 Removed extra logging. 2017-03-24 12:54:28 -03:00
Alberto Miranda
996a81ddaa added folder-watcher-controller 2017-03-23 07:14:27 -03:00
Alberto Miranda
4a8ab6e1f6 fixed styling issue 2017-03-23 07:00:45 -03:00
Alberto Miranda
7f817241ed added start / stop events; starting / stopping folder watcher. 2017-03-22 23:21:07 -03:00
Alberto Miranda
a6c53d06d2 fixed styling issues 2017-03-22 22:27:03 -03:00
Alberto Miranda
a70c4d1bf2 Working on watch-folder
Added chokidar to watch for folder changes; added folder-watcher;
passing state to delayedInit on main;  added default values for new
preferences; added “Auto add torrents” preference with its checkbox and
path selector; TODO: start/stop watching on preference change, start
watching on init, add dialog when trying to enable preference without a
torrents folder.
2017-03-22 09:42:17 -03:00
Feross Aboukhadijeh
cdfb907c75 standard 2017-03-20 19:41:44 -07:00
Alberto Miranda
c2abb50e9e Merge pull request #840 from feross/playback-priority
Playback priority
2017-03-15 14:39:26 -03:00
Alberto Miranda
99f4fc96bf Fixed resuming when downloading finishes. 2017-03-14 19:52:29 -03:00
Alberto Miranda
043f81996e fixed styling issues; returning early in pauseActiveTorrents. 2017-03-14 19:50:49 -03:00
Alberto Miranda
213f2f124d Merge branch 'playback-priority' of https://github.com/feross/webtorrent-desktop into playback-priority 2017-03-14 19:44:45 -03:00
Alberto Miranda
96e7e83e20 Merge branch 'master' into playback-priority
# Conflicts:
#	src/renderer/controllers/playback-controller.js
#	src/renderer/controllers/torrent-list-controller.js
#	src/renderer/main.js
2017-03-14 19:43:57 -03:00
Feross Aboukhadijeh
e5fccd93a8 Tweak pinch-to-zoom timing
Toggle fullscreen immediately, then prevent future toggles for 1s.
2017-03-08 15:00:42 -08:00
Feross Aboukhadijeh
78e46f5576 Merge branch 'ebraminio-master' 2017-03-08 14:56:38 -08:00
Ebrahim Byagowi
69ce07fbf7 Toggle fullscreen on pinch to zoom 2017-03-08 23:13:05 +03:30
Feross Aboukhadijeh
827131e136 standard 2017-03-02 00:33:36 -08:00
Feross Aboukhadijeh
c764bf4884 fixes for standard v10 2017-03-01 23:08:29 -08:00
Feross Aboukhadijeh
972203d675 material-ui@0.17
Tested and everything seems to work alright with the new version.
2017-02-20 03:09:56 -08:00
Feross Aboukhadijeh
2c382e14b5 electron@1.6.0
This version of Electron is 3 versions of Chromium newer, so there are
lots of performance improvements.

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

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

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

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

This PR attempts to resolve the issue by just guarding against the
exception.
2016-10-10 22:31:33 -07:00
Alberto Miranda
44a0f760de Resuming paused torrents when streaming torrent finishes downloading (wt-done). 2016-08-30 12:09:23 -03:00
Alberto Miranda
1e5ac1df9c removed logging 2016-08-27 03:56:08 -03:00
Alberto Miranda
4cdc3085ff not pausing active torrents when playing an already downloaded torrent. 2016-08-27 03:55:23 -03:00
Alberto Miranda
e483263d70 removed logging 2016-08-27 03:36:28 -03:00
Alberto Miranda
bd41bd4db8 added highest playback priority feature; added highest playback priority checkbox on preferences. 2016-08-27 03:35:53 -03:00
128 changed files with 13301 additions and 1346 deletions

View File

@@ -1,9 +0,0 @@
**What version of WebTorrent Desktop?** (See the 'About WebTorrent' menu)
**What operating system and version?**
**What did you do?**
**What did you expect to happen?**
**What actually happened?**

20
.github/ISSUE_TEMPLATE/BUG_REPORT.md vendored Normal file
View File

@@ -0,0 +1,20 @@
---
name: "🐞 Bug report"
about: Report an issue with this software
title: ''
labels: ''
assignees: ''
---
<!-- DO NOT POST LINKS OR REFERENCES TO COPYRIGHTED CONTENT IN YOUR ISSUE. -->
**What version of WebTorrent Desktop are you using?**
**What operating system and version?**
**What happened?**
**What did you expect to happen?**
**Are you willing to submit a pull request to fix this bug?**

View File

@@ -0,0 +1,20 @@
---
name: "⭐️ Feature request"
about: Request a new feature to be added
title: ''
labels: ''
assignees: ''
---
<!-- DO NOT POST LINKS OR REFERENCES TO COPYRIGHTED CONTENT IN YOUR ISSUE. -->
**What version of WebTorrent Desktop are you using?**
**What operating system and version?**
**What problem do you want to solve?**
**What do you think is the correct solution to this problem?**
**Are you willing to submit a pull request to implement this change?**

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

@@ -34,5 +34,48 @@
- PurgingPanda (t3ch0wn3r@gmail.com)
- Kai Curtis (morecode@kcurtis.com)
- Omri Litov (omrilitov@gmail.com)
- Alexey Romanov (romanalexey@gmail.com)
- Karan Thakkar (karanjthakkar@gmail.com)
- Nuno Campos (nuno.campos@me.com)
- Ebrahim Byagowi (ebrahim@gnu.org)
- Josip Janzic (josip@jjanzic.com)
- Egor Yurtaev (yurtaev.egor@gmail.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)
- Sharon Grossman (sharong1337@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)
- Дамјан Георгиевски (gdamjan@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)
- Pieter Goetschalckx (3.14.e.ter@gmail.com)
- Carey Metcalfe (carey@cmetcalfe.ca)
- Ameet Kaustav (akaustav@users.noreply.github.com)
- gpatarin (gael.patarin@outlook.com)
- Gael Patarin (gael.patarin@outlook.com)
#### Generated by bin/update-authors.sh.

View File

@@ -1,5 +1,132 @@
# WebTorrent Desktop Version History
## v0.23.0 - 2020-07-15
This release contains a critical security fix.
### Changed
- Update to Electron 10 beta [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834)
## v0.22.0 - 2020-07-15
### Added
- Linux `.rpm` packages and `arm64` builds are now available! [\#1694](https://github.com/webtorrent/webtorrent-desktop/pull/1694) ([hicom150](https://github.com/hicom150))
- Add support for multiple audio tracks [\#1712](https://github.com/webtorrent/webtorrent-desktop/pull/1712) ([hicom150](https://github.com/hicom150))
- Improve codec unsupported detection [\#1711](https://github.com/webtorrent/webtorrent-desktop/pull/1711) ([hicom150](https://github.com/hicom150))
- Report when files are being verified [\#1717](https://github.com/webtorrent/webtorrent-desktop/pull/1717) ([pR0Ps](https://github.com/pR0Ps))
- Support additional audio files: MPEG-Layer-2, Musepack, Matroska audio, WavePack [\#1772](https://github.com/webtorrent/webtorrent-desktop/pull/1772)
### Changed
- Update to Electron 9 [\#1729](https://github.com/webtorrent/webtorrent-desktop/pull/1729) [\#1832](https://github.com/webtorrent/webtorrent-desktop/issues/1832)
- Update to music-metadata 4.8.0 [\#1719](https://github.com/webtorrent/webtorrent-desktop/pull/1719) ([Borewit](https://github.com/Borewit))
- Update Windows build documentation [\#1715](https://github.com/webtorrent/webtorrent-desktop/pull/1715) ([RecoX](https://github.com/RecoX))
- Remove unneeded dependencies
### Fixed
- Fix a few type errors [\#1720](https://github.com/webtorrent/webtorrent-desktop/pull/1720) ([mathiasvr](https://github.com/mathiasvr))
- Fix electron SUID sandbox error [\#1707](https://github.com/webtorrent/webtorrent-desktop/pull/1707) ([hicom150](https://github.com/hicom150))
- Fix percentage rounding error [\#1716](https://github.com/webtorrent/webtorrent-desktop/pull/1716) ([pR0Ps](https://github.com/pR0Ps))
- Fix path-selector in preferences page [\#1702](https://github.com/webtorrent/webtorrent-desktop/pull/1702) ([314eter](https://github.com/314eter))
- Fix path-selector in preferences page [\#1704](https://github.com/webtorrent/webtorrent-desktop/pull/1702) ([mathiasvr](https://github.com/mathiasvr))
- Fix: Increase height of 'About' window [\#1737](https://github.com/webtorrent/webtorrent-desktop/pull/1737)
- Fix "Save Torrent File As..." [\#1743](https://github.com/webtorrent/webtorrent-desktop/pull/1743)
## v0.21.0 - 2019-09-14
### 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
- Added watch folder feature: Automatically add new torrent files added to a folder on disk (#1154)
- Added highest playback priority feature: pauses other active torrents when playback starts (#840)
- Add 'Start Speaking' and 'Stop Speaking' menu item (Mac) (#439)
- Add pinch-to-zoom gesture to enter/exit fullscreen (#1148)
### Changed
- [SECURITY] Mitigate Electron protocol handler issue (Windows)
- Moved project from Feross's GitHub account to the WebTorrent GitHub organization
- Updated to electron@1.6.16
- Updated to material-ui@0.17
- Treat .FLAC as playable audio (#1127)
### Fixed
- Fix time and duration so it doesn't bounce in the UI (#1233)
- Fix 'About WebTorrent' menu location on Windows (#1120)
## v0.18.0 - 2017-02-03
### Added
- Add a new "Transfers" menu for pausing or resuming all torrents (#1027)
### Changed
- Update Electron to 1.4.15
- Windows 32-bit: App can use 4GB of memory instead of just 2GB
- Fix "Portable App" writing crash reports to "%APPDATA%\Temp" (Windows)
- Updated WebTorrent engine to 0.98.5
- Fix issue where http web seeds would sometimes stall
- Don't send 'completed' event to tracker again if torrent is already complete
- Add more peer ID entropy
- Set user-agent header for tracker http requests
### Fixed
- Fix paste shortcut in tracker list on Create Torrent page (#1112)
- Auto-focus the 'OK' button in modal dialogs (#1058)
- Fix formatting issue in the speed stats on the Player page (#1039)
## v0.17.2 - 2016-10-10
### Fixed
- Windows: Fix impossible-to-delete "Wired CD" default torrent
- Throttle browser-window 'move' and 'resize' events
- Fix crash ("Cannot read property 'files' of null" error)
- Fix crash ("TypeError: Cannot read property 'startPiece' of undefined")
## v0.17.1 - 2016-10-03
### Changed
@@ -39,7 +166,7 @@
## v0.16.0 - 2016-09-18
### Added
- **Windows 64-bit support!** ([#931](https://github.com/feross/webtorrent-desktop/pull/931))
- **Windows 64-bit support!** (#931)
- Existing 32-bit users will update to 64-bit automatically in next release
- 64-bit reduces likelihood of out-of-memory errors by increasing the address space
@@ -99,16 +226,13 @@
## v0.12.0 - 2016-08-23
### Added
- Custom external media player
- Linux: add system-wide launcher and icons for Debian, including Ubuntu
### Changed
- Telemetry improvements: redact stacktraces, log app version
### Fixed
- Fix playback and download of default torrents ("missing path" error) (#804)
- Fix Delete Torrent + Data for newly added magnet links
- Fix jumpToTime error (#804)
@@ -121,13 +245,11 @@
- Check for missing default download path and torrent folders on start up (#776)
### Changed
- Do not automatically set WebTorrent as the default handler for torrents (#771)
- Torrents can only be created from the home screen (#770)
- Update Electron to 1.3.3 (#772)
### Fixed
- Allow modifying the default tracker list on the Create Torrent page (#775)
- Prevent opening multiple stacked Preference windows or Create Torrent windows (#770)
- Windows: Player window auto-resize does not match video aspect ratio (#565)
@@ -136,13 +258,11 @@
## v0.10.0 - 2016-08-05
### Added
- Drag-and-drop magnet links (selected text) is now supported (#284)
- Windows: Add "User Tasks" shortcuts to app icon in Start Menu (#114)
- Linux: Show badge count for completed torrent downloads
### Changed
- Change WebTorrent Desktop peer ID prefix to 'WD' to distinguish from WebTorrent in the browser, 'WW' (#688)
- Switch UI to React to improve UI rendering speed (#729)
- The primary bottleneck was actually `hyperx`, not `virtual-dom`.
@@ -159,7 +279,6 @@
- Location history abstraction released independently as [`location-history`](https://www.npmjs.com/package/location-history)
### Fixed
- When streaming to VLC, set VLC window title to torrent file name (#746)
- Fix "Cannot read property 'numPiecesPresent' of undefined" exception (#695)
- Fix rare case where config file could not be completely written (#733)
@@ -167,35 +286,29 @@
## v0.9.0 - 2016-07-20
### Added
- Save selected subtitles
- Ask for confirmation before deleting torrents
- Support Debian Jessie
### Changed
- Only send telemetry in production
- Clean up the code. Split main.js, refactor lots of things
### Fixed
- Fix state.playing.jumpToTime behavior
- Remove torrent file and poster image when deleting a torrent
## v0.8.1 - 2016-06-24
### Added
- New URI handler: stream-magnet
### Fixed
- DLNA crashing bug
## v0.8.0 - 2016-06-23
### Added
- Cast menu: choose which Chromecast, Airplay, or DLNA device you want to use
- Telemetry: send basic data, plus stats on how often the play button works
- Make posters from jpeg files, not just jpg
@@ -203,17 +316,14 @@
- Windows thumbnail bar with a play/pause button
### Changed
- Nicer modal styles
### Fixed
- Windows tray icon now stays in the right state
## v0.7.2 - 2016-06-02
### Fixed
- Fix exception that affects users upgrading from v0.5.1 or older
- Ensure `state.saved.prefs` configuration exists
- Fix window title on "About WebTorrent" window
@@ -221,23 +331,19 @@
## v0.7.1 - 2016-06-02
### Changed
- Change "Step Forward" keyboard shortcut to `Alt+Left` (Windows)
- Change "Step Backward" keyboard shortcut to to `Alt+Right` (Windows)
### Fixed
- First time startup bug -- invalid torrent/poster paths
## v0.7.0 - 2016-06-02
### Added
- Improved AirPlay support -- using the new [`airplayer`](https://www.npmjs.com/package/airplayer) package
- Remember volume setting in player, for as long as the app is open
### Changed
- Add (+) button now also accepts non .torrent files and creates a torrent from
those files
- Show prompt text in title bar for open dialogs (OS X)
@@ -247,7 +353,6 @@
- Fix crash reporter not working (Windows)
### Fixed
- Re-enable WebRTC (web peers)! (OS X, Windows)
- Windows support was disabled in v0.6.1 to work around a bug in Electron
- OS X support was disabled in v0.4.0 to work around a 100% CPU bug
@@ -258,7 +363,6 @@
- Fix torrent loading message UI misalignment
### Known issues
- When upgrading to WebTorrent Desktop v0.7.0, some torrent metadata (file list,
selected files, whether torrent is streamable) will be cleared. Just start the
torrent to re-populate the metadata.
@@ -266,7 +370,6 @@
## v0.6.1 - 2016-05-26
### Fixed
- Disable WebRTC to work around Electron crash (Windows)
- Will be re-enabled in the next version of WebTorrent, which will be based on
the next version of Electron, where the bug is fixed.
@@ -278,7 +381,6 @@
## v0.6.0 - 2016-05-24
### Added
- Added Preferences page to set Download folder
- Save video position, resume playback from saved position
- Add additional video player keyboard shortcuts (#275)
@@ -288,7 +390,6 @@
- Add announcement feature
### Changed
- Nicer player UI
- Reduce startup jank, improve startup time (#568)
- Cleanup unsupported codec detection (#569, #570)
@@ -296,7 +397,6 @@
- Improve subtitle positioning (#551)
### Fixed
- Fix Uncaught TypeError: Cannot read property 'update' of undefined (#567)
- Fix bugs in LocationHistory
- When player is active, and magnet link is pasted, go back to list
@@ -307,23 +407,19 @@
## v0.5.1 - 2016-05-18
### Fixed
- Fix auto-updater (OS X, Windows).
## v0.5.0 - 2016-05-17
### Added
- Select/deselect individual files to torrent.
- Automatically include subtitle files (.srt, .vtt) from torrent in the subtitles menu.
- "Add Subtitle File..." menu item.
### Changed
- When manually adding subtitle track(s), always switch to the new track.
### Fixed
- Magnet links throw exception on app launch. (OS X)
- Multi-file torrents would not seed in-place, were copied to Downloads folder.
- Missing 'About WebTorrent' menu item. (Windows)
@@ -332,7 +428,6 @@
## v0.4.0 - 2016-05-13
### Added
- Better Windows support!
- Windows 32-bit build.
- Windows Portable App build.
@@ -355,7 +450,6 @@
- New default torrent on first launch: The WIRED CD.
### Changed
- Improve app startup time by 40%.
- UI tweaks: Reduce font size, reduce torrent list item height.
- Add Playback menu for playback-related functionality.
@@ -366,7 +460,6 @@
- Remove "Add Fake Airplay/Chromecast" menu items.
### Fixed
- Disable WebRTC to fix 100% CPU usage/crashes caused by Chromium issue. This is
temporary. (OS X)
- When fullscreen, make controls use the full window. (OS X)
@@ -391,24 +484,20 @@ to this release!
## v0.3.3 - 2016-04-07
### Fixed
- App icon was incorrect (OS X)
## v0.3.2 - 2016-04-07
### Added
- Register WebTorrent as default handler for magnet links (OS X)
### Changed
- Faster startup time (50ms)
- Update Electron to 0.37.5
- Remove the white flash when loading pages and resizing the window
- Fix crash when sending IPC messages
### Fixed
- Fix installation bugs with .deb file (Linux)
- Pause audio reliably when closing the window
- Enforce minimimum window size when resizing player (for audio-only .mov files, which are 0x0)
@@ -416,17 +505,14 @@ to this release!
## v0.3.1 - 2016-04-06
### Added
- Add crash reporter to torrent engine process
### Fixed
- Fix cast screen background: cover, don't tile
## v0.3.0 - 2016-04-06
### Added
- **Ubuntu/Debian support!** (.deb installer)
- **DLNA streaming support**
- Add "File > Quit" menu item (Linux)
@@ -434,14 +520,12 @@ to this release!
- Crash reporting
### Changed
- On startup, do not re-verify files when timestamps are unchanged
- Moved torrent engine to an independent process, for better UI performance
- Removed media queries (UI resizing based on window width)
- Improved Chromecast icon, when connected
### Fixed
- "Download Complete" notification shows consistently
- Create new torrents and seed them without copying to temporary folder
- Clicking the "Download Complete" notification will always activate app
@@ -460,7 +544,6 @@ Thanks to @dcposch, @grunjol, and @feross for contributing to this release.
## v0.2.0 - 2016-03-29
### Added
- Minimise to tray (Windows, Linux)
- Show spinner and download speed when player is stalled waiting for data
- Highlight window on drag-and-drop
@@ -469,12 +552,10 @@ Thanks to @dcposch, @grunjol, and @feross for contributing to this release.
Linux users need to download new versions manually.
### Changed
- Renamed WebTorrent.app to WebTorrent Desktop
- Add Cosmos Laundromat as a default torrent
### Fixed
- Only capture media keys when player is active
- Update WebTorrent to 0.88.1 for performance improvements
- When seeding, do not proactively connect to new peers
@@ -534,7 +615,7 @@ Windows, and Linux. For now, we're only releasing binaries for OS X.
WebTorrent Desktop is in ALPHA and under very active development  expect lots more polish in
the coming weeks! If you know JavaScript and want to help us out, there's
[lots to do](https://github.com/feross/webtorrent-desktop/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+contribution%22)!
[lots to do](https://github.com/webtorrent/webtorrent-desktop/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+contribution%22)!
### Features

View File

@@ -1,95 +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://github.com/feross/standard
## 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** or modifying the Git history in any way.
2. **Non-master branches** should be used for ongoing work.
3. **Significant modifications** like API changes should be subject to a **pull request**
to solicit feedback from other contributors.
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.
## 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,6 +1,8 @@
<h1 align="center">
<br>
<a href="https://webtorrent.io"><img src="https://webtorrent.io/img/WebTorrent.png" alt="WebTorrent" width="200"></a>
<a href="https://webtorrent.io">
<img src="https://webtorrent.io/img/WebTorrent.png" alt="WebTorrent" width="200">
</a>
<br>
WebTorrent Desktop
<br>
@@ -10,20 +12,34 @@
<h4 align="center">The streaming torrent app. For Mac, Windows, and Linux.</h4>
<p align="center">
<a href="https://gitter.im/feross/webtorrent"><img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg" alt="Gitter"></a>
<a href="https://travis-ci.org/feross/webtorrent-desktop"><img src="https://img.shields.io/travis/feross/webtorrent-desktop/master.svg" alt="Travis"></a>
<a href="https://github.com/feross/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/feross/webtorrent-desktop.svg" alt="Release"></a>
<a href="https://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/feross/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
@@ -37,7 +53,7 @@ instructions below in the ["How to Contribute"](#how-to-contribute) section.
### Get the code
```
$ git clone https://github.com/feross/webtorrent-desktop.git
$ git clone https://github.com/webtorrent/webtorrent-desktop.git
$ cd webtorrent-desktop
$ npm install
```
@@ -68,19 +84,25 @@ $ npm test
$ npm run test-integration
```
The integration tests use Spectron and Tape. They click through the app, taking screenshots and comparing each one to a reference. Why screenshots?
The integration tests use Spectron and Tape. They click through the app, taking screenshots and
comparing each one to a reference. Why screenshots?
* Ad-hoc checking makes the tests a lot more work to write
* Even diffing the whole HTML is not as thorough as screenshot diffing. For example, it wouldn't catch an bug where hitting ESC from a video doesn't correctly restore window size.
* Even diffing the whole HTML is not as thorough as screenshot diffing. For example, it wouldn't
catch an bug where hitting ESC from a video doesn't correctly restore window size.
* Chrome's own integration tests use screenshot diffing iirc
* Small UI changes will break a few tests, but the fix is as easy as deleting the offending screenshots and running the tests, which will recreate them with the new look.
* The resulting Github PR will then show, pixel by pixel, the exact UI changes that were made! Ses https://github.com/blog/817-behold-image-view-modes
* Small UI changes will break a few tests, but the fix is as easy as deleting the offending
screenshots and running the tests, which will recreate them with the new look.
* The resulting Github PR will then show, pixel by pixel, the exact UI changes that were made! See
https://github.com/blog/817-behold-image-view-modes
For MacOS, you'll need a Retina screen for the integration tests to pass. Your screen should have the same resolution as a 2016 12" Macbook.
For MacOS, you'll need a Retina screen for the integration tests to pass. Your screen should have
the same resolution as a 2018 MacBook Pro 13".
For Windows, you'll need Windows 10 with a 1366x768 screen.
When running integration tests, keep the mouse on the edge of the screen and don't touch the mouse or keyboard while the tests are running.
When running integration tests, keep the mouse on the edge of the screen and don't touch the mouse
or keyboard while the tests are running.
### Package the app
@@ -103,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
@@ -110,7 +133,7 @@ The following optional arguments are available:
- `all` - All platforms (default)
Note: Even with the `--package` option, the auto-update files (.nupkg for Windows,
*-darwin.zip for Mac) will always be produced.
-darwin.zip for Mac) will always be produced.
#### Windows build notes
@@ -119,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.)
@@ -136,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/installation
### Privacy
WebTorrent Desktop collects some basic usage stats to help us make the app better.
@@ -145,10 +185,6 @@ Time out? Show a missing codec error?
The app never sends any personally identifying information, nor does it track which
torrents you add.
### Code Style
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
## License
MIT. Copyright (c) [WebTorrent, LLC](https://webtorrent.io).

117
RELEASE_PROCESS.md Normal file
View File

@@ -0,0 +1,117 @@
## 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
```
Move the `.zip` and `.dmg` file somewhere because the next step wipes the `dist/` folder away.
```
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 that the auto-updater is working.
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

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

View File

@@ -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

@@ -8,7 +8,6 @@ const cp = require('child_process')
const electronPackager = require('electron-packager')
const fs = require('fs')
const minimist = require('minimist')
const mkdirp = require('mkdirp')
const os = require('os')
const path = require('path')
const rimraf = require('rimraf')
@@ -21,6 +20,7 @@ const pkg = require('../package.json')
const BUILD_NAME = config.APP_NAME + '-v' + config.APP_VERSION
const BUILD_PATH = path.join(config.ROOT_PATH, 'build')
const DIST_PATH = path.join(config.ROOT_PATH, 'dist')
const NODE_MODULES_PATH = path.join(config.ROOT_PATH, 'node_modules')
const argv = minimist(process.argv.slice(2), {
boolean: [
@@ -36,6 +36,11 @@ const argv = minimist(process.argv.slice(2), {
})
function build () {
console.log('Installing node_modules...')
rimraf.sync(NODE_MODULES_PATH)
cp.execSync('npm ci', { stdio: 'inherit' })
console.log('Nuking dist/ and build/...')
rimraf.sync(DIST_PATH)
rimraf.sync(BUILD_PATH)
@@ -64,11 +69,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
@@ -82,14 +87,14 @@ 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,
// Pattern which specifies which files to ignore when copying files to create the
// package(s).
ignore: /^\/src|^\/dist|\/(appveyor.yml|\.appveyor.yml|\.github|appdmg|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^\/]*|.*\.md|.*\.markdown)$/,
ignore: /^\/src|^\/dist|\/(appveyor.yml|\.appveyor.yml|\.github|appdmg|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^/]*|.*\.md|.*\.markdown)$/,
// The application name.
name: config.APP_NAME,
@@ -104,26 +109,26 @@ const all = {
// "devDependencies" before starting to package the app.
prune: true,
// The Electron version with which the app is built (without the leading 'v')
version: require('electron/package.json').version
// The Electron version that the app is built with (without the leading 'v')
electronVersion: require('electron/package.json').version
}
const darwin = {
// 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'
@@ -133,8 +138,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: {
@@ -167,8 +172,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.
}
@@ -179,8 +184,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')
@@ -191,16 +195,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
@@ -212,13 +216,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']
}
]
@@ -235,7 +239,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'
}
}
@@ -349,6 +353,8 @@ function buildDarwin (cb) {
cb(null)
})
}
}).catch(function (err) {
cb(err)
})
}
@@ -369,8 +375,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
@@ -389,21 +394,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,
@@ -416,107 +417,69 @@ 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,
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)
fs.mkdirSync(portablePath, { recursive: true })
const downloadsPath = path.join(portablePath, 'Downloads')
mkdirp.sync(downloadsPath)
fs.mkdirSync(downloadsPath, { recursive: true })
const tempPath = path.join(portablePath, 'Temp')
mkdirp.sync(tempPath)
const archStr = destArch === 'ia32' ? '-ia32' : ''
fs.mkdirSync(tempPath, { recursive: true })
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 = []
@@ -526,56 +489,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.`)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

10922
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +1,71 @@
{
"name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.17.1",
"version": "0.23.0",
"author": {
"name": "WebTorrent, LLC",
"email": "feross@webtorrent.io",
"url": "https://webtorrent.io"
},
"bugs": {
"url": "https://github.com/feross/webtorrent-desktop/issues"
"url": "https://github.com/webtorrent/webtorrent-desktop/issues"
},
"dependencies": {
"airplayer": "^2.0.0",
"application-config": "^1.0.0",
"arch": "^2.0.0",
"auto-launch": "^4.0.1",
"bitfield": "^1.0.2",
"capture-frame": "^1.0.0",
"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": "^2.0.0",
"arch": "^2.1.2",
"auto-launch": "^5.0.5",
"bitfield": "^3.0.0",
"capture-frame": "^3.0.2",
"chokidar": "^3.4.1",
"chromecasts": "^1.9.1",
"create-torrent": "^4.4.2",
"debounce": "^1.2.0",
"dlnacasts": "^0.1.0",
"drag-drop": "^2.12.1",
"es6-error": "^3.0.1",
"drag-drop": "^6.0.2",
"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",
"lodash.merge": "^4.6.0",
"material-ui": "^0.15.4",
"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.2.1",
"react-dom": "^15.2.1",
"react-tap-event-plugin": "^1.0.0",
"rimraf": "^2.5.2",
"run-parallel": "^1.1.6",
"semver": "^5.1.0",
"iso-639-1": "^2.1.3",
"languagedetect": "^2.0.0",
"location-history": "^1.1.2",
"material-ui": "^0.20.2",
"music-metadata": "6.3.6",
"network-address": "^1.1.2",
"parse-torrent": "^7.1.3",
"prettier-bytes": "^1.0.4",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"rimraf": "^3.0.2",
"run-parallel": "^1.1.9",
"semver": "^7.3.2",
"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": "^4.0.0",
"srt-to-vtt": "^1.1.3",
"vlc-command": "^1.2.0",
"webtorrent": ">=0.108.6",
"winreg": "^1.2.4"
},
"devDependencies": {
"buble": "^0.14.0",
"cross-zip": "^2.0.1",
"depcheck": "^0.6.4",
"electron": "1.4.2",
"electron-osx-sign": "^0.3.0",
"electron-packager": "^8.0.0",
"electron-winstaller": "^2.3.0",
"gh-release": "^2.0.3",
"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",
"babel-eslint": "^10.1.0",
"buble": "^0.20.0",
"cross-zip": "^3.1.0",
"depcheck": "^1.0.0",
"electron": "~9.1.0",
"electron-osx-sign": "^0.4.17",
"electron-packager": "^15.0.0",
"electron-winstaller": "^4.0.1",
"gh-release": "^3.5.0",
"minimist": "^1.2.5",
"nodemon": "^2.0.4",
"open": "^7.0.4",
"plist": "^3.0.1",
"pngjs": "^5.0.0",
"run-series": "^1.1.8",
"spectron": "~11.0.0",
"standard": "*",
"tape": "^4.6.0",
"walk-sync": "^0.3.1"
"tape": "^5.0.1",
"walk-sync": "^2.2.0"
},
"engines": {
"node": ">=4.0.0"
@@ -88,24 +84,30 @@
"license": "MIT",
"main": "index.js",
"optionalDependencies": {
"appdmg": "^0.4.3"
"appdmg": "^0.6.0",
"electron-installer-debian": "^3.1.0",
"electron-installer-redhat": "^3.1.0"
},
"private": true,
"productName": "WebTorrent",
"repository": {
"type": "git",
"url": "git://github.com/feross/webtorrent-desktop.git"
"url": "git://github.com/webtorrent/webtorrent-desktop.git"
},
"scripts": {
"build": "buble src --output build",
"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,lodash.merge,nodemon,gh-release --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"start": "npm run build && electron --no-sandbox .",
"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-2016 ' + APP_TEAM,
APP_COPYRIGHT: `Copyright © 2014-${new Date().getFullYear()} ${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_NAME,
APP_TEAM,
APP_VERSION,
APP_WINDOW_TITLE: APP_NAME,
CONFIG_PATH: getConfigPath(),
@@ -70,15 +70,17 @@ module.exports = {
DEFAULT_DOWNLOAD_PATH: getDefaultDownloadPath(),
GITHUB_URL: 'https://github.com/feross/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/feross/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/feross/webtorrent-desktop/master',
GITHUB_URL: 'https://github.com/webtorrent/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/webtorrent/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/webtorrent/webtorrent-desktop/master',
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,
IS_TEST: IS_TEST,
IS_PORTABLE,
IS_PRODUCTION,
IS_TEST,
OS_SYSARCH: arch() === 'x64' ? 'x64' : 'ia32',
@@ -98,8 +100,8 @@ module.exports = {
WINDOW_MIN_HEIGHT: UI_HEADER_HEIGHT + (UI_TORRENT_HEIGHT * 2), // header + 2 torrents
WINDOW_MIN_WIDTH: 425,
UI_HEADER_HEIGHT: UI_HEADER_HEIGHT,
UI_TORRENT_HEIGHT: UI_TORRENT_HEIGHT
UI_HEADER_HEIGHT,
UI_TORRENT_HEIGHT
}
function getConfigPath () {

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
@@ -51,7 +50,5 @@ function onResponse (err, res, data) {
title: data.title,
message: data.message,
detail: data.detail
}, noop)
})
}
function noop () {}

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,18 +54,17 @@ 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) {
resetTitle()
if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('onOpen', selectedPaths)
})
const selectedPaths = electron.dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle()
if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('onOpen', selectedPaths)
}
/*
@@ -77,15 +76,14 @@ 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) {
resetTitle()
if (!Array.isArray(selectedPaths)) return
selectedPaths.forEach(function (selectedPath) {
windows.main.dispatch('addTorrent', selectedPath)
})
const selectedPaths = electron.dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle()
if (!Array.isArray(selectedPaths)) return
selectedPaths.forEach(function (selectedPath) {
windows.main.dispatch('addTorrent', selectedPath)
})
}
@@ -116,9 +114,8 @@ function resetTitle () {
*/
function showOpenSeed (opts) {
setTitle(opts.title)
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
resetTitle()
if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('showCreateTorrent', selectedPaths)
})
const selectedPaths = electron.dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle()
if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('showCreateTorrent', 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')
@@ -32,9 +32,9 @@ function downloadFinished (path) {
*/
function setBadge (count) {
if (process.platform === 'darwin' ||
process.platform === 'linux' && app.isUnityRunning()) {
(process.platform === 'linux' && app.isUnityRunning())) {
log(`setBadge: ${count}`)
app.setBadgeCount(Number(count))
app.badgeCount = Number(count)
}
}

View File

@@ -17,19 +17,18 @@ let proc = null
function checkInstall (playerPath, cb) {
// check for VLC if external player has not been specified by the user
// otherwise assume the player is installed
if (playerPath == null) return vlcCommand((err) => cb(!err))
process.nextTick(() => cb(true))
if (playerPath == null) return vlcCommand(cb)
process.nextTick(() => cb(null))
}
function spawn (playerPath, url, title) {
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

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

View File

@@ -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
@@ -72,22 +79,31 @@ function init () {
if (err) throw err
isReady = true
const state = results.state
windows.main.init(results.state, {hidden: hidden})
windows.main.init(state, { hidden })
windows.webtorrent.init()
menu.init()
// To keep app startup fast, some code is delayed.
setTimeout(delayedInit, config.DELAYED_INIT)
setTimeout(() => {
delayedInit(state)
}, config.DELAYED_INIT)
// Report uncaught exceptions
process.on('uncaughtException', (err) => {
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)
@@ -121,17 +137,24 @@ function init () {
})
}
function delayedInit () {
function delayedInit (state) {
if (app.isQuitting) return
const announcement = require('./announcement')
const dock = require('./dock')
const updater = require('./updater')
const FolderWatcher = require('./folder-watcher')
const folderWatcher = new FolderWatcher({ window: windows.main, state })
announcement.init()
dock.init()
updater.init()
ipc.setModule('folderWatcher', folderWatcher)
if (folderWatcher.isEnabled()) {
folderWatcher.start()
}
if (process.platform === 'win32') {
const userTasks = require('./user-tasks')
userTasks.init()
@@ -152,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)
}
@@ -178,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
@@ -198,7 +221,7 @@ function processArgv (argv) {
// Ignore hidden argument, already being handled
} else if (arg.startsWith('-psn')) {
// Ignore Mac launchd "process serial number" argument
// Issue: https://github.com/feross/webtorrent-desktop/issues/214
// Issue: https://github.com/webtorrent/webtorrent-desktop/issues/214
} else if (arg.startsWith('--')) {
// Ignore Spectron flags
} else if (arg === 'data:,') {

View File

@@ -1,5 +1,6 @@
module.exports = {
init
init,
setModule
}
const electron = require('electron')
@@ -13,6 +14,14 @@ const windows = require('./windows')
// Messages from the main process, to be sent once the WebTorrent process starts
const messageQueueMainToWebTorrent = []
// Will hold modules injected from the app that will be used on fired
// IPC events.
const modules = {}
function setModule (name, module) {
modules[name] = module
}
function init () {
const ipc = electron.ipcMain
@@ -58,7 +67,7 @@ function init () {
})
/**
* Events
* Player Events
*/
ipc.on('onPlayerOpen', function () {
@@ -106,13 +115,35 @@ function init () {
thumbar.onPlayerPause()
})
/**
* Folder Watcher Events
*/
ipc.on('startFolderWatcher', function () {
if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules.folderWatcher.start()
})
ipc.on('stopFolderWatcher', function () {
if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules.folderWatcher.stop()
})
/**
* Shell
*/
ipc.on('openItem', (e, ...args) => {
ipc.on('openPath', (e, ...args) => {
const shell = require('./shell')
shell.openItem(...args)
shell.openPath(...args)
})
ipc.on('showItemInFolder', (e, ...args) => {
const shell = require('./shell')
@@ -166,8 +197,8 @@ function init () {
ipc.on('checkForExternalPlayer', function (e, path) {
const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, function (isInstalled) {
windows.main.send('checkForExternalPlayer', isInstalled)
externalPlayer.checkInstall(path, function (err) {
windows.main.send('checkForExternalPlayer', !err)
})
})
@@ -204,8 +235,8 @@ function init () {
} else {
// Queue message for webtorrent window, it hasn't finished loading yet
messageQueueMainToWebTorrent.push({
name: name,
args: args
name,
args
})
log('webtorrent: queueing %s', name)
}

View File

@@ -278,6 +278,27 @@ function getMenuTemplate () {
}
]
},
{
label: 'Transfers',
submenu: [
{
label: 'Pause All',
click: () => windows.main.dispatch('pauseAllTorrents')
},
{
label: 'Resume All',
click: () => windows.main.dispatch('resumeAllTorrents')
},
{
label: 'Remove All From List',
click: () => windows.main.dispatch('confirmDeleteAllTorrents', false)
},
{
label: 'Remove All Data Files',
click: () => windows.main.dispatch('confirmDeleteAllTorrents', true)
}
]
},
{
label: 'Help',
role: 'help',
@@ -289,6 +310,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: () => {
@@ -305,13 +333,20 @@ 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)
}
}
]
}
]
if (process.platform === 'darwin') {
// Add WebTorrent app menu (Mac)
// WebTorrent menu (Mac)
template.unshift({
label: config.APP_NAME,
submenu: [
@@ -330,8 +365,7 @@ function getMenuTemplate () {
type: 'separator'
},
{
role: 'services',
submenu: []
role: 'services'
},
{
type: 'separator'
@@ -354,8 +388,26 @@ function getMenuTemplate () {
]
})
// Add Window menu (Mac)
template.splice(5, 0, {
// Edit menu (Mac)
template[2].submenu.push(
{
type: 'separator'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
}
)
// Window menu (Mac)
template.splice(6, 0, {
role: 'window',
submenu: [
{
@@ -395,7 +447,7 @@ function getMenuTemplate () {
})
// Help menu (Windows, Linux)
template[4].submenu.push(
template[5].submenu.push(
{
type: 'separator'
},

View File

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

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

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

View File

@@ -72,6 +72,6 @@ function initDarwinWin32 () {
(e, notes, name, date, url) => log(`Update downloaded: ${name}: ${url}`)
)
electron.autoUpdater.setFeedURL(AUTO_UPDATE_URL)
electron.autoUpdater.setFeedURL({ url: AUTO_UPDATE_URL })
electron.autoUpdater.checkForUpdates()
}

View File

@@ -15,7 +15,7 @@ function init () {
backgroundColor: '#ECECEC',
center: true,
fullscreen: false,
height: 170,
height: 250,
icon: getIconPath(),
maximizable: false,
minimizable: false,
@@ -24,16 +24,21 @@ function init () {
skipTaskbar: true,
title: 'About ' + config.APP_WINDOW_TITLE,
useContentSize: true,
webPreferences: {
nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks'
},
width: 300
})
win.loadURL(config.WINDOW_ABOUT)
// No menu on the About window
win.setMenu(null)
win.once('ready-to-show', function () {
win.show()
// No menu on the About window
// Hack: BrowserWindow removeMenu method not working on electron@7
// https://github.com/electron/electron/issues/21088
win.setMenuBarVisibility(false)
})
win.once('closed', function () {

View File

@@ -33,15 +33,19 @@ function init (state, options) {
const win = main.win = new electron.BrowserWindow({
backgroundColor: '#282828',
darkTheme: true, // Forces dark theme (GTK+3)
icon: getIconPath(), // Window icon (Windows, Linux)
minWidth: config.WINDOW_MIN_WIDTH,
minHeight: config.WINDOW_MIN_HEIGHT,
title: config.APP_WINDOW_TITLE,
titleBarStyle: 'hidden-inset', // Hide title bar (Mac)
useContentSize: true, // Specify web page size without OS chrome
show: false,
width: initialBounds.width,
height: initialBounds.height,
icon: getIconPath(), // Window icon (Windows, Linux)
minHeight: config.WINDOW_MIN_HEIGHT,
minWidth: config.WINDOW_MIN_WIDTH,
show: false,
title: config.APP_WINDOW_TITLE,
titleBarStyle: 'hiddenInset', // Hide title bar (Mac)
useContentSize: true, // Specify web page size without OS chrome
width: initialBounds.width,
webPreferences: {
nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks'
},
x: initialBounds.x,
y: initialBounds.y
})
@@ -56,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())
})
@@ -72,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()) {
@@ -137,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))
bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2))
bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2))
log(`setBounds: centered to ${JSON.stringify(bounds)}`)
}
// Resize the window's content area (so window border doesn't need to be taken
// into account)
@@ -216,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

@@ -24,6 +24,10 @@ function init () {
skipTaskbar: true,
title: 'webtorrent-hidden-window',
useContentSize: true,
webPreferences: {
nodeIntegration: true,
enableBlinkFeatures: 'AudioVideoTracks'
},
width: 150
})
@@ -55,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

@@ -0,0 +1,31 @@
const React = require('react')
const ModalOKCancel = require('./modal-ok-cancel')
const { dispatch, dispatcher } = require('../lib/dispatcher')
module.exports = class DeleteAllTorrentsModal extends React.Component {
render () {
const { state: { modal: { deleteData } } } = this.props
const message = deleteData
? 'Are you sure you want to remove all the torrents from the list and delete the data files?'
: 'Are you sure you want to remove all the torrents from the list?'
const buttonText = deleteData ? 'REMOVE DATA' : 'REMOVE'
return (
<div>
<p><strong>{message}</strong></p>
<ModalOKCancel
cancelText='CANCEL'
onCancel={dispatcher('exitModal')}
okText={buttonText}
onOK={handleRemove}
/>
</div>
)
function handleRemove () {
dispatch('deleteAllTorrents', deleteData)
dispatch('exitModal')
}
}
}

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,19 +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} />
onClick={onOK}
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,18 +32,13 @@ 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(
remote.getCurrentWindow(),
opts,
(filenames) => {
if (!Array.isArray(filenames)) return
this.props.onChange && this.props.onChange(filenames[0])
}
)
const filenames = remote.dialog.showOpenDialogSync(remote.getCurrentWindow(), opts)
if (!Array.isArray(filenames)) return
this.props.onChange && this.props.onChange(filenames[0])
}
render () {
@@ -64,7 +61,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 +71,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,12 +18,14 @@ module.exports = class UpdateAvailableModal extends React.Component {
cancelText='SKIP THIS RELEASE'
onCancel={handleSkip}
okText='SHOW DOWNLOAD PAGE'
onOK={handleShow} />
onOK={handleShow}
/>
</div>
)
function handleShow () {
electron.shell.openExternal('https://github.com/feross/webtorrent-desktop/releases')
// TODO: use the GitHub urls from config.js
electron.shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases')
dispatch('exitModal')
}

View File

@@ -0,0 +1,17 @@
const { dispatch } = require('../lib/dispatcher')
module.exports = class AudioTracksController {
constructor (state) {
this.state = state
}
selectAudioTrack (ix) {
this.state.playing.audioTracks.selectedIndex = ix
dispatch('skip', 0.2) // HACK: hardcoded seek value for smooth audio change
}
toggleAudioTracksMenu () {
const audioTracks = this.state.playing.audioTracks
audioTracks.showMenu = !audioTracks.showMenu
}
}

View File

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

View File

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

View File

@@ -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')
@@ -27,6 +27,8 @@ module.exports = class PlaybackController {
// * Stream, if not already fully downloaded
// * If no file index is provided, restore the most recently viewed file or autoplay the first
playFile (infoHash, index /* optional */) {
this.pauseActiveTorrents(infoHash)
const state = this.state
if (state.location.url() === 'player') {
this.updatePlayer(infoHash, index, false, (err) => {
@@ -59,12 +61,12 @@ module.exports = class PlaybackController {
}
// Open a file in OS default app.
openItem (infoHash, index) {
openPath (infoHash, index) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
const filePath = path.join(
torrentSummary.path,
torrentSummary.files[index].path)
ipcRenderer.send('openItem', filePath)
ipcRenderer.send('openPath', filePath)
}
// Toggle (play or pause) the currently playing media
@@ -84,15 +86,26 @@ module.exports = class PlaybackController {
else this.pause()
}
pauseActiveTorrents (infoHash) {
// Playback Priority: pause all active torrents if needed.
if (!this.state.saved.prefs.highestPlaybackPriority) return
// Do not pause active torrents if playing a fully downloaded torrent.
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
if (torrentSummary.status === 'seeding') return
dispatch('prioritizeTorrent', infoHash)
}
// Play next file in list (if any)
nextTrack () {
const state = this.state
if (Playlist.hasNext(state) && state.playing.location !== 'external') {
this.updatePlayer(
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err)
else this.play()
})
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err)
else this.play()
})
}
}
@@ -211,6 +224,7 @@ module.exports = class PlaybackController {
const torrentSummary = TorrentSummary.getByKey(state, infoHash)
state.playing.infoHash = torrentSummary.infoHash
state.playing.isReady = false
// update UI to show pending playback
sound.play('PLAY')
@@ -222,6 +236,8 @@ module.exports = class PlaybackController {
// Starts WebTorrent server for media streaming
startServer (torrentSummary) {
const state = this.state
if (torrentSummary.status === 'paused') {
dispatch('startTorrentingSummary', torrentSummary.torrentKey)
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash,
@@ -232,6 +248,7 @@ module.exports = class PlaybackController {
function onTorrentReady () {
ipcRenderer.send('wt-start-server', torrentSummary.infoHash)
ipcRenderer.once('wt-server-running', () => { state.playing.isReady = true })
}
}
@@ -252,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
@@ -275,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)
}
}
@@ -320,12 +338,7 @@ module.exports = class PlaybackController {
// Save volume (this session only, not in state.saved)
state.previousVolume = state.playing.volume
// Telemetry: track what happens after the user clicks play
const result = state.playing.result // 'success' or 'error'
if (result === 'success') telemetry.logPlayAttempt('success') // first frame displayed
else if (result === 'error') telemetry.logPlayAttempt('error') // codec missing, etc
else if (result === undefined) telemetry.logPlayAttempt('abandoned') // user gave up waiting
else console.error('Unknown state.playing.result', state.playing.result)
if (!state.playing.isReady) telemetry.logPlayAttempt('abandoned') // user gave up waiting
// Reset the window contents back to the home screen
state.playing = State.getDefaultPlayState()
@@ -342,6 +355,11 @@ module.exports = class PlaybackController {
ipcRenderer.send('onPlayerClose')
// Playback Priority: resume previously paused downloads.
if (this.state.saved.prefs.highestPlaybackPriority) {
dispatch('resumePausedTorrents')
}
this.update()
}
}

View File

@@ -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) {
@@ -13,14 +13,13 @@ module.exports = class SubtitlesController {
}
openSubtitles () {
remote.dialog.showOpenDialog({
const filenames = remote.dialog.showOpenDialogSync({
title: 'Select a subtitles file.',
filters: [ { name: 'Subtitles', extensions: ['vtt', 'srt'] } ],
properties: [ 'openFile' ]
}, (filenames) => {
if (!Array.isArray(filenames)) return
this.addSubtitles(filenames, true)
filters: [{ name: 'Subtitles', extensions: ['vtt', 'srt'] }],
properties: ['openFile']
})
if (!Array.isArray(filenames)) return
this.addSubtitles(filenames, true)
}
selectSubtitle (ix) {
@@ -110,7 +109,7 @@ function loadSubtitle (file, cb) {
buffer: 'data:text/vtt;base64,' + buf.toString('base64'),
language: langDetected,
label: langDetected,
filePath: filePath
filePath
}
cb(null, track)

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)
@@ -25,7 +25,7 @@ module.exports = class TorrentController {
}
torrentSummary = {
torrentKey: torrentKey,
torrentKey,
status: 'new'
}
torrents.unshift(torrentSummary)
@@ -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')
@@ -55,7 +55,7 @@ module.exports = class TorrentListController {
if (files.length === 0 || typeof files[0] !== 'string') {
this.state.location.go({
url: 'create-torrent',
files: files,
files,
setup: (cb) => {
this.state.window.title = 'Create New Torrent'
cb(null)
@@ -121,11 +121,66 @@ module.exports = class TorrentListController {
torrentSummary.status = 'new'
this.startTorrentingSummary(torrentSummary.torrentKey)
sound.play('ENABLE')
} else {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
sound.play('DISABLE')
return
}
this.pauseTorrent(torrentSummary, true)
}
pauseAllTorrents () {
this.state.saved.torrents.forEach((torrentSummary) => {
if (torrentSummary.status === 'downloading' ||
torrentSummary.status === 'seeding') {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
}
})
sound.play('DISABLE')
}
resumeAllTorrents () {
this.state.saved.torrents.forEach((torrentSummary) => {
if (torrentSummary.status === 'paused') {
torrentSummary.status = 'downloading'
this.startTorrentingSummary(torrentSummary.torrentKey)
}
})
sound.play('ENABLE')
}
pauseTorrent (torrentSummary, playSound) {
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
if (playSound) sound.play('DISABLE')
}
prioritizeTorrent (infoHash) {
this.state.saved.torrents
.filter((torrent) => { // We're interested in active torrents only.
return (['downloading', 'seeding'].indexOf(torrent.status) !== -1)
})
.map((torrent) => { // Pause all active torrents except the one that started playing.
if (infoHash === torrent.infoHash) return
// Pause torrent without playing sounds.
this.pauseTorrent(torrent, false)
this.state.saved.torrentsToResume.push(torrent.infoHash)
})
console.log('Playback Priority: paused torrents: ', this.state.saved.torrentsToResume)
}
resumePausedTorrents () {
console.log('Playback Priority: resuming paused torrents')
if (!this.state.saved.torrentsToResume || !this.state.saved.torrentsToResume.length) return
this.state.saved.torrentsToResume.map((infoHash) => {
this.toggleTorrent(infoHash)
})
// reset paused torrents
this.state.saved.torrentsToResume = []
}
toggleTorrentFile (infoHash, index) {
@@ -146,26 +201,38 @@ module.exports = class TorrentListController {
}
}
confirmDeleteAllTorrents (deleteData) {
this.state.modal = {
id: 'delete-all-torrents-modal',
deleteData
}
}
// TODO: use torrentKey, not infoHash
deleteTorrent (infoHash, deleteData) {
ipcRenderer.send('wt-stop-torrenting', infoHash)
const index = this.state.saved.torrents.findIndex((x) => x.infoHash === infoHash)
if (index > -1) {
const summary = this.state.saved.torrents[index]
// remove torrent and poster file
deleteFile(TorrentSummary.getTorrentPath(summary))
deleteFile(TorrentSummary.getPosterPath(summary))
// optionally delete the torrent data
if (deleteData) moveItemToTrash(summary)
deleteTorrentFile(summary, deleteData)
// remove torrent from saved list
this.state.saved.torrents.splice(index, 1)
dispatch('stateSave')
// prevent user from going forward to a deleted torrent
this.state.location.clearForward('player')
sound.play('DELETE')
} else {
throw new TorrentKeyNotFoundError(infoHash)
}
}
deleteAllTorrents (deleteData) {
this.state.saved.torrents.forEach((summary) => deleteTorrentFile(summary, deleteData))
this.state.saved.torrents = []
dispatch('stateSave')
// prevent user from going forward to a deleted torrent
this.state.location.clearForward('player')
@@ -220,17 +287,28 @@ module.exports = class TorrentListController {
menu.append(new electron.remote.MenuItem({
label: 'Save Torrent File As...',
click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey)
click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey),
enabled: torrentSummary.torrentFileName != null
}))
menu.popup(electron.remote.getCurrentWindow())
menu.append(new electron.remote.MenuItem({
type: 'separator'
}))
const sortedByName = this.state.saved.prefs.sortByName
menu.append(new electron.remote.MenuItem({
label: `${sortedByName ? '✓ ' : ''}Sort by Name`,
click: () => dispatch('updatePreferences', 'sortByName', !sortedByName)
}))
menu.popup({ window: electron.remote.getCurrentWindow() })
}
// Takes a torrentSummary or torrentKey
// Shows a Save File dialog, then saves the .torrent file wherever the user requests
saveTorrentFileAs (torrentKey) {
const torrentSummary = TorrentSummary.getByKey(this.state, torrentKey)
if (!torrentSummary) throw new Error('Missing torrentKey: ' + torrentKey)
if (!torrentSummary) throw new TorrentKeyNotFoundError(torrentKey)
const downloadPath = this.state.saved.prefs.downloadPath
const newFileName = path.parse(torrentSummary.name).name + '.torrent'
const win = electron.remote.getCurrentWindow()
@@ -240,18 +318,19 @@ module.exports = class TorrentListController {
filters: [
{ name: 'Torrent Files', extensions: ['torrent'] },
{ name: 'All Files', extensions: ['*'] }
]
],
buttonLabel: 'Save'
}
electron.remote.dialog.showSaveDialog(win, opts, function (savePath) {
console.log('Saving torrent ' + torrentKey + ' to ' + savePath)
if (!savePath) return // They clicked Cancel
const torrentPath = TorrentSummary.getTorrentPath(torrentSummary)
fs.readFile(torrentPath, function (err, torrentFile) {
const savePath = electron.remote.dialog.showSaveDialogSync(win, opts)
if (!savePath) return // They clicked Cancel
console.log('Saving torrent ' + torrentKey + ' to ' + savePath)
const torrentPath = TorrentSummary.getTorrentPath(torrentSummary)
fs.readFile(torrentPath, function (err, torrentFile) {
if (err) return dispatch('error', err)
fs.writeFile(savePath, torrentFile, function (err) {
if (err) return dispatch('error', err)
fs.writeFile(savePath, torrentFile, function (err) {
if (err) return dispatch('error', err)
})
})
})
}
@@ -259,16 +338,16 @@ module.exports = class TorrentListController {
// Recursively finds {name, path, size} for all files in a folder
// Calls `cb` on success, calls `onError` on failure
function findFilesRecursive (paths, cb) {
function findFilesRecursive (paths, cb_) {
if (paths.length > 1) {
let numComplete = 0
let ret = []
const ret = []
paths.forEach(function (path) {
findFilesRecursive([path], function (fileObjs) {
ret.push(...fileObjs)
if (++numComplete === paths.length) {
ret.sort((a, b) => a.path < b.path ? -1 : a.path > b.path)
cb(ret)
ret.sort((a, b) => a.path < b.path ? -1 : Number(a.path > b.path))
cb_(ret)
}
})
})
@@ -282,7 +361,7 @@ function findFilesRecursive (paths, cb) {
// Files: return name, path, and size
if (!stat.isDirectory()) {
const filePath = fileOrFolder
return cb([{
return cb_([{
name: path.basename(filePath),
path: filePath,
size: stat.size
@@ -294,7 +373,7 @@ function findFilesRecursive (paths, cb) {
fs.readdir(folderPath, function (err, fileNames) {
if (err) return dispatch('error', err)
const paths = fileNames.map((fileName) => path.join(folderPath, fileName))
findFilesRecursive(paths, cb)
findFilesRecursive(paths, cb_)
})
})
}
@@ -315,3 +394,14 @@ function moveItemToTrash (torrentSummary) {
function showItemInFolder (torrentSummary) {
ipcRenderer.send('showItemInFolder', TorrentSummary.getFileOrFolder(torrentSummary))
}
function deleteTorrentFile (torrentSummary, deleteData) {
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
// remove torrent and poster file
deleteFile(TorrentSummary.getTorrentPath(torrentSummary))
deleteFile(TorrentSummary.getPosterPath(torrentSummary))
// optionally delete the torrent data
if (deleteData) moveItemToTrash(torrentSummary)
}

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 {
@@ -13,7 +13,7 @@ module.exports = class UpdateController {
console.log('new version skipped by user: v' + version)
return
}
this.state.modal = { id: 'update-available-modal', version: version }
this.state.modal = { id: 'update-available-modal', version }
}
// Don't show the modal again until the next version

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 () {
@@ -388,7 +418,8 @@ function toggleMenu (location) {
if (state.playing.location !== 'local') {
throw new CastingError(
`You can't connect to ${location} when already connected to another device`
) }
)
}
// Find all cast devices of the given type
const player = getPlayer(location)
@@ -398,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',
'.m2a', '.m4a', '.mpc', '.m4b', '.mka', '.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.22.0')) migrate_0_22_0(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,14 +73,14 @@ 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
}
// Fix exception caused by incorrect file ordering.
// https://github.com/feross/webtorrent-desktop/pull/604#issuecomment-222805214
// https://github.com/webtorrent/webtorrent-desktop/pull/604#issuecomment-222805214
delete ts.defaultPlayFileIndex
delete ts.files
delete ts.selections
@@ -111,7 +113,7 @@ function migrate_0_12_0 (saved) {
// Undo a terrible bug where clicking Play on a default torrent on a fresh
// install results in a "path missing" error
// See https://github.com/feross/webtorrent-desktop/pull/806
// See https://github.com/webtorrent/webtorrent-desktop/pull/806
const defaultTorrentFiles = [
'6a9759bffd5c0af65319979fb7832189f4f3c35d.torrent',
'88594aaacbde40ef3e2510c47374ec0aa396c08e.torrent',
@@ -153,9 +155,9 @@ function migrate_0_17_0 (saved) {
function migrate_0_17_2 (saved) {
// Remove the trailing dot (.) from the Wired CD torrent name, since
// folders/files that end in a trailing dot (.) or space are not deletable from
// Windows Explorer. See: https://github.com/feross/webtorrent-desktop/issues/905
// 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_22_0 (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,10 +88,12 @@ 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 */
duration: 1, /* seconds */
isReady: false,
isPaused: true,
isStalled: false,
lastTimeUpdate: 0, /* Unix time in ms */
@@ -103,55 +105,59 @@ function getDefaultPlayState () {
selectedIndex: -1, /* current subtitle track */
showMenu: false /* popover menu, above the video */
},
audioTracks: {
tracks: [],
selectedIndex: 0, /* current audio track */
showMenu: false /* popover menu, above the video */
},
aspectRatio: 0 /* aspect ratio of the video */
}
}
/* 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,
startup: false
externalPlayerPath: '',
startup: false,
soundNotifications: true,
autoAddTorrents: false,
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 {
@@ -196,15 +202,20 @@ function shouldHidePlayerControls () {
this.playing.playbackRate === 1
}
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)
async function load (cb) {
let saved = await appConfig.read()
if (!saved || !saved.version) {
console.log('Missing config file: Creating new one')
try {
saved = setupStateSaved()
} catch (err) {
onSavedState(err)
return
}
})
}
onSavedState(null, saved)
function onSavedState (err, saved) {
if (err) return cb(err)
@@ -222,7 +233,7 @@ function load (cb) {
}
// Write state.saved to the JSON state file
function saveImmediate (state, cb) {
async function saveImmediate (state, cb) {
console.log('Saving state to ' + appConfig.filePath)
// Clean up, so that we're not saving any pending state
@@ -233,7 +244,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
}
@@ -245,8 +256,10 @@ function saveImmediate (state, cb) {
return torrent
})
appConfig.write(copy, (err) => {
if (err) console.error(err)
else State.emit('stateSaved')
})
try {
await appConfig.write(copy)
State.emit('stateSaved')
} catch (err) {
console.error(err)
}
}

View File

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

View File

@@ -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,29 +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',
'.m4a'
].includes(getFileExtension(file))
return mediaExtensions.audio.includes(getFileExtension(file))
}
// Checks if the argument is either:
@@ -46,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 {Array} 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,
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

@@ -1,18 +1,13 @@
/**
* Perf optimization: Hook into require() to modify how certain modules load:
*
* - `lodash/merge` (used by `material-ui`) causes 119 require() calls at startup,
* which take ~100ms. Replace it with `lodash.merge` which is equivalent.
* See: https://github.com/callemall/material-ui/pull/4380#issuecomment-250894552
*
* - `inline-style-prefixer` (used by `material-ui`) takes ~40ms. It is not
* actually used because auto-prefixing is disabled with
* `darkBaseTheme.userAgent = false`. Return a fake object.
*/
let Module = require('module')
const Module = require('module')
const _require = Module.prototype.require
Module.prototype.require = function (id) {
if (id === 'lodash/merge') id = 'lodash.merge'
if (id === 'inline-style-prefixer') return {}
return _require.apply(this, arguments)
}
@@ -29,6 +24,7 @@ const State = require('./lib/state')
State.load(onState)
const createGetter = require('fn-getter')
const debounce = require('debounce')
const dragDrop = require('drag-drop')
const electron = require('electron')
const fs = require('fs')
@@ -43,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
@@ -81,6 +74,7 @@ function onState (err, _state) {
window.dispatch = dispatch
telemetry.init(state)
sound.init(state)
// Log uncaught JS errors
window.addEventListener(
@@ -105,6 +99,10 @@ function onState (err, _state) {
const SubtitlesController = require('./controllers/subtitles-controller')
return new SubtitlesController(state)
}),
audioTracks: createGetter(() => {
const AudioTracksController = require('./controllers/audio-tracks-controller')
return new AudioTracksController(state)
}),
torrent: createGetter(() => {
const TorrentController = require('./controllers/torrent-controller')
return new TorrentController(state)
@@ -115,6 +113,10 @@ function onState (err, _state) {
update: createGetter(() => {
const UpdateController = require('./controllers/update-controller')
return new UpdateController(state)
}),
folderWatcher: createGetter(() => {
const FolderWatcherController = require('./controllers/folder-watcher-controller')
return new FolderWatcherController()
})
}
@@ -150,6 +152,21 @@ 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)
document.addEventListener('wheel', function (event) {
// ctrlKey detects pinch to zoom, http://crbug.com/289887
if (event.ctrlKey) {
event.preventDefault()
debouncedFullscreenToggle()
}
})
// ...focus and blur. Needed to show correct dock icon text ('badge') in OSX
window.addEventListener('focus', onFocus)
window.addEventListener('blur', onBlur)
@@ -222,94 +239,108 @@ 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),
'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) =>
confirmDeleteAllTorrents: (deleteData) =>
controllers.torrentList().confirmDeleteAllTorrents(deleteData),
deleteAllTorrents: (deleteData) =>
controllers.torrentList().deleteAllTorrents(deleteData),
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(),
// 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),
openPath: (infoHash, index) => controllers.playback().openPath(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),
// Audio Tracks
selectAudioTrack: (index) => controllers.audioTracks().selectAudioTrack(index),
toggleAudioTracksMenu: () => controllers.audioTracks().toggleAudioTracksMenu(),
// 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,
preferences: () => controllers.prefs().show(),
updatePreferences: (key, value) => controllers.prefs().update(key, value),
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,
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,
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,
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()
@@ -341,9 +372,10 @@ 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())
ipcRenderer.on('wt-warning', (e, ...args) => tc.torrentWarning(...args))
ipcRenderer.on('wt-error', (e, ...args) => tc.torrentError(...args))
@@ -431,14 +463,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)
@@ -475,13 +514,43 @@ function onError (err) {
update()
}
const editableHtmlTags = new Set(['input', 'textarea'])
function onPaste (e) {
if (e.target.tagName.toLowerCase() === 'input') return
if (editableHtmlTags.has(e.target.tagName.toLowerCase())) return
controllers.torrentList().addTorrent(electron.clipboard.readText())
update()
}
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
@@ -494,7 +563,7 @@ function onBlur () {
}
function onVisibilityChange () {
state.window.isVisible = !document.webkitHidden
state.window.isVisible = !document.hidden
}
function onFullscreenChanged (e, isFullScreen) {

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 = {
@@ -24,7 +24,9 @@ const Modals = {
),
'remove-torrent-modal': createGetter(() => require('../components/remove-torrent-modal')),
'update-available-modal': createGetter(() => require('../components/update-available-modal')),
'unsupported-media-modal': createGetter(() => require('../components/unsupported-media-modal'))
'unsupported-media-modal': createGetter(() => require('../components/unsupported-media-modal')),
'delete-all-torrents-modal':
createGetter(() => require('../components/delete-all-torrents-modal'))
}
const fontFamily = process.platform === 'win32'
@@ -88,8 +90,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>
@@ -95,6 +95,13 @@ function renderMedia (state) {
delete file.selectedSubtitle
}
// Switch to selected audio track
const audioTracks = mediaElement.audioTracks || []
for (let j = 0; j < audioTracks.length; j++) {
const isSelectedTrack = j === state.playing.audioTracks.selectedIndex
audioTracks[j].enabled = isSelectedTrack
}
state.playing.volume = mediaElement.volume
}
@@ -107,10 +114,11 @@ function renderMedia (state) {
trackTags.push(
<track
key={i}
default={isSelected ? 'default' : ''}
default={isSelected}
label={track.label}
type='subtitles'
src={track.buffer} />
kind='subtitles'
src={track.buffer}
/>
)
}
}
@@ -127,7 +135,7 @@ function renderMedia (state) {
onError={dispatcher('mediaError')}
onTimeUpdate={dispatcher('mediaTimeUpdate')}
onEncrypted={dispatcher('mediaEncrypted')}
onCanPlay={onCanPlay}>
>
{trackTags}
</MediaTagName>
)
@@ -137,21 +145,57 @@ function renderMedia (state) {
<div
key='letterbox'
className='letterbox'
onMouseMove={dispatcher('mediaMouseMoved')}>
onMouseMove={dispatcher('mediaMouseMoved')}
>
{mediaTag}
{renderOverlay(state)}
</div>
)
// As soon as we know the video dimensions, resize the window
function onLoadedMetadata (e) {
if (state.playing.type !== 'video') return
const video = e.target
const dimensions = {
width: video.videoWidth,
height: video.videoHeight
const mediaElement = e.target
// check if we can decode video and audio track
if (state.playing.type === 'video') {
if (mediaElement.videoTracks.length === 0) {
dispatch('mediaError', 'Video codec unsupported')
}
if (mediaElement.audioTracks.length === 0) {
dispatch('mediaError', 'Audio codec unsupported')
}
dispatch('mediaSuccess')
const dimensions = {
width: mediaElement.videoWidth,
height: mediaElement.videoHeight
}
// As soon as we know the video dimensions, resize the window
dispatch('setDimensions', dimensions)
// set audioTracks
const tracks = []
for (let i = 0; i < mediaElement.audioTracks.length; i++) {
tracks.push({
label: mediaElement.audioTracks[i].label || `Track ${i + 1}`,
language: mediaElement.audioTracks[i].language
})
}
state.playing.audioTracks.tracks = tracks
state.playing.audioTracks.selectedIndex = 0
}
// check if we can decode audio track
if (state.playing.type === 'audio') {
if (mediaElement.audioTracks.length === 0) {
dispatch('mediaError', 'Audio codec unsupported')
}
dispatch('mediaSuccess')
}
dispatch('setDimensions', dimensions)
}
function onEnded (e) {
@@ -160,19 +204,7 @@ function renderMedia (state) {
} else {
// When the last video completes, pause the video instead of looping
state.playing.isPaused = true
}
}
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()
if (state.window.isFullScreen) dispatch('toggleFullScreen')
}
}
}
@@ -203,28 +235,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'>
@@ -232,17 +277,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>
))
}
@@ -273,11 +391,11 @@ function renderLoadingSpinner (state) {
return (
<div key='loading' className='media-stalled'>
<div key='loading-spinner' className='loading-spinner'>&nbsp;</div>
<div key='loading-spinner' className='loading-spinner' />
<div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded,
<span> {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> {prettyBytes(prog.uploadSpeed || 0)}/s</span>
<span><span className='progress'>{fileProgress}%</span> downloaded</span>
<span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> ↑ {prettyBytes(prog.uploadSpeed || 0)}/s</span>
</div>
</div>
)
@@ -303,7 +421,7 @@ function renderCastScreen (state) {
isCast = false
} else if (state.playing.location === 'error') {
castIcon = 'error_outline'
castType = 'Error'
castType = 'Unable to Play'
isCast = false
}
@@ -333,7 +451,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) {
@@ -342,6 +460,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>
)
@@ -381,6 +500,27 @@ function renderSubtitleOptions (state) {
)
}
function renderAudioTrackOptions (state) {
const audioTracks = state.playing.audioTracks
if (!audioTracks.tracks.length || !audioTracks.showMenu) return
const items = audioTracks.tracks.map(function (track, ix) {
const isSelected = state.playing.audioTracks.selectedIndex === ix
return (
<li key={ix} onClick={dispatcher('selectAudioTrack', ix)}>
<i className='icon'>{'radio_button_' + (isSelected ? 'checked' : 'unchecked')}</i>
{track.label}
</li>
)
})
return (
<ul key='audio-track-options' className='options-list'>
{items}
</ul>
)
}
function renderPlayerControls (state) {
const positionPercent = 100 * state.playing.currentTime / state.playing.duration
const playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 3px)' }
@@ -389,6 +529,9 @@ function renderPlayerControls (state) {
: state.playing.subtitles.selectedIndex >= 0
? 'active'
: ''
const multiAudioClass = state.playing.audioTracks.tracks.length > 1
? 'active'
: 'disabled'
const prevClass = Playlist.hasPrevious(state) ? '' : 'disabled'
const nextClass = Playlist.hasNext(state) ? '' : 'disabled'
@@ -398,41 +541,47 @@ function renderPlayerControls (state) {
<div
key='cursor'
className='playback-cursor'
style={playbackCursorStyle} />
style={playbackCursorStyle}
/>
<div
key='scrub-bar'
className='scrub-bar'
draggable='true'
draggable
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>
]
@@ -443,9 +592,18 @@ function renderPlayerControls (state) {
<i
key='subtitles'
className={'icon closed-caption float-right ' + captionsClass}
onClick={handleSubtitles}>
onClick={handleSubtitles}
>
closed_caption
</i>
), (
<i
key='audio-tracks'
className={'icon multi-audio float-right ' + multiAudioClass}
onClick={handleAudioTracks}
>
library_music
</i>
))
}
@@ -456,9 +614,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?
@@ -487,7 +645,8 @@ function renderPlayerControls (state) {
<i
key={castType}
className={'icon device float-right ' + buttonClass}
onClick={buttonHandler}>
onClick={buttonHandler}
>
{buttonIcon}
</i>
))
@@ -497,9 +656,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), ' +
@@ -510,7 +669,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
@@ -518,13 +678,14 @@ function renderPlayerControls (state) {
type='range' min='0' max='1' step='0.05'
value={volume}
onChange={handleVolumeScrub}
style={volumeStyle} />
style={volumeStyle}
/>
</div>
))
// Show video playback progress
const currentTimeStr = formatTime(state.playing.currentTime)
const durationStr = formatTime(state.playing.duration)
const currentTimeStr = formatTime(state.playing.currentTime, state.playing.duration)
const durationStr = formatTime(state.playing.duration, state.playing.duration)
elements.push((
<span key='time' className='time float-left'>
{currentTimeStr} / {durationStr}
@@ -541,12 +702,15 @@ 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)}
{renderAudioTrackOptions(state)}
</div>
)
@@ -590,6 +754,10 @@ function renderPlayerControls (state) {
dispatch('toggleSubtitlesMenu')
}
}
function handleAudioTracks (e) {
dispatch('toggleAudioTracksMenu')
}
}
// Renders the loading bar. Shows which parts of the torrent are loaded, which
@@ -599,18 +767,21 @@ function renderLoadingBar (state) {
const torrentSummary = state.getPlayingTorrentSummary()
if (!torrentSummary.progress) {
return []
return null
}
// Find all contiguous parts of the torrent which are loaded
const prog = torrentSummary.progress
const fileProg = prog.files[state.playing.fileIndex]
if (!fileProg) return null
const parts = []
let lastPiecePresent = false
for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) {
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++
}
@@ -626,6 +797,7 @@ function renderLoadingBar (state) {
return (<div key={i} className='loading-bar-part' style={style} />)
})
return (<div key='loading-bar' className='loading-bar'>{loadingBarElems}</div>)
}
@@ -642,17 +814,19 @@ function cssBackgroundImageDarkGradient () {
'rgba(0,0,0,0.4) 0%, rgba(0,0,0,1) 100%)'
}
function formatTime (time) {
function formatTime (time, total) {
if (typeof time !== 'number' || Number.isNaN(time)) {
return '0:00'
}
let 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 (hours > 0) {
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 (hours > 0 ? hours + ':' : '') + minutes + ':' + seconds
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>
)
}
@@ -62,11 +67,29 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'openExternalPlayer', !isChecked)
}
highestPlaybackPriorityCheckbox () {
return (
<Preference>
<Checkbox
className='control'
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>
</Preference>
)
}
handleHighestPlaybackPriorityChange (e, isChecked) {
dispatch('updatePreferences', 'highestPlaybackPriority', isChecked)
}
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.`
@@ -76,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>
)
}
@@ -90,8 +113,61 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'externalPlayerPath', filePath)
}
autoAddTorrentsCheckbox () {
return (
<Preference>
<Checkbox
className='control'
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>
)
}
handleAutoAddTorrentsChange (e, isChecked) {
const torrentsFolderPath = this.props.state.saved.prefs.torrentsFolderPath
if (isChecked && !torrentsFolderPath) {
alert('Select a torrents folder first.') // eslint-disable-line
e.preventDefault()
return
}
dispatch('updatePreferences', 'autoAddTorrents', isChecked)
if (isChecked) {
dispatch('startFolderWatcher')
return
}
dispatch('stopFolderWatcher')
}
torrentsFolderPathSelector () {
const torrentsFolderPath = this.props.state.saved.prefs.torrentsFolderPath
return (
<Preference>
<PathSelector
dialog={{
title: 'Select folder to watch for new torrents',
properties: ['openDirectory']
}}
onChange={this.handleTorrentsFolderPathChange}
title='Folder to watch'
value={torrentsFolderPath}
/>
</Preference>
)
}
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>
@@ -105,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>
)
}
@@ -114,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)
}
@@ -145,17 +237,23 @@ class PreferencesPage extends React.Component {
}
return (
<div style={style}>
<PreferencesSection title='Downloads'>
<PreferencesSection title='Folders'>
{this.downloadPathSelector()}
{this.autoAddTorrentsCheckbox()}
{this.torrentsFolderPathSelector()}
</PreferencesSection>
<PreferencesSection title='Playback'>
{this.openExternalPlayerCheckbox()}
{this.externalPlayerPathSelector()}
{this.highestPlaybackPriorityCheckbox()}
</PreferencesSection>
<PreferencesSection title='Default torrent app'>
{this.setDefaultAppButton()}
</PreferencesSection>
{this.setStartupSection()}
<PreferencesSection title='General'>
{this.setStartupCheckbox()}
{this.soundNotificationsCheckbox()}
</PreferencesSection>
</div>
)
}
@@ -164,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>
@@ -278,10 +284,17 @@ module.exports = class TorrentList extends React.Component {
)
} else {
// We do know the files. List them and show download stats for each one
const fileRows = torrentSummary.files
const sortByName = this.props.state.saved.prefs.sortByName
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })
let fileRows = torrentSummary.files
.filter((file) => !file.path.includes('/.____padding_file/'))
.map((file, index) => ({ file, index }))
.map((object) => this.renderFileRow(torrentSummary, object.file, object.index))
if (sortByName) {
fileRows = fileRows.sort((a, b) => collator.compare(a.file.name, b.file.name))
}
fileRows = fileRows.map((obj) => this.renderFileRow(torrentSummary, obj.file, obj.index))
filesElement = (
<div key='files' className='files'>
@@ -312,7 +325,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
@@ -333,7 +346,7 @@ module.exports = class TorrentList extends React.Component {
} else {
icon = 'description' /* file icon, opens in OS default app */
handleClick = isDone
? dispatcher('openItem', infoHash, index)
? dispatcher('openPath', infoHash, index)
: (e) => e.stopPropagation() // noop if file is not ready
}
// TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed
@@ -355,8 +368,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 +380,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 +408,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

@@ -3,20 +3,18 @@
console.time('init')
const crypto = require('crypto')
const deepEqual = require('deep-equal')
const util = require('util')
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 +39,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 +94,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')
@@ -115,8 +112,8 @@ function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections)
console.log('starting torrent %s: %s', torrentKey, torrentID)
const torrent = client.add(torrentID, {
path: path,
fileModtimes: fileModtimes
path,
fileModtimes
})
torrent.key = torrentKey
@@ -128,6 +125,7 @@ function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections)
}
function stopTorrenting (infoHash) {
console.log('--- STOP TORRENTING: ', infoHash)
const torrent = client.get(infoHash)
if (torrent) torrent.destroy()
}
@@ -148,7 +146,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)
@@ -217,7 +215,7 @@ function saveTorrentFile (torrentKey) {
}
// Otherwise, save the .torrent file, under the app config folder
mkdirp(config.TORRENT_PATH, function (_) {
fs.mkdir(config.TORRENT_PATH, { recursive: true }, function (_) {
fs.writeFile(torrentPath, torrent.torrentFile, function (err) {
if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
console.log('saved torrent file %s', torrentPath)
@@ -234,7 +232,7 @@ function generateTorrentPoster (torrentKey) {
torrentPoster(torrent, function (err, buf, extension) {
if (err) return console.log('error generating poster: %o', err)
// save it for next time
mkdirp(config.POSTER_PATH, function (err) {
fs.mkdir(config.POSTER_PATH, { recursive: true }, function (err) {
if (err) return console.log('error creating poster dir: %o', err)
const posterFileName = torrent.infoHash + extension
const posterFilePath = path.join(config.POSTER_PATH, posterFileName)
@@ -250,7 +248,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 && util.isDeepStrictEqual(progress, prevProgress)) {
return /* don't send heavy object if it hasn't changed */
}
ipc.send('wt-progress', progress)
@@ -319,7 +317,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)
@@ -333,16 +332,43 @@ 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(
metadata => {
ipc.send('wt-audio-metadata', infoHash, index, metadata)
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

@@ -32,8 +32,8 @@
<p>
Version <script>document.write(require('../package.json').version)</script>
(<script>document.write(require('webtorrent/package.json').version)</script>)
(<script>document.write(process.arch === 'x64' ? '64-bit' : '32-bit')</script>)
(<script>document.write(process.arch)</script>)
</p>
<p><script>document.write(require('../config').APP_COPYRIGHT)</script></p>
<p><script>document.write(require('../build/config').APP_COPYRIGHT)</script></p>
</body>
</html>

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 %> --no-sandbox %U<% } %><%/*HACK: --no-sandbox fixes an Electron 6 bug. See: #1703*/%>
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 %> --no-sandbox -n <% } %><%/*HACK: --no-sandbox fixes an Electron 6 bug. See: #1703*/%>
[Desktop Action OpenTorrentFile]
Name=Open Torrent File...
<% if (name) { %>Exec=<%= name %> --no-sandbox -o <% } %><%/*HACK: --no-sandbox fixes an Electron 6 bug. See: #1703*/%>
[Desktop Action OpenTorrentAddress]
Name=Open Torrent Address...
<% if (name) { %>Exec=<%= name %> --no-sandbox -u <% } %><%/*HACK: --no-sandbox fixes an Electron 6 bug. See: #1703*/%>

View File

@@ -615,6 +615,7 @@ body.drag .app::after {
font-size: 13px;
margin: 9px 8px 8px 8px;
opacity: 0.8;
font-variant-numeric: tabular-nums;
}
.player .controls .icon.closed-caption {
@@ -622,6 +623,11 @@ body.drag .app::after {
margin-top: 6px;
}
.player .controls .icon.multi-audio {
font-size: 26px;
margin-top: 6px;
}
.player .controls .icon.fullscreen {
font-size: 26px;
margin-right: 15px;
@@ -800,6 +806,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 {
@@ -819,12 +842,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')

View File

@@ -1,15 +1,18 @@
const electron = require('electron')
const config = require('./config')
console.log('Mocking electron.dialog.showOpenDialog...')
electron.dialog.showOpenDialog = function (win, opts, cb) {
const ret = /select.*torrent file/i.test(opts.title)
console.log('Mocking electron.dialog.showOpenDialogSync...')
electron.dialog.showOpenDialogSync = showOpenDialogSync
function showOpenDialogSync (win, opts) {
return /select.*torrent file/i.test(opts.title)
? config.TORRENT_FILES
: config.SEED_FILES
cb(ret)
}
console.log('Mocking electron.remote.dialog.showSaveDialog...')
electron.dialog.showSaveDialog = function (win, opts, cb) {
cb(config.SAVED_TORRENT_FILE)
console.log('Mocking electron.dialog.showSaveDialogSync...')
electron.dialog.showSaveDialogSync = showSaveDialogSync
function showSaveDialogSync (win, opts) {
return config.SAVED_TORRENT_FILE
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 966 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 KiB

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 2.2 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: 381 KiB

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