Compare commits

...

1099 Commits

Author SHA1 Message Date
renovate[bot]
0a2ea1e09d chore(deps): update dependency @babel/core to v7.29.0 (#2504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-27 01:29:47 +00:00
renovate[bot]
be77363515 chore(deps): update babel monorepo to v7.28.6 (#2501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-15 05:51:38 +00:00
renovate[bot]
b1a1c5c01b chore(deps): update babel monorepo to v7.28.5 (#2494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-28 03:47:34 +00:00
renovate[bot]
6b60360ce1 chore(deps): update babel monorepo to v7.28.4 (#2488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 16:07:58 +00:00
renovate[bot]
56e92101b0 chore(deps): update babel monorepo to v7.28.3 (#2487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 12:46:15 +00:00
renovate[bot]
2a5c67f9e3 fix(deps): update dependency @electron/remote to v2.1.3 (#2485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 21:13:11 +00:00
renovate[bot]
ef39f41e96 chore(deps): update babel monorepo to v7.28.0 (#2484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 13:11:16 +00:00
renovate[bot]
d317ea3ff8 chore(deps): update dependency @babel/core to v7.27.7 (#2482)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 15:49:04 +00:00
renovate[bot]
787e4c441b chore(deps): update dependency @babel/eslint-parser to v7.27.5 (#2480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 16:06:08 +00:00
renovate[bot]
ef116d01b7 chore(deps): update dependency @babel/core to v7.27.4 (#2479)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 17:03:45 +00:00
renovate[bot]
0a5be1f166 chore(deps): update dependency @babel/core to v7.27.3 (#2478)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-28 09:28:35 +00:00
renovate[bot]
4a35518da7 chore(deps): update dependency @babel/cli to v7.27.2 (#2475)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-07 20:22:11 +00:00
renovate[bot]
8f3301cd1e chore(deps): update babel monorepo to v7.27.1 (#2474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-01 17:29:51 +00:00
renovate[bot]
97b10558f6 chore(deps): update babel monorepo (#2466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 12:10:52 +00:00
renovate[bot]
e61501f13f chore(deps): update babel monorepo (#2459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 21:49:47 +00:00
renovate[bot]
b1d1778479 chore(deps): update dependency @babel/eslint-parser to v7.26.5 (#2457)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-15 14:14:09 +00:00
renovate[bot]
9cca2d39c3 chore(deps): update babel monorepo (#2453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-08 21:05:11 +00:00
renovate[bot]
fcee4017d7 chore(deps): update dependency tape to v5.9.0 2024-10-16 06:34:29 +00:00
renovate[bot]
2890419d6d chore(deps): update babel monorepo 2024-10-16 05:21:16 +00:00
renovate[bot]
0fe3dd977f chore(deps): update dependency @babel/cli to v7.25.6 2024-08-30 13:58:05 +00:00
renovate[bot]
9ba088f76f chore(deps): update dependency electron-winstaller to v5.4.0 2024-08-12 11:46:38 +00:00
renovate[bot]
1a7404ee8a chore(deps): update babel monorepo 2024-08-12 11:31:09 +00:00
renovate[bot]
f1de7606a5 chore(deps): update npm to v10 (#2426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-12 13:28:56 +02:00
Borewit
d4b493cebe Fix CI shield in README (#2437)
Replace deprecated Travis shield with GitHub Action CI shield
2024-08-12 13:24:03 +02:00
renovate[bot]
1df3d025bd chore(deps): update babel monorepo 2024-07-21 17:27:22 +00:00
renovate[bot]
61b7681fd1 chore(deps): update dependency tape to v5.8.1 2024-06-17 18:50:56 +00:00
renovate[bot]
ac7f16e71c chore(deps): update dependency tape to v5.8.0 2024-06-16 04:07:26 +00:00
renovate[bot]
b363dca77f chore(deps): update dependency electron-winstaller to v5.3.1 2024-06-09 19:36:43 +00:00
renovate[bot]
16cabd19ff chore(deps): update dependency electron to v27.3.11 2024-06-09 15:22:50 +00:00
renovate[bot]
390b4ad7ec chore(deps): update babel monorepo to v7.24.7 2024-06-09 12:08:50 +00:00
renovate[bot]
b3dc3292b7 chore(deps): update dependency electron-winstaller to v5.3.0 2024-04-02 19:13:47 +00:00
renovate[bot]
244a242bdc chore(deps): update dependency electron to v27.3.8 2024-04-02 16:20:07 +00:00
renovate[bot]
241f3cb9a3 chore(deps): update babel monorepo 2024-04-02 16:17:23 +00:00
Herwig Hochleitner
aa4fb3d08e chore(deps): Upgrade to Electron v27 (#2388) 2024-04-02 18:14:50 +02:00
renovate[bot]
ebaf9cf848 fix(deps): update dependency music-metadata to v7.14.0 2024-02-26 01:21:30 +00:00
renovate[bot]
df05db5583 fix(deps): update dependency @electron/remote to v2.1.2 2024-02-25 22:28:16 +00:00
renovate[bot]
1ccfa404d2 chore(deps): update dependency tape to v5.7.5 2024-02-25 19:37:03 +00:00
renovate[bot]
bc19caf8ee chore(deps): update dependency electron-winstaller to v5.2.2 2024-02-25 15:50:03 +00:00
renovate[bot]
36c24a4940 chore(deps): update babel monorepo 2024-02-25 12:07:37 +00:00
renovate[bot]
1cefcff3da chore(deps): update dependency depcheck to v1.4.7 2023-12-20 00:09:20 +00:00
renovate[bot]
1eb61201d6 chore(deps): update dependency electron-installer-debian to v3.2.0 2023-09-07 03:37:53 +00:00
renovate[bot]
3e514f9cf6 fix(deps): update dependency @electron/remote to v2.0.11 2023-09-07 01:35:26 +00:00
renovate[bot]
b8f1d950f2 chore(deps): update dependency depcheck to v1.4.5 2023-09-06 22:04:13 +00:00
renovate[bot]
a85effc8ec chore(deps): update babel monorepo to v7.22.15 2023-09-06 22:01:22 +00:00
renovate[bot]
cf846979b0 chore(deps): update babel monorepo to v7.22.10 2023-08-08 20:16:46 +00:00
renovate[bot]
9f630c9bfb chore(deps): update dependency tape to v5.6.6 2023-07-19 23:16:44 +00:00
renovate[bot]
1d66b6d069 chore(deps): update dependency tape to v5.6.5 2023-07-13 22:50:50 +00:00
renovate[bot]
6b83ed34b9 chore(deps): update babel monorepo to v7.22.9 2023-07-13 18:39:30 +00:00
renovate[bot]
a0aeae7e91 chore(deps): update dependency plist to v3.1.0 2023-07-07 20:28:35 +00:00
renovate[bot]
99e3058676 chore(deps): update babel monorepo 2023-07-07 15:29:59 +00:00
renovate[bot]
20d1d8a224 chore(deps): update babel monorepo to v7.22.6 2023-07-05 12:31:07 +00:00
renovate[bot]
0b34ff1148 chore(deps): update dependency tape to v5.6.4 2023-07-03 07:26:03 +00:00
renovate[bot]
4120fcf4af chore(deps): update babel monorepo to v7.22.5 2023-06-14 12:29:59 +00:00
renovate[bot]
7ea277ccf6 fix(deps): update dependency @electron/remote to v2.0.10 2023-06-07 00:00:55 +00:00
renovate[bot]
77a981178b chore(deps): update dependency @babel/plugin-transform-react-jsx to v7.22.3 2023-05-28 14:03:53 +00:00
renovate[bot]
ea19545ec6 chore(deps): update dependency @babel/core to v7.22.1 2023-05-27 19:28:13 +00:00
renovate[bot]
6f6ccb029c chore(deps): update babel monorepo to v7.22.0 2023-05-27 15:44:47 +00:00
renovate[bot]
43b2d28fb5 chore(deps): update babel monorepo to v7.21.8 2023-05-03 21:19:42 +00:00
renovate[bot]
f20d24ac3c chore(deps): update babel monorepo to v7.21.5 2023-04-30 02:00:10 +00:00
renovate[bot]
b1332ee76b chore(deps): update dependency @babel/core to v7.21.4 2023-04-01 15:08:16 +00:00
renovate[bot]
34b8f3fb64 fix(deps): update dependency music-metadata to v7.13.4 2023-03-30 20:52:34 +00:00
renovate[bot]
b4f98e91ff chore(deps): update dependency nodemon to v2.0.22 2023-03-23 22:26:52 +00:00
renovate[bot]
ebb8d23ef3 chore(deps): update dependency gh-release to v7 (#2301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-16 14:23:49 +01:00
renovate[bot]
e5bf6745bf chore(deps): update dependency pngjs to v7 (#2326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-16 14:14:54 +01:00
renovate[bot]
686f63b35f fix(deps): update dependency rimraf to v4 (#2328)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-16 14:09:44 +01:00
renovate[bot]
4ed5f3ff5f chore(deps): update dependency appdmg to v0.6.6 (#2318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2023-03-16 14:01:51 +01:00
dependabot[bot]
af8379012a build(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#2313)
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-16 13:58:45 +01:00
renovate[bot]
1c53102e12 chore(deps): update dependency minimist to v1.2.8 (#2319)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-16 13:52:55 +01:00
renovate[bot]
1a074b65e6 chore(deps): update dependency open to v8.4.2 (#2321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-16 13:42:26 +01:00
renovate[bot]
2052adde7d chore(deps): update dependency electron-installer-redhat to v3.4.0 2023-03-16 12:37:17 +00:00
renovate[bot]
eed9eebd45 fix(deps): update dependency webtorrent to v1.9.7 2023-03-16 12:31:43 +00:00
renovate[bot]
e36eacd05a fix(deps): update dependency music-metadata to v7.13.3 2023-03-16 12:26:12 +00:00
renovate[bot]
9c7cffa1a3 chore(deps): update dependency tape to v5.6.3 2023-03-16 12:20:53 +00:00
renovate[bot]
94d2bd7807 chore(deps): update dependency nodemon to v2.0.21 2023-03-16 12:15:21 +00:00
renovate[bot]
5efbf7e5c9 chore(deps): update babel monorepo 2023-03-16 12:10:29 +00:00
renovate[bot]
fce078defe fix(deps): update dependency music-metadata to v7.13.1 2023-01-06 17:32:19 +00:00
renovate[bot]
10f355ba0f chore(deps): update dependency @babel/core to v7.20.12 2023-01-05 19:46:05 +00:00
renovate[bot]
9d8293b970 chore(deps): update babel monorepo to v7.20.7 2022-12-23 12:23:19 +00:00
renovate[bot]
73b8b54ae8 fix(deps): update dependency @electron/remote to v2.0.9 2022-12-07 02:25:28 +00:00
renovate[bot]
bc49dbc41d fix(deps): update dependency webtorrent to v1.9.6 2022-12-04 00:00:17 +00:00
renovate[bot]
69b1ff58d0 chore(deps): update dependency @babel/core to v7.20.5 2022-11-29 15:34:34 +00:00
renovate[bot]
cf48311fcb fix(deps): update dependency webtorrent to v1.9.4 2022-11-21 14:56:21 +00:00
renovate[bot]
c919af1fd9 fix(deps): update dependency webtorrent to v1.9.3 2022-11-19 00:57:08 +00:00
dependabot[bot]
cdc5ac6538 build(deps): bump loader-utils from 1.4.0 to 1.4.2 (#2288)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.2.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.2)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-11 15:04:46 +01:00
renovate[bot]
05881478b5 fix(deps): update dependency webtorrent to v1.9.2 2022-11-11 05:52:42 +00:00
renovate[bot]
fb279010d0 chore(deps): update dependency electron-winstaller to v5.1.0 (#2277)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-11 01:10:49 +01:00
renovate[bot]
e54f61c480 fix(deps): update dependency create-torrent to v5.0.9 (#2287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-11 01:00:56 +01:00
renovate[bot]
ab9468f84f chore(deps): update npm to v9 (#2283)
* chore(deps): update npm to v9

* Update package.json

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Diego Rodríguez Baquero <diego@protocol.ai>
2022-11-10 15:15:19 -05:00
renovate[bot]
0f20c0acd1 chore(deps): update dependency @babel/core to v7.20.2 2022-11-06 00:06:21 +00:00
renovate[bot]
a4ef6cb562 fix(deps): update dependency webtorrent to v1.9.1 2022-10-28 16:53:40 +00:00
renovate[bot]
21c260dc13 chore(deps): update dependency electron-notarize to v1.2.2 2022-10-22 01:44:22 +00:00
renovate[bot]
0df520c5ba chore(deps): update dependency @babel/core to v7.19.6 2022-10-21 12:02:18 +00:00
renovate[bot]
707461a264 chore(deps): update dependency minimist to v1.2.7 (#2271)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-15 16:35:45 +02:00
renovate[bot]
a22f94df80 fix(deps): update dependency semver to v7.3.8 (#2268)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-09 12:05:24 +02:00
renovate[bot]
a60e817e0c fix(deps): update dependency webtorrent to v1.8.32 2022-10-09 07:34:24 +00:00
renovate[bot]
f1865dda4f fix(deps): update dependency webtorrent to v1.8.31 2022-10-09 02:35:05 +00:00
renovate[bot]
8cdc6b1307 chore(deps): update dependency spectron to v19 (#2133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-10-01 16:31:12 +02:00
dependabot[bot]
660facf8c2 build(deps): bump jsonpointer and is-my-json-valid (#2264)
Bumps [jsonpointer](https://github.com/janl/node-jsonpointer) and [is-my-json-valid](https://github.com/mafintosh/is-my-json-valid). These dependencies needed to be updated together.

Updates `jsonpointer` from 4.1.0 to 5.0.1
- [Release notes](https://github.com/janl/node-jsonpointer/releases)
- [Commits](https://github.com/janl/node-jsonpointer/compare/v4.1.0...v5.0.1)

Updates `is-my-json-valid` from 2.20.5 to 2.20.6
- [Release notes](https://github.com/mafintosh/is-my-json-valid/releases)
- [Commits](https://github.com/mafintosh/is-my-json-valid/compare/v2.20.5...v2.20.6)

---
updated-dependencies:
- dependency-name: jsonpointer
  dependency-type: indirect
- dependency-name: is-my-json-valid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-10-01 12:08:04 +02:00
renovate[bot]
fee5b42813 chore(deps): update actions/stale action to v6 (#2259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-01 12:03:49 +02:00
renovate[bot]
ddb8500082 chore(deps): update babel monorepo to v7.19.3 2022-09-28 18:55:54 +00:00
renovate[bot]
cc6af9829e chore(deps): update dependency tape to v5.6.1 2022-09-20 23:51:15 +00:00
renovate[bot]
cfba6b503a fix(deps): update dependency music-metadata to v7.13.0 2022-09-19 12:15:21 +00:00
renovate[bot]
04c766bb53 chore(deps): update dependency nodemon to v2.0.20 2022-09-17 13:23:51 +00:00
renovate[bot]
2389a1aa35 chore(deps): update babel monorepo to v7.19.1 2022-09-15 17:58:20 +00:00
renovate[bot]
430b4c409a fix(deps): update dependency webtorrent to v1.8.30 2022-09-12 22:50:38 +00:00
renovate[bot]
810952f234 chore(deps): update babel monorepo to v7.19.0 2022-09-07 01:54:36 +00:00
renovate[bot]
ae4a8e40f8 fix(deps): update dependency webtorrent to v1.8.29 2022-09-03 10:27:11 +00:00
renovate[bot]
ea100dda36 fix(deps): update webtorrent 2022-09-03 01:22:01 +00:00
Diego Rodríguez Baquero
46cbce00ca Update stale.yml 2022-09-02 11:07:08 -05:00
dependabot[bot]
00ebbc7e17 build(deps): bump protobufjs from 6.10.2 to 6.11.3 (#2214)
Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.10.2 to 6.11.3.
- [Release notes](https://github.com/protobufjs/protobuf.js/releases)
- [Changelog](https://github.com/protobufjs/protobuf.js/blob/v6.11.3/CHANGELOG.md)
- [Commits](https://github.com/protobufjs/protobuf.js/compare/v6.10.2...v6.11.3)

---
updated-dependencies:
- dependency-name: protobufjs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-28 18:48:05 +02:00
renovate[bot]
8109ce5410 fix(deps): update dependency iso-639-1 to v2.1.15 (#2213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-28 00:24:16 +02:00
renovate[bot]
9200070269 chore(deps): update dependency electron-packager to v15.5.2 2022-08-24 03:23:12 +00:00
renovate[bot]
16ab96e4ac chore(deps): update dependency @babel/core to v7.18.13 2022-08-23 19:04:25 +00:00
renovate[bot]
d8632f6386 chore(deps): update dependency tape to v5.6.0 2022-08-17 22:07:38 +00:00
renovate[bot]
5558bad0de fix(deps): update dependency music-metadata to v7.12.6 2022-08-05 12:27:45 +00:00
renovate[bot]
0f5b7fd80e chore(deps): update babel monorepo to v7.18.10 2022-08-02 19:54:53 +00:00
renovate[bot]
6a288efb57 fix(deps): update dependency music-metadata to v7.12.5 2022-07-25 10:26:40 +00:00
renovate[bot]
f07a307b0d chore(deps): update babel monorepo to v7.18.9 2022-07-19 11:40:08 +00:00
renovate[bot]
cb1de722fe chore(deps): update dependency plist to v3.0.6 2022-07-13 22:41:20 +00:00
renovate[bot]
45036451c7 chore(deps): update dependency nodemon to v2.0.19 2022-07-06 17:13:24 +00:00
renovate[bot]
58a4f7a34a fix(deps): update dependency music-metadata to v7.12.4 2022-07-05 12:43:25 +00:00
renovate[bot]
9082c6f63e fix(deps): update dependency webtorrent to v1.8.26 2022-07-04 02:50:37 +00:00
renovate[bot]
9fb8a2ae09 fix(deps): update webtorrent 2022-07-04 02:42:36 +00:00
renovate[bot]
0e501b498e fix(deps): update dependency create-torrent to v5.0.3 2022-07-03 19:35:47 +00:00
renovate[bot]
85e0c5709e chore(deps): update babel monorepo to v7.18.6 2022-06-28 19:52:17 +00:00
renovate[bot]
cee88a7e24 chore(deps): update dependency nodemon to v2.0.18 2022-06-24 15:37:09 +00:00
renovate[bot]
f503a46447 fix(deps): update dependency webtorrent to v1.8.24 2022-06-24 05:42:10 +00:00
renovate[bot]
1cdd54e421 fix(deps): update dependency webtorrent to v1.8.23 2022-06-23 05:47:20 +00:00
Renovate Bot
73be2513ad chore(deps): update dependency @babel/core to v7.18.5 2022-06-14 12:06:06 +00:00
Renovate Bot
e9eb22a530 chore(deps): update babel monorepo to v7.18.2 2022-05-26 11:32:46 +00:00
Renovate Bot
d17f287ba2 chore(deps): update dependency electron to v15.5.7 2022-05-25 20:03:10 +00:00
Renovate Bot
06b904b837 fix(deps): update dependency webtorrent to v1.8.22 2022-05-24 05:20:44 +00:00
Renovate Bot
807c6aebdd chore(deps): update dependency @babel/core to v7.18.0 2022-05-20 20:27:48 +00:00
Renovate Bot
ab54a7cfb3 chore(deps): update babel monorepo to v7.17.12 2022-05-17 20:07:55 +00:00
Renovate Bot
77d4eed9fc fix(deps): update dependency webtorrent to v1.8.20 2022-05-14 04:56:53 +00:00
Diego Rodriguez Baquero
9c73638d1b fix: standard 2022-05-13 17:15:56 -05:00
Alex Lavallee
3dd7ebb34b feat(Player Page): Add support for mediaSession (#2170)
* feat(Player Page): Add support for mediaSession

* Update player-page.js
2022-05-11 18:44:43 -05:00
Subin Siby
c943f39f6b feat: add preference to customize global trackers (#1836)
* Add preference to customize global trackers. Requires restart to apply

* Use IPC to pass global trackers list, torrent pause and resume will now update trackers

* Make the default tracker list an array from array of arrays

* Use globalThis instead of just global

Co-authored-by: Diego Rodríguez Baquero <github@diegorbaquero.com>
2022-05-11 18:43:54 -05:00
CommanderRoot
401698e616 refactor: replace deprecated String.prototype.substr() (#2173)
.substr() is deprecated so we replace it with .slice() which works similarily but isn't deprecated

Signed-off-by: Tobias Speicher <rootcommander@gmail.com>
2022-05-11 18:41:56 -05:00
Christopher Toth
c85b3e4fd1 feat(a11y): Added roles and labels to the playback controls (#2167) 2022-05-11 18:40:43 -05:00
renovate[bot]
be15ee073d chore(deps): update dependency electron to v15.5.5 (#2204)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:39:01 -05:00
renovate[bot]
d838e6276d chore(deps): update babel monorepo to v7.17.10 (#2203)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:38:43 -05:00
renovate[bot]
35af086fad chore(deps): update dependency electron-packager to v15.5.1 (#2197)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:38:31 -05:00
renovate[bot]
cf9fbf2b0e fix(deps): update dependency semver to v7.3.7 (#2192)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:38:09 -05:00
renovate[bot]
ef2225c8a5 fix(deps): update dependency bitfield to v4.1.0 (#2201)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:37:22 -05:00
renovate[bot]
ffacb6085d chore(deps): update dependency nodemon to v2.0.16 (#2202)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:37:00 -05:00
renovate[bot]
20a84359cd chore(deps): update dependency standard to v17 (#2198)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:33:58 -05:00
renovate[bot]
a3d123537f fix(deps): update dependency webtorrent to v1.8.19 (#2200)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-11 18:21:13 -05:00
Renovate Bot
30fb222fb9 chore(deps): update dependency gh-release to v6.0.4 2022-04-16 00:08:23 +00:00
Renovate Bot
4c2a08dd52 chore(deps): update dependency tape to v5.5.3 2022-04-09 20:19:02 +00:00
Renovate Bot
406c568c1e chore(deps): update dependency electron to v15.5.2 2022-04-07 21:01:30 +00:00
Renovate Bot
a86424b7df chore(deps): update dependency @babel/core to v7.17.9 2022-04-07 17:13:51 +00:00
Renovate Bot
f6ffc16964 fix(deps): update dependency music-metadata to v7.12.3 2022-04-02 19:49:42 +00:00
Renovate Bot
05e8a0d284 chore(deps): update dependency electron to v15.5.1 2022-03-31 18:17:15 +00:00
Renovate Bot
1dc38afc85 fix(deps): update dependency webtorrent to v1.8.13 2022-03-31 04:44:02 +00:00
renovate[bot]
ca73b3c286 chore(deps): update actions/cache action to v3 (#2171)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-30 12:21:51 +03:00
renovate[bot]
bad30e8f1a chore(deps): update dependency minimist to v1.2.6 (#2172)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-30 12:21:22 +03:00
Renovate Bot
56036e89af chore(deps): update dependency electron to v15.5.0 2022-03-30 02:28:31 +00:00
Renovate Bot
bb7a9bb018 fix(deps): update dependency webtorrent to v1.8.12 2022-03-28 05:58:37 +00:00
Renovate Bot
f9ba0750d6 fix(deps): update dependency webtorrent to v1.8.11 2022-03-28 00:47:47 +00:00
Renovate Bot
d98299268f fix(deps): update dependency webtorrent to v1.8.9 2022-03-27 00:08:53 +00:00
Renovate Bot
601d01fe85 fix(deps): update dependency parse-torrent to v9.1.5 2022-03-26 19:32:10 +00:00
Renovate Bot
b24cc29523 chore(deps): update dependency electron-notarize to v1.2.1 2022-03-25 10:03:02 +00:00
Renovate Bot
dd06ac7cc5 fix(deps): update dependency webtorrent to v1.8.8 2022-03-25 04:31:32 +00:00
Renovate Bot
4097c5ee18 chore(deps): update dependency electron to v15.4.2 2022-03-24 22:26:07 +00:00
Renovate Bot
240128943c chore(deps): update dependency plist to v3.0.5 2022-03-24 17:50:55 +00:00
Renovate Bot
6d8a2a0b9e chore(deps): update dependency @babel/core to v7.17.8 2022-03-19 21:08:10 +00:00
Renovate Bot
6433da4901 fix(deps): update dependency @electron/remote to v2.0.8 2022-03-16 21:23:58 +00:00
renovate[bot]
96b27fa1cb chore(deps): update dependency gh-release to v6.0.3 (#2165)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-16 12:21:09 -05:00
Renovate Bot
9134620fe7 chore(deps): update dependency @babel/core to v7.17.7 2022-03-15 19:13:56 +00:00
Renovate Bot
1ff1c035b7 fix(deps): update webtorrent 2022-03-11 16:29:50 +00:00
Renovate Bot
58e165eb30 fix(deps): update dependency @electron/remote to v2.0.7 2022-03-11 16:22:58 +00:00
Renovate Bot
4fdafc91c1 chore(deps): update dependency electron to v15.4.1 2022-03-11 16:14:31 +00:00
Renovate Bot
7ef740ebe0 fix(deps): update dependency webtorrent to v1.8.5 2022-03-06 13:25:50 +00:00
renovate[bot]
138ece60fd chore(deps): update actions/upload-artifact action to v3 (#2158)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-04 17:49:01 +01:00
renovate[bot]
a2d0cc09eb chore(deps): update actions/stale action to v5 (#2155)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 15:22:31 +01:00
renovate[bot]
a701d44d25 chore(deps): update dependency hosted-git-info to 2.8.9 [security] (#2138)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 15:12:18 +01:00
renovate[bot]
4a67d1c679 chore(deps): update actions/setup-node action to v3 (#2154)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-03 15:00:35 +01:00
Renovate Bot
450ecedad4 fix(deps): update dependency webtorrent to v1.8.4 2022-03-03 12:28:01 +00:00
renovate[bot]
55ea243862 chore(deps): update dependency electron to v15.4.0 (#2150)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 12:42:00 +01:00
renovate[bot]
319a27399e chore(deps): update dependency tape to v5.5.2 (#2147)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 12:40:30 +01:00
renovate[bot]
f532d15222 chore(deps): update actions/checkout action to v3 (#2152)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-03 12:37:01 +01:00
renovate[bot]
3b4a2da2be fix(deps): update dependency music-metadata to v7.12.2 (#2151)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-03 12:31:12 +01:00
renovate[bot]
c464074c92 fix(deps): update dependency webtorrent to v1.8.3 (#2149)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 12:09:43 +01:00
renovate[bot]
4945ba9073 fix(deps): update dependency @electron/remote to v2.0.5 (#2148)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-03 12:07:52 +01:00
renovate[bot]
a4b7cfea3f chore(deps): update babel monorepo (#2146)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 10:40:33 +01:00
renovate[bot]
d0ed20f10b fix(deps): update dependency chokidar to v3.5.3 (#2121)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 10:36:42 +01:00
renovate[bot]
8964d9f349 fix(deps): update dependency iso-639-1 to v2.1.13 (#2081)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 10:34:57 +01:00
renovate[bot]
e1525a64fc chore(deps): update dependency open to v8.4.0 (#2076)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 10:29:09 +01:00
Christopher Toth
3a0415578a fix(accessibility): Fix roles and add correct label for navigation controls (#2143)
* Accessibility: Fix roles and add correct label for navigation controls

This makes screen readers properly indicate the control name, type, and state for the Back, Forward, and Add buttons.

* Update src/renderer/components/header.js

Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 10:25:54 +01:00
dependabot[bot]
3dc023828d build(deps): bump nanoid from 3.1.23 to 3.3.1 (#2144)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.23 to 3.3.1.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.23...3.3.1)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-03 03:23:01 +01:00
dependabot[bot]
3d01225b6d build(deps): bump shelljs from 0.8.4 to 0.8.5 (#2145)
Bumps [shelljs](https://github.com/shelljs/shelljs) from 0.8.4 to 0.8.5.
- [Release notes](https://github.com/shelljs/shelljs/releases)
- [Changelog](https://github.com/shelljs/shelljs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/shelljs/shelljs/compare/v0.8.4...v0.8.5)

---
updated-dependencies:
- dependency-name: shelljs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-03 03:13:08 +01:00
renovate[bot]
0feb97275c fix(deps): update dependency prop-types to v15.8.1 (#2104)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 03:01:40 +01:00
Alex Vale
036ac72e3c fix: remove erroneous playbackRate check (#2106)
* remove erroneous playbackRate check

A playback rate != 1 causes shouldHidePlayerControls to erroneously return false.

* Add change

Co-authored-by: Alex <alxmorais8@msn.com>
2022-03-03 01:53:23 +00:00
Renovate Bot
f097a1ed52 fix(deps): update dependency webtorrent to v1.8.1 2022-02-09 17:28:55 +00:00
Renovate Bot
16485f29c8 chore(deps): update dependency @babel/core to v7.17.2 2022-02-09 17:13:30 +00:00
Renovate Bot
0b2cb410bd fix(deps): update dependency music-metadata to v7.11.8 2022-02-04 01:29:14 +00:00
Renovate Bot
330b4adb0e chore(deps): update babel monorepo to v7.17.0 2022-02-04 01:17:08 +00:00
Renovate Bot
32818c60a4 chore(deps): update dependency electron to v15.3.6 2022-01-28 20:51:14 +00:00
Renovate Bot
a55d7e897d fix(deps): update dependency @electron/remote to v2.0.4 2022-01-28 18:52:06 +00:00
Renovate Bot
bd34106a31 fix(deps): update dependency webtorrent to v1.7.2 2022-01-28 00:31:23 +00:00
Renovate Bot
85fb83fb67 chore(deps): update dependency tape to v5.5.0 2022-01-27 21:03:52 +00:00
renovate[bot]
0cfce0ff26 fix(deps): update dependency simple-get to v4.0.1 (#2124) 2022-01-24 23:03:59 -05:00
Renovate Bot
65147eb53a chore(deps): update dependency @babel/core to v7.16.12 2022-01-23 16:22:43 +00:00
Renovate Bot
5b6d29efcb fix(deps): update dependency webtorrent to v1.7.1 2022-01-20 18:45:36 +00:00
Renovate Bot
c5c53646eb fix(deps): update dependency music-metadata to v7.11.7 2022-01-18 15:06:57 +00:00
Renovate Bot
0927cedfa0 fix(deps): update dependency webtorrent to v1.6.0 2022-01-18 00:41:33 +00:00
Renovate Bot
22962e9b7c chore(deps): update dependency tape to v5.4.1 2022-01-17 05:31:02 +00:00
Renovate Bot
13932b0567 fix(deps): update dependency music-metadata to v7.11.6 2022-01-14 09:42:12 +00:00
Renovate Bot
ed38a55711 fix(deps): update dependency webtorrent to v1.5.11 2022-01-14 03:46:52 +00:00
Renovate Bot
e55d9b6d15 chore(deps): update dependency electron to v15.3.5 2022-01-13 03:57:35 +00:00
Renovate Bot
e15ae42c32 fix(deps): update dependency webtorrent to v1.5.9 2022-01-12 19:09:47 +00:00
Renovate Bot
a71808445e chore(deps): update dependency @babel/cli to v7.16.8 2022-01-11 23:30:20 +00:00
Renovate Bot
b742b10553 chore(deps): update dependency depcheck to v1.4.3 2022-01-10 20:32:22 +00:00
Renovate Bot
e8e9314486 chore(deps): update babel monorepo to v7.16.7 2022-01-01 02:06:53 +00:00
Renovate Bot
0b16be24c1 chore(deps): update dependency tape to v5.4.0 2021-12-27 08:13:46 +00:00
Renovate Bot
a90df37123 chore(deps): update dependency electron to v15.3.4 2021-12-17 21:37:54 +00:00
Renovate Bot
fc7608fa87 chore(deps): update babel monorepo to v7.16.5 2021-12-15 00:20:04 +00:00
Renovate Bot
82a61e9da4 chore(deps): update dependency electron to v15.3.3 2021-12-02 00:33:53 +00:00
Renovate Bot
2d0c1d597f chore(deps): update dependency tape to v5.3.2 2021-11-17 08:31:43 +00:00
Renovate Bot
4a9566665e chore(deps): update dependency electron to v15.3.2 2021-11-17 02:41:06 +00:00
Renovate Bot
fad7a21287 chore(deps): update dependency @babel/eslint-parser to v7.16.3 2021-11-10 21:58:46 +00:00
Renovate Bot
7893da9426 chore(deps): update dependency nodemon to v2.0.15 2021-11-10 19:46:09 +00:00
Renovate Bot
f0ddc79fcc chore(deps): update dependency electron to v15.3.1 2021-11-09 19:56:29 +00:00
Renovate Bot
f92afd3f10 chore(deps): update babel monorepo to v7.16.0 2021-10-31 00:31:49 +00:00
Renovate Bot
9ce6c3b461 chore(deps): update dependency electron-osx-sign to v0.6.0 2021-10-30 00:38:05 +00:00
Renovate Bot
b404b44aa1 fix(deps): update dependency webtorrent to v1.5.8 2021-10-26 19:12:55 +00:00
Renovate Bot
8c38377e38 fix(deps): update dependency webtorrent to v1.5.7 2021-10-25 19:02:31 +00:00
Renovate Bot
eda83e858d chore(deps): update dependency electron to v15.3.0 2021-10-21 18:02:23 +00:00
Renovate Bot
a6639aadcf chore(deps): update dependency nodemon to v2.0.14 2021-10-20 14:33:30 +00:00
Renovate Bot
2cc62e26b3 chore(deps): update dependency electron to v15.2.0 2021-10-15 22:43:17 +00:00
Diego Rodríguez Baquero
e42a515199 fix: modernify code (#2068)
* fix: modernify code

* standard fix

* fixes
2021-10-11 18:23:09 -05:00
Diego Rodríguez Baquero
c36e43eaa3 fix: electron upgrade fixes (#2058)
* fix: electron upgrade fixes

* fix: electron remote

* fixes

* fixes

* pin

* actually
2021-10-10 13:21:45 -05:00
Renovate Bot
0732bd93d2 chore(deps): update dependency electron to v15.1.2 2021-10-09 19:34:19 +00:00
Renovate Bot
f12027f836 chore(deps): update babel monorepo to v7.15.8 2021-10-07 21:33:39 +00:00
Renovate Bot
d4dfaa6047 chore(deps): update dependency electron to v15.1.1 2021-10-05 23:46:19 +00:00
Renovate Bot
809d122d98 fix(deps): update dependency webtorrent to v1.5.6 2021-10-05 17:07:29 +00:00
Renovate Bot
6dbbf8b85b fix(deps): update dependency chromecasts to v1.10.2 2021-10-05 15:11:48 +00:00
Renovate Bot
3150c04ef4 chore(deps): update dependency electron to v15.1.0 2021-10-02 16:55:35 +00:00
Alex
770c641f1b refactor: ETA calculation (#2055)
* Refactor ETA calculation

* Update src/renderer/pages/player-page.js

Co-authored-by: Alex <alxmorais8@msn.com>

* Fix lint issue

Co-authored-by: Alex <alxmorais8@msn.com>
2021-10-02 15:11:45 +02:00
Renovate Bot
9c0ad29db9 chore(deps): update dependency nodemon to v2.0.13 2021-09-24 21:10:00 +00:00
renovate[bot]
add6b2e900 chore(deps): update dependency electron to v15 (#2051)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-23 10:41:07 -05:00
Renovate Bot
83c2e7d731 fix(deps): update dependency music-metadata to v7.11.4 2021-09-23 03:38:47 +00:00
Renovate Bot
29393b1803 chore(deps): update dependency electron-packager to v15.4.0 2021-09-23 03:33:12 +00:00
Renovate Bot
0ba4d3b831 chore(deps): update dependency electron-notarize to v1.1.1 2021-09-23 03:26:29 +00:00
Renovate Bot
872f60e161 chore(deps): update babel monorepo 2021-09-23 03:17:49 +00:00
Renovate Bot
65886933ee fix(deps): update dependency chromecasts to v1.10.1 2021-09-23 03:07:39 +00:00
Renovate Bot
f300d8af21 chore(deps): update dependency plist to v3.0.4 2021-09-23 02:57:06 +00:00
Renovate Bot
f87068a288 chore(deps): update dependency gh-release to v6.0.1 2021-09-23 02:45:16 +00:00
renovate[bot]
1916f4f1a3 chore(deps): update dependency electron to v13.4.0 (#1997)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-22 20:53:54 -05:00
renovate[bot]
b0d9b89451 chore(deps): update actions/setup-node action to v2 (#2052)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-22 20:51:12 -05:00
Benjamin Tan
0d512db6e2 Ensure that default file/protocol handlers are re-installed after updating. (#997)
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.
2021-09-22 20:11:53 -05:00
Vincenzo De Petris
24969c87f3 Fix ghost image showing on media player drag (#1922)
* fix: ghost image showing on media player drag

* fix: add missing space

* fix: access Image from window object
2021-09-22 20:10:30 -05:00
Adam Gotlib
5619f1805b Release media keys when opening external player (#1237) 2021-09-22 20:07:00 -05:00
Alex
e9d72ebb17 fix: resume torrents after deletion (#1974)
* fix: resume torrents after deletion

* Update torrent-list-controller.js

Co-authored-by: Diego Rodríguez Baquero <github@diegorbaquero.com>
2021-09-22 19:56:00 -05:00
Julen Garcia Leunda
c7647440ba build: add package Github Action (#1888)
* Add package Github Action

* Update package.yml

Co-authored-by: Diego Rodríguez Baquero <github@diegorbaquero.com>
2021-09-22 19:52:06 -05:00
Renovate Bot
86ba117e04 fix(deps): update dependency webtorrent to v1.5.5 2021-09-02 13:01:29 +00:00
Renovate Bot
27cf60d5dc fix(deps): update dependency webtorrent to v1.5.4 2021-08-25 23:15:05 +00:00
Renovate Bot
d06cddd94a fix(deps): update dependency webtorrent to v1.5.3 2021-08-20 19:17:40 +00:00
Renovate Bot
21ad84daf8 fix(deps): update dependency webtorrent to v1.5.1 2021-08-20 02:28:03 +00:00
Renovate Bot
c882e13889 fix(deps): update dependency webtorrent to v1.4.0 2021-08-17 20:38:57 +00:00
Renovate Bot
9a68910e31 fix(deps): update dependency webtorrent to v1.3.10 2021-08-16 14:21:14 +00:00
Agyant Tiwari
610abea1c3 Play/Pause video on click (#1878)
* Play/Pause video on click

* Fixing lint

* fix: use dispatcher

Co-authored-by: Alex <alxmorais8@msn.com>
2021-08-16 13:47:53 +02:00
Renovate Bot
b132fb73df chore(deps): update dependency tape to v5.3.1 2021-08-08 07:07:12 +00:00
Renovate Bot
eff52911c2 fix(deps): update dependency webtorrent to v1.3.9 2021-08-07 00:35:35 +00:00
Renovate Bot
3ff41a965f fix(deps): update dependency create-torrent to v5.0.1 2021-08-06 22:42:34 +00:00
Renovate Bot
8373fd17b8 fix(deps): update dependency webtorrent to v1.3.8 2021-08-06 05:07:39 +00:00
renovate[bot]
c531169a37 fix(deps): update dependency create-torrent to v5 (#2034)
* fix(deps): update dependency create-torrent to v5

* Update webtorrent.js

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Diego Rodríguez Baquero <github@diegorbaquero.com>
2021-08-05 20:54:37 -05:00
renovate[bot]
d00b9ba081 fix(deps): update dependency webtorrent to v1.3.7 (#2033)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-05 07:49:04 +00:00
renovate[bot]
1d644c82de fix(deps): update webtorrent (#2032)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-05 02:22:15 +00:00
renovate[bot]
d6308734ea fix(deps): update dependency chromecasts to v1.10.0 (#1991)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-04 15:09:59 -05:00
renovate[bot]
534c6719d1 chore(deps): update dependency electron-notarize to v1.0.1 (#2027)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:25:10 -05:00
renovate[bot]
4e4315160e fix(deps): update dependency chokidar to v3.5.2 (#1993)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:23:40 -05:00
renovate[bot]
25398030fa chore(deps): update babel monorepo (#1992)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:22:50 -05:00
renovate[bot]
b261a24bda chore(deps): update dependency open to v8.2.1 (#1996)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:21:56 -05:00
renovate[bot]
dcbe5851c2 fix(deps): update dependency drag-drop to v7.2.0 (#2004)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:21:28 -05:00
renovate[bot]
24ef81387e chore(deps): update dependency tape to v5.3.0 (#2023)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:16:45 -05:00
renovate[bot]
6a761c9a10 chore(deps): update dependency electron-packager to v15.3.0 (#2016)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-03 19:16:33 -05:00
Renovate Bot
8ac6e9effe chore(deps): update dependency nodemon to v2.0.12 2021-08-03 23:51:42 +00:00
Renovate Bot
fec063bd1d fix(deps): update dependency webtorrent to v1.3.4 2021-07-30 17:56:47 +00:00
Renovate Bot
8b76535ce2 fix(deps): update dependency webtorrent to v1.3.3 2021-07-25 02:21:47 +00:00
Renovate Bot
7072caf3ce fix(deps): update dependency webtorrent to v1.3.2 2021-07-24 20:29:48 +00:00
Renovate Bot
2ba8fd20ee fix(deps): update dependency webtorrent to v1.3.0 2021-07-24 00:22:23 +00:00
Renovate Bot
7f962109e9 fix(deps): update dependency webtorrent to v1.2.6 2021-07-23 20:45:48 +00:00
Renovate Bot
33e6b6565c fix(deps): update dependency create-torrent to v4.7.1 2021-07-22 16:38:52 +00:00
Diego Rodríguez Baquero
04cdc64a18 chore: add stale 2021-07-21 19:20:42 -05:00
Renovate Bot
452bc0e235 fix(deps): update dependency webtorrent to v1.2.5 2021-07-19 17:26:26 +00:00
Renovate Bot
607c5b8c11 fix(deps): update dependency webtorrent to v1.2.4 2021-07-13 22:53:36 +00:00
Renovate Bot
7ef697ed72 fix(deps): update dependency webtorrent to v1.2.3 2021-07-13 20:32:00 +00:00
Renovate Bot
86182417cc fix(deps): update dependency webtorrent to v1.2.2 2021-07-11 04:09:25 +00:00
Renovate Bot
200f385d23 fix(deps): update dependency webtorrent to v1.2.0 2021-07-10 00:09:01 +00:00
Renovate Bot
8a5140d93d fix(deps): update dependency webtorrent to v1.1.6 2021-07-08 22:26:41 +00:00
Renovate Bot
a5c2fa08a5 fix(deps): update dependency webtorrent to v1.1.5 2021-07-06 18:43:20 +00:00
Renovate Bot
d0198a4419 fix(deps): update dependency webtorrent to v1.1.4 2021-07-03 06:48:43 +00:00
Renovate Bot
de1b38e660 fix(deps): update dependency webtorrent to v1.1.3 2021-07-03 00:49:17 +00:00
Renovate Bot
50bf04c73a fix(deps): update dependency webtorrent to v1.1.0 2021-06-30 22:08:43 +00:00
Renovate Bot
1cfa101b90 fix(deps): update dependency webtorrent to v1.0.4 2021-06-30 07:13:21 +00:00
Renovate Bot
14e2775980 fix(deps): update dependency webtorrent to v1.0.2 2021-06-23 20:32:19 +00:00
Julen Garcia Leunda
8e86c855d3 Add armv7l arch support (#1901) 2021-06-12 12:50:48 -05:00
renovate[bot]
01b3772138 fix(deps): update dependency music-metadata to v7.8.6 (#1958)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-12 12:50:16 -05:00
renovate[bot]
1f84a81d97 chore(deps): update dependency electron to v13.1.0 (#1983)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-12 12:49:19 -05:00
renovate[bot]
12ab547bbc chore(deps): update babel monorepo to v7.14.5 (#1990)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-12 12:49:02 -05:00
Alex
ef4fc2d28d feat: paste torrent address with right click (#1989)
* feat: paste torrent address with right click

* Change right click to directly pasting
2021-06-09 23:55:40 +02:00
Feross Aboukhadijeh
d12fd7768a fix: Link to VLC using HTTPS (#1987)
May help with users who are man-in-the-middled and redirected to malicious websites.

See: https://github.com/webtorrent/webtorrent-desktop/issues/1908
2021-06-02 16:46:17 -07:00
Feross Aboukhadijeh
756480a69f chore: Update to Electron 13; Bump all deps (#1986)
* chore: Update to Electron 12; Bump all deps

* Fixes for Electron 12

* chore: Update to Electron 13
2021-06-02 08:21:35 -07:00
renovate[bot]
19a8e9aa3e chore(deps): update dependency gh-release to v6 (#1961)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Feross Aboukhadijeh <feross@feross.org>
2021-06-02 08:04:27 -07:00
renovate[bot]
23f20546f7 chore(deps): pin dependency electron to v11.1.0 (#1976)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 08:00:12 -07:00
renovate[bot]
7a2d1d1807 fix(deps): update dependency webtorrent to v0.118.0 (#1981)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:59:43 -07:00
renovate[bot]
0138a178c3 fix(deps): update dependency iso-639-1 to v2.1.9 (#1948)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:56:22 -07:00
renovate[bot]
ff6900d8f1 fix(deps): update dependency semver to v7.3.5 (#1949)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:54:49 -07:00
renovate[bot]
7217d45ff9 fix(deps): update react monorepo to v17.0.2 (#1950)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:53:00 -07:00
renovate[bot]
ba86131e4a chore(deps): update dependency electron-winstaller to v5 (#1960)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:52:21 -07:00
renovate[bot]
b57278f7f1 chore(deps): update dependency open to v8 (#1962)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:52:03 -07:00
renovate[bot]
c9136422b9 fix(deps): update dependency drag-drop to v7 (#1964)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:51:57 -07:00
renovate[bot]
34692dfd38 chore(deps): update babel monorepo (#1951)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:51:49 -07:00
renovate[bot]
a990b00b21 chore(deps): update dependency ws to 7.4.6 [security] (#1972)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:41:44 -07:00
Alberto Miranda
5f5e4e0ee2 Fix cannot set property 'enabled' of undefined (#1938) 2021-06-02 07:41:09 -07:00
Feross Aboukhadijeh
e9fa721ca8 Switch from Travis to GitHub Actions (#1978) 2021-06-02 07:33:48 -07:00
Feross Aboukhadijeh
7ba1510131 chore: Set node 16 and npm 7 as minimum versions (#1977) 2021-06-02 07:25:48 -07:00
renovate[bot]
057c192dc4 chore(deps): update dependency electron to 11.1.0 [security] (#1939)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-02 07:18:43 -07:00
renovate[bot]
b78966ae2e chore(deps): pin dependencies (#1965)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-31 22:02:24 -05:00
renovate[bot]
2c34f7af1c chore(deps): update dependency lodash to 4.17.21 [security] (#1941)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-24 08:09:49 -05:00
renovate[bot]
6885d23bde fix(deps): update dependency debounce to ^1.2.1 (#1947)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-24 08:09:16 -05:00
Diego Rodríguez Baquero
277010a1b8 Merge pull request #1956 from webtorrent/renovate_tape-5.x
chore(deps): update dependency tape to ^5.2.2
2021-05-24 08:08:00 -05:00
Diego Rodríguez Baquero
d0849accbb Merge pull request #1957 from webtorrent/renovate_chokidar-3.x
fix(deps): update dependency chokidar to ^3.5.1
2021-05-24 08:06:23 -05:00
Renovate Bot
080e8da591 fix(deps): update dependency chokidar to ^3.5.1 2021-05-24 04:34:36 +00:00
Renovate Bot
bf0b5f8cdb chore(deps): update dependency tape to ^5.2.2 2021-05-24 04:33:03 +00:00
Diego Rodríguez Baquero
6e151cd2a1 chore: add renovate config 2021-05-23 23:00:09 -05:00
Alex
62273fa6b2 Merge pull request #1932 from webtorrent/dependabot/npm_and_yarn/ua-parser-js-0.7.28
Bump ua-parser-js from 0.7.22 to 0.7.28
2021-05-07 11:31:34 +02:00
dependabot[bot]
00d0c3d828 Bump ua-parser-js from 0.7.22 to 0.7.28
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.22 to 0.7.28.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.22...0.7.28)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 21:11:35 +00:00
Feross Aboukhadijeh
61efbd6823 Merge pull request #1928 from RayBB/patch-1
update brew install command
2021-04-26 16:17:48 -07:00
Alex
e6a33fb53a Merge pull request #1905 from getsnoopy/fix-units
Fix formatting of measurement units
2021-04-14 09:57:26 +02:00
Alex
228d733539 Merge pull request #1927 from webtorrent/dependabot/npm_and_yarn/y18n-4.0.1
Bump y18n from 4.0.0 to 4.0.1
2021-04-14 09:53:16 +02:00
Raymond Berger
83eb584422 update brew install command 2021-04-10 19:24:07 -10:00
dependabot[bot]
47e89813a0 Bump y18n from 4.0.0 to 4.0.1
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-30 09:23:23 +00:00
Alex
f0894ced54 Merge pull request #1904 from webtorrent/dependabot/npm_and_yarn/ini-1.3.7
Bump ini from 1.3.5 to 1.3.7
2021-02-01 18:37:45 +01:00
Alex
5372887d46 Merge pull request #1920 from BlueManCZ/tray-fix
Improve libappindicator checking
2021-02-01 18:37:13 +01:00
BlueManCZ
f8d9567422 Change command 2021-01-31 22:21:13 +01:00
BlueManCZ
97b54744df Update comment 2021-01-31 11:19:44 +01:00
BlueManCZ
4a974a1d3d Look for libappindicator libraries instead of package 2021-01-31 11:12:18 +01:00
BlueManCZ
192560755c Merge branch 'master' of github.com:webtorrent/webtorrent-desktop 2021-01-31 10:40:05 +01:00
Varun Varada
d299704f57 Fix formatting of measurement units
There were some incorrectly formatted unit symbols and units names in
the app, so this commit fixes them per SI and ISO/IEC specifications.
2020-12-22 19:00:05 -06:00
dependabot[bot]
314e74d1ed Bump ini from 1.3.5 to 1.3.7
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-10 23:07:23 +00:00
Feross Aboukhadijeh
919af5bf15 Merge pull request #1217 from cezar-carneiro/show-progress-on-cast-screen 2020-11-26 01:44:32 +01:00
Feross Aboukhadijeh
d819acedd5 Merge pull request #1865 from BlueManCZ/patch-1 2020-11-25 22:33:25 +01:00
Feross Aboukhadijeh
54fcc5057b recursively update all deps 2020-11-24 12:02:08 -10:00
Feross Aboukhadijeh
cd5da2fe9b cross-zip@4 2020-11-24 12:00:00 -10:00
Feross Aboukhadijeh
1c79ffc720 bump deps; including electron ~11.0.3
Fixes: https://github.com/webtorrent/webtorrent-desktop/issues/1896
2020-11-24 11:56:17 -10:00
Feross Aboukhadijeh
a1a590fbba travis: disable cache
the cache keeps caching outdated versions which cause test failures
2020-11-22 16:32:36 -10:00
Feross Aboukhadijeh
606c02aef4 Remove extra-lint
- standard enforces 'let' and 'const' now
- I've never been a fan of the <100 chars per line rule since it violates the standard philosophy. it should be possible to write conformant code without needing a tool, so 'magic numbers' like 'no more than X lines/chars/statements' are not ideal
2020-11-22 16:29:59 -10:00
Feross Aboukhadijeh
4f68a8ad0b parse-torrent@9 2020-11-22 16:26:42 -10:00
Feross Aboukhadijeh
37d962fcf3 bump deps 2020-11-22 16:24:46 -10:00
Feross Aboukhadijeh
baf06461df Remove unused 'buble' dependency 2020-11-22 16:20:09 -10:00
Feross Aboukhadijeh
464e63fd92 standard (Fixes for PR #1818) 2020-11-22 16:12:04 -10:00
Feross Aboukhadijeh
2761e9c768 Merge pull request #1818 from surajrathod/master 2020-11-23 03:11:34 +01:00
suraj rathod
0efb5fa7ad Merge branch 'master' into master 2020-11-20 11:27:33 +05:30
Feross Aboukhadijeh
34947c355d standard@16 2020-11-18 12:38:20 -10:00
Feross Aboukhadijeh
958814f2ea use @babel/eslint-parser 2020-11-18 12:37:03 -10:00
Feross Aboukhadijeh
ec0c0c7870 Update package-lock.json 2020-11-18 12:30:13 -10:00
Feross Aboukhadijeh
ec0a1d1bb4 Merge pull request #1895 from webtorrent/electron11 2020-11-18 03:41:45 +01:00
Feross Aboukhadijeh
969d71afec Merge pull request #1698 from webtorrent/babel
Use babel instead of buble
2020-11-18 03:41:19 +01:00
Feross Aboukhadijeh
c1db01b936 Use babel instead of buble
This way we can target just the JSX tranformation. We don't use any other cutting edge JS features that require compilation, nor should we in the future.
2020-11-17 16:40:14 -10:00
Feross Aboukhadijeh
90670a4645 changelog: add link to security issue explanation 2020-11-17 15:42:11 -10:00
Feross Aboukhadijeh
b5fad54f4a Upgrade to Electron 11 2020-11-16 18:03:35 -10:00
Feross Aboukhadijeh
50718d3791 Merge pull request #1887 from webtorrent/fix_lintian_warnings
Fix lintian warnings
2020-11-09 22:58:14 +01:00
Julen Garcia Leunda
bd54a8c214 Fix lintian warnings 2020-11-06 17:13:22 +01:00
Feross Aboukhadijeh
0cd0edf4cc Merge pull request #1886 from webtorrent/throttling
Disable background throttling
2020-11-05 22:30:22 +01:00
Feross Aboukhadijeh
9e0d91f144 Disable background throttling
Reference: https://github.com/webtorrent/webtorrent-desktop/pull/1720#issuecomment-722011487

I actually think we want backgroundThrottling: false so our timers and intervals aren't throttled when the app is backgrounded.
2020-11-04 12:34:48 -10:00
Feross Aboukhadijeh
38d9c4c504 Merge pull request #1885 from webtorrent/bitfield
bitfield@4
2020-11-04 10:15:40 +01:00
Feross Aboukhadijeh
9e1721990d bitfield@4 2020-11-03 13:03:40 -10:00
Feross Aboukhadijeh
a20f82cc61 standard 2020-10-29 11:30:07 -10:00
Feross Aboukhadijeh
630ed76b8c standard 2020-10-28 09:52:07 -10:00
Feross Aboukhadijeh
1155cba4dd Merge pull request #1883 from webtorrent/deps 2020-10-28 19:18:16 +01:00
Feross Aboukhadijeh
2343181e1b Update package-lock.json 2020-10-28 08:15:53 -10:00
Feross Aboukhadijeh
9bb7330caa spectron@12 2020-10-28 08:14:10 -10:00
Feross Aboukhadijeh
3aae1614ea pngjs@6 2020-10-28 08:13:13 -10:00
Feross Aboukhadijeh
cdd308fc4a gh-release@4 2020-10-28 08:12:11 -10:00
Feross Aboukhadijeh
594d18e76e react 17 2020-10-28 08:11:10 -10:00
Feross Aboukhadijeh
50d772eb54 parse-torrent@8 2020-10-28 08:10:43 -10:00
Feross Aboukhadijeh
57ffb5e0c4 bump deps 2020-10-28 08:10:26 -10:00
Feross Aboukhadijeh
49ab4bd201 Merge pull request #1882 from webtorrent/render-deprecated 2020-10-28 19:07:03 +01:00
Feross Aboukhadijeh
7b06acb972 don't rely on deprecated return value from ReactDOM.render 2020-10-27 18:18:30 -10:00
Feross Aboukhadijeh
80b86b5693 standard 2020-10-21 13:50:42 -10:00
Diego Rodríguez Baquero
748b996522 Merge pull request #1875 from webtorrent/dependabot/npm_and_yarn/electron-10.1.2
Bump electron from 10.1.1 to 10.1.2
2020-10-20 10:51:26 -05:00
Julen Garcia Leunda
77877d4201 Merge pull request #1812 from dsernst/scrub-preview
Adds Youtube-style Scrubbing preview
2020-10-20 17:31:43 +02:00
dependabot[bot]
696fa4479c Bump electron from 10.1.1 to 10.1.2
Bumps [electron](https://github.com/electron/electron) from 10.1.1 to 10.1.2.
- [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/v10.1.1...v10.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-06 17:49:29 +00:00
Feross Aboukhadijeh
e0bb7562be Update package-lock.json 2020-09-03 11:36:21 -07:00
Feross Aboukhadijeh
8f20d9676b electron 10.1.1 2020-09-03 11:35:08 -07:00
BlueManCZ
b61a4e84f5 Remove Version entry 2020-08-29 14:19:36 +02:00
BlueManCZ
9c59157a98 Remove VERSION entry 2020-08-29 14:03:27 +02:00
Ivo Šmerek
5def2d3947 Fix StartupWMClass 2020-08-29 13:53:02 +02:00
Feross Aboukhadijeh
ce6326a869 Update RELEASE_PROCESS.md 2020-08-28 17:58:07 -07:00
Feross Aboukhadijeh
7e56550887 Update RELEASE_PROCESS.md 2020-08-28 17:51:12 -07:00
Feross Aboukhadijeh
aa42627d45 0.24.0 2020-08-28 17:19:47 -07:00
Feross Aboukhadijeh
bd6dc1a7c1 authors 2020-08-28 17:19:20 -07:00
Feross Aboukhadijeh
dfedbcea32 add 0.24.0 changelog 2020-08-28 17:18:42 -07:00
Feross Aboukhadijeh
d930b97b30 Merge pull request #1864 from webtorrent/update 2020-08-28 17:14:58 -07:00
Feross Aboukhadijeh
de7899c8f8 Update package-lock.json 2020-08-28 17:06:51 -07:00
Feross Aboukhadijeh
2f53c5e137 bump electron deps 2020-08-28 17:03:33 -07:00
Feross Aboukhadijeh
ff54f48abb bump deps 2020-08-28 17:00:29 -07:00
Feross Aboukhadijeh
a7766e8d3f fixes for capture-frame@4 2020-08-28 16:58:40 -07:00
Feross Aboukhadijeh
844e34e596 capture-frame@4 2020-08-28 16:58:36 -07:00
Julen Garcia Leunda
8515fee681 Merge pull request #1859 from webtorrent/add_m2ts_video_container
Add m2ts video container support
2020-08-17 21:58:32 +02:00
Borewit
10a1ee3a09 Merge pull request #1861 from webtorrent/update-music-metadata
Update music-metadata to version 7.0.2
2020-08-17 19:26:32 +02:00
Borewit
a1b2641130 Update music-metadata to version 7.0.2 2020-08-16 08:48:37 +02:00
Julen Garcia Leunda
bb55b3fab2 Add m2ts video container support 2020-08-12 23:06:14 +02:00
Julen Garcia Leunda
57d02aa316 Merge pull request #1850 from Hinara/patch-1
Fix #1807 (VLC does not start)
2020-07-31 17:11:07 +02:00
David Ernst
d3bfccaa6b Check for type === video in clearPreview() 2020-07-30 19:10:14 -07:00
Julen Garcia Leunda
3a92f596ff Merge pull request #1838 from webtorrent/update-trackers
Update trackers to be consistent
2020-07-30 18:31:43 +02:00
Julen Garcia Leunda
60b4365a76 Merge pull request #1840 from subins2000/fix-1839-no-keyevents-on-user-input
Fix #1839: Prevent keypress events on user input elements
2020-07-30 18:31:22 +02:00
Julen Garcia Leunda
cb0ad73c80 Merge pull request #1754 from hicom150/fix_windows_installer
Fix windows installer event handler
2020-07-30 18:30:59 +02:00
Julen Garcia Leunda
9c865050bb Merge pull request #1847 from webtorrent/update-music-metadata
Update music metadata
2020-07-30 18:30:08 +02:00
Hinara
0edeaa4d58 Accept null value as playerPath 2020-07-30 15:06:26 +02:00
Mathias Rasmussen
dfd1ca3a8e Merge pull request #1841 from webtorrent/update_loading_gif
Update loading GIF
2020-07-30 03:59:49 +02:00
Borewit
75d9939b98 Update dependency music-metadata to version 7 2020-07-29 18:00:29 +02:00
Hinara
f88d5426c9 Fix #1807
eb5a291c8b/src/renderer/lib/migrations.js (L219) cause external player to be a empty string instead of a null one
2020-07-25 02:39:09 +02:00
Borewit
1b7d4f09a9 Fix issue #1833, only pass metadata. Prevent MetadataCollector (from music-metadata) is serialized. 2020-07-21 23:00:22 +02:00
Alex
680376c4a6 Update loading.gif 2020-07-17 21:14:15 +02:00
Subin Siby
15cca44bd2 Fix #1839: Prevent keypress events on user input elements 2020-07-17 18:47:23 +05:30
Feross Aboukhadijeh
29d17870cb Update trackers to be consistent
Updated all .torrent files to include:

```
  "announce": [
    "udp://tracker.leechers-paradise.org:6969",
    "udp://tracker.coppersurfer.tk:6969",
    "udp://tracker.opentrackr.org:1337",
    "udp://explodie.org:6969",
    "udp://tracker.empire-js.us:1337",
    "wss://tracker.btorrent.xyz",
    "wss://tracker.openwebtorrent.com"
  ]
```

Removed dead trackers from the list.
2020-07-16 20:18:15 -07:00
Feross Aboukhadijeh
c150d22c97 Update package-lock.json 2020-07-16 10:14:43 -07:00
Feross Aboukhadijeh
4e0c1ed393 Update CHANGELOG.md 2020-07-15 23:04:06 -07:00
Feross Aboukhadijeh
c6240c3253 Update CHANGELOG.md 2020-07-15 22:38:58 -07:00
Feross Aboukhadijeh
5deb0d7e9a Merge pull request #1835 from webtorrent/osx-notarize
Add macOS Notarization
2020-07-16 07:28:56 +02:00
Feross Aboukhadijeh
803cce808b Add macOS Notarization
Fixes: https://github.com/webtorrent/webtorrent-desktop/issues/1675
2020-07-15 22:26:29 -07:00
Feross Aboukhadijeh
8e89c0962b 0.23.0 2020-07-15 20:13:51 -07:00
Feross Aboukhadijeh
50cc394a27 Update CHANGELOG.md 2020-07-15 20:13:51 -07:00
Feross Aboukhadijeh
0165d541b8 update dep 2020-07-15 20:13:51 -07:00
Feross Aboukhadijeh
5af36428fa Merge pull request #1834 from webtorrent/electron10 2020-07-16 05:01:40 +02:00
Feross Aboukhadijeh
48ad571a59 electron 10: re-enable remote module
Electron 10 disables the remote module by default. We've always tried to avoid using `remote` in WTD so there aren't many occurrances of it, but there are still a few places where we use it
2020-07-15 20:01:08 -07:00
Feross Aboukhadijeh
57a003d7c5 electron 10 crash reporter changes
Crash reporter doesn't need to be started in child processes, and in fact the functions are all deprecated. Instead start it from the main process, and the child processes will automatically be covered

Change of params to crashReporter.start() function
2020-07-15 19:44:34 -07:00
Feross Aboukhadijeh
5f5b13d7fe update to electron 10.0.0-beta.11 2020-07-15 19:42:08 -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
SurajRathod
ab9f86d6c9 added .vscode ignore 2020-06-06 17:47:38 +05:30
SurajRathod
583cecc661 selective import 2020-06-06 17:46:30 +05:30
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
David Ernst
c4b318bd5f Clean up code and add better comments 2020-05-02 11:40:00 -07:00
David Ernst
d55acf9c12 Fix 'extra-lint' errors 2020-05-01 22:41:19 -07:00
David Ernst
24bc40c95e Fix preview border rendering 2020-05-01 22:06:05 -07:00
David Ernst
0716444be5 Working video scrubbing preview 2020-05-01 21:57:42 -07:00
David Ernst
85c0e99e16 Only show previews for video 2020-05-01 21:05:29 -07:00
David Ernst
0b57961ff7 Basic placeholder UI for scrubbing preview 2020-05-01 19:43:47 -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
97426621bf Fix windows installer event handler 2019-12-12 22:48:13 +01: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
Cezar Carneiro
ab41d55e4b fix ETA and reorganize 2017-08-02 22:52:45 -03:00
Cezar Carneiro
739e7a50f7 improve infos 2017-08-02 00:03:52 -03:00
Cezar Carneiro
64ff5ebfd5 show percentage, size, speed and peers 2017-07-31 22:47:53 -03: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
146 changed files with 30615 additions and 1846 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?**

21
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: ci
on: [push,pull_request]
jobs:
test:
name: Node ${{ matrix.node }} / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
node:
- '16'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npm run build --if-present
- run: npm test

69
.github/workflows/package.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: package
on:
workflow_dispatch:
jobs:
package_linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm install
- run: npm run package -- linux
- uses: actions/upload-artifact@v3
with:
name: linux
path: |
dist/*.deb
dist/*.rpm
dist/*.zip
package_macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm install
- run: npm run package -- darwin
- uses: actions/upload-artifact@v3
with:
name: macos
path: |
dist/*.dmg
dist/*.zip
package_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm install
- run: npm run package -- win32
- uses: actions/upload-artifact@v3
with:
name: windows
path: |
dist/*.exe
dist/*.nupkg
dist/*.zip

23
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: '0 12 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?'
stale-pr-message: 'Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?'
exempt-issue-labels: accepted,blocked,dependency,security,meta
exempt-pr-labels: accepted,blocked,bug,dependency,security,meta
stale-issue-label: 'stale'
stale-pr-label: 'stale'

5
.gitignore vendored
View File

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

1
.npmrc Normal file
View File

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

View File

@@ -1,4 +0,0 @@
language: node_js
node_js:
- 'node'
install: npm install standard depcheck walk-sync

View File

@@ -34,5 +34,50 @@
- PurgingPanda (t3ch0wn3r@gmail.com) - PurgingPanda (t3ch0wn3r@gmail.com)
- Kai Curtis (morecode@kcurtis.com) - Kai Curtis (morecode@kcurtis.com)
- Omri Litov (omrilitov@gmail.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)
- Subin Siby (mail@subinsb.com)
- Hinara (hinara.turevel@gmail.com)
#### Generated by bin/update-authors.sh. #### Generated by bin/update-authors.sh.

View File

@@ -1,5 +1,155 @@
# WebTorrent Desktop Version History # WebTorrent Desktop Version History
## v0.24.0 - 2020-08-28
### Added
- Support the `.m2ts` video container format ([hicom150](https://github.com/hicom150))
### Changed
- Update to Electron 10.1.0 [\#1864](https://github.com/webtorrent/webtorrent-desktop/pull/1864) ([feross](https://github.com/feross))
- Update the Windows installer loading image [\#1841](https://github.com/webtorrent/webtorrent-desktop/pull/1841) ([alxhotel](https://github.com/alxhotel))
### Fixed
- Fix music metadata not showing up [\#1847](https://github.com/webtorrent/webtorrent-desktop/pull/1847) ([Borewit](https://github.com/Borewit))
- Fix the "Play in VLC" functionality [\#1850](https://github.com/webtorrent/webtorrent-desktop/pull/1850) ([Hinara](https://github.com/Hinara))
- Prevent shortcuts from activating when user input elements are focused [\#1840](https://github.com/webtorrent/webtorrent-desktop/pull/1840) ([subins2000](https://github.com/subins2000))
## v0.23.0 - 2020-07-15
🔒 This release contains a critical security fix. Please update as soon as possible. [\#1837](https://github.com/webtorrent/webtorrent-desktop/issues/1837#issuecomment-729320901)
### Added
- Add macOS Notarization [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834) ([feross](https://github.com/feross))
### Changed
- Update to Electron 10 beta [\#1834](https://github.com/webtorrent/webtorrent-desktop/pull/1834) ([feross](https://github.com/feross))
## v0.22.0 - 2020-07-15
❤️✨ A new version of WebTorrent Desktop is out! ❤️✨
### 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 ([feross](https://github.com/feross))
### 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) ([akaustav](https://github.com/akaustav))
- Fix "Save Torrent File As..." [\#1743](https://github.com/webtorrent/webtorrent-desktop/pull/1743) ([gpatarin](https://github.com/gpatarin))
## 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 ## v0.17.1 - 2016-10-03
### Changed ### Changed
@@ -39,7 +189,7 @@
## v0.16.0 - 2016-09-18 ## v0.16.0 - 2016-09-18
### Added ### 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 - 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 - 64-bit reduces likelihood of out-of-memory errors by increasing the address space
@@ -99,16 +249,13 @@
## v0.12.0 - 2016-08-23 ## v0.12.0 - 2016-08-23
### Added ### Added
- Custom external media player - Custom external media player
- Linux: add system-wide launcher and icons for Debian, including Ubuntu - Linux: add system-wide launcher and icons for Debian, including Ubuntu
### Changed ### Changed
- Telemetry improvements: redact stacktraces, log app version - Telemetry improvements: redact stacktraces, log app version
### Fixed ### Fixed
- Fix playback and download of default torrents ("missing path" error) (#804) - Fix playback and download of default torrents ("missing path" error) (#804)
- Fix Delete Torrent + Data for newly added magnet links - Fix Delete Torrent + Data for newly added magnet links
- Fix jumpToTime error (#804) - Fix jumpToTime error (#804)
@@ -121,13 +268,11 @@
- Check for missing default download path and torrent folders on start up (#776) - Check for missing default download path and torrent folders on start up (#776)
### Changed ### Changed
- Do not automatically set WebTorrent as the default handler for torrents (#771) - Do not automatically set WebTorrent as the default handler for torrents (#771)
- Torrents can only be created from the home screen (#770) - Torrents can only be created from the home screen (#770)
- Update Electron to 1.3.3 (#772) - Update Electron to 1.3.3 (#772)
### Fixed ### Fixed
- Allow modifying the default tracker list on the Create Torrent page (#775) - Allow modifying the default tracker list on the Create Torrent page (#775)
- Prevent opening multiple stacked Preference windows or Create Torrent windows (#770) - Prevent opening multiple stacked Preference windows or Create Torrent windows (#770)
- Windows: Player window auto-resize does not match video aspect ratio (#565) - Windows: Player window auto-resize does not match video aspect ratio (#565)
@@ -136,13 +281,11 @@
## v0.10.0 - 2016-08-05 ## v0.10.0 - 2016-08-05
### Added ### Added
- Drag-and-drop magnet links (selected text) is now supported (#284) - Drag-and-drop magnet links (selected text) is now supported (#284)
- Windows: Add "User Tasks" shortcuts to app icon in Start Menu (#114) - Windows: Add "User Tasks" shortcuts to app icon in Start Menu (#114)
- Linux: Show badge count for completed torrent downloads - Linux: Show badge count for completed torrent downloads
### Changed ### Changed
- Change WebTorrent Desktop peer ID prefix to 'WD' to distinguish from WebTorrent in the browser, 'WW' (#688) - 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) - Switch UI to React to improve UI rendering speed (#729)
- The primary bottleneck was actually `hyperx`, not `virtual-dom`. - The primary bottleneck was actually `hyperx`, not `virtual-dom`.
@@ -159,7 +302,6 @@
- Location history abstraction released independently as [`location-history`](https://www.npmjs.com/package/location-history) - Location history abstraction released independently as [`location-history`](https://www.npmjs.com/package/location-history)
### Fixed ### Fixed
- When streaming to VLC, set VLC window title to torrent file name (#746) - When streaming to VLC, set VLC window title to torrent file name (#746)
- Fix "Cannot read property 'numPiecesPresent' of undefined" exception (#695) - Fix "Cannot read property 'numPiecesPresent' of undefined" exception (#695)
- Fix rare case where config file could not be completely written (#733) - Fix rare case where config file could not be completely written (#733)
@@ -167,35 +309,29 @@
## v0.9.0 - 2016-07-20 ## v0.9.0 - 2016-07-20
### Added ### Added
- Save selected subtitles - Save selected subtitles
- Ask for confirmation before deleting torrents - Ask for confirmation before deleting torrents
- Support Debian Jessie - Support Debian Jessie
### Changed ### Changed
- Only send telemetry in production - Only send telemetry in production
- Clean up the code. Split main.js, refactor lots of things - Clean up the code. Split main.js, refactor lots of things
### Fixed ### Fixed
- Fix state.playing.jumpToTime behavior - Fix state.playing.jumpToTime behavior
- Remove torrent file and poster image when deleting a torrent - Remove torrent file and poster image when deleting a torrent
## v0.8.1 - 2016-06-24 ## v0.8.1 - 2016-06-24
### Added ### Added
- New URI handler: stream-magnet - New URI handler: stream-magnet
### Fixed ### Fixed
- DLNA crashing bug - DLNA crashing bug
## v0.8.0 - 2016-06-23 ## v0.8.0 - 2016-06-23
### Added ### Added
- Cast menu: choose which Chromecast, Airplay, or DLNA device you want to use - 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 - Telemetry: send basic data, plus stats on how often the play button works
- Make posters from jpeg files, not just jpg - Make posters from jpeg files, not just jpg
@@ -203,17 +339,14 @@
- Windows thumbnail bar with a play/pause button - Windows thumbnail bar with a play/pause button
### Changed ### Changed
- Nicer modal styles - Nicer modal styles
### Fixed ### Fixed
- Windows tray icon now stays in the right state - Windows tray icon now stays in the right state
## v0.7.2 - 2016-06-02 ## v0.7.2 - 2016-06-02
### Fixed ### Fixed
- Fix exception that affects users upgrading from v0.5.1 or older - Fix exception that affects users upgrading from v0.5.1 or older
- Ensure `state.saved.prefs` configuration exists - Ensure `state.saved.prefs` configuration exists
- Fix window title on "About WebTorrent" window - Fix window title on "About WebTorrent" window
@@ -221,23 +354,19 @@
## v0.7.1 - 2016-06-02 ## v0.7.1 - 2016-06-02
### Changed ### Changed
- Change "Step Forward" keyboard shortcut to `Alt+Left` (Windows) - Change "Step Forward" keyboard shortcut to `Alt+Left` (Windows)
- Change "Step Backward" keyboard shortcut to to `Alt+Right` (Windows) - Change "Step Backward" keyboard shortcut to to `Alt+Right` (Windows)
### Fixed ### Fixed
- First time startup bug -- invalid torrent/poster paths - First time startup bug -- invalid torrent/poster paths
## v0.7.0 - 2016-06-02 ## v0.7.0 - 2016-06-02
### Added ### Added
- Improved AirPlay support -- using the new [`airplayer`](https://www.npmjs.com/package/airplayer) package - 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 - Remember volume setting in player, for as long as the app is open
### Changed ### Changed
- Add (+) button now also accepts non .torrent files and creates a torrent from - Add (+) button now also accepts non .torrent files and creates a torrent from
those files those files
- Show prompt text in title bar for open dialogs (OS X) - Show prompt text in title bar for open dialogs (OS X)
@@ -247,7 +376,6 @@
- Fix crash reporter not working (Windows) - Fix crash reporter not working (Windows)
### Fixed ### Fixed
- Re-enable WebRTC (web peers)! (OS X, Windows) - Re-enable WebRTC (web peers)! (OS X, Windows)
- Windows support was disabled in v0.6.1 to work around a bug in Electron - 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 - OS X support was disabled in v0.4.0 to work around a 100% CPU bug
@@ -258,7 +386,6 @@
- Fix torrent loading message UI misalignment - Fix torrent loading message UI misalignment
### Known issues ### Known issues
- When upgrading to WebTorrent Desktop v0.7.0, some torrent metadata (file list, - 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 selected files, whether torrent is streamable) will be cleared. Just start the
torrent to re-populate the metadata. torrent to re-populate the metadata.
@@ -266,7 +393,6 @@
## v0.6.1 - 2016-05-26 ## v0.6.1 - 2016-05-26
### Fixed ### Fixed
- Disable WebRTC to work around Electron crash (Windows) - Disable WebRTC to work around Electron crash (Windows)
- Will be re-enabled in the next version of WebTorrent, which will be based on - 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. the next version of Electron, where the bug is fixed.
@@ -278,7 +404,6 @@
## v0.6.0 - 2016-05-24 ## v0.6.0 - 2016-05-24
### Added ### Added
- Added Preferences page to set Download folder - Added Preferences page to set Download folder
- Save video position, resume playback from saved position - Save video position, resume playback from saved position
- Add additional video player keyboard shortcuts (#275) - Add additional video player keyboard shortcuts (#275)
@@ -288,7 +413,6 @@
- Add announcement feature - Add announcement feature
### Changed ### Changed
- Nicer player UI - Nicer player UI
- Reduce startup jank, improve startup time (#568) - Reduce startup jank, improve startup time (#568)
- Cleanup unsupported codec detection (#569, #570) - Cleanup unsupported codec detection (#569, #570)
@@ -296,7 +420,6 @@
- Improve subtitle positioning (#551) - Improve subtitle positioning (#551)
### Fixed ### Fixed
- Fix Uncaught TypeError: Cannot read property 'update' of undefined (#567) - Fix Uncaught TypeError: Cannot read property 'update' of undefined (#567)
- Fix bugs in LocationHistory - Fix bugs in LocationHistory
- When player is active, and magnet link is pasted, go back to list - When player is active, and magnet link is pasted, go back to list
@@ -307,23 +430,19 @@
## v0.5.1 - 2016-05-18 ## v0.5.1 - 2016-05-18
### Fixed ### Fixed
- Fix auto-updater (OS X, Windows). - Fix auto-updater (OS X, Windows).
## v0.5.0 - 2016-05-17 ## v0.5.0 - 2016-05-17
### Added ### Added
- Select/deselect individual files to torrent. - Select/deselect individual files to torrent.
- Automatically include subtitle files (.srt, .vtt) from torrent in the subtitles menu. - Automatically include subtitle files (.srt, .vtt) from torrent in the subtitles menu.
- "Add Subtitle File..." menu item. - "Add Subtitle File..." menu item.
### Changed ### Changed
- When manually adding subtitle track(s), always switch to the new track. - When manually adding subtitle track(s), always switch to the new track.
### Fixed ### Fixed
- Magnet links throw exception on app launch. (OS X) - Magnet links throw exception on app launch. (OS X)
- Multi-file torrents would not seed in-place, were copied to Downloads folder. - Multi-file torrents would not seed in-place, were copied to Downloads folder.
- Missing 'About WebTorrent' menu item. (Windows) - Missing 'About WebTorrent' menu item. (Windows)
@@ -332,7 +451,6 @@
## v0.4.0 - 2016-05-13 ## v0.4.0 - 2016-05-13
### Added ### Added
- Better Windows support! - Better Windows support!
- Windows 32-bit build. - Windows 32-bit build.
- Windows Portable App build. - Windows Portable App build.
@@ -355,7 +473,6 @@
- New default torrent on first launch: The WIRED CD. - New default torrent on first launch: The WIRED CD.
### Changed ### Changed
- Improve app startup time by 40%. - Improve app startup time by 40%.
- UI tweaks: Reduce font size, reduce torrent list item height. - UI tweaks: Reduce font size, reduce torrent list item height.
- Add Playback menu for playback-related functionality. - Add Playback menu for playback-related functionality.
@@ -366,7 +483,6 @@
- Remove "Add Fake Airplay/Chromecast" menu items. - Remove "Add Fake Airplay/Chromecast" menu items.
### Fixed ### Fixed
- Disable WebRTC to fix 100% CPU usage/crashes caused by Chromium issue. This is - Disable WebRTC to fix 100% CPU usage/crashes caused by Chromium issue. This is
temporary. (OS X) temporary. (OS X)
- When fullscreen, make controls use the full window. (OS X) - When fullscreen, make controls use the full window. (OS X)
@@ -391,24 +507,20 @@ to this release!
## v0.3.3 - 2016-04-07 ## v0.3.3 - 2016-04-07
### Fixed ### Fixed
- App icon was incorrect (OS X) - App icon was incorrect (OS X)
## v0.3.2 - 2016-04-07 ## v0.3.2 - 2016-04-07
### Added ### Added
- Register WebTorrent as default handler for magnet links (OS X) - Register WebTorrent as default handler for magnet links (OS X)
### Changed ### Changed
- Faster startup time (50ms) - Faster startup time (50ms)
- Update Electron to 0.37.5 - Update Electron to 0.37.5
- Remove the white flash when loading pages and resizing the window - Remove the white flash when loading pages and resizing the window
- Fix crash when sending IPC messages - Fix crash when sending IPC messages
### Fixed ### Fixed
- Fix installation bugs with .deb file (Linux) - Fix installation bugs with .deb file (Linux)
- Pause audio reliably when closing the window - Pause audio reliably when closing the window
- Enforce minimimum window size when resizing player (for audio-only .mov files, which are 0x0) - Enforce minimimum window size when resizing player (for audio-only .mov files, which are 0x0)
@@ -416,17 +528,14 @@ to this release!
## v0.3.1 - 2016-04-06 ## v0.3.1 - 2016-04-06
### Added ### Added
- Add crash reporter to torrent engine process - Add crash reporter to torrent engine process
### Fixed ### Fixed
- Fix cast screen background: cover, don't tile - Fix cast screen background: cover, don't tile
## v0.3.0 - 2016-04-06 ## v0.3.0 - 2016-04-06
### Added ### Added
- **Ubuntu/Debian support!** (.deb installer) - **Ubuntu/Debian support!** (.deb installer)
- **DLNA streaming support** - **DLNA streaming support**
- Add "File > Quit" menu item (Linux) - Add "File > Quit" menu item (Linux)
@@ -434,14 +543,12 @@ to this release!
- Crash reporting - Crash reporting
### Changed ### Changed
- On startup, do not re-verify files when timestamps are unchanged - On startup, do not re-verify files when timestamps are unchanged
- Moved torrent engine to an independent process, for better UI performance - Moved torrent engine to an independent process, for better UI performance
- Removed media queries (UI resizing based on window width) - Removed media queries (UI resizing based on window width)
- Improved Chromecast icon, when connected - Improved Chromecast icon, when connected
### Fixed ### Fixed
- "Download Complete" notification shows consistently - "Download Complete" notification shows consistently
- Create new torrents and seed them without copying to temporary folder - Create new torrents and seed them without copying to temporary folder
- Clicking the "Download Complete" notification will always activate app - Clicking the "Download Complete" notification will always activate app
@@ -460,7 +567,6 @@ Thanks to @dcposch, @grunjol, and @feross for contributing to this release.
## v0.2.0 - 2016-03-29 ## v0.2.0 - 2016-03-29
### Added ### Added
- Minimise to tray (Windows, Linux) - Minimise to tray (Windows, Linux)
- Show spinner and download speed when player is stalled waiting for data - Show spinner and download speed when player is stalled waiting for data
- Highlight window on drag-and-drop - Highlight window on drag-and-drop
@@ -469,12 +575,10 @@ Thanks to @dcposch, @grunjol, and @feross for contributing to this release.
Linux users need to download new versions manually. Linux users need to download new versions manually.
### Changed ### Changed
- Renamed WebTorrent.app to WebTorrent Desktop - Renamed WebTorrent.app to WebTorrent Desktop
- Add Cosmos Laundromat as a default torrent - Add Cosmos Laundromat as a default torrent
### Fixed ### Fixed
- Only capture media keys when player is active - Only capture media keys when player is active
- Update WebTorrent to 0.88.1 for performance improvements - Update WebTorrent to 0.88.1 for performance improvements
- When seeding, do not proactively connect to new peers - When seeding, do not proactively connect to new peers
@@ -534,7 +638,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 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 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 ### 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"> <h1 align="center">
<br> <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> <br>
WebTorrent Desktop WebTorrent Desktop
<br> <br>
@@ -10,20 +12,34 @@
<h4 align="center">The streaming torrent app. For Mac, Windows, and Linux.</h4> <h4 align="center">The streaming torrent app. For Mac, Windows, and Linux.</h4>
<p align="center"> <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://discord.gg/cnXkm4Z"><img src="https://img.shields.io/discord/612575111718895616" alt="discord"></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/webtorrent/webtorrent-desktop/actions/workflows/ci.yml"><img src="https://github.com/webtorrent/webtorrent-desktop/actions/workflows/ci.yml/badge.svg" alt="GitHub CI action"></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://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> </p>
## Install ## Install
Download the latest version of WebTorrent Desktop from ### Recommended Install
[the official website](https://webtorrent.io/desktop/) or the
[GitHub releases](https://github.com/feross/webtorrent-desktop/releases) page.
**WebTorrent Desktop** is under very active development. You can try out the Download the latest version of WebTorrent Desktop from
current (unstable) development version by cloning the Git repo. See the [the official website](https://webtorrent.io/desktop/):
instructions below in the ["How to Contribute"](#how-to-contribute) section.
### [✨ 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 install --cask webtorrent
```
- Try the (unstable) development version by cloning the Git repository. See the
["How to Contribute"](#how-to-contribute) instructions.
## Screenshots ## Screenshots
@@ -37,7 +53,7 @@ instructions below in the ["How to Contribute"](#how-to-contribute) section.
### Get the code ### Get the code
``` ```
$ git clone https://github.com/feross/webtorrent-desktop.git $ git clone https://github.com/webtorrent/webtorrent-desktop.git
$ cd webtorrent-desktop $ cd webtorrent-desktop
$ npm install $ npm install
``` ```
@@ -68,19 +84,25 @@ $ npm test
$ npm run test-integration $ 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 * 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 * 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. * Small UI changes will break a few tests, but the fix is as easy as deleting the offending
* 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 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. 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 ### Package the app
@@ -103,6 +125,7 @@ The following optional arguments are available:
- `--sign` - Sign the application (Mac, Windows) - `--sign` - Sign the application (Mac, Windows)
- `--package=[type]` - Package single output type. - `--package=[type]` - Package single output type.
- `deb` - Debian package - `deb` - Debian package
- `rpm` - RedHat package
- `zip` - Linux zip file - `zip` - Linux zip file
- `dmg` - Mac disk image - `dmg` - Mac disk image
- `exe` - Windows installer - `exe` - Windows installer
@@ -110,7 +133,7 @@ The following optional arguments are available:
- `all` - All platforms (default) - `all` - All platforms (default)
Note: Even with the `--package` option, the auto-update files (.nupkg for Windows, 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 #### 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: Windows code signing only works from **Windows**, for now.
Note: To package the Windows app from non-Windows platforms, Note: To package the Windows app from non-Windows platforms,
[Wine](https://www.winehq.org/) needs to be installed. For example on Mac, first [Wine](https://www.winehq.org/) and [Mono](https://www.mono-project.com/) need
install [XQuartz](http://www.xquartz.org/), then run: 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.) (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. 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 ### Privacy
WebTorrent Desktop collects some basic usage stats to help us make the app better. 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 The app never sends any personally identifying information, nor does it track which
torrents you add. torrents you add.
### Code Style
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
## License ## License
MIT. Copyright (c) [WebTorrent, LLC](https://webtorrent.io). MIT. Copyright (c) [WebTorrent, LLC](https://webtorrent.io).

109
RELEASE_PROCESS.md Normal file
View File

@@ -0,0 +1,109 @@
## 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. 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. (If the auto updater does not run, users will successfully auto update to this new version, and then be stuck there forever.)
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.
### 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.
Once this PR is merged and Feross redeploys the WebTorrent website,
hundreds of thousands of users around the world will start auto updating. **Merge with care.**

View File

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

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
</dict>
</plist>

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env node
const walkSync = require('walk-sync')
const fs = require('fs')
const path = require('path')
let hasErrors = false
// Find all Javascript source files
const files = walkSync('src', {globs: ['**/*.js']})
console.log('Running extra-lint on ' + files.length + ' files...')
// Read each file, line by line
files.forEach(function (file) {
const filepath = path.join('src', file)
const lines = fs.readFileSync(filepath, 'utf8').split('\n')
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'
}
if (line.match(/^var /) || line.match(/ var /)) {
error = 'Use const or let'
}
if (error) {
let name = path.basename(file)
console.log('%s:%d - %s:\n%s', name, i + 1, error, line)
hasErrors = true
}
})
})
if (hasErrors) process.exit(1)
else console.log('Looks good!')

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
const config = require('../src/config') const { CONFIG_PATH } = require('../src/config')
const open = require('open') 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 electronPackager = require('electron-packager')
const fs = require('fs') const fs = require('fs')
const minimist = require('minimist') const minimist = require('minimist')
const mkdirp = require('mkdirp')
const os = require('os') const os = require('os')
const path = require('path') const path = require('path')
const rimraf = require('rimraf') 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_NAME = config.APP_NAME + '-v' + config.APP_VERSION
const BUILD_PATH = path.join(config.ROOT_PATH, 'build') const BUILD_PATH = path.join(config.ROOT_PATH, 'build')
const DIST_PATH = path.join(config.ROOT_PATH, 'dist') 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), { const argv = minimist(process.argv.slice(2), {
boolean: [ boolean: [
@@ -36,6 +36,11 @@ const argv = minimist(process.argv.slice(2), {
}) })
function build () { 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(DIST_PATH)
rimraf.sync(BUILD_PATH) rimraf.sync(BUILD_PATH)
@@ -64,11 +69,11 @@ function build () {
const all = { const all = {
// The human-readable copyright line for the app. Maps to the `LegalCopyright` metadata // The human-readable copyright line for the app. Maps to the `LegalCopyright` metadata
// property on Windows, and `NSHumanReadableCopyright` on Mac. // 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 // The release version of the application. Maps to the `ProductVersion` metadata
// property on Windows, and `CFBundleShortVersionString` on Mac. // 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 // 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 // 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 // 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 // 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. // 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. // The application source directory.
dir: config.ROOT_PATH, dir: config.ROOT_PATH,
// Pattern which specifies which files to ignore when copying files to create the // Pattern which specifies which files to ignore when copying files to create the
// package(s). // 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. // The application name.
name: config.APP_NAME, name: config.APP_NAME,
@@ -104,26 +109,26 @@ const all = {
// "devDependencies" before starting to package the app. // "devDependencies" before starting to package the app.
prune: true, prune: true,
// The Electron version with which the app is built (without the leading 'v') // The Electron version that the app is built with (without the leading 'v')
version: require('electron/package.json').version electronVersion: require('electron/package.json').version
} }
const darwin = { const darwin = {
// Build for Mac // Build for Mac
platform: 'darwin', platform: 'darwin',
// Build x64 binaries only. // Build x64 binary only.
arch: 'x64', arch: 'x64',
// The bundle identifier to use in the application's plist (Mac only). // 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 // The application category type, as shown in the Finder via "View" -> "Arrange by
// Application Category" when viewing the Applications directory (Mac only). // 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). // 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. // Application icon.
icon: config.APP_ICON + '.icns' icon: config.APP_ICON + '.icns'
@@ -133,8 +138,8 @@ const win32 = {
// Build for Windows. // Build for Windows.
platform: 'win32', platform: 'win32',
// Build ia32 and x64 binaries. // Build x64 binary only.
arch: ['ia32', 'x64'], arch: 'x64',
// Object hash of application metadata to embed into the executable (Windows only) // Object hash of application metadata to embed into the executable (Windows only)
win32metadata: { win32metadata: {
@@ -167,8 +172,8 @@ const linux = {
// Build for Linux. // Build for Linux.
platform: 'linux', platform: 'linux',
// Build ia32 and x64 binaries. // Build x64, armv7l, and arm64 binaries.
arch: ['ia32', 'x64'] arch: ['x64', 'armv7l', 'arm64']
// Note: Application icon for Linux is specified via the BrowserWindow `icon` option. // Note: Application icon for Linux is specified via the BrowserWindow `icon` option.
} }
@@ -179,8 +184,7 @@ function buildDarwin (cb) {
const plist = require('plist') const plist = require('plist')
console.log('Mac: Packaging electron...') console.log('Mac: Packaging electron...')
electronPackager(Object.assign({}, all, darwin), function (err, buildPath) { electronPackager(Object.assign({}, all, darwin)).then(function (buildPath) {
if (err) return cb(err)
console.log('Mac: Packaged electron. ' + buildPath) console.log('Mac: Packaged electron. ' + buildPath)
const appPath = path.join(buildPath[0], config.APP_NAME + '.app') const appPath = path.join(buildPath[0], config.APP_NAME + '.app')
@@ -191,16 +195,16 @@ function buildDarwin (cb) {
infoPlist.CFBundleDocumentTypes = [ infoPlist.CFBundleDocumentTypes = [
{ {
CFBundleTypeExtensions: [ 'torrent' ], CFBundleTypeExtensions: ['torrent'],
CFBundleTypeIconFile: path.basename(config.APP_FILE_ICON) + '.icns', CFBundleTypeIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
CFBundleTypeName: 'BitTorrent Document', CFBundleTypeName: 'BitTorrent Document',
CFBundleTypeRole: 'Editor', CFBundleTypeRole: 'Editor',
LSHandlerRank: 'Owner', LSHandlerRank: 'Owner',
LSItemContentTypes: [ 'org.bittorrent.torrent' ] LSItemContentTypes: ['org.bittorrent.torrent']
}, },
{ {
CFBundleTypeName: 'Any', CFBundleTypeName: 'Any',
CFBundleTypeOSTypes: [ '****' ], CFBundleTypeOSTypes: ['****'],
CFBundleTypeRole: 'Editor', CFBundleTypeRole: 'Editor',
LSHandlerRank: 'Owner', LSHandlerRank: 'Owner',
LSTypeIsPackage: false LSTypeIsPackage: false
@@ -212,13 +216,13 @@ function buildDarwin (cb) {
CFBundleTypeRole: 'Editor', CFBundleTypeRole: 'Editor',
CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns', CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
CFBundleURLName: 'BitTorrent Magnet URL', CFBundleURLName: 'BitTorrent Magnet URL',
CFBundleURLSchemes: [ 'magnet' ] CFBundleURLSchemes: ['magnet']
}, },
{ {
CFBundleTypeRole: 'Editor', CFBundleTypeRole: 'Editor',
CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns', CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
CFBundleURLName: 'BitTorrent Stream-Magnet URL', 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', UTTypeReferenceURL: 'http://www.bittorrent.org/beps/bep_0000.html',
UTTypeTagSpecification: { UTTypeTagSpecification: {
'com.apple.ostype': 'TORR', 'com.apple.ostype': 'TORR',
'public.filename-extension': [ 'torrent' ], 'public.filename-extension': ['torrent'],
'public.mime-type': 'application/x-bittorrent' 'public.mime-type': 'application/x-bittorrent'
} }
} }
@@ -262,6 +266,7 @@ function buildDarwin (cb) {
function signApp (cb) { function signApp (cb) {
const sign = require('electron-osx-sign') const sign = require('electron-osx-sign')
const { notarize } = require('electron-notarize')
/* /*
* Sign the app with Apple Developer ID certificates. We sign the app for 2 reasons: * Sign the app with Apple Developer ID certificates. We sign the app for 2 reasons:
@@ -277,16 +282,37 @@ function buildDarwin (cb) {
* - Membership in the Apple Developer Program * - Membership in the Apple Developer Program
*/ */
const signOpts = { const signOpts = {
verbose: true,
app: appPath, app: appPath,
platform: 'darwin', platform: 'darwin',
verbose: true identity: 'Developer ID Application: WebTorrent, LLC (5MAMC8G3L8)',
hardenedRuntime: true,
entitlements: path.join(config.ROOT_PATH, 'bin', 'darwin-entitlements.plist'),
'entitlements-inherit': path.join(config.ROOT_PATH, 'bin', 'darwin-entitlements.plist'),
'signature-flags': 'library'
}
const notarizeOpts = {
appBundleId: darwin.appBundleId,
appPath,
appleId: 'feross@feross.org',
appleIdPassword: '@keychain:AC_PASSWORD'
} }
console.log('Mac: Signing app...') console.log('Mac: Signing app...')
sign(signOpts, function (err) { sign(signOpts, function (err) {
if (err) return cb(err) if (err) return cb(err)
console.log('Mac: Signed app.') console.log('Mac: Signed app.')
cb(null)
console.log('Mac: Notarizing app...')
notarize(notarizeOpts).then(
function () {
console.log('Mac: Notarized app.')
cb(null)
},
function (err) {
cb(err)
})
}) })
} }
@@ -349,6 +375,8 @@ function buildDarwin (cb) {
cb(null) cb(null)
}) })
} }
}).catch(function (err) {
cb(err)
}) })
} }
@@ -369,8 +397,7 @@ function buildWin32 (cb) {
CERT_PATH = path.join(os.homedir(), 'Desktop') CERT_PATH = path.join(os.homedir(), 'Desktop')
} }
electronPackager(Object.assign({}, all, win32), function (err, buildPath) { electronPackager(Object.assign({}, all, win32)).then(function (buildPath) {
if (err) return cb(err)
console.log('Windows: Packaged electron. ' + buildPath) console.log('Windows: Packaged electron. ' + buildPath)
let signWithParams let signWithParams
@@ -389,21 +416,17 @@ function buildWin32 (cb) {
const tasks = [] const tasks = []
buildPath.forEach(function (filesPath) { buildPath.forEach(function (filesPath) {
const destArch = filesPath.split('-').pop()
if (argv.package === 'exe' || argv.package === 'all') { 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') { if (argv.package === 'portable' || argv.package === 'all') {
tasks.push((cb) => packagePortable(filesPath, destArch, cb)) tasks.push((cb) => packagePortable(filesPath, cb))
} }
}) })
series(tasks, cb) series(tasks, cb)
function packageInstaller (filesPath, destArch, cb) { function packageInstaller (filesPath, cb) {
console.log(`Windows: Creating ${destArch} installer...`) console.log('Windows: Creating installer...')
const archStr = destArch === 'ia32' ? '-ia32' : ''
installer.createWindowsInstaller({ installer.createWindowsInstaller({
appDirectory: filesPath, appDirectory: filesPath,
@@ -416,107 +439,69 @@ function buildWin32 (cb) {
noMsi: true, noMsi: true,
outputDirectory: DIST_PATH, outputDirectory: DIST_PATH,
productName: config.APP_NAME, 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 // TODO: Re-enable Windows 64-bit delta updates when we confirm that they
// work correctly in the presence of the "ia32" .nupkg files. I // work correctly in the presence of the "ia32" .nupkg files. I
// (feross) noticed them listed in the 64-bit RELEASES file and // (feross) noticed them listed in the 64-bit RELEASES file and
// manually edited them out for the v0.17 release. Shipping only // manually edited them out for the v0.17 release. Shipping only
// full updates for now will work fine, with no ill-effects. // full updates for now will work fine, with no ill-effects.
// remoteReleases: destArch === 'x64' // remoteReleases: config.GITHUB_URL,
// ? config.GITHUB_URL
// : undefined,
/** /**
* If you hit a "GitHub API rate limit exceeded" error, set this token! * If you hit a "GitHub API rate limit exceeded" error, set this token!
*/ */
// remoteToken: process.env.WEBTORRENT_GITHUB_API_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', setupIcon: config.APP_ICON + '.ico',
signWithParams: signWithParams, signWithParams,
title: config.APP_NAME, title: config.APP_NAME,
usePackageJson: false, usePackageJson: false,
version: pkg.version version: pkg.version
}) })
.then(function () { .then(function () {
console.log(`Windows: Created ${destArch} installer.`) console.log('Windows: Created installer.')
/** /**
* Delete extraneous Squirrel files (i.e. *.nupkg delta files for older * Delete extraneous Squirrel files (i.e. *.nupkg delta files for older
* versions of the app) * versions of the app)
*/ */
fs.readdirSync(DIST_PATH) fs.readdirSync(DIST_PATH)
.filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version)) .filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version))
.forEach((filename) => { .forEach((filename) => {
fs.unlinkSync(path.join(DIST_PATH, filename)) fs.unlinkSync(path.join(DIST_PATH, filename))
}) })
if (destArch === 'ia32') { cb(null)
console.log('Windows: Renaming ia32 installer files...') })
.catch(cb)
// 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)
} }
function packagePortable (filesPath, destArch, cb) { function packagePortable (filesPath, cb) {
console.log(`Windows: Creating ${destArch} portable app...`) console.log('Windows: Creating portable app...')
const portablePath = path.join(filesPath, 'Portable Settings') const portablePath = path.join(filesPath, 'Portable Settings')
mkdirp.sync(portablePath) fs.mkdirSync(portablePath, { recursive: true })
const downloadsPath = path.join(portablePath, 'Downloads') const downloadsPath = path.join(portablePath, 'Downloads')
mkdirp.sync(downloadsPath) fs.mkdirSync(downloadsPath, { recursive: true })
const tempPath = path.join(portablePath, 'Temp') const tempPath = path.join(portablePath, 'Temp')
mkdirp.sync(tempPath) fs.mkdirSync(tempPath, { recursive: true })
const archStr = destArch === 'ia32' ? '-ia32' : ''
const inPath = path.join(DIST_PATH, path.basename(filesPath)) 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) zip.zipSync(inPath, outPath)
console.log(`Windows: Created ${destArch} portable app.`) console.log('Windows: Created portable app.')
cb(null) cb(null)
} }
}).catch(function (err) {
cb(err)
}) })
} }
function buildLinux (cb) { function buildLinux (cb) {
console.log('Linux: Packaging electron...') 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) console.log('Linux: Packaged electron. ' + buildPath)
const tasks = [] const tasks = []
@@ -526,56 +511,102 @@ function buildLinux (cb) {
if (argv.package === 'deb' || argv.package === 'all') { if (argv.package === 'deb' || argv.package === 'all') {
tasks.push((cb) => packageDeb(filesPath, destArch, cb)) 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') { if (argv.package === 'zip' || argv.package === 'all') {
tasks.push((cb) => packageZip(filesPath, destArch, cb)) tasks.push((cb) => packageZip(filesPath, destArch, cb))
} }
}) })
series(tasks, cb) series(tasks, cb)
}).catch(function (err) {
cb(err)
}) })
function packageDeb (filesPath, destArch, cb) { 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 // Create .deb file for Debian-based platforms
console.log(`Linux: Creating ${destArch} deb...`) console.log(`Linux: Creating ${destArch} deb...`)
const deb = require('nobin-debian-installer')() const installer = require('electron-installer-debian')
const destPath = path.join('/opt', pkg.name)
deb.pack({ const options = {
package: pkg, src: filesPath + '/',
info: { dest: DIST_PATH,
arch: destArch === 'x64' ? 'amd64' : 'i386', arch: destArch,
targetDir: DIST_PATH, bin: 'WebTorrent',
depends: 'gconf2, libgtk2.0-0, libnss3, libxss1', icon: {
scripts: { '48x48': path.join(config.STATIC_PATH, 'linux/share/icons/hicolor/48x48/apps/webtorrent-desktop.png'),
postinst: path.join(config.STATIC_PATH, 'linux', 'postinst'), '256x256': path.join(config.STATIC_PATH, 'linux/share/icons/hicolor/256x256/apps/webtorrent-desktop.png')
prerm: path.join(config.STATIC_PATH, 'linux', 'prerm') },
} 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'),
src: ['./**'], lintianOverrides: [
dest: destPath, 'unstripped-binary-or-object',
expand: true, 'embedded-library',
cwd: filesPath 'missing-dependency-on-libc',
}, { 'changelog-file-missing-in-native-package',
src: ['./**'], 'description-synopsis-is-duplicated',
dest: path.join('/usr', 'share'), 'setuid-binary',
expand: true, 'binary-without-manpage',
cwd: path.join(config.STATIC_PATH, 'linux', 'share') 'shlib-with-executable-bit'
}], function (err) { ]
if (err) return cb(err) }
console.log(`Linux: Created ${destArch} deb.`)
cb(null) 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) { function packageZip (filesPath, destArch, cb) {
// Create .zip file for Linux // Create .zip file for Linux
console.log(`Linux: Creating ${destArch} zip...`) console.log(`Linux: Creating ${destArch} zip...`)
const archStr = destArch === 'ia32' ? '-ia32' : ''
const inPath = path.join(DIST_PATH, path.basename(filesPath)) 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) zip.zipSync(inPath, outPath)
console.log(`Linux: Created ${destArch} zip.`) 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 /(dc\@DCs-MacBook.local)/;
next if /(rolandoguedes\@gmail.com)/; next if /(rolandoguedes\@gmail.com)/;
next if /(grunjol\@users.noreply.github.com)/; next if /(grunjol\@users.noreply.github.com)/;
next if /(dependabot)/;
$seen{$_} = push @authors, "- ", $_; $seen{$_} = push @authors, "- ", $_;
} }
END { END {

27431
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,78 +1,88 @@
{ {
"name": "webtorrent-desktop", "name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.", "description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.17.1", "version": "0.24.0",
"author": { "author": {
"name": "WebTorrent, LLC", "name": "WebTorrent, LLC",
"email": "feross@webtorrent.io", "email": "feross@webtorrent.io",
"url": "https://webtorrent.io" "url": "https://webtorrent.io"
}, },
"babel": {
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"useBuiltIns": true
}
]
]
},
"bugs": { "bugs": {
"url": "https://github.com/feross/webtorrent-desktop/issues" "url": "https://github.com/webtorrent/webtorrent-desktop/issues"
}, },
"dependencies": { "dependencies": {
"airplayer": "^2.0.0", "@electron/remote": "2.1.3",
"application-config": "^1.0.0", "airplayer": "github:webtorrent/airplayer#fix-security",
"arch": "^2.0.0", "application-config": "2.0.0",
"auto-launch": "^4.0.1", "arch": "2.2.0",
"bitfield": "^1.0.2", "auto-launch": "5.0.5",
"capture-frame": "^1.0.0", "bitfield": "4.1.0",
"chromecasts": "^1.8.0", "capture-frame": "4.0.0",
"cp-file": "^4.0.1", "chokidar": "3.5.3",
"create-torrent": "^3.24.5", "chromecasts": "1.10.2",
"debounce": "^1.0.0", "create-torrent": "5.0.9",
"deep-equal": "^1.0.1", "debounce": "1.2.1",
"dlnacasts": "^0.1.0", "dlnacasts": "0.1.0",
"drag-drop": "^2.12.1", "drag-drop": "7.2.0",
"es6-error": "^3.0.1", "es6-error": "4.1.1",
"fn-getter": "^1.0.0", "fn-getter": "1.0.0",
"iso-639-1": "^1.2.1", "iso-639-1": "2.1.15",
"languagedetect": "^1.1.1", "languagedetect": "2.0.0",
"location-history": "^1.0.0", "location-history": "1.1.2",
"lodash.merge": "^4.6.0", "material-ui": "0.20.2",
"material-ui": "^0.15.4", "music-metadata": "7.14.0",
"mkdirp": "^0.5.1", "network-address": "1.1.2",
"musicmetadata": "^2.0.2", "parse-torrent": "9.1.5",
"network-address": "^1.1.0", "prettier-bytes": "1.0.4",
"parse-torrent": "^5.7.3", "prop-types": "15.8.1",
"prettier-bytes": "^1.0.1", "react": "17.0.2",
"react": "^15.2.1", "react-dom": "17.0.2",
"react-dom": "^15.2.1", "rimraf": "4.4.0",
"react-tap-event-plugin": "^1.0.0", "run-parallel": "1.2.0",
"rimraf": "^2.5.2", "semver": "7.3.8",
"run-parallel": "^1.1.6", "simple-concat": "1.0.1",
"semver": "^5.1.0", "simple-get": "4.0.1",
"simple-concat": "^1.0.0", "srt-to-vtt": "1.1.3",
"simple-get": "^2.0.0", "vlc-command": "1.2.0",
"srt-to-vtt": "^1.1.1", "webtorrent": "1.9.7",
"vlc-command": "^1.0.1", "winreg": "1.2.4"
"webtorrent": "0.x",
"winreg": "^1.2.0",
"zero-fill": "^2.2.3"
}, },
"devDependencies": { "devDependencies": {
"buble": "^0.14.0", "@babel/cli": "7.28.6",
"cross-zip": "^2.0.1", "@babel/core": "7.29.0",
"depcheck": "^0.6.4", "@babel/eslint-parser": "7.28.6",
"electron": "1.4.2", "@babel/plugin-transform-react-jsx": "7.28.6",
"electron-osx-sign": "^0.3.0", "cross-zip": "4.0.0",
"electron-packager": "^8.0.0", "depcheck": "1.4.7",
"electron-winstaller": "^2.3.0", "electron": "27.3.11",
"gh-release": "^2.0.3", "electron-notarize": "1.2.2",
"minimist": "^1.2.0", "electron-osx-sign": "0.6.0",
"nobin-debian-installer": "^0.0.10", "electron-packager": "17.1.2",
"nodemon": "^1.10.2", "electron-winstaller": "5.4.0",
"open": "0.0.5", "gh-release": "7.0.2",
"plist": "^2.0.1", "minimist": "1.2.8",
"pngjs": "^3.0.0", "nodemon": "2.0.22",
"run-series": "^1.1.4", "open": "8.4.2",
"spectron": "^3.3.0", "plist": "3.1.0",
"standard": "*", "pngjs": "7.0.0",
"tape": "^4.6.0", "run-series": "1.1.9",
"walk-sync": "^0.3.1" "spectron": "19.0.0",
"standard": "17.0.0",
"tape": "5.9.0"
}, },
"engines": { "engines": {
"node": ">=4.0.0" "node": "^16.0.0 || ^18.0.0",
"npm": "^7.10.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
}, },
"homepage": "https://webtorrent.io", "homepage": "https://webtorrent.io",
"keywords": [ "keywords": [
@@ -88,24 +98,35 @@
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
"optionalDependencies": { "optionalDependencies": {
"appdmg": "^0.4.3" "appdmg": "^0.6.0",
"electron-installer-debian": "^3.2.0",
"electron-installer-redhat": "^3.4.0"
}, },
"private": true,
"productName": "WebTorrent", "productName": "WebTorrent",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/feross/webtorrent-desktop.git" "url": "git://github.com/webtorrent/webtorrent-desktop.git"
}, },
"scripts": { "scripts": {
"build": "buble src --output build", "build": "babel src --out-dir build",
"clean": "node ./bin/clean.js", "clean": "node ./bin/clean.js",
"gh-release": "gh-release", "gh-release": "gh-release",
"install-system-deps": "brew install fakeroot dpkg rpm",
"open-config": "node ./bin/open-config.js", "open-config": "node ./bin/open-config.js",
"package": "node ./bin/package.js", "package": "node ./bin/package.js",
"prepublish": "npm run build", "start": "npm run build && electron --no-sandbox .",
"start": "npm run build && electron .", "test": "standard && depcheck --ignores=standard,@babel/eslint-parser --ignore-dirs=build,dist",
"test": "standard && depcheck --ignores=buble,lodash.merge,nodemon,gh-release --ignore-dirs=build,dist && node ./bin/extra-lint.js",
"test-integration": "npm run build && node ./test", "test-integration": "npm run build && node ./test",
"update-authors": "./bin/update-authors.sh", "update-authors": "./bin/update-authors.sh",
"watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/" "watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/"
},
"standard": {
"parser": "@babel/eslint-parser"
},
"renovate": {
"extends": [
"github>webtorrent/renovate-config"
]
} }
} }

View File

@@ -1,6 +1,6 @@
const appConfig = require('application-config')('WebTorrent') const appConfig = require('application-config')('WebTorrent')
const path = require('path') const path = require('path')
const electron = require('electron') const { app } = require('electron')
const arch = require('arch') const arch = require('arch')
const APP_NAME = 'WebTorrent' const APP_NAME = 'WebTorrent'
@@ -23,13 +23,13 @@ module.exports = {
CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report', CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report',
TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry', 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_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'),
APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'), APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'),
APP_NAME: APP_NAME, APP_NAME,
APP_TEAM: APP_TEAM, APP_TEAM,
APP_VERSION: APP_VERSION, APP_VERSION,
APP_WINDOW_TITLE: APP_NAME + ' (BETA)', APP_WINDOW_TITLE: APP_NAME,
CONFIG_PATH: getConfigPath(), CONFIG_PATH: getConfigPath(),
@@ -70,15 +70,17 @@ module.exports = {
DEFAULT_DOWNLOAD_PATH: getDefaultDownloadPath(), DEFAULT_DOWNLOAD_PATH: getDefaultDownloadPath(),
GITHUB_URL: 'https://github.com/feross/webtorrent-desktop', GITHUB_URL: 'https://github.com/webtorrent/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/feross/webtorrent-desktop/issues', GITHUB_URL_ISSUES: 'https://github.com/webtorrent/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/feross/webtorrent-desktop/master', 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', HOME_PAGE_URL: 'https://webtorrent.io',
TWITTER_PAGE_URL: 'https://twitter.com/WebTorrentApp',
IS_PORTABLE: IS_PORTABLE, IS_PORTABLE,
IS_PRODUCTION: IS_PRODUCTION, IS_PRODUCTION,
IS_TEST: IS_TEST, IS_TEST,
OS_SYSARCH: arch() === 'x64' ? 'x64' : 'ia32', 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_HEIGHT: UI_HEADER_HEIGHT + (UI_TORRENT_HEIGHT * 2), // header + 2 torrents
WINDOW_MIN_WIDTH: 425, WINDOW_MIN_WIDTH: 425,
UI_HEADER_HEIGHT: UI_HEADER_HEIGHT, UI_HEADER_HEIGHT,
UI_TORRENT_HEIGHT: UI_TORRENT_HEIGHT UI_TORRENT_HEIGHT
} }
function getConfigPath () { function getConfigPath () {
@@ -124,10 +126,10 @@ function getPath (key) {
return '' return ''
} else if (process.type === 'renderer') { } else if (process.type === 'renderer') {
// Electron renderer process // Electron renderer process
return electron.remote.app.getPath(key) return require('@electron/remote').app.getPath(key)
} else { } else {
// Electron main process // Electron main process
return electron.app.getPath(key) return app.getPath(key)
} }
} }

View File

@@ -4,11 +4,12 @@ module.exports = {
function init () { function init () {
const config = require('./config') const config = require('./config')
const electron = require('electron') const { crashReporter } = require('electron')
electron.crashReporter.start({ crashReporter.start({
companyName: config.APP_NAME,
productName: config.APP_NAME, productName: config.APP_NAME,
submitURL: config.CRASH_REPORT_URL submitURL: config.CRASH_REPORT_URL,
globalExtra: { _companyName: config.APP_NAME },
compress: true
}) })
} }

View File

@@ -2,14 +2,13 @@ module.exports = {
init init
} }
const electron = require('electron') const { dialog } = require('electron')
const config = require('../config') const config = require('../config')
const log = require('./log') const log = require('./log')
const ANNOUNCEMENT_URL = config.ANNOUNCEMENT_URL + const ANNOUNCEMENT_URL =
'?version=' + config.APP_VERSION + `${config.ANNOUNCEMENT_URL}?version=${config.APP_VERSION}&platform=${process.platform}`
'&platform=' + process.platform
/** /**
* In certain situations, the WebTorrent team may need to show an announcement to * In certain situations, the WebTorrent team may need to show an announcement to
@@ -45,13 +44,11 @@ function onResponse (err, res, data) {
} }
} }
electron.dialog.showMessageBox({ dialog.showMessageBox({
type: 'info', type: 'info',
buttons: ['OK'], buttons: ['OK'],
title: data.title, title: data.title,
message: data.message, message: data.message,
detail: data.detail detail: data.detail
}, noop) })
} }
function noop () {}

View File

@@ -6,7 +6,7 @@ module.exports = {
openFiles openFiles
} }
const electron = require('electron') const { dialog } = require('electron')
const log = require('./log') const log = require('./log')
const windows = require('./windows') const windows = require('./windows')
@@ -19,7 +19,7 @@ function openSeedFile () {
log('openSeedFile') log('openSeedFile')
const opts = { const opts = {
title: 'Select a file for the torrent.', title: 'Select a file for the torrent.',
properties: [ 'openFile' ] properties: ['openFile']
} }
showOpenSeed(opts) showOpenSeed(opts)
} }
@@ -34,13 +34,13 @@ function openSeedDirectory () {
log('openSeedDirectory') log('openSeedDirectory')
const opts = process.platform === 'darwin' const opts = process.platform === 'darwin'
? { ? {
title: 'Select a file or folder for the torrent.', title: 'Select a file or folder for the torrent.',
properties: [ 'openFile', 'openDirectory' ] properties: ['openFile', 'openDirectory']
} }
: { : {
title: 'Select a folder for the torrent.', title: 'Select a folder for the torrent.',
properties: [ 'openDirectory' ] properties: ['openDirectory']
} }
showOpenSeed(opts) showOpenSeed(opts)
} }
@@ -53,19 +53,18 @@ function openFiles () {
log('openFiles') log('openFiles')
const opts = process.platform === 'darwin' const opts = process.platform === 'darwin'
? { ? {
title: 'Select a file or folder to add.', title: 'Select a file or folder to add.',
properties: [ 'openFile', 'openDirectory' ] properties: ['openFile', 'openDirectory']
} }
: { : {
title: 'Select a file to add.', title: 'Select a file to add.',
properties: [ 'openFile' ] properties: ['openFile']
} }
setTitle(opts.title) setTitle(opts.title)
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) { const selectedPaths = dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('onOpen', selectedPaths) windows.main.dispatch('onOpen', selectedPaths)
})
} }
/* /*
@@ -77,15 +76,14 @@ function openTorrentFile () {
const opts = { const opts = {
title: 'Select a .torrent file.', title: 'Select a .torrent file.',
filters: [{ name: 'Torrent Files', extensions: ['torrent'] }], filters: [{ name: 'Torrent Files', extensions: ['torrent'] }],
properties: [ 'openFile', 'multiSelections' ] properties: ['openFile', 'multiSelections']
} }
setTitle(opts.title) setTitle(opts.title)
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) { const selectedPaths = dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
selectedPaths.forEach(function (selectedPath) { selectedPaths.forEach(selectedPath => {
windows.main.dispatch('addTorrent', selectedPath) windows.main.dispatch('addTorrent', selectedPath)
})
}) })
} }
@@ -116,9 +114,8 @@ function resetTitle () {
*/ */
function showOpenSeed (opts) { function showOpenSeed (opts) {
setTitle(opts.title) setTitle(opts.title)
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) { const selectedPaths = dialog.showOpenDialogSync(windows.main.win, opts)
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
windows.main.dispatch('showCreateTorrent', selectedPaths) windows.main.dispatch('showCreateTorrent', selectedPaths)
})
} }

View File

@@ -4,7 +4,7 @@ module.exports = {
setBadge setBadge
} }
const {app, Menu} = require('electron') const { app, Menu } = require('electron')
const dialog = require('./dialog') const dialog = require('./dialog')
const log = require('./log') const log = require('./log')
@@ -32,9 +32,9 @@ function downloadFinished (path) {
*/ */
function setBadge (count) { function setBadge (count) {
if (process.platform === 'darwin' || if (process.platform === 'darwin' ||
process.platform === 'linux' && app.isUnityRunning()) { (process.platform === 'linux' && app.isUnityRunning())) {
log(`setBadge: ${count}`) log(`setBadge: ${count}`)
app.setBadgeCount(Number(count)) app.badgeCount = Number(count)
} }
} }

View File

@@ -17,19 +17,18 @@ let proc = null
function checkInstall (playerPath, cb) { function checkInstall (playerPath, cb) {
// check for VLC if external player has not been specified by the user // check for VLC if external player has not been specified by the user
// otherwise assume the player is installed // otherwise assume the player is installed
if (playerPath == null) return vlcCommand((err) => cb(!err)) if (!playerPath) return vlcCommand(cb)
process.nextTick(() => cb(true)) process.nextTick(() => cb(null))
} }
function spawn (playerPath, url, title) { function spawn (playerPath, url, title) {
if (playerPath != null) return spawnExternal(playerPath, [url]) if (playerPath) return spawnExternal(playerPath, [url])
// Try to find and use VLC if external player is not specified // 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') if (err) return windows.main.dispatch('externalPlayerNotFound')
const args = [ const args = [
'--play-and-exit', '--play-and-exit',
'--video-on-top',
'--quiet', '--quiet',
`--meta-title=${JSON.stringify(title)}`, `--meta-title=${JSON.stringify(title)}`,
url url
@@ -40,26 +39,26 @@ function spawn (playerPath, url, title) {
function kill () { function kill () {
if (!proc) return if (!proc) return
log('Killing external player, pid ' + proc.pid) log(`Killing external player, pid ${proc.pid}`)
proc.kill('SIGKILL') // kill -9 proc.kill('SIGKILL') // kill -9
proc = null proc = null
} }
function spawnExternal (playerPath, args) { 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') { if (process.platform === 'darwin' && path.extname(playerPath) === '.app') {
// Mac: Use executable in packaged .app bundle // 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 // If it works, close the modal after a second
const closeModalTimeout = setTimeout(() => const closeModalTimeout = setTimeout(() =>
windows.main.dispatch('exitModal'), 1000) windows.main.dispatch('exitModal'), 1000)
proc.on('close', function (code) { proc.on('close', code => {
clearTimeout(closeModalTimeout) clearTimeout(closeModalTimeout)
if (!proc) return // Killed if (!proc) return // Killed
log('External player exited with code ', code) log('External player exited with code ', code)
@@ -71,7 +70,7 @@ function spawnExternal (playerPath, args) {
proc = null proc = null
}) })
proc.on('error', function (e) { proc.on('error', err => {
log('External player error', e) 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,32 +7,25 @@ const config = require('../config')
const path = require('path') const path = require('path')
function install () { function install () {
if (process.platform === 'darwin') { switch (process.platform) {
installDarwin() case 'darwin': installDarwin()
} break
if (process.platform === 'win32') { case 'win32': installWin32()
installWin32() break
}
if (process.platform === 'linux') {
installLinux()
} }
} }
function uninstall () { function uninstall () {
if (process.platform === 'darwin') { switch (process.platform) {
uninstallDarwin() case 'darwin': uninstallDarwin()
} break
if (process.platform === 'win32') { case 'win32': uninstallWin32()
uninstallWin32() break
}
if (process.platform === 'linux') {
uninstallLinux()
} }
} }
function installDarwin () { function installDarwin () {
const electron = require('electron') const { app } = require('electron')
const app = electron.app
// On Mac, only protocols that are listed in `Info.plist` can be set as the // On Mac, only protocols that are listed in `Info.plist` can be set as the
// default handler at runtime. // default handler at runtime.
@@ -44,7 +37,7 @@ function installDarwin () {
function uninstallDarwin () {} function uninstallDarwin () {}
const EXEC_COMMAND = [ process.execPath ] const EXEC_COMMAND = [process.execPath, '--']
if (!config.IS_PRODUCTION) { if (!config.IS_PRODUCTION) {
EXEC_COMMAND.push(config.ROOT_PATH) EXEC_COMMAND.push(config.ROOT_PATH)
@@ -108,37 +101,37 @@ function installWin32 () {
setProtocol() setProtocol()
function setProtocol (err) { function setProtocol (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
protocolKey.set('', Registry.REG_SZ, name, setURLProtocol) protocolKey.set('', Registry.REG_SZ, name, setURLProtocol)
} }
function setURLProtocol (err) { function setURLProtocol (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
protocolKey.set('URL Protocol', Registry.REG_SZ, '', setIcon) protocolKey.set('URL Protocol', Registry.REG_SZ, '', setIcon)
} }
function setIcon (err) { function setIcon (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
const iconKey = new Registry({ const iconKey = new Registry({
hive: Registry.HKCU, hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol + '\\DefaultIcon' key: `\\Software\\Classes\\${protocol}\\DefaultIcon`
}) })
iconKey.set('', Registry.REG_SZ, icon, setCommand) iconKey.set('', Registry.REG_SZ, icon, setCommand)
} }
function setCommand (err) { function setCommand (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
const commandKey = new Registry({ const commandKey = new Registry({
hive: Registry.HKCU, 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) commandKey.set('', Registry.REG_SZ, `${commandToArgs(command)} "%1"`, done)
} }
function done (err) { function done (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
} }
} }
@@ -163,43 +156,43 @@ function installWin32 () {
function setExt () { function setExt () {
const extKey = new Registry({ const extKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + ext key: `\\Software\\Classes\\${ext}`
}) })
extKey.set('', Registry.REG_SZ, id, setId) extKey.set('', Registry.REG_SZ, id, setId)
} }
function setId (err) { function setId (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
const idKey = new Registry({ const idKey = new Registry({
hive: Registry.HKCU, hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id key: `\\Software\\Classes\\${id}`
}) })
idKey.set('', Registry.REG_SZ, name, setIcon) idKey.set('', Registry.REG_SZ, name, setIcon)
} }
function setIcon (err) { function setIcon (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
const iconKey = new Registry({ const iconKey = new Registry({
hive: Registry.HKCU, hive: Registry.HKCU,
key: '\\Software\\Classes\\' + id + '\\DefaultIcon' key: `\\Software\\Classes\\${id}\\DefaultIcon`
}) })
iconKey.set('', Registry.REG_SZ, icon, setCommand) iconKey.set('', Registry.REG_SZ, icon, setCommand)
} }
function setCommand (err) { function setCommand (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
const commandKey = new Registry({ const commandKey = new Registry({
hive: Registry.HKCU, 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) commandKey.set('', Registry.REG_SZ, `${commandToArgs(command)} "%1"`, done)
} }
function done (err) { function done (err) {
if (err) log.error(err.message) if (err) return log.error(err.message)
} }
} }
} }
@@ -217,9 +210,9 @@ function uninstallWin32 () {
function getCommand () { function getCommand () {
const commandKey = new Registry({ const commandKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER 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) { if (!err && item.value.indexOf(commandToArgs(command)) >= 0) {
destroyProtocol() destroyProtocol()
} }
@@ -229,9 +222,9 @@ function uninstallWin32 () {
function destroyProtocol () { function destroyProtocol () {
const protocolKey = new Registry({ const protocolKey = new Registry({
hive: Registry.HKCU, hive: Registry.HKCU,
key: '\\Software\\Classes\\' + protocol key: `\\Software\\Classes\\${protocol}`
}) })
protocolKey.destroy(function () {}) protocolKey.destroy(() => {})
} }
} }
@@ -241,7 +234,7 @@ function uninstallWin32 () {
function eraseId () { function eraseId () {
const idKey = new Registry({ const idKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + id key: `\\Software\\Classes\\${id}`
}) })
idKey.destroy(getExt) idKey.destroy(getExt)
} }
@@ -249,9 +242,9 @@ function uninstallWin32 () {
function getExt () { function getExt () {
const extKey = new Registry({ const extKey = new Registry({
hive: Registry.HKCU, 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) { if (!err && item.value === id) {
destroyExt() destroyExt()
} }
@@ -261,9 +254,9 @@ function uninstallWin32 () {
function destroyExt () { function destroyExt () {
const extKey = new Registry({ const extKey = new Registry({
hive: Registry.HKCU, // HKEY_CURRENT_USER hive: Registry.HKCU, // HKEY_CURRENT_USER
key: '\\Software\\Classes\\' + ext key: `\\Software\\Classes\\${ext}`
}) })
extKey.destroy(function () {}) extKey.destroy(() => {})
} }
} }
} }
@@ -271,99 +264,3 @@ function uninstallWin32 () {
function commandToArgs (command) { function commandToArgs (command) {
return command.map((arg) => `"${arg}"`).join(' ') 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

@@ -1,21 +1,29 @@
console.time('init') console.time('init')
const electron = require('electron') require('@electron/remote/main').initialize()
const app = electron.app const { app, ipcMain } = require('electron')
// Start crash reporter early, so it takes effect for child processes
const crashReporter = require('../crash-reporter')
crashReporter.init()
const parallel = require('run-parallel') const parallel = require('run-parallel')
const config = require('../config') const config = require('../config')
const crashReporter = require('../crash-reporter')
const ipc = require('./ipc') const ipc = require('./ipc')
const log = require('./log') const log = require('./log')
const menu = require('./menu') const menu = require('./menu')
const State = require('../renderer/lib/state') const State = require('../renderer/lib/state')
const windows = require('./windows') const windows = require('./windows')
const WEBTORRENT_VERSION = require('webtorrent/package.json').version
let shouldQuit = false let shouldQuit = false
let argv = sliceArgv(process.argv) 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 // 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.) // (On Windows and Linux, we get a flag. On MacOS, we get special API.)
const hidden = argv.includes('--hidden') || const hidden = argv.includes('--hidden') ||
@@ -38,17 +46,19 @@ if (!shouldQuit && !config.IS_PORTABLE) {
// signal this instance and quit. Note: This feature creates a lock file in // 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 // %APPDATA%\Roaming\WebTorrent so we do not do it for the Portable App since
// we want to be "silent" as well as "portable". // we want to be "silent" as well as "portable".
shouldQuit = app.makeSingleInstance(onAppOpen) if (!app.requestSingleInstanceLock()) {
if (shouldQuit) { shouldQuit = true
app.quit()
} }
} }
if (!shouldQuit) { if (shouldQuit) {
app.quit()
} else {
init() init()
} }
function init () { function init () {
app.on('second-instance', (event, commandLine, workingDirectory) => onAppOpen(commandLine))
if (config.IS_PORTABLE) { if (config.IS_PORTABLE) {
const path = require('path') const path = require('path')
// Put all user data into the "Portable Settings" folder // Put all user data into the "Portable Settings" folder
@@ -57,8 +67,6 @@ function init () {
app.setPath('temp', path.join(config.CONFIG_PATH, 'Temp')) app.setPath('temp', path.join(config.CONFIG_PATH, 'Temp'))
} }
const ipcMain = electron.ipcMain
let isReady = false // app ready, windows can be created let isReady = false // app ready, windows can be created
app.ipcReady = false // main window has finished loading and IPC is ready app.ipcReady = false // main window has finished loading and IPC is ready
app.isQuitting = false app.isQuitting = false
@@ -72,38 +80,43 @@ function init () {
if (err) throw err if (err) throw err
isReady = true isReady = true
const state = results.state
windows.main.init(results.state, {hidden: hidden})
windows.webtorrent.init()
menu.init() menu.init()
windows.main.init(state, { hidden })
windows.webtorrent.init()
// To keep app startup fast, some code is delayed. // To keep app startup fast, some code is delayed.
setTimeout(delayedInit, config.DELAYED_INIT) setTimeout(() => {
delayedInit(state)
}, config.DELAYED_INIT)
// Report uncaught exceptions // Report uncaught exceptions
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
console.error(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) 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-file', onOpen)
app.on('open-url', onOpen) app.on('open-url', onOpen)
ipc.init() ipc.init()
app.once('will-finish-launching', function () { app.once('ipcReady', () => {
crashReporter.init()
})
app.once('ipcReady', function () {
log('Command line args:', argv) log('Command line args:', argv)
processArgv(argv) processArgv(argv)
console.timeEnd('init') console.timeEnd('init')
}) })
app.on('before-quit', function (e) { app.on('before-quit', e => {
if (app.isQuitting) return if (app.isQuitting) return
app.isQuitting = true app.isQuitting = true
@@ -116,22 +129,29 @@ function init () {
}, 4000) // quit after 4 secs, at most }, 4000) // quit after 4 secs, at most
}) })
app.on('activate', function () { app.on('activate', () => {
if (isReady) windows.main.show() if (isReady) windows.main.show()
}) })
} }
function delayedInit () { function delayedInit (state) {
if (app.isQuitting) return if (app.isQuitting) return
const announcement = require('./announcement') const announcement = require('./announcement')
const dock = require('./dock') const dock = require('./dock')
const updater = require('./updater') const updater = require('./updater')
const FolderWatcher = require('./folder-watcher')
const folderWatcher = new FolderWatcher({ window: windows.main, state })
announcement.init() announcement.init()
dock.init() dock.init()
updater.init() updater.init()
ipc.setModule('folderWatcher', folderWatcher)
if (folderWatcher.isEnabled()) {
folderWatcher.start()
}
if (process.platform === 'win32') { if (process.platform === 'win32') {
const userTasks = require('./user-tasks') const userTasks = require('./user-tasks')
userTasks.init() userTasks.init()
@@ -152,7 +172,7 @@ function onOpen (e, torrentId) {
// Electron issue: https://github.com/atom/electron/issues/4338 // Electron issue: https://github.com/atom/electron/issues/4338
setTimeout(() => windows.main.show(), 100) setTimeout(() => windows.main.show(), 100)
processArgv([ torrentId ]) processArgv([torrentId])
} else { } else {
argv.push(torrentId) argv.push(torrentId)
} }
@@ -176,14 +196,18 @@ function onAppOpen (newArgv) {
// Development: 2 args, eg: electron . // Development: 2 args, eg: electron .
// Test: 4 args, eg: electron -r .../mocks.js . // Test: 4 args, eg: electron -r .../mocks.js .
function sliceArgv (argv) { function sliceArgv (argv) {
return argv.slice(config.IS_PRODUCTION ? 1 return argv.slice(
: config.IS_TEST ? 4 config.IS_PRODUCTION
: 2) ? 1
: config.IS_TEST
? 4
: 2
)
} }
function processArgv (argv) { function processArgv (argv) {
let torrentIds = [] const torrentIds = []
argv.forEach(function (arg) { argv.forEach(arg => {
if (arg === '-n' || arg === '-o' || arg === '-u') { if (arg === '-n' || arg === '-o' || arg === '-u') {
// Critical path: Only load the 'dialog' package if it is needed // Critical path: Only load the 'dialog' package if it is needed
const dialog = require('./dialog') const dialog = require('./dialog')
@@ -198,7 +222,7 @@ function processArgv (argv) {
// Ignore hidden argument, already being handled // Ignore hidden argument, already being handled
} else if (arg.startsWith('-psn')) { } else if (arg.startsWith('-psn')) {
// Ignore Mac launchd "process serial number" argument // 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('--')) { } else if (arg.startsWith('--')) {
// Ignore Spectron flags // Ignore Spectron flags
} else if (arg === 'data:,') { } else if (arg === 'data:,') {

View File

@@ -1,10 +1,9 @@
module.exports = { module.exports = {
init init,
setModule
} }
const electron = require('electron') const { app, ipcMain } = require('electron')
const app = electron.app
const log = require('./log') const log = require('./log')
const menu = require('./menu') const menu = require('./menu')
@@ -13,19 +12,25 @@ const windows = require('./windows')
// Messages from the main process, to be sent once the WebTorrent process starts // Messages from the main process, to be sent once the WebTorrent process starts
const messageQueueMainToWebTorrent = [] const messageQueueMainToWebTorrent = []
function init () { // Will hold modules injected from the app that will be used on fired
const ipc = electron.ipcMain // IPC events.
const modules = {}
ipc.once('ipcReady', function (e) { function setModule (name, module) {
modules[name] = module
}
function init () {
ipcMain.once('ipcReady', e => {
app.ipcReady = true app.ipcReady = true
app.emit('ipcReady') app.emit('ipcReady')
}) })
ipc.once('ipcReadyWebTorrent', function (e) { ipcMain.once('ipcReadyWebTorrent', e => {
app.ipcReadyWebTorrent = true app.ipcReadyWebTorrent = true
log('sending %d queued messages from the main win to the webtorrent window', log('sending %d queued messages from the main win to the webtorrent window',
messageQueueMainToWebTorrent.length) messageQueueMainToWebTorrent.length)
messageQueueMainToWebTorrent.forEach(function (message) { messageQueueMainToWebTorrent.forEach(message => {
windows.webtorrent.send(message.name, ...message.args) windows.webtorrent.send(message.name, ...message.args)
log('webtorrent: sent queued %s', message.name) log('webtorrent: sent queued %s', message.name)
}) })
@@ -35,11 +40,11 @@ function init () {
* Dialog * Dialog
*/ */
ipc.on('openTorrentFile', () => { ipcMain.on('openTorrentFile', () => {
const dialog = require('./dialog') const dialog = require('./dialog')
dialog.openTorrentFile() dialog.openTorrentFile()
}) })
ipc.on('openFiles', () => { ipcMain.on('openFiles', () => {
const dialog = require('./dialog') const dialog = require('./dialog')
dialog.openFiles() dialog.openFiles()
}) })
@@ -48,20 +53,20 @@ function init () {
* Dock * Dock
*/ */
ipc.on('setBadge', (e, ...args) => { ipcMain.on('setBadge', (e, ...args) => {
const dock = require('./dock') const dock = require('./dock')
dock.setBadge(...args) dock.setBadge(...args)
}) })
ipc.on('downloadFinished', (e, ...args) => { ipcMain.on('downloadFinished', (e, ...args) => {
const dock = require('./dock') const dock = require('./dock')
dock.downloadFinished(...args) dock.downloadFinished(...args)
}) })
/** /**
* Events * Player Events
*/ */
ipc.on('onPlayerOpen', function () { ipcMain.on('onPlayerOpen', () => {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts') const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -72,14 +77,14 @@ function init () {
thumbar.enable() thumbar.enable()
}) })
ipc.on('onPlayerUpdate', function (e, ...args) { ipcMain.on('onPlayerUpdate', (e, ...args) => {
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
menu.onPlayerUpdate(...args) menu.onPlayerUpdate(...args)
thumbar.onPlayerUpdate(...args) thumbar.onPlayerUpdate(...args)
}) })
ipc.on('onPlayerClose', function () { ipcMain.on('onPlayerClose', () => {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts') const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -90,7 +95,7 @@ function init () {
thumbar.disable() thumbar.disable()
}) })
ipc.on('onPlayerPlay', function () { ipcMain.on('onPlayerPlay', () => {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -98,7 +103,7 @@ function init () {
thumbar.onPlayerPlay() thumbar.onPlayerPlay()
}) })
ipc.on('onPlayerPause', function () { ipcMain.on('onPlayerPause', () => {
const powerSaveBlocker = require('./power-save-blocker') const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
@@ -106,19 +111,41 @@ function init () {
thumbar.onPlayerPause() thumbar.onPlayerPause()
}) })
/**
* Folder Watcher Events
*/
ipcMain.on('startFolderWatcher', () => {
if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules.folderWatcher.start()
})
ipcMain.on('stopFolderWatcher', () => {
if (!modules.folderWatcher) {
log('IPC ERR: folderWatcher module is not defined.')
return
}
modules.folderWatcher.stop()
})
/** /**
* Shell * Shell
*/ */
ipc.on('openItem', (e, ...args) => { ipcMain.on('openPath', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.openItem(...args) shell.openPath(...args)
}) })
ipc.on('showItemInFolder', (e, ...args) => { ipcMain.on('showItemInFolder', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.showItemInFolder(...args) shell.showItemInFolder(...args)
}) })
ipc.on('moveItemToTrash', (e, ...args) => { ipcMain.on('moveItemToTrash', (e, ...args) => {
const shell = require('./shell') const shell = require('./shell')
shell.moveItemToTrash(...args) shell.moveItemToTrash(...args)
}) })
@@ -127,7 +154,7 @@ function init () {
* File handlers * File handlers
*/ */
ipc.on('setDefaultFileHandler', (e, flag) => { ipcMain.on('setDefaultFileHandler', (e, flag) => {
const handlers = require('./handlers') const handlers = require('./handlers')
if (flag) handlers.install() if (flag) handlers.install()
@@ -138,7 +165,7 @@ function init () {
* Auto start on login * Auto start on login
*/ */
ipc.on('setStartup', (e, flag) => { ipcMain.on('setStartup', (e, flag) => {
const startup = require('./startup') const startup = require('./startup')
if (flag) startup.install() if (flag) startup.install()
@@ -151,36 +178,38 @@ function init () {
const main = windows.main const main = windows.main
ipc.on('setAspectRatio', (e, ...args) => main.setAspectRatio(...args)) ipcMain.on('setAspectRatio', (e, ...args) => main.setAspectRatio(...args))
ipc.on('setBounds', (e, ...args) => main.setBounds(...args)) ipcMain.on('setBounds', (e, ...args) => main.setBounds(...args))
ipc.on('setProgress', (e, ...args) => main.setProgress(...args)) ipcMain.on('setProgress', (e, ...args) => main.setProgress(...args))
ipc.on('setTitle', (e, ...args) => main.setTitle(...args)) ipcMain.on('setTitle', (e, ...args) => main.setTitle(...args))
ipc.on('show', () => main.show()) ipcMain.on('show', () => main.show())
ipc.on('toggleFullScreen', (e, ...args) => main.toggleFullScreen(...args)) ipcMain.on('toggleFullScreen', (e, ...args) => main.toggleFullScreen(...args))
ipc.on('setAllowNav', (e, ...args) => menu.setAllowNav(...args)) ipcMain.on('setAllowNav', (e, ...args) => menu.setAllowNav(...args))
/** /**
* External Media Player * External Media Player
*/ */
ipc.on('checkForExternalPlayer', function (e, path) { ipcMain.on('checkForExternalPlayer', (e, path) => {
const externalPlayer = require('./external-player') const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, function (isInstalled) { externalPlayer.checkInstall(path, err => {
windows.main.send('checkForExternalPlayer', isInstalled) windows.main.send('checkForExternalPlayer', !err)
}) })
}) })
ipc.on('openExternalPlayer', (e, ...args) => { ipcMain.on('openExternalPlayer', (e, ...args) => {
const externalPlayer = require('./external-player') const externalPlayer = require('./external-player')
const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar') const thumbar = require('./thumbar')
menu.togglePlaybackControls(false) menu.togglePlaybackControls(false)
shortcuts.disable()
thumbar.disable() thumbar.disable()
externalPlayer.spawn(...args) externalPlayer.spawn(...args)
}) })
ipc.on('quitExternalPlayer', () => { ipcMain.on('quitExternalPlayer', () => {
const externalPlayer = require('./external-player') const externalPlayer = require('./external-player')
externalPlayer.kill() externalPlayer.kill()
}) })
@@ -189,11 +218,12 @@ function init () {
* Message passing * Message passing
*/ */
const oldEmit = ipc.emit const oldEmit = ipcMain.emit
ipc.emit = function (name, e, ...args) { ipcMain.emit = (name, e, ...args) => {
// Relay messages between the main window and the WebTorrent hidden window // Relay messages between the main window and the WebTorrent hidden window
if (name.startsWith('wt-') && !app.isQuitting) { if (name.startsWith('wt-') && !app.isQuitting) {
if (e.sender.browserWindowOptions.title === 'webtorrent-hidden-window') { console.dir(e.sender.getTitle())
if (e.sender.getTitle() === 'WebTorrent Hidden Window') {
// Send message to main window // Send message to main window
windows.main.send(name, ...args) windows.main.send(name, ...args)
log('webtorrent: got %s', name) log('webtorrent: got %s', name)
@@ -204,8 +234,8 @@ function init () {
} else { } else {
// Queue message for webtorrent window, it hasn't finished loading yet // Queue message for webtorrent window, it hasn't finished loading yet
messageQueueMainToWebTorrent.push({ messageQueueMainToWebTorrent.push({
name: name, name,
args: args args
}) })
log('webtorrent: queueing %s', name) log('webtorrent: queueing %s', name)
} }
@@ -213,6 +243,6 @@ function init () {
} }
// Emit all other events normally // Emit all other events normally
oldEmit.call(ipc, name, e, ...args) oldEmit.call(ipcMain, name, e, ...args)
} }
} }

View File

@@ -8,11 +8,9 @@ module.exports.error = error
* where they can be viewed in Developer Tools. * where they can be viewed in Developer Tools.
*/ */
const electron = require('electron') const { app } = require('electron')
const windows = require('./windows') const windows = require('./windows')
const app = electron.app
function log (...args) { function log (...args) {
if (app.ipcReady) { if (app.ipcReady) {
windows.main.send('log', ...args) windows.main.send('log', ...args)

View File

@@ -8,9 +8,7 @@ module.exports = {
onToggleFullScreen onToggleFullScreen
} }
const electron = require('electron') const { app, Menu } = require('electron')
const app = electron.app
const config = require('../config') const config = require('../config')
const windows = require('./windows') const windows = require('./windows')
@@ -18,8 +16,8 @@ const windows = require('./windows')
let menu = null let menu = null
function init () { function init () {
menu = electron.Menu.buildFromTemplate(getMenuTemplate()) menu = Menu.buildFromTemplate(getMenuTemplate())
electron.Menu.setApplicationMenu(menu) Menu.setApplicationMenu(menu)
} }
function togglePlaybackControls (flag) { function togglePlaybackControls (flag) {
@@ -70,12 +68,11 @@ function onToggleFullScreen (flag) {
} }
function getMenuItem (label) { function getMenuItem (label) {
for (let i = 0; i < menu.items.length; i++) { for (const menuItem of menu.items) {
const menuItem = menu.items[i].submenu.items.find(function (item) { const submenuItem = menuItem.submenu.items.find(item => item.label === label)
return item.label === label if (submenuItem) return submenuItem
})
if (menuItem) return menuItem
} }
return {}
} }
function getMenuTemplate () { function getMenuTemplate () {
@@ -278,6 +275,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', label: 'Help',
role: 'help', role: 'help',
@@ -289,6 +307,13 @@ function getMenuTemplate () {
shell.openExternal(config.HOME_PAGE_URL) shell.openExternal(config.HOME_PAGE_URL)
} }
}, },
{
label: 'Release Notes',
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_RELEASES)
}
},
{ {
label: 'Contribute on GitHub', label: 'Contribute on GitHub',
click: () => { click: () => {
@@ -305,13 +330,20 @@ function getMenuTemplate () {
const shell = require('./shell') const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_ISSUES) 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') { if (process.platform === 'darwin') {
// Add WebTorrent app menu (Mac) // WebTorrent menu (Mac)
template.unshift({ template.unshift({
label: config.APP_NAME, label: config.APP_NAME,
submenu: [ submenu: [
@@ -330,8 +362,7 @@ function getMenuTemplate () {
type: 'separator' type: 'separator'
}, },
{ {
role: 'services', role: 'services'
submenu: []
}, },
{ {
type: 'separator' type: 'separator'
@@ -354,8 +385,26 @@ function getMenuTemplate () {
] ]
}) })
// Add Window menu (Mac) // Edit menu (Mac)
template.splice(5, 0, { template[2].submenu.push(
{
type: 'separator'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
}
)
// Window menu (Mac)
template.splice(6, 0, {
role: 'window', role: 'window',
submenu: [ submenu: [
{ {
@@ -395,7 +444,7 @@ function getMenuTemplate () {
}) })
// Help menu (Windows, Linux) // Help menu (Windows, Linux)
template[4].submenu.push( template[5].submenu.push(
{ {
type: 'separator' type: 'separator'
}, },

View File

@@ -3,7 +3,7 @@ module.exports = {
disable disable
} }
const electron = require('electron') const { powerSaveBlocker } = require('electron')
const log = require('./log') const log = require('./log')
let blockId = 0 let blockId = 0
@@ -13,11 +13,11 @@ let blockId = 0
* display. * display.
*/ */
function enable () { function enable () {
if (electron.powerSaveBlocker.isStarted(blockId)) { if (powerSaveBlocker.isStarted(blockId)) {
// If a power saver block already exists, do nothing. // If a power saver block already exists, do nothing.
return return
} }
blockId = electron.powerSaveBlocker.start('prevent-display-sleep') blockId = powerSaveBlocker.start('prevent-display-sleep')
log(`powerSaveBlocker.enable: ${blockId}`) log(`powerSaveBlocker.enable: ${blockId}`)
} }
@@ -25,10 +25,10 @@ function enable () {
* Stop blocking the system from entering low-power mode. * Stop blocking the system from entering low-power mode.
*/ */
function disable () { function disable () {
if (!electron.powerSaveBlocker.isStarted(blockId)) { if (!powerSaveBlocker.isStarted(blockId)) {
// If a power saver block does not exist, do nothing. // If a power saver block does not exist, do nothing.
return return
} }
electron.powerSaveBlocker.stop(blockId) powerSaveBlocker.stop(blockId)
log(`powerSaveBlocker.disable: ${blockId}`) log(`powerSaveBlocker.disable: ${blockId}`)
} }

View File

@@ -1,11 +1,11 @@
module.exports = { module.exports = {
openExternal, openExternal,
openItem, openPath,
showItemInFolder, showItemInFolder,
moveItemToTrash moveItemToTrash
} }
const electron = require('electron') const { shell } = require('electron')
const log = require('./log') const log = require('./log')
/** /**
@@ -13,15 +13,16 @@ const log = require('./log')
*/ */
function openExternal (url) { function openExternal (url) {
log(`openExternal: ${url}`) log(`openExternal: ${url}`)
electron.shell.openExternal(url) shell.openExternal(url)
} }
/** /**
* Open the given file in the desktops default manner. * Open the given file in the desktops default manner.
*/ */
function openItem (path) {
log(`openItem: ${path}`) function openPath (path) {
electron.shell.openItem(path) log(`openPath: ${path}`)
shell.openPath(path)
} }
/** /**
@@ -29,7 +30,7 @@ function openItem (path) {
*/ */
function showItemInFolder (path) { function showItemInFolder (path) {
log(`showItemInFolder: ${path}`) log(`showItemInFolder: ${path}`)
electron.shell.showItemInFolder(path) shell.showItemInFolder(path)
} }
/** /**
@@ -37,5 +38,5 @@ function showItemInFolder (path) {
*/ */
function moveItemToTrash (path) { function moveItemToTrash (path) {
log(`moveItemToTrash: ${path}`) log(`moveItemToTrash: ${path}`)
electron.shell.moveItemToTrash(path) shell.trashItem(path)
} }

View File

@@ -3,20 +3,20 @@ module.exports = {
enable enable
} }
const electron = require('electron') const { globalShortcut } = require('electron')
const windows = require('./windows') const windows = require('./windows')
function enable () { function enable () {
// Register play/pause media key, available on some keyboards. // Register play/pause media key, available on some keyboards.
electron.globalShortcut.register( globalShortcut.register(
'MediaPlayPause', 'MediaPlayPause',
() => windows.main.dispatch('playPause') () => windows.main.dispatch('playPause')
) )
electron.globalShortcut.register( globalShortcut.register(
'MediaNextTrack', 'MediaNextTrack',
() => windows.main.dispatch('nextTrack') () => windows.main.dispatch('nextTrack')
) )
electron.globalShortcut.register( globalShortcut.register(
'MediaPreviousTrack', 'MediaPreviousTrack',
() => windows.main.dispatch('previousTrack') () => windows.main.dispatch('previousTrack')
) )
@@ -24,7 +24,7 @@ function enable () {
function disable () { function disable () {
// Return the media key to the OS, so other apps can use it. // Return the media key to the OS, so other apps can use it.
electron.globalShortcut.unregister('MediaPlayPause') globalShortcut.unregister('MediaPlayPause')
electron.globalShortcut.unregister('MediaNextTrack') globalShortcut.unregister('MediaNextTrack')
electron.globalShortcut.unregister('MediaPreviousTrack') globalShortcut.unregister('MediaPreviousTrack')
} }

View File

@@ -2,150 +2,39 @@ module.exports = {
handleEvent handleEvent
} }
const cp = require('child_process') const { app } = require('electron')
const electron = require('electron')
const fs = require('fs')
const os = require('os')
const path = require('path')
const app = electron.app const path = require('path')
const spawn = require('child_process').spawn
const handlers = require('./handlers') const handlers = require('./handlers')
const EXE_NAME = path.basename(process.execPath) const EXE_NAME = path.basename(process.execPath)
const UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe') const UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe')
function handleEvent (cmd) { const run = (args, done) => {
if (cmd === '--squirrel-install') { spawn(UPDATE_EXE, args, { detached: true })
// App was installed. Install desktop/start menu shortcuts. .on('close', done)
createShortcuts(function () { }
// Ensure user sees install splash screen so they realize that Setup.exe actually
// installed an application and isn't the application itself.
setTimeout(function () {
app.quit()
}, 3000)
})
return true
}
if (cmd === '--squirrel-updated') { function handleEvent (cmd) {
// App was updated. (Called on new version of app) if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
updateShortcuts(function () { run([`--createShortcut=${EXE_NAME}`], app.quit)
app.quit()
})
return true return true
} }
if (cmd === '--squirrel-uninstall') { if (cmd === '--squirrel-uninstall') {
// App was just uninstalled. Undo anything we did in the --squirrel-install and
// --squirrel-updated handlers
// Uninstall .torrent file and magnet link handlers // Uninstall .torrent file and magnet link handlers
handlers.uninstall() handlers.uninstall()
// Remove desktop/start menu shortcuts. run([`--removeShortcut=${EXE_NAME}`], app.quit)
// HACK: add a callback to handlers.uninstall() so we can remove this setTimeout
setTimeout(function () {
removeShortcuts(function () {
app.quit()
})
}, 1000)
return true return true
} }
if (cmd === '--squirrel-obsolete') { if (cmd === '--squirrel-obsolete') {
// App will be updated. (Called on outgoing version of app)
app.quit() app.quit()
return true return true
} }
if (cmd === '--squirrel-firstrun') {
// App is running for the first time. Do not quit, allow startup to continue.
return false
}
return false return false
} }
/**
* Spawn a command and invoke the callback when it completes with an error and
* the output from standard out.
*/
function spawn (command, args, cb) {
let stdout = ''
let error = null
let child = null
try {
child = cp.spawn(command, args)
} catch (err) {
// Spawn can throw an error
process.nextTick(function () {
cb(error, stdout)
})
return
}
child.stdout.on('data', function (data) {
stdout += data
})
child.on('error', function (processError) {
error = processError
})
child.on('close', function (code, signal) {
if (code !== 0 && !error) error = new Error('Command failed: #{signal || code}')
if (error) error.stdout = stdout
cb(error, stdout)
})
}
/**
* Spawn the Squirrel `Update.exe` command with the given arguments and invoke
* the callback when the command completes.
*/
function spawnUpdate (args, cb) {
spawn(UPDATE_EXE, args, cb)
}
/**
* Create desktop and start menu shortcuts using the Squirrel `Update.exe`
* command.
*/
function createShortcuts (cb) {
spawnUpdate(['--createShortcut', EXE_NAME], cb)
}
/**
* Update desktop and start menu shortcuts using the Squirrel `Update.exe`
* command.
*/
function updateShortcuts (cb) {
const homeDir = os.homedir()
if (homeDir) {
const desktopShortcutPath = path.join(homeDir, 'Desktop', 'WebTorrent.lnk')
// If the desktop shortcut was deleted by the user, then keep it deleted.
fs.access(desktopShortcutPath, function (err) {
const desktopShortcutExists = !err
createShortcuts(function () {
if (desktopShortcutExists) {
cb()
} else {
// Remove the unwanted desktop shortcut that was recreated
fs.unlink(desktopShortcutPath, cb)
}
})
})
} else {
createShortcuts(cb)
}
}
/**
* Remove desktop and start menu shortcuts using the Squirrel `Update.exe`
* command.
*/
function removeShortcuts (cb) {
spawnUpdate(['--removeShortcut', EXE_NAME], cb)
}

View File

@@ -3,19 +3,11 @@ module.exports = {
uninstall uninstall
} }
const config = require('../config') const { APP_NAME } = require('../config')
const AutoLaunch = require('auto-launch') 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({ const appLauncher = new AutoLaunch({
name: config.APP_NAME, name: APP_NAME,
path: appPath,
isHidden: true isHidden: true
}) })

View File

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

View File

@@ -4,9 +4,7 @@ module.exports = {
setWindowFocus setWindowFocus
} }
const electron = require('electron') const { app, Tray, Menu } = require('electron')
const app = electron.app
const config = require('../config') const config = require('../config')
const windows = require('./windows') const windows = require('./windows')
@@ -36,8 +34,8 @@ function setWindowFocus (flag) {
} }
function initLinux () { function initLinux () {
checkLinuxTraySupport(function (supportsTray) { checkLinuxTraySupport(err => {
if (supportsTray) createTray() if (!err) createTray()
}) })
} }
@@ -46,24 +44,20 @@ function initWin32 () {
} }
/** /**
* Check for libappindicator1 support before creating tray icon * Check for libappindicator support before creating tray icon.
*/ */
function checkLinuxTraySupport (cb) { function checkLinuxTraySupport (cb) {
const cp = require('child_process') const cp = require('child_process')
// Check that we're on Ubuntu (or another debian system) and that we have // Check that libappindicator libraries are installed in system.
// libappindicator1. If WebTorrent was installed from the deb file, we should cp.exec('ldconfig -p | grep libappindicator', (err, stdout) => {
// always have it. If it was installed from the zip file, we might not. if (err) return cb(err)
cp.exec('dpkg --get-selections libappindicator1', function (err, stdout) { cb(null)
if (err) return cb(false)
// Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed:
cb(stdout.endsWith('\tinstall\n'))
}) })
} }
function createTray () { function createTray () {
tray = new electron.Tray(getIconPath()) tray = new Tray(getIconPath())
// On Windows, left click opens the app, right click opens the context menu. // On Windows, left click opens the app, right click opens the context menu.
// On Linux, any click (left or right) opens the context menu. // On Linux, any click (left or right) opens the context menu.
@@ -74,7 +68,7 @@ function createTray () {
} }
function updateTrayMenu () { function updateTrayMenu () {
const contextMenu = electron.Menu.buildFromTemplate(getMenuTemplate()) const contextMenu = Menu.buildFromTemplate(getMenuTemplate())
tray.setContextMenu(contextMenu) tray.setContextMenu(contextMenu)
} }

View File

@@ -2,7 +2,7 @@ module.exports = {
init init
} }
const electron = require('electron') const { autoUpdater } = require('electron')
const get = require('simple-get') const get = require('simple-get')
const config = require('../config') const config = require('../config')
@@ -47,31 +47,31 @@ function onResponse (err, res, data) {
} }
function initDarwinWin32 () { function initDarwinWin32 () {
electron.autoUpdater.on( autoUpdater.on(
'error', 'error',
(err) => log.error(`Update error: ${err.message}`) (err) => log.error(`Update error: ${err.message}`)
) )
electron.autoUpdater.on( autoUpdater.on(
'checking-for-update', 'checking-for-update',
() => log('Checking for update') () => log('Checking for update')
) )
electron.autoUpdater.on( autoUpdater.on(
'update-available', 'update-available',
() => log('Update available') () => log('Update available')
) )
electron.autoUpdater.on( autoUpdater.on(
'update-not-available', 'update-not-available',
() => log('No update available') () => log('No update available')
) )
electron.autoUpdater.on( autoUpdater.on(
'update-downloaded', 'update-downloaded',
(e, notes, name, date, url) => log(`Update downloaded: ${name}: ${url}`) (e, notes, name, date, url) => log(`Update downloaded: ${name}: ${url}`)
) )
electron.autoUpdater.setFeedURL(AUTO_UPDATE_URL) autoUpdater.setFeedURL({ url: AUTO_UPDATE_URL })
electron.autoUpdater.checkForUpdates() autoUpdater.checkForUpdates()
} }

View File

@@ -2,9 +2,7 @@ module.exports = {
init init
} }
const electron = require('electron') const { app } = require('electron')
const app = electron.app
/** /**
* Add a user task menu to the app icon on right-click. (Windows) * Add a user task menu to the app icon on right-click. (Windows)

View File

@@ -4,18 +4,18 @@ const about = module.exports = {
} }
const config = require('../../config') const config = require('../../config')
const electron = require('electron') const { BrowserWindow } = require('electron')
function init () { function init () {
if (about.win) { if (about.win) {
return about.win.show() return about.win.show()
} }
const win = about.win = new electron.BrowserWindow({ const win = about.win = new BrowserWindow({
backgroundColor: '#ECECEC', backgroundColor: '#ECECEC',
center: true, center: true,
fullscreen: false, fullscreen: false,
height: 170, height: 250,
icon: getIconPath(), icon: getIconPath(),
maximizable: false, maximizable: false,
minimizable: false, minimizable: false,
@@ -24,19 +24,28 @@ function init () {
skipTaskbar: true, skipTaskbar: true,
title: 'About ' + config.APP_WINDOW_TITLE, title: 'About ' + config.APP_WINDOW_TITLE,
useContentSize: true, useContentSize: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true,
backgroundThrottling: false
},
width: 300 width: 300
}) })
require('@electron/remote/main').enable(win.webContents)
win.loadURL(config.WINDOW_ABOUT) win.loadURL(config.WINDOW_ABOUT)
// No menu on the About window win.once('ready-to-show', () => {
win.setMenu(null)
win.once('ready-to-show', function () {
win.show() 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 () { win.once('closed', () => {
about.win = null about.win = null
}) })
} }

View File

@@ -14,11 +14,9 @@ const main = module.exports = {
win: null win: null
} }
const electron = require('electron') const { app, BrowserWindow, screen } = require('electron')
const debounce = require('debounce') const debounce = require('debounce')
const app = electron.app
const config = require('../../config') const config = require('../../config')
const log = require('../log') const log = require('../log')
const menu = require('../menu') const menu = require('../menu')
@@ -30,21 +28,29 @@ function init (state, options) {
const initialBounds = Object.assign(config.WINDOW_INITIAL_BOUNDS, state.saved.bounds) const initialBounds = Object.assign(config.WINDOW_INITIAL_BOUNDS, state.saved.bounds)
const win = main.win = new electron.BrowserWindow({ const win = main.win = new BrowserWindow({
backgroundColor: '#282828', backgroundColor: '#282828',
darkTheme: true, // Forces dark theme (GTK+3) 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, 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,
contextIsolation: false,
enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true,
backgroundThrottling: false
},
x: initialBounds.x, x: initialBounds.x,
y: initialBounds.y y: initialBounds.y
}) })
require('@electron/remote/main').enable(win.webContents)
win.loadURL(config.WINDOW_MAIN) win.loadURL(config.WINDOW_MAIN)
@@ -56,11 +62,11 @@ function init (state, options) {
win.setSheetOffset(config.UI_HEADER_HEIGHT) win.setSheetOffset(config.UI_HEADER_HEIGHT)
} }
win.webContents.on('dom-ready', function () { win.webContents.on('dom-ready', () => {
menu.onToggleFullScreen(main.win.isFullScreen()) menu.onToggleFullScreen(main.win.isFullScreen())
}) })
win.webContents.on('will-navigate', (e, url) => { win.webContents.on('will-navigate', (e) => {
// Prevent drag-and-drop from navigating the Electron window, which can happen // Prevent drag-and-drop from navigating the Electron window, which can happen
// before our drag-and-drop handlers have been initialized. // before our drag-and-drop handlers have been initialized.
e.preventDefault() e.preventDefault()
@@ -72,27 +78,27 @@ function init (state, options) {
win.on('hide', onWindowBlur) win.on('hide', onWindowBlur)
win.on('show', onWindowFocus) win.on('show', onWindowFocus)
win.on('enter-full-screen', function () { win.on('enter-full-screen', () => {
menu.onToggleFullScreen(true) menu.onToggleFullScreen(true)
send('fullscreenChanged', true) send('fullscreenChanged', true)
win.setMenuBarVisibility(false) win.setMenuBarVisibility(false)
}) })
win.on('leave-full-screen', function () { win.on('leave-full-screen', () => {
menu.onToggleFullScreen(false) menu.onToggleFullScreen(false)
send('fullscreenChanged', false) send('fullscreenChanged', false)
win.setMenuBarVisibility(true) win.setMenuBarVisibility(true)
}) })
win.on('move', debounce(function (e) { win.on('move', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds()) send('windowBoundsChanged', main.win.getBounds())
}, 1000)) }, 1000))
win.on('resize', debounce(function (e) { win.on('resize', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds()) send('windowBoundsChanged', main.win.getBounds())
}, 1000)) }, 1000))
win.on('close', function (e) { win.on('close', e => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
const tray = require('../tray') const tray = require('../tray')
if (!tray.hasTray()) { if (!tray.hasTray()) {
@@ -137,37 +143,29 @@ function setAspectRatio (aspectRatio) {
function setBounds (bounds, maximize) { function setBounds (bounds, maximize) {
// Do nothing in fullscreen // Do nothing in fullscreen
if (!main.win || main.win.isFullScreen()) { 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 return
} }
// Maximize or minimize, if the second argument is present // Maximize or minimize, if the second argument is present
let willBeMaximized if (maximize === true && !main.win.isMaximized()) {
if (maximize === true) { log('setBounds: maximizing')
if (!main.win.isMaximized()) { main.win.maximize()
log('setBounds: maximizing') } else if (maximize === false && main.win.isMaximized()) {
main.win.maximize() log('setBounds: minimizing')
} main.win.unmaximize()
willBeMaximized = true
} else if (maximize === false) {
if (main.win.isMaximized()) {
log('setBounds: unmaximizing')
main.win.unmaximize()
}
willBeMaximized = false
} else {
willBeMaximized = main.win.isMaximized()
} }
const willBeMaximized = typeof maximize === 'boolean' ? maximize : main.win.isMaximized()
// Assuming we're not maximized or maximizing, set the window size // Assuming we're not maximized or maximizing, set the window size
if (!willBeMaximized) { 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) { if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen // X and Y not specified? By default, center on current screen
const scr = electron.screen.getDisplayMatching(main.win.getBounds()) const scr = screen.getDisplayMatching(main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + scr.bounds.width / 2 - bounds.width / 2) bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2))
bounds.y = Math.round(scr.bounds.y + scr.bounds.height / 2 - bounds.height / 2) bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2))
log('setBounds: centered to ' + JSON.stringify(bounds)) log(`setBounds: centered to ${JSON.stringify(bounds)}`)
} }
// Resize the window's content area (so window border doesn't need to be taken // Resize the window's content area (so window border doesn't need to be taken
// into account) // into account)
@@ -216,7 +214,7 @@ function toggleDevTools () {
if (main.win.webContents.isDevToolsOpened()) { if (main.win.webContents.isDevToolsOpened()) {
main.win.webContents.closeDevTools() main.win.webContents.closeDevTools()
} else { } else {
main.win.webContents.openDevTools({ detach: true }) main.win.webContents.openDevTools({ mode: 'detach' })
} }
} }

View File

@@ -6,12 +6,12 @@ const webtorrent = module.exports = {
win: null win: null
} }
const electron = require('electron') const { app, BrowserWindow } = require('electron')
const config = require('../../config') const config = require('../../config')
function init () { function init () {
const win = webtorrent.win = new electron.BrowserWindow({ const win = webtorrent.win = new BrowserWindow({
backgroundColor: '#1E1E1E', backgroundColor: '#1E1E1E',
center: true, center: true,
fullscreen: false, fullscreen: false,
@@ -24,14 +24,22 @@ function init () {
skipTaskbar: true, skipTaskbar: true,
title: 'webtorrent-hidden-window', title: 'webtorrent-hidden-window',
useContentSize: true, useContentSize: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableBlinkFeatures: 'AudioVideoTracks',
enableRemoteModule: true,
backgroundThrottling: false
},
width: 150 width: 150
}) })
require('@electron/remote/main').enable(win.webContents)
win.loadURL(config.WINDOW_WEBTORRENT) win.loadURL(config.WINDOW_WEBTORRENT)
// Prevent killing the WebTorrent process // Prevent killing the WebTorrent process
win.on('close', function (e) { win.on('close', e => {
if (electron.app.isQuitting) { if (app.isQuitting) {
return return
} }
e.preventDefault() e.preventDefault()
@@ -55,6 +63,6 @@ function toggleDevTools () {
webtorrent.win.webContents.closeDevTools() webtorrent.win.webContents.closeDevTools()
webtorrent.win.hide() webtorrent.win.hide()
} else { } else {
webtorrent.win.webContents.openDevTools({ detach: true }) webtorrent.win.webContents.openDevTools({ mode: 'detach' })
} }
} }

View File

@@ -1,6 +1,6 @@
const React = require('react') const React = require('react')
const {dispatcher} = require('../lib/dispatcher') const { dispatcher } = require('../lib/dispatcher')
module.exports = class CreateTorrentErrorPage extends React.Component { module.exports = class CreateTorrentErrorPage extends React.Component {
render () { 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,38 @@
const React = require('react') const React = require('react')
const {dispatcher} = require('../lib/dispatcher') const { dispatcher } = require('../lib/dispatcher')
class Header extends React.Component { class Header extends React.Component {
render () { render () {
const loc = this.props.state.location const loc = this.props.state.location
return ( return (
<div className='header' <div
className='header'
onMouseMove={dispatcher('mediaMouseMoved')} onMouseMove={dispatcher('mediaMouseMoved')}
onMouseEnter={dispatcher('mediaControlsMouseEnter')} onMouseEnter={dispatcher('mediaControlsMouseEnter')}
onMouseLeave={dispatcher('mediaControlsMouseLeave')}> onMouseLeave={dispatcher('mediaControlsMouseLeave')}
role='navigation'
>
{this.getTitle()} {this.getTitle()}
<div className='nav left float-left'> <div className='nav left float-left'>
<i <i
className={'icon back ' + (loc.hasBack() ? '' : 'disabled')} className={'icon back ' + (loc.hasBack() ? '' : 'disabled')}
title='Back' title='Back'
onClick={dispatcher('back')}> onClick={dispatcher('back')}
role='button'
aria-disabled={!loc.hasBack()}
aria-label='Back'
>
chevron_left chevron_left
</i> </i>
<i <i
className={'icon forward ' + (loc.hasForward() ? '' : 'disabled')} className={'icon forward ' + (loc.hasForward() ? '' : 'disabled')}
title='Forward' title='Forward'
onClick={dispatcher('forward')}> onClick={dispatcher('forward')}
role='button'
aria-disabled={!loc.hasForward()}
aria-label='Forward'
>
chevron_right chevron_right
</i> </i>
</div> </div>
@@ -45,7 +56,9 @@ class Header extends React.Component {
<i <i
className='icon add' className='icon add'
title='Add torrent' title='Add torrent'
onClick={dispatcher('openFiles')}> onClick={dispatcher('openFiles')}
role='button'
>
add add
</i> </i>
) )

View File

@@ -1,11 +1,12 @@
const React = require('react') const React = require('react')
const PropTypes = require('prop-types')
const colors = require('material-ui/styles/colors') const colors = require('material-ui/styles/colors')
class Heading extends React.Component { class Heading extends React.Component {
static get propTypes () { static get propTypes () {
return { 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 { module.exports = class ModalOKCancel extends React.Component {
render () { render () {
const cancelStyle = { marginRight: 10, color: 'black' } const cancelStyle = { marginRight: 10, color: 'black' }
const {cancelText, onCancel, okText, onOK} = this.props const { cancelText, onCancel, okText, onOK } = this.props
return ( return (
<div className='float-right'> <div className='float-right'>
<FlatButton <FlatButton
className='control cancel' className='control cancel'
style={cancelStyle} style={cancelStyle}
label={cancelText} label={cancelText}
onClick={onCancel} /> onClick={onCancel}
/>
<RaisedButton <RaisedButton
className='control ok' className='control ok'
primary primary
label={okText} label={okText}
onClick={onOK} /> onClick={onOK}
autoFocus
/>
</div> </div>
) )
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
const React = require('react') const React = require('react')
const electron = require('electron') const { shell } = require('electron')
const ModalOKCancel = require('./modal-ok-cancel') const ModalOKCancel = require('./modal-ok-cancel')
const {dispatcher} = require('../lib/dispatcher') const { dispatcher } = require('../lib/dispatcher')
module.exports = class UnsupportedMediaModal extends React.Component { module.exports = class UnsupportedMediaModal extends React.Component {
render () { render () {
@@ -28,14 +28,15 @@ module.exports = class UnsupportedMediaModal extends React.Component {
cancelText='CANCEL' cancelText='CANCEL'
onCancel={dispatcher('backToList')} onCancel={dispatcher('backToList')}
okText={actionText} okText={actionText}
onOK={onAction} /> onOK={onAction}
/>
<p className='error-text'>{errorMessage}</p> <p className='error-text'>{errorMessage}</p>
</div> </div>
) )
} }
onInstall () { onInstall () {
electron.shell.openExternal('http://www.videolan.org/vlc/') shell.openExternal('https://www.videolan.org/vlc/')
// TODO: dcposch send a dispatch rather than modifying state directly // TODO: dcposch send a dispatch rather than modifying state directly
const state = this.props.state const state = this.props.state

View File

@@ -1,8 +1,8 @@
const React = require('react') const React = require('react')
const electron = require('electron') const { shell } = require('electron')
const ModalOKCancel = require('./modal-ok-cancel') const ModalOKCancel = require('./modal-ok-cancel')
const {dispatch} = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
module.exports = class UpdateAvailableModal extends React.Component { module.exports = class UpdateAvailableModal extends React.Component {
render () { render () {
@@ -18,12 +18,14 @@ module.exports = class UpdateAvailableModal extends React.Component {
cancelText='SKIP THIS RELEASE' cancelText='SKIP THIS RELEASE'
onCancel={handleSkip} onCancel={handleSkip}
okText='SHOW DOWNLOAD PAGE' okText='SHOW DOWNLOAD PAGE'
onOK={handleShow} /> onOK={handleShow}
/>
</div> </div>
) )
function handleShow () { function handleShow () {
electron.shell.openExternal('https://github.com/feross/webtorrent-desktop/releases') // TODO: use the GitHub urls from config.js
shell.openExternal('https://github.com/webtorrent/webtorrent-desktop/releases')
dispatch('exitModal') 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 } = require('electron')
const telemetry = require('../lib/telemetry')
const ipcRenderer = electron.ipcRenderer
const Playlist = require('../lib/playlist') const Playlist = require('../lib/playlist')
// Controls local play back: the <video>/<audio> tag and VLC // Controls local play back: the <video>/<audio> tag and VLC
@@ -12,7 +10,7 @@ module.exports = class MediaController {
} }
mediaSuccess () { mediaSuccess () {
this.state.playing.result = 'success' telemetry.logPlayAttempt('success')
} }
mediaStalled () { mediaStalled () {
@@ -22,13 +20,13 @@ module.exports = class MediaController {
mediaError (error) { mediaError (error) {
const state = this.state const state = this.state
if (state.location.url() === 'player') { if (state.location.url() === 'player') {
state.playing.result = 'error' telemetry.logPlayAttempt('error')
state.playing.location = 'error' state.playing.location = 'error'
ipcRenderer.send('checkForExternalPlayer', state.saved.prefs.externalPlayerPath) ipcRenderer.send('checkForExternalPlayer', state.saved.prefs.externalPlayerPath)
ipcRenderer.once('checkForExternalPlayer', function (e, isInstalled) { ipcRenderer.once('checkForExternalPlayer', (e, isInstalled) => {
state.modal = { state.modal = {
id: 'unsupported-media-modal', id: 'unsupported-media-modal',
error: error, error,
externalPlayerInstalled: isInstalled externalPlayerInstalled: isInstalled
} }
}) })
@@ -58,7 +56,10 @@ module.exports = class MediaController {
const state = this.state const state = this.state
state.playing.location = 'external' state.playing.location = 'external'
let open = function () { const onServerRunning = () => {
state.playing.isReady = true
telemetry.logPlayAttempt('external')
const mediaURL = Playlist.getCurrentLocalURL(state) const mediaURL = Playlist.getCurrentLocalURL(state)
ipcRenderer.send('openExternalPlayer', ipcRenderer.send('openExternalPlayer',
state.saved.prefs.externalPlayerPath, state.saved.prefs.externalPlayerPath,
@@ -66,8 +67,8 @@ module.exports = class MediaController {
state.window.title) state.window.title)
} }
if (state.server != null) open() if (state.server != null) onServerRunning()
else ipcRenderer.once('wt-server-running', open) else ipcRenderer.once('wt-server-running', onServerRunning)
} }
externalPlayerNotFound () { externalPlayerNotFound () {

View File

@@ -1,18 +1,16 @@
const electron = require('electron') const { ipcRenderer } = require('electron')
const path = require('path') const path = require('path')
const Cast = require('../lib/cast') const Cast = require('../lib/cast')
const {dispatch} = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
const telemetry = require('../lib/telemetry') const telemetry = require('../lib/telemetry')
const {UnplayableFileError, UnplayableTorrentError} = require('../lib/errors') const { UnplayableFileError, UnplayableTorrentError } = require('../lib/errors')
const sound = require('../lib/sound') const sound = require('../lib/sound')
const TorrentPlayer = require('../lib/torrent-player') const TorrentPlayer = require('../lib/torrent-player')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist') const Playlist = require('../lib/playlist')
const State = require('../lib/state') const State = require('../lib/state')
const ipcRenderer = electron.ipcRenderer
// Controls playback of torrents and files within torrents // Controls playback of torrents and files within torrents
// both local (<video>,<audio>,external player) and remote (cast) // both local (<video>,<audio>,external player) and remote (cast)
module.exports = class PlaybackController { module.exports = class PlaybackController {
@@ -27,6 +25,8 @@ module.exports = class PlaybackController {
// * Stream, if not already fully downloaded // * Stream, if not already fully downloaded
// * If no file index is provided, restore the most recently viewed file or autoplay the first // * If no file index is provided, restore the most recently viewed file or autoplay the first
playFile (infoHash, index /* optional */) { playFile (infoHash, index /* optional */) {
this.pauseActiveTorrents(infoHash)
const state = this.state const state = this.state
if (state.location.url() === 'player') { if (state.location.url() === 'player') {
this.updatePlayer(infoHash, index, false, (err) => { this.updatePlayer(infoHash, index, false, (err) => {
@@ -59,12 +59,12 @@ module.exports = class PlaybackController {
} }
// Open a file in OS default app. // Open a file in OS default app.
openItem (infoHash, index) { openPath (infoHash, index) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash) const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
const filePath = path.join( const filePath = path.join(
torrentSummary.path, torrentSummary.path,
torrentSummary.files[index].path) torrentSummary.files[index].path)
ipcRenderer.send('openItem', filePath) ipcRenderer.send('openPath', filePath)
} }
// Toggle (play or pause) the currently playing media // Toggle (play or pause) the currently playing media
@@ -84,15 +84,26 @@ module.exports = class PlaybackController {
else this.pause() 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) // Play next file in list (if any)
nextTrack () { nextTrack () {
const state = this.state const state = this.state
if (Playlist.hasNext(state) && state.playing.location !== 'external') { if (Playlist.hasNext(state) && state.playing.location !== 'external') {
this.updatePlayer( this.updatePlayer(
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => { state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err) if (err) dispatch('error', err)
else this.play() else this.play()
}) })
} }
} }
@@ -145,6 +156,20 @@ module.exports = class PlaybackController {
else this.state.playing.jumpToTime = time else this.state.playing.jumpToTime = time
} }
// Show video preview
preview (x) {
if (!Number.isFinite(x)) {
console.error('Tried to preview a non-finite position ' + x)
return console.trace()
}
this.state.playing.previewXCoord = x
}
// Hide video preview
clearPreview () {
this.state.playing.previewXCoord = null
}
// Change playback speed. 1 = faster, -1 = slower // Change playback speed. 1 = faster, -1 = slower
// Playback speed ranges from 16 (fast forward) to 1 (normal playback) // Playback speed ranges from 16 (fast forward) to 1 (normal playback)
// to 0.25 (quarter-speed playback), then goes to -0.25, -0.5, -1, -2, etc // to 0.25 (quarter-speed playback), then goes to -0.25, -0.5, -1, -2, etc
@@ -211,6 +236,7 @@ module.exports = class PlaybackController {
const torrentSummary = TorrentSummary.getByKey(state, infoHash) const torrentSummary = TorrentSummary.getByKey(state, infoHash)
state.playing.infoHash = torrentSummary.infoHash state.playing.infoHash = torrentSummary.infoHash
state.playing.isReady = false
// update UI to show pending playback // update UI to show pending playback
sound.play('PLAY') sound.play('PLAY')
@@ -222,6 +248,8 @@ module.exports = class PlaybackController {
// Starts WebTorrent server for media streaming // Starts WebTorrent server for media streaming
startServer (torrentSummary) { startServer (torrentSummary) {
const state = this.state
if (torrentSummary.status === 'paused') { if (torrentSummary.status === 'paused') {
dispatch('startTorrentingSummary', torrentSummary.torrentKey) dispatch('startTorrentingSummary', torrentSummary.torrentKey)
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash, ipcRenderer.once('wt-ready-' + torrentSummary.infoHash,
@@ -232,6 +260,7 @@ module.exports = class PlaybackController {
function onTorrentReady () { function onTorrentReady () {
ipcRenderer.send('wt-start-server', torrentSummary.infoHash) ipcRenderer.send('wt-start-server', torrentSummary.infoHash)
ipcRenderer.once('wt-server-running', () => { state.playing.isReady = true })
} }
} }
@@ -252,9 +281,12 @@ module.exports = class PlaybackController {
// update state // update state
state.playing.infoHash = infoHash state.playing.infoHash = infoHash
state.playing.fileIndex = index state.playing.fileIndex = index
state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video' state.playing.fileName = fileSummary.name
: TorrentPlayer.isAudio(fileSummary) ? 'audio' state.playing.type = TorrentPlayer.isVideo(fileSummary)
: 'other' ? 'video'
: TorrentPlayer.isAudio(fileSummary)
? 'audio'
: 'other'
// pick up where we left off // pick up where we left off
let jumpToTime = 0 let jumpToTime = 0
@@ -275,7 +307,7 @@ module.exports = class PlaybackController {
} }
function getAudioMetadata () { function getAudioMetadata () {
if (state.playing.type === 'audio' && !fileSummary.audioInfo) { if (state.playing.type === 'audio') {
ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index) ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index)
} }
} }
@@ -320,12 +352,7 @@ module.exports = class PlaybackController {
// Save volume (this session only, not in state.saved) // Save volume (this session only, not in state.saved)
state.previousVolume = state.playing.volume state.previousVolume = state.playing.volume
// Telemetry: track what happens after the user clicks play if (!state.playing.isReady) telemetry.logPlayAttempt('abandoned') // user gave up waiting
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)
// Reset the window contents back to the home screen // Reset the window contents back to the home screen
state.playing = State.getDefaultPlayState() state.playing = State.getDefaultPlayState()
@@ -342,6 +369,11 @@ module.exports = class PlaybackController {
ipcRenderer.send('onPlayerClose') ipcRenderer.send('onPlayerClose')
// Playback Priority: resume previously paused downloads.
if (this.state.saved.prefs.highestPlaybackPriority) {
dispatch('resumePausedTorrents')
}
this.update() this.update()
} }
} }

View File

@@ -1,5 +1,5 @@
const {dispatch} = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
const ipcRenderer = require('electron').ipcRenderer const { ipcRenderer } = require('electron')
// Controls the Preferences screen // Controls the Preferences screen
module.exports = class PrefsController { module.exports = class PrefsController {
@@ -13,48 +13,25 @@ module.exports = class PrefsController {
const state = this.state const state = this.state
state.location.go({ state.location.go({
url: 'preferences', url: 'preferences',
setup: function (cb) { setup (cb) {
// initialize preferences // initialize preferences
state.window.title = 'Preferences' state.window.title = 'Preferences'
state.unsaved = Object.assign(state.unsaved || {}, {
prefs: Object.assign({}, state.saved.prefs)
})
ipcRenderer.send('setAllowNav', false) ipcRenderer.send('setAllowNav', false)
cb() cb()
}, },
destroy: () => { destroy: () => {
ipcRenderer.send('setAllowNav', true) ipcRenderer.send('setAllowNav', true)
this.save()
} }
}) })
} }
// Updates a single property in the UNSAVED prefs // Updates a single property in the saved prefs
// For example: updatePreferences('foo.bar', 'baz') // For example: updatePreferences('isFileHandler', true)
// Call save() to save to config.json
update (property, value) { update (property, value) {
const path = property.split('.') if (property === 'isFileHandler') ipcRenderer.send('setDefaultFileHandler', value)
let obj = this.state.unsaved.prefs else if (property === 'startup') ipcRenderer.send('setStartup', value)
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
}
// All unsaved prefs take effect atomically, and are saved to config.json this.state.saved.prefs[property] = value
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)
dispatch('stateSaveImmediate') dispatch('stateSaveImmediate')
dispatch('checkDownloadPath') dispatch('checkDownloadPath')
} }

View File

@@ -1,11 +1,9 @@
const electron = require('electron') const remote = require('@electron/remote')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const parallel = require('run-parallel') const parallel = require('run-parallel')
const remote = electron.remote const { dispatch } = require('../lib/dispatcher')
const {dispatch} = require('../lib/dispatcher')
module.exports = class SubtitlesController { module.exports = class SubtitlesController {
constructor (state) { constructor (state) {
@@ -13,14 +11,13 @@ module.exports = class SubtitlesController {
} }
openSubtitles () { openSubtitles () {
remote.dialog.showOpenDialog({ const filenames = remote.dialog.showOpenDialogSync({
title: 'Select a subtitles file.', title: 'Select a subtitles file.',
filters: [ { name: 'Subtitles', extensions: ['vtt', 'srt'] } ], filters: [{ name: 'Subtitles', extensions: ['vtt', 'srt'] }],
properties: [ 'openFile' ] properties: ['openFile']
}, (filenames) => {
if (!Array.isArray(filenames)) return
this.addSubtitles(filenames, true)
}) })
if (!Array.isArray(filenames)) return
this.addSubtitles(filenames, true)
} }
selectSubtitle (ix) { selectSubtitle (ix) {
@@ -40,12 +37,11 @@ module.exports = class SubtitlesController {
// Read the files concurrently, then add all resulting subtitle tracks // Read the files concurrently, then add all resulting subtitle tracks
const tasks = files.map((file) => (cb) => loadSubtitle(file, cb)) const tasks = files.map((file) => (cb) => loadSubtitle(file, cb))
parallel(tasks, function (err, tracks) { parallel(tasks, (err, tracks) => {
if (err) return dispatch('error', err) if (err) return dispatch('error', err)
for (let i = 0; i < tracks.length; i++) { // No dupes allowed
// No dupes allowed tracks.forEach((track, i) => {
const track = tracks[i]
let trackIndex = subtitles.tracks.findIndex((t) => let trackIndex = subtitles.tracks.findIndex((t) =>
track.filePath === t.filePath) track.filePath === t.filePath)
@@ -58,7 +54,7 @@ module.exports = class SubtitlesController {
if (autoSelect && (i === 0 || isSystemLanguage(track.language))) { if (autoSelect && (i === 0 || isSystemLanguage(track.language))) {
subtitles.selectedIndex = trackIndex subtitles.selectedIndex = trackIndex
} }
} })
// Finally, make sure no two tracks have the same label // Finally, make sure no two tracks have the same label
relabelSubtitles(subtitles) relabelSubtitles(subtitles)
@@ -97,7 +93,7 @@ function loadSubtitle (file, cb) {
const vttStream = fs.createReadStream(filePath).pipe(srtToVtt()) const vttStream = fs.createReadStream(filePath).pipe(srtToVtt())
concat(vttStream, function (err, buf) { concat(vttStream, (err, buf) => {
if (err) return dispatch('error', 'Can\'t parse subtitles file.') if (err) return dispatch('error', 'Can\'t parse subtitles file.')
// Detect what language the subtitles are in // Detect what language the subtitles are in
@@ -110,7 +106,7 @@ function loadSubtitle (file, cb) {
buffer: 'data:text/vtt;base64,' + buf.toString('base64'), buffer: 'data:text/vtt;base64,' + buf.toString('base64'),
language: langDetected, language: langDetected,
label: langDetected, label: langDetected,
filePath: filePath filePath
} }
cb(null, track) cb(null, track)
@@ -130,7 +126,7 @@ function isSystemLanguage (language) {
// Labels each track by language, eg 'German', 'English', 'English 2', ... // Labels each track by language, eg 'German', 'English', 'English 2', ...
function relabelSubtitles (subtitles) { function relabelSubtitles (subtitles) {
const counts = {} const counts = {}
subtitles.tracks.forEach(function (track) { subtitles.tracks.forEach(track => {
const lang = track.language const lang = track.language
counts[lang] = (counts[lang] || 0) + 1 counts[lang] = (counts[lang] || 0) + 1
track.label = counts[lang] > 1 ? (lang + ' ' + counts[lang]) : lang track.label = counts[lang] > 1 ? (lang + ' ' + counts[lang]) : lang

View File

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

View File

@@ -1,14 +1,13 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const electron = require('electron') const { ipcRenderer, clipboard } = require('electron')
const remote = require('@electron/remote')
const {dispatch} = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
const {TorrentKeyNotFoundError} = require('../lib/errors') const { TorrentKeyNotFoundError } = require('../lib/errors')
const sound = require('../lib/sound') const sound = require('../lib/sound')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
const ipcRenderer = electron.ipcRenderer
const instantIoRegex = /^(https:\/\/)?instant\.io\/#/ const instantIoRegex = /^(https:\/\/)?instant\.io\/#/
// Controls the torrent list: creating, adding, deleting, & manipulating torrents // Controls the torrent list: creating, adding, deleting, & manipulating torrents
@@ -55,7 +54,7 @@ module.exports = class TorrentListController {
if (files.length === 0 || typeof files[0] !== 'string') { if (files.length === 0 || typeof files[0] !== 'string') {
this.state.location.go({ this.state.location.go({
url: 'create-torrent', url: 'create-torrent',
files: files, files,
setup: (cb) => { setup: (cb) => {
this.state.window.title = 'Create New Torrent' this.state.window.title = 'Create New Torrent'
cb(null) cb(null)
@@ -95,7 +94,7 @@ module.exports = class TorrentListController {
if (!fileOrFolder) return start() if (!fileOrFolder) return start()
// Existing torrent: check that the path is still there // Existing torrent: check that the path is still there
fs.stat(fileOrFolder, function (err) { fs.stat(fileOrFolder, err => {
if (err) { if (err) {
s.error = 'path-missing' s.error = 'path-missing'
dispatch('backToList') dispatch('backToList')
@@ -114,6 +113,10 @@ module.exports = class TorrentListController {
} }
} }
setGlobalTrackers (globalTrackers) {
ipcRenderer.send('wt-set-global-trackers', globalTrackers)
}
// TODO: use torrentKey, not infoHash // TODO: use torrentKey, not infoHash
toggleTorrent (infoHash) { toggleTorrent (infoHash) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash) const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
@@ -121,11 +124,64 @@ module.exports = class TorrentListController {
torrentSummary.status = 'new' torrentSummary.status = 'new'
this.startTorrentingSummary(torrentSummary.torrentKey) this.startTorrentingSummary(torrentSummary.torrentKey)
sound.play('ENABLE') sound.play('ENABLE')
} else { return
torrentSummary.status = 'paused'
ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash)
sound.play('DISABLE')
} }
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 => ['downloading', 'seeding'].includes(torrent.status)) // Active torrents only.
.forEach((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.forEach((infoHash) => {
this.toggleTorrent(infoHash)
})
// reset paused torrents
this.state.saved.torrentsToResume = []
} }
toggleTorrentFile (infoHash, index) { toggleTorrentFile (infoHash, index) {
@@ -146,26 +202,43 @@ module.exports = class TorrentListController {
} }
} }
confirmDeleteAllTorrents (deleteData) {
this.state.modal = {
id: 'delete-all-torrents-modal',
deleteData
}
}
// TODO: use torrentKey, not infoHash // TODO: use torrentKey, not infoHash
deleteTorrent (infoHash, deleteData) { deleteTorrent (infoHash, deleteData) {
ipcRenderer.send('wt-stop-torrenting', infoHash)
const index = this.state.saved.torrents.findIndex((x) => x.infoHash === infoHash) const index = this.state.saved.torrents.findIndex((x) => x.infoHash === infoHash)
if (index > -1) { if (index > -1) {
const summary = this.state.saved.torrents[index] const summary = this.state.saved.torrents[index]
deleteTorrentFile(summary, deleteData)
// remove torrent and poster file
deleteFile(TorrentSummary.getTorrentPath(summary))
deleteFile(TorrentSummary.getPosterPath(summary))
// optionally delete the torrent data
if (deleteData) moveItemToTrash(summary)
// remove torrent from saved list // remove torrent from saved list
this.state.saved.torrents.splice(index, 1) this.state.saved.torrents.splice(index, 1)
dispatch('stateSave') 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) {
// Go back to list before the current playing torrent is deleted
if (this.state.location.url() === 'player') {
dispatch('backToList')
}
this.state.saved.torrents.forEach((summary) => deleteTorrentFile(summary, deleteData))
this.state.saved.torrents = []
dispatch('stateSave')
// prevent user from going forward to a deleted torrent // prevent user from going forward to a deleted torrent
this.state.location.clearForward('player') this.state.location.clearForward('player')
@@ -182,76 +255,88 @@ module.exports = class TorrentListController {
openTorrentContextMenu (infoHash) { openTorrentContextMenu (infoHash) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash) const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
const menu = new electron.remote.Menu() const menu = new remote.Menu()
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Remove From List', label: 'Remove From List',
click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, false) click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, false)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Remove Data File', label: 'Remove Data File',
click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, true) click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, true)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
if (torrentSummary.files) { if (torrentSummary.files) {
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: process.platform === 'darwin' ? 'Show in Finder' : 'Show in Folder', label: process.platform === 'darwin' ? 'Show in Finder' : 'Show in Folder',
click: () => showItemInFolder(torrentSummary) click: () => showItemInFolder(torrentSummary)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
type: 'separator' type: 'separator'
})) }))
} }
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Copy Magnet Link to Clipboard', label: 'Copy Magnet Link to Clipboard',
click: () => electron.clipboard.writeText(torrentSummary.magnetURI) click: () => clipboard.writeText(torrentSummary.magnetURI)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Copy Instant.io Link to Clipboard', label: 'Copy Instant.io Link to Clipboard',
click: () => electron.clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`) click: () => clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`)
})) }))
menu.append(new electron.remote.MenuItem({ menu.append(new remote.MenuItem({
label: 'Save Torrent File As...', 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 remote.MenuItem({
type: 'separator'
}))
const sortedByName = this.state.saved.prefs.sortByName
menu.append(new remote.MenuItem({
label: `${sortedByName ? '✓ ' : ''}Sort by Name`,
click: () => dispatch('updatePreferences', 'sortByName', !sortedByName)
}))
menu.popup({ window: remote.getCurrentWindow() })
} }
// Takes a torrentSummary or torrentKey // Takes a torrentSummary or torrentKey
// Shows a Save File dialog, then saves the .torrent file wherever the user requests // Shows a Save File dialog, then saves the .torrent file wherever the user requests
saveTorrentFileAs (torrentKey) { saveTorrentFileAs (torrentKey) {
const torrentSummary = TorrentSummary.getByKey(this.state, 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 downloadPath = this.state.saved.prefs.downloadPath
const newFileName = path.parse(torrentSummary.name).name + '.torrent' const newFileName = path.parse(torrentSummary.name).name + '.torrent'
const win = electron.remote.getCurrentWindow() const win = remote.getCurrentWindow()
const opts = { const opts = {
title: 'Save Torrent File', title: 'Save Torrent File',
defaultPath: path.join(downloadPath, newFileName), defaultPath: path.join(downloadPath, newFileName),
filters: [ filters: [
{ name: 'Torrent Files', extensions: ['torrent'] }, { name: 'Torrent Files', extensions: ['torrent'] },
{ name: 'All Files', extensions: ['*'] } { name: 'All Files', extensions: ['*'] }
] ],
buttonLabel: 'Save'
} }
electron.remote.dialog.showSaveDialog(win, opts, function (savePath) { const savePath = remote.dialog.showSaveDialogSync(win, opts)
console.log('Saving torrent ' + torrentKey + ' to ' + savePath)
if (!savePath) return // They clicked Cancel if (!savePath) return // They clicked Cancel
const torrentPath = TorrentSummary.getTorrentPath(torrentSummary) console.log('Saving torrent ' + torrentKey + ' to ' + savePath)
fs.readFile(torrentPath, function (err, torrentFile) { const torrentPath = TorrentSummary.getTorrentPath(torrentSummary)
fs.readFile(torrentPath, (err, torrentFile) => {
if (err) return dispatch('error', err)
fs.writeFile(savePath, torrentFile, err => {
if (err) return dispatch('error', err) if (err) return dispatch('error', err)
fs.writeFile(savePath, torrentFile, function (err) {
if (err) return dispatch('error', err)
})
}) })
}) })
} }
@@ -259,16 +344,16 @@ module.exports = class TorrentListController {
// Recursively finds {name, path, size} for all files in a folder // Recursively finds {name, path, size} for all files in a folder
// Calls `cb` on success, calls `onError` on failure // Calls `cb` on success, calls `onError` on failure
function findFilesRecursive (paths, cb) { function findFilesRecursive (paths, cb_) {
if (paths.length > 1) { if (paths.length > 1) {
let numComplete = 0 let numComplete = 0
let ret = [] const ret = []
paths.forEach(function (path) { paths.forEach(path => {
findFilesRecursive([path], function (fileObjs) { findFilesRecursive([path], fileObjs => {
ret.push(...fileObjs) ret.push(...fileObjs)
if (++numComplete === paths.length) { if (++numComplete === paths.length) {
ret.sort((a, b) => a.path < b.path ? -1 : a.path > b.path) ret.sort((a, b) => a.path < b.path ? -1 : Number(a.path > b.path))
cb(ret) cb_(ret)
} }
}) })
}) })
@@ -276,13 +361,13 @@ function findFilesRecursive (paths, cb) {
} }
const fileOrFolder = paths[0] const fileOrFolder = paths[0]
fs.stat(fileOrFolder, function (err, stat) { fs.stat(fileOrFolder, (err, stat) => {
if (err) return dispatch('error', err) if (err) return dispatch('error', err)
// Files: return name, path, and size // Files: return name, path, and size
if (!stat.isDirectory()) { if (!stat.isDirectory()) {
const filePath = fileOrFolder const filePath = fileOrFolder
return cb([{ return cb_([{
name: path.basename(filePath), name: path.basename(filePath),
path: filePath, path: filePath,
size: stat.size size: stat.size
@@ -291,17 +376,17 @@ function findFilesRecursive (paths, cb) {
// Folders: recurse, make a list of all the files // Folders: recurse, make a list of all the files
const folderPath = fileOrFolder const folderPath = fileOrFolder
fs.readdir(folderPath, function (err, fileNames) { fs.readdir(folderPath, (err, fileNames) => {
if (err) return dispatch('error', err) if (err) return dispatch('error', err)
const paths = fileNames.map((fileName) => path.join(folderPath, fileName)) const paths = fileNames.map((fileName) => path.join(folderPath, fileName))
findFilesRecursive(paths, cb) findFilesRecursive(paths, cb_)
}) })
}) })
} }
function deleteFile (path) { function deleteFile (path) {
if (!path) return if (!path) return
fs.unlink(path, function (err) { fs.unlink(path, err => {
if (err) dispatch('error', err) if (err) dispatch('error', err)
}) })
} }
@@ -315,3 +400,14 @@ function moveItemToTrash (torrentSummary) {
function showItemInFolder (torrentSummary) { function showItemInFolder (torrentSummary) {
ipcRenderer.send('showItemInFolder', TorrentSummary.getFileOrFolder(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 // Controls the UI checking for new versions of the app, prompting install
module.exports = class UpdateController { module.exports = class UpdateController {
@@ -13,7 +13,7 @@ module.exports = class UpdateController {
console.log('new version skipped by user: v' + version) console.log('new version skipped by user: v' + version)
return 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 // Don't show the modal again until the next version

View File

@@ -13,8 +13,10 @@ module.exports = {
setRate setRate
} }
const http = require('http')
const config = require('../../config') const config = require('../../config')
const {CastingError} = require('./errors') const { CastingError } = require('./errors')
// Lazy load these for a ~300ms improvement in startup time // Lazy load these for a ~300ms improvement in startup time
let airplayer, chromecasts, dlnacasts let airplayer, chromecasts, dlnacasts
@@ -52,17 +54,17 @@ function init (appState, callback) {
state.devices.airplay = airplayPlayer() state.devices.airplay = airplayPlayer()
// Listen for devices: Chromecast, DLNA and Airplay // Listen for devices: Chromecast, DLNA and Airplay
chromecasts.on('update', function (device) { chromecasts.on('update', device => {
// TODO: how do we tell if there are *no longer* any Chromecasts available? // TODO: how do we tell if there are *no longer* any Chromecasts available?
// From looking at the code, chromecasts.players only grows, never shrinks // From looking at the code, chromecasts.players only grows, never shrinks
state.devices.chromecast.addDevice(device) state.devices.chromecast.addDevice(device)
}) })
dlnacasts.on('update', function (device) { dlnacasts.on('update', device => {
state.devices.dlna.addDevice(device) state.devices.dlna.addDevice(device)
}) })
airplayer.on('update', function (device) { airplayer.on('update', device => {
state.devices.airplay.addDevice(device) state.devices.airplay.addDevice(device)
}) })
} }
@@ -81,7 +83,7 @@ function testPlayer (type) {
} }
function getDevices () { function getDevices () {
return [{name: type + '-1'}, {name: type + '-2'}] return [{ name: type + '-1' }, { name: type + '-2' }]
} }
function open () {} function open () {}
@@ -114,7 +116,7 @@ function chromecastPlayer () {
} }
function addDevice (device) { function addDevice (device) {
device.on('error', function (err) { device.on('error', err => {
if (device !== ret.device) return if (device !== ret.device) return
state.playing.location = 'local' state.playing.location = 'local'
state.errors.push({ state.errors.push({
@@ -123,29 +125,54 @@ function chromecastPlayer () {
}) })
update() update()
}) })
device.on('disconnect', function () { device.on('disconnect', () => {
if (device !== ret.device) return if (device !== ret.device) return
state.playing.location = 'local' state.playing.location = 'local'
update() update()
}) })
} }
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.slice(21), 'base64'))
}).listen(0, () => {
const port = ret.subServer.address().port
const subtitlesUrl = 'http://' + state.server.networkAddress + ':' + port + '/'
callback(subtitlesUrl)
})
}
}
function open () { function open () {
const torrentSummary = state.saved.torrents.find((x) => x.infoHash === state.playing.infoHash) const torrentSummary = state.saved.torrents.find((x) => x.infoHash === state.playing.infoHash)
ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, { serveSubtitles(subtitlesUrl => {
type: 'video/mp4', ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, {
title: config.APP_NAME + ' - ' + torrentSummary.name type: 'video/mp4',
}, function (err) { title: config.APP_NAME + ' - ' + torrentSummary.name,
if (err) { subtitles: subtitlesUrl ? [subtitlesUrl] : [],
state.playing.location = 'local' autoSubtitles: !!subtitlesUrl
state.errors.push({ }, err => {
time: new Date().getTime(), if (err) {
message: 'Could not connect to Chromecast. ' + err.message state.playing.location = 'local'
}) state.errors.push({
} else { time: new Date().getTime(),
state.playing.location = 'chromecast' message: 'Could not connect to Chromecast. ' + err.message
} })
update() } else {
state.playing.location = 'chromecast'
}
update()
})
}) })
} }
@@ -159,6 +186,9 @@ function chromecastPlayer () {
function stop (callback) { function stop (callback) {
ret.device.stop(callback) ret.device.stop(callback)
if (ret.subServer) {
ret.subServer.close()
}
} }
function status () { function status () {
@@ -191,7 +221,7 @@ function airplayPlayer () {
return ret return ret
function addDevice (player) { function addDevice (player) {
player.on('event', function (event) { player.on('event', event => {
switch (event.state) { switch (event.state) {
case 'loading': case 'loading':
break break
@@ -213,7 +243,7 @@ function airplayPlayer () {
} }
function open () { function open () {
ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, function (err, res) { ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, (err, res) => {
if (err) { if (err) {
state.playing.location = 'local' state.playing.location = 'local'
state.errors.push({ state.errors.push({
@@ -240,7 +270,7 @@ function airplayPlayer () {
} }
function status () { function status () {
ret.device.playbackInfo(function (err, res, status) { ret.device.playbackInfo((err, res, status) => {
if (err) { if (err) {
state.playing.location = 'local' state.playing.location = 'local'
state.errors.push({ state.errors.push({
@@ -287,7 +317,7 @@ function dlnaPlayer (player) {
} }
function addDevice (device) { function addDevice (device) {
device.on('error', function (err) { device.on('error', err => {
if (device !== ret.device) return if (device !== ret.device) return
state.playing.location = 'local' state.playing.location = 'local'
state.errors.push({ state.errors.push({
@@ -296,7 +326,7 @@ function dlnaPlayer (player) {
}) })
update() update()
}) })
device.on('disconnect', function () { device.on('disconnect', () => {
if (device !== ret.device) return if (device !== ret.device) return
state.playing.location = 'local' state.playing.location = 'local'
update() update()
@@ -309,7 +339,7 @@ function dlnaPlayer (player) {
type: 'video/mp4', type: 'video/mp4',
title: config.APP_NAME + ' - ' + torrentSummary.name, title: config.APP_NAME + ' - ' + torrentSummary.name,
seek: state.playing.currentTime > 10 ? state.playing.currentTime : 0 seek: state.playing.currentTime > 10 ? state.playing.currentTime : 0
}, function (err) { }, err => {
if (err) { if (err) {
state.playing.location = 'local' state.playing.location = 'local'
state.errors.push({ state.errors.push({
@@ -344,7 +374,7 @@ function dlnaPlayer (player) {
} }
function volume (volume, callback) { function volume (volume, callback) {
ret.device.volume(volume, function (err) { ret.device.volume(volume, err => {
// quick volume update // quick volume update
state.playing.volume = volume state.playing.volume = volume
callback(err) callback(err)
@@ -366,7 +396,7 @@ function handleStatus (err, status) {
// Start polling cast device state, whenever we're connected // Start polling cast device state, whenever we're connected
function startStatusInterval () { function startStatusInterval () {
statusInterval = setInterval(function () { statusInterval = setInterval(() => {
const player = getPlayer() const player = getPlayer()
if (player) player.status() if (player) player.status()
}, 1000) }, 1000)
@@ -388,7 +418,8 @@ function toggleMenu (location) {
if (state.playing.location !== 'local') { if (state.playing.location !== 'local') {
throw new CastingError( throw new CastingError(
`You can't connect to ${location} when already connected to another device` `You can't connect to ${location} when already connected to another device`
) } )
}
// Find all cast devices of the given type // Find all cast devices of the given type
const player = getPlayer(location) const player = getPlayer(location)
@@ -398,11 +429,11 @@ function toggleMenu (location) {
} }
// Show a menu // Show a menu
state.devices.castMenu = {location, devices} state.devices.castMenu = { location, devices }
} }
function selectDevice (index) { function selectDevice (index) {
const {location, devices} = state.devices.castMenu const { location, devices } = state.devices.castMenu
// Start casting // Start casting
const player = getPlayer(location) const player = getPlayer(location)
@@ -423,7 +454,7 @@ function selectDevice (index) {
function stop () { function stop () {
const player = getPlayer() const player = getPlayer()
if (player) { if (player) {
player.stop(function () { player.stop(() => {
player.device = null player.device = null
stoppedCasting() stoppedCasting()
}) })
@@ -491,6 +522,6 @@ function setVolume (volume) {
if (player) player.volume(volume, castCallback) if (player) player.volume(volume, castCallback)
} }
function castCallback () { function castCallback (...args) {
console.log('%s callback: %o', state.playing.location, arguments) console.log('%s callback: %o', state.playing.location, args)
} }

View File

@@ -5,7 +5,7 @@ module.exports = {
} }
const dispatchers = {} const dispatchers = {}
let _dispatch = function () {} let _dispatch = () => {}
function setDispatch (dispatch) { function setDispatch (dispatch) {
_dispatch = dispatch _dispatch = dispatch
@@ -23,7 +23,7 @@ function dispatcher (...args) {
const str = JSON.stringify(args) const str = JSON.stringify(args)
let handler = dispatchers[str] let handler = dispatchers[str]
if (!handler) { if (!handler) {
handler = dispatchers[str] = function (e) { handler = dispatchers[str] = e => {
// Do not propagate click to elements below the button // Do not propagate click to elements below the button
e.stopPropagation() e.stopPropagation()

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', '.m2ts'],
image: ['.gif', '.jpg', '.jpeg', '.png']
}
module.exports = mediaExtensions

View File

@@ -28,16 +28,31 @@ function run (state) {
if (semver.lt(version, '0.14.0')) migrate_0_14_0(saved) if (semver.lt(version, '0.14.0')) migrate_0_14_0(saved)
if (semver.lt(version, '0.17.0')) migrate_0_17_0(saved) if (semver.lt(version, '0.17.0')) migrate_0_17_0(saved)
if (semver.lt(version, '0.17.2')) migrate_0_17_2(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)
if (semver.lt(version, config.APP_VERSION)) {
installHandlers(state.saved)
}
// Config is now on the new version // Config is now on the new version
state.saved.version = config.APP_VERSION state.saved.version = config.APP_VERSION
} }
// Whenever the app is updated, re-install default handlers if the user has
// enabled them.
function installHandlers (saved) {
if (saved.prefs.isFileHandler) {
const ipcRenderer = require('electron').ipcRenderer
ipcRenderer.send('setDefaultFileHandler', true)
}
}
function migrate_0_7_0 (saved) { function migrate_0_7_0 (saved) {
const cpFile = require('cp-file') const { copyFileSync } = require('fs')
const path = require('path') const path = require('path')
saved.torrents.forEach(function (ts) { saved.torrents.forEach(ts => {
const infoHash = ts.infoHash const infoHash = ts.infoHash
// Replace torrentPath with torrentFileName // Replace torrentPath with torrentFileName
@@ -56,7 +71,7 @@ function migrate_0_7_0 (saved) {
dst = path.join(config.TORRENT_PATH, infoHash + '.torrent') dst = path.join(config.TORRENT_PATH, infoHash + '.torrent')
// Synchronous FS calls aren't ideal, but probably OK in a migration // Synchronous FS calls aren't ideal, but probably OK in a migration
// that only runs once // that only runs once
if (src !== dst) cpFile.sync(src, dst) if (src !== dst) copyFileSync(src, dst)
delete ts.torrentPath delete ts.torrentPath
ts.torrentFileName = infoHash + '.torrent' ts.torrentFileName = infoHash + '.torrent'
@@ -71,14 +86,14 @@ function migrate_0_7_0 (saved) {
dst = path.join(config.POSTER_PATH, infoHash + extension) dst = path.join(config.POSTER_PATH, infoHash + extension)
// Synchronous FS calls aren't ideal, but probably OK in a migration // Synchronous FS calls aren't ideal, but probably OK in a migration
// that only runs once // that only runs once
if (src !== dst) cpFile.sync(src, dst) if (src !== dst) copyFileSync(src, dst)
delete ts.posterURL delete ts.posterURL
ts.posterFileName = infoHash + extension ts.posterFileName = infoHash + extension
} }
// Fix exception caused by incorrect file ordering. // 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.defaultPlayFileIndex
delete ts.files delete ts.files
delete ts.selections delete ts.selections
@@ -111,7 +126,7 @@ function migrate_0_12_0 (saved) {
// Undo a terrible bug where clicking Play on a default torrent on a fresh // Undo a terrible bug where clicking Play on a default torrent on a fresh
// install results in a "path missing" error // 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 = [ const defaultTorrentFiles = [
'6a9759bffd5c0af65319979fb7832189f4f3c35d.torrent', '6a9759bffd5c0af65319979fb7832189f4f3c35d.torrent',
'88594aaacbde40ef3e2510c47374ec0aa396c08e.torrent', '88594aaacbde40ef3e2510c47374ec0aa396c08e.torrent',
@@ -119,7 +134,7 @@ function migrate_0_12_0 (saved) {
'02767050e0be2fd4db9a2ad6c12416ac806ed6ed.torrent', '02767050e0be2fd4db9a2ad6c12416ac806ed6ed.torrent',
'3ba219a8634bf7bae3d848192b2da75ae995589d.torrent' '3ba219a8634bf7bae3d848192b2da75ae995589d.torrent'
] ]
saved.torrents.forEach(function (torrentSummary) { saved.torrents.forEach(torrentSummary => {
if (!defaultTorrentFiles.includes(torrentSummary.torrentFileName)) return if (!defaultTorrentFiles.includes(torrentSummary.torrentFileName)) return
const fileOrFolder = TorrentSummary.getFileOrFolder(torrentSummary) const fileOrFolder = TorrentSummary.getFileOrFolder(torrentSummary)
if (!fileOrFolder) return if (!fileOrFolder) return
@@ -133,16 +148,16 @@ function migrate_0_12_0 (saved) {
} }
function migrate_0_14_0 (saved) { function migrate_0_14_0 (saved) {
saved.torrents.forEach(function (ts) { saved.torrents.forEach(ts => {
delete ts.defaultPlayFileIndex delete ts.defaultPlayFileIndex
}) })
} }
function migrate_0_17_0 (saved) { function migrate_0_17_0 (saved) {
// Fix a sad, sad bug that resulted in 100MB+ config.json files // Fix a sad, sad bug that resulted in 100MB+ config.json files
saved.torrents.forEach(function (ts) { saved.torrents.forEach(ts => {
if (!ts.files) return if (!ts.files) return
ts.files.forEach(function (file) { ts.files.forEach(file => {
if (!file.audioInfo || !file.audioInfo.picture) return if (!file.audioInfo || !file.audioInfo.picture) return
// This contained a Buffer, which 30x'd in size when serialized to JSON // This contained a Buffer, which 30x'd in size when serialized to JSON
delete file.audioInfo.picture delete file.audioInfo.picture
@@ -153,9 +168,9 @@ function migrate_0_17_0 (saved) {
function migrate_0_17_2 (saved) { function migrate_0_17_2 (saved) {
// Remove the trailing dot (.) from the Wired CD torrent name, since // 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 // 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 rimraf = require('rimraf')
const OLD_NAME = 'The WIRED CD - Rip. Sample. Mash. Share.' const OLD_NAME = 'The WIRED CD - Rip. Sample. Mash. Share.'
@@ -164,9 +179,7 @@ function migrate_0_17_2 (saved) {
const OLD_HASH = '3ba219a8634bf7bae3d848192b2da75ae995589d' const OLD_HASH = '3ba219a8634bf7bae3d848192b2da75ae995589d'
const NEW_HASH = 'a88fda5954e89178c372716a6a78b8180ed4dad3' const NEW_HASH = 'a88fda5954e89178c372716a6a78b8180ed4dad3'
const ts = saved.torrents.find((ts) => { const ts = saved.torrents.find(ts => ts.infoHash === OLD_HASH)
return ts.infoHash === OLD_HASH
})
if (!ts) return // Wired CD torrent does not exist if (!ts) return // Wired CD torrent does not exist
@@ -190,7 +203,7 @@ function migrate_0_17_2 (saved) {
ts.posterFileName = NEW_HASH + '.jpg' ts.posterFileName = NEW_HASH + '.jpg'
rimraf.sync(path.join(config.TORRENT_PATH, ts.torrentFileName)) rimraf.sync(path.join(config.TORRENT_PATH, ts.torrentFileName))
cpFile.sync( copyFileSync(
path.join(config.STATIC_PATH, 'wiredCd.torrent'), path.join(config.STATIC_PATH, 'wiredCd.torrent'),
path.join(config.TORRENT_PATH, NEW_HASH + '.torrent') path.join(config.TORRENT_PATH, NEW_HASH + '.torrent')
) )
@@ -206,3 +219,16 @@ function migrate_0_17_2 (saved) {
} catch (err) {} } 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) { function getCurrentLocalURL (state) {
return state.server 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 = { module.exports = {
init,
play play
} }
const config = require('../../config') const config = require('../../config')
const {InvalidSoundNameError} = require('./errors') const { InvalidSoundNameError } = require('./errors')
const path = require('path') const path = require('path')
const VOLUME = 0.25 const VOLUME = 0.25
// App state to access the soundNotifications preference
let state
/* Cache of Audio elements, for instant playback */ /* Cache of Audio elements, for instant playback */
const cache = {} const cache = {}
@@ -46,7 +50,19 @@ const sounds = {
} }
} }
function init (appState) {
state = appState
}
function play (name) { function play (name) {
if (state == null) {
return
}
if (!state.saved.prefs.soundNotifications) {
return
}
let audio = cache[name] let audio = cache[name]
if (!audio) { if (!audio) {
const sound = sounds[name] const sound = sounds[name]

View File

@@ -1,8 +1,9 @@
const appConfig = require('application-config')('WebTorrent') const appConfig = require('application-config')('WebTorrent')
const path = require('path') const path = require('path')
const {EventEmitter} = require('events') const { EventEmitter } = require('events')
const config = require('../../config') const config = require('../../config')
const defaultAnnounceList = require('create-torrent').announceList.map((arr) => arr[0])
const SAVE_DEBOUNCE_INTERVAL = 1000 const SAVE_DEBOUNCE_INTERVAL = 1000
@@ -12,13 +13,13 @@ const State = module.exports = Object.assign(new EventEmitter(), {
getDefaultPlayState, getDefaultPlayState,
load, load,
// state.save() calls are rate-limited. Use state.saveImmediate() to skip limit. // state.save() calls are rate-limited. Use state.saveImmediate() to skip limit.
save: function () { save (...args) {
// Perf optimization: Lazy-require debounce (and it's dependencies) // Perf optimization: Lazy-require debounce (and it's dependencies)
const debounce = require('debounce') const debounce = require('debounce')
// After first State.save() invokation, future calls go straight to the // After first State.save() invokation, future calls go straight to the
// debounced function // debounced function
State.save = debounce(saveImmediate, SAVE_DEBOUNCE_INTERVAL) State.save = debounce(saveImmediate, SAVE_DEBOUNCE_INTERVAL)
State.save(...arguments) State.save(...args)
}, },
saveImmediate saveImmediate
}) })
@@ -79,6 +80,7 @@ function getDefaultState () {
getPlayingTorrentSummary, getPlayingTorrentSummary,
getPlayingFileSummary, getPlayingFileSummary,
getExternalPlayerName, getExternalPlayerName,
getGlobalTrackers,
shouldHidePlayerControls shouldHidePlayerControls
} }
} }
@@ -88,10 +90,12 @@ function getDefaultPlayState () {
return { return {
infoHash: null, /* the info hash of the torrent we're playing */ infoHash: null, /* the info hash of the torrent we're playing */
fileIndex: null, /* the zero-based index within the torrent */ fileIndex: null, /* the zero-based index within the torrent */
fileName: null, /* name of the file that is playing */
location: 'local', /* 'local', 'chromecast', 'airplay' */ location: 'local', /* 'local', 'chromecast', 'airplay' */
type: null, /* 'audio' or 'video', could be 'other' if ever support eg streaming to VLC */ type: null, /* 'audio' or 'video', could be 'other' if ever support eg streaming to VLC */
currentTime: 0, /* seconds */ currentTime: 0, /* seconds */
duration: 1, /* seconds */ duration: 1, /* seconds */
isReady: false,
isPaused: true, isPaused: true,
isStalled: false, isStalled: false,
lastTimeUpdate: 0, /* Unix time in ms */ lastTimeUpdate: 0, /* Unix time in ms */
@@ -103,55 +107,60 @@ function getDefaultPlayState () {
selectedIndex: -1, /* current subtitle track */ selectedIndex: -1, /* current subtitle track */
showMenu: false /* popover menu, above the video */ 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 */ aspectRatio: 0 /* aspect ratio of the video */
} }
} }
/* If the saved state file doesn't exist yet, here's what we use instead */ /* If the saved state file doesn't exist yet, here's what we use instead */
function setupStateSaved (cb) { function setupStateSaved () {
const cpFile = require('cp-file') const { copyFileSync, mkdirSync, readFileSync } = require('fs')
const fs = require('fs')
const parseTorrent = require('parse-torrent') const parseTorrent = require('parse-torrent')
const parallel = require('run-parallel')
const saved = { const saved = {
prefs: { prefs: {
downloadPath: config.DEFAULT_DOWNLOAD_PATH, downloadPath: config.DEFAULT_DOWNLOAD_PATH,
isFileHandler: false, isFileHandler: false,
openExternalPlayer: false, openExternalPlayer: false,
externalPlayerPath: null, externalPlayerPath: '',
startup: false startup: false,
soundNotifications: true,
autoAddTorrents: false,
torrentsFolderPath: '',
highestPlaybackPriority: true,
globalTrackers: defaultAnnounceList
}, },
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject), torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
torrentsToResume: [],
version: config.APP_VERSION /* make sure we can upgrade gracefully later */ 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 const infoHash = saved.torrents[i].infoHash
tasks.push(function (cb) { // TODO: Doing several sync calls during first startup is not ideal
cpFile( copyFileSync(
path.join(config.STATIC_PATH, t.posterFileName), path.join(config.STATIC_PATH, t.posterFileName),
path.join(config.POSTER_PATH, infoHash + path.extname(t.posterFileName)) path.join(config.POSTER_PATH, infoHash + path.extname(t.posterFileName))
).then(cb).catch(cb) )
}) copyFileSync(
tasks.push(function (cb) { path.join(config.STATIC_PATH, t.torrentFileName),
cpFile( path.join(config.TORRENT_PATH, infoHash + '.torrent')
path.join(config.STATIC_PATH, t.torrentFileName), )
path.join(config.TORRENT_PATH, infoHash + '.torrent')
).then(cb).catch(cb)
})
}) })
parallel(tasks, function (err) { return saved
if (err) return cb(err)
cb(null, saved)
})
function createTorrentObject (t) { function createTorrentObject (t) {
// TODO: Doing several fs.readFileSync calls during first startup is not ideal // TODO: Doing several sync calls during first startup is not ideal
const torrent = fs.readFileSync(path.join(config.STATIC_PATH, t.torrentFileName)) const torrent = readFileSync(path.join(config.STATIC_PATH, t.torrentFileName))
const parsedTorrent = parseTorrent(torrent) const parsedTorrent = parseTorrent(torrent)
return { return {
@@ -192,19 +201,31 @@ function shouldHidePlayerControls () {
new Date().getTime() - this.playing.mouseStationarySince > 2000 && new Date().getTime() - this.playing.mouseStationarySince > 2000 &&
!this.playing.mouseInControls && !this.playing.mouseInControls &&
!this.playing.isPaused && !this.playing.isPaused &&
this.playing.location === 'local' && this.playing.location === 'local'
this.playing.playbackRate === 1
} }
function load (cb) { function getGlobalTrackers () {
appConfig.read(function (err, saved) { const trackers = this.saved.prefs.globalTrackers
if (err || !saved.version) { if (!trackers) {
console.log('Missing config file: Creating new one') return defaultAnnounceList
setupStateSaved(onSavedState) }
} else { return trackers
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) { function onSavedState (err, saved) {
if (err) return cb(err) if (err) return cb(err)
@@ -222,7 +243,7 @@ function load (cb) {
} }
// Write state.saved to the JSON state file // Write state.saved to the JSON state file
function saveImmediate (state, cb) { async function saveImmediate (state, cb) {
console.log('Saving state to ' + appConfig.filePath) console.log('Saving state to ' + appConfig.filePath)
// Clean up, so that we're not saving any pending state // Clean up, so that we're not saving any pending state
@@ -231,9 +252,9 @@ function saveImmediate (state, cb) {
// reading the torrent file or file(s) to seed & don't have an infohash // reading the torrent file or file(s) to seed & don't have an infohash
copy.torrents = copy.torrents copy.torrents = copy.torrents
.filter((x) => x.infoHash) .filter((x) => x.infoHash)
.map(function (x) { .map(x => {
const torrent = {} const torrent = {}
for (let key in x) { for (const key in x) {
if (key === 'progress' || key === 'torrentKey') { if (key === 'progress' || key === 'torrentKey') {
continue // Don't save progress info or key for the webtorrent process continue // Don't save progress info or key for the webtorrent process
} }
@@ -245,8 +266,10 @@ function saveImmediate (state, cb) {
return torrent return torrent
}) })
appConfig.write(copy, (err) => { try {
if (err) console.error(err) await appConfig.write(copy)
else State.emit('stateSaved') State.emit('stateSaved')
}) } catch (err) {
console.error(err)
}
} }

View File

@@ -7,7 +7,7 @@ module.exports = {
logPlayAttempt logPlayAttempt
} }
const electron = require('electron') const remote = require('@electron/remote')
const config = require('../../config') const config = require('../../config')
@@ -50,7 +50,7 @@ function send (state) {
json: true json: true
} }
get.post(opts, function (err, res) { get.post(opts, (err, res) => {
if (err) return console.error('Error sending telemetry', err) if (err) return console.error('Error sending telemetry', err)
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
return console.error(`Error sending telemetry, status code: ${res.statusCode}`) return console.error(`Error sending telemetry, status code: ${res.statusCode}`)
@@ -66,15 +66,15 @@ function reset () {
minVersion: config.APP_VERSION, minVersion: config.APP_VERSION,
total: 0, total: 0,
success: 0, success: 0,
timeout: 0,
error: 0, error: 0,
external: 0,
abandoned: 0 abandoned: 0
} }
} }
// Track screen resolution // Track screen resolution
function getScreenInfo () { function getScreenInfo () {
return electron.screen.getAllDisplays().map((screen) => ({ return remote.screen.getAllDisplays().map((screen) => ({
width: screen.size.width, width: screen.size.width,
height: screen.size.height, height: screen.size.height,
scaleFactor: screen.scaleFactor scaleFactor: screen.scaleFactor
@@ -98,7 +98,7 @@ function getSystemInfo () {
function getTorrentStats (state) { function getTorrentStats (state) {
const count = state.saved.torrents.length const count = state.saved.torrents.length
let sizeMB = 0 let sizeMB = 0
let byStatus = { const byStatus = {
new: { count: 0, sizeMB: 0 }, new: { count: 0, sizeMB: 0 },
downloading: { count: 0, sizeMB: 0 }, downloading: { count: 0, sizeMB: 0 },
seeding: { count: 0, sizeMB: 0 }, seeding: { count: 0, sizeMB: 0 },
@@ -106,22 +106,21 @@ function getTorrentStats (state) {
} }
// First, count torrents & total file size // First, count torrents & total file size
for (let i = 0; i < count; i++) { for (const torrent of state.saved.torrents) {
const t = state.saved.torrents[i] const stat = byStatus[torrent.status]
const stat = byStatus[t.status] if (!torrent || !torrent.files || !stat) continue
if (!t || !t.files || !stat) continue
stat.count++ stat.count++
for (let j = 0; j < t.files.length; j++) {
const f = t.files[j] for (const file of torrent.files) {
if (!f || !f.length) continue if (!file || !file.length) continue
const fileSizeMB = f.length / (1 << 20) const fileSizeMB = file.length / (1 << 20)
sizeMB += fileSizeMB sizeMB += fileSizeMB
stat.sizeMB += fileSizeMB stat.sizeMB += fileSizeMB
} }
} }
// Then, round all the counts and sums to the nearest power of 2 // 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 = { ret.byStatus = {
new: roundTorrentStats(byStatus.new), new: roundTorrentStats(byStatus.new),
downloading: roundTorrentStats(byStatus.downloading), downloading: roundTorrentStats(byStatus.downloading),
@@ -145,7 +144,7 @@ function roundPow2 (n) {
if (n <= 0) return 0 if (n <= 0) return 0
// Otherwise, return 1, 2, 4, 8, etc by rounding in log space // Otherwise, return 1, 2, 4, 8, etc by rounding in log space
const log2 = Math.log(n) / Math.log(2) const log2 = Math.log(n) / Math.log(2)
return Math.pow(2, Math.round(log2)) return 2 ** Math.round(log2)
} }
// An uncaught error happened in the main process or in one of the windows // An uncaught error happened in the main process or in one of the windows
@@ -198,7 +197,7 @@ function logUncaughtError (procName, e) {
// Log the app version *at the time of the error* // Log the app version *at the time of the error*
const version = config.APP_VERSION 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" // Turns a DOM element into a string, eg "DIV.my-class.visible"
@@ -210,10 +209,10 @@ function getElemString (elem) {
return ret return ret
} }
// The user pressed play. It either worked, timed out, or showed the // The user pressed play. Did it work, display an error,
// 'Play in VLC' codec error // open an external player or did user abandon the attempt?
function logPlayAttempt (result) { 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) return console.error('Unknown play attempt result', result)
} }

20
src/renderer/lib/time.js Normal file
View File

@@ -0,0 +1,20 @@
module.exports = {
calculateEta
}
function calculateEta (missing, downloadSpeed) {
if (downloadSpeed === 0 || missing === 0) return
const rawEta = missing / downloadSpeed
const hours = Math.floor(rawEta / 3600) % 24
const minutes = Math.floor(rawEta / 60) % 60
const seconds = Math.floor(rawEta % 60)
// Only display hours and minutes if they are greater than 0 but always
// display minutes if hours is being displayed
const hoursStr = hours ? hours + ' h' : ''
const minutesStr = (hours || minutes) ? minutes + ' min' : ''
const secondsStr = seconds + ' s'
return `${hoursStr} ${minutesStr} ${secondsStr} remaining`
}

View File

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

View File

@@ -3,39 +3,132 @@ module.exports = torrentPoster
const captureFrame = require('capture-frame') const captureFrame = require('capture-frame')
const path = require('path') 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) { function torrentPoster (torrent, cb) {
// First, try to use a poster image if available // First, try to use a poster image if available
const posterFile = torrent.files.filter(function (file) { const posterFile = torrent.files.filter(file => /^poster\.(jpg|png|gif)$/.test(file.name))[0]
return /^poster\.(jpg|png|gif)$/.test(file.name) if (posterFile) return extractPoster(posterFile, cb)
})[0]
if (posterFile) return torrentPosterFromImage(posterFile, torrent, cb)
// Second, try to use the largest video file // 'score' each media type based on total size present in torrent
// Filter out file formats that the <video> tag definitely can't play const bestScore = ['audio', 'video', 'image'].map(mediaType => ({
const videoFile = getLargestFileByExtension(torrent, ['.mp4', '.m4v', '.webm', '.mov', '.mkv']) type: mediaType,
if (videoFile) return torrentPosterFromVideo(videoFile, torrent, cb) size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType])
})).sort((a, b) => b.size - a.size)[0] // sort descending on size
// Third, try to use the largest image file if (bestScore.size === 0) {
const imgFile = getLargestFileByExtension(torrent, ['.gif', '.jpg', '.jpeg', '.png']) // Admit defeat, no video, audio or image had a significant presence
if (imgFile) return torrentPosterFromImage(imgFile, torrent, cb) return cb(new Error(msgNoSuitablePoster))
}
// TODO: generate a waveform from the largest sound file // Based on which media type is dominant we select the corresponding poster function
// Finally, admit defeat switch (bestScore.type) {
return cb(new Error('Cannot generate a poster from any files in the torrent')) 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) => 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) { function getLargestFileByExtension (torrent, extensions) {
const files = torrent.files.filter(function (file) { const files = filterOnExtension(torrent, extensions)
if (files.length === 0) return undefined
return files.reduce((a, b) => a.length > b.length ? a : b)
}
/**
* Filter file on a list extension, can be used to find al image files
* @param torrent Torrent to filter files from
* @param extensions File extensions to filter on
* @returns {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() const extname = path.extname(file.name).toLowerCase()
return extensions.indexOf(extname) !== -1 return extensions.indexOf(extname) !== -1
}) })
if (files.length === 0) return undefined
return files.reduce(function (a, b) {
return a.length > b.length ? a : b
})
} }
function torrentPosterFromVideo (file, torrent, cb) { /**
* 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 => ({
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 index = torrent.files.indexOf(file)
const server = torrent.createServer(0) const server = torrent.createServer(0)
@@ -61,7 +154,8 @@ function torrentPosterFromVideo (file, torrent, cb) {
function onSeeked () { function onSeeked () {
video.removeEventListener('seeked', onSeeked) video.removeEventListener('seeked', onSeeked)
const buf = captureFrame(video) const frame = captureFrame(video)
const buf = frame && frame.image
// unload video element // unload video element
video.pause() video.pause()
@@ -70,14 +164,19 @@ function torrentPosterFromVideo (file, torrent, cb) {
server.destroy() 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') cb(null, buf, '.jpg')
} }
} }
} }
function torrentPosterFromImage (file, torrent, cb) { function torrentPosterFromImage (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.image)
extractPoster(file, cb)
}
function extractPoster (file, cb) {
const extname = path.extname(file.name) const extname = path.extname(file.name)
file.getBuffer((err, buf) => cb(err, buf, extname)) file.getBuffer((err, buf) => cb(err, buf, extname))
} }

View File

@@ -1,27 +1,19 @@
/** /**
* Perf optimization: Hook into require() to modify how certain modules load: * 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 * - `inline-style-prefixer` (used by `material-ui`) takes ~40ms. It is not
* actually used because auto-prefixing is disabled with * actually used because auto-prefixing is disabled with
* `darkBaseTheme.userAgent = false`. Return a fake object. * `darkBaseTheme.userAgent = false`. Return a fake object.
*/ */
let Module = require('module') const Module = require('module')
const _require = Module.prototype.require const _require = Module.prototype.require
Module.prototype.require = function (id) { Module.prototype.require = function (id) {
if (id === 'lodash/merge') id = 'lodash.merge'
if (id === 'inline-style-prefixer') return {} if (id === 'inline-style-prefixer') return {}
return _require.apply(this, arguments) return _require.apply(this, arguments)
} }
console.time('init') console.time('init')
const crashReporter = require('../crash-reporter')
crashReporter.init()
// Perf optimization: Start asynchronously read on config file before all the // Perf optimization: Start asynchronously read on config file before all the
// blocking require() calls below. // blocking require() calls below.
@@ -29,6 +21,7 @@ const State = require('./lib/state')
State.load(onState) State.load(onState)
const createGetter = require('fn-getter') const createGetter = require('fn-getter')
const debounce = require('debounce')
const dragDrop = require('drag-drop') const dragDrop = require('drag-drop')
const electron = require('electron') const electron = require('electron')
const fs = require('fs') const fs = require('fs')
@@ -43,9 +36,6 @@ const TorrentPlayer = require('./lib/torrent-player')
// Perf optimization: Needed immediately, so do not lazy load it below // Perf optimization: Needed immediately, so do not lazy load it below
const TorrentListController = require('./controllers/torrent-list-controller') const TorrentListController = require('./controllers/torrent-list-controller')
// Required by Material UI -- adds `onTouchTap` event
require('react-tap-event-plugin')()
const App = require('./pages/app') const App = require('./pages/app')
// Electron apps have two processes: a main process (node) runs first and starts // Electron apps have two processes: a main process (node) runs first and starts
@@ -81,6 +71,7 @@ function onState (err, _state) {
window.dispatch = dispatch window.dispatch = dispatch
telemetry.init(state) telemetry.init(state)
sound.init(state)
// Log uncaught JS errors // Log uncaught JS errors
window.addEventListener( window.addEventListener(
@@ -105,16 +96,22 @@ function onState (err, _state) {
const SubtitlesController = require('./controllers/subtitles-controller') const SubtitlesController = require('./controllers/subtitles-controller')
return new SubtitlesController(state) return new SubtitlesController(state)
}), }),
audioTracks: createGetter(() => {
const AudioTracksController = require('./controllers/audio-tracks-controller')
return new AudioTracksController(state)
}),
torrent: createGetter(() => { torrent: createGetter(() => {
const TorrentController = require('./controllers/torrent-controller') const TorrentController = require('./controllers/torrent-controller')
return new TorrentController(state) return new TorrentController(state)
}), }),
torrentList: createGetter(() => { torrentList: createGetter(() => new TorrentListController(state)),
return new TorrentListController(state)
}),
update: createGetter(() => { update: createGetter(() => {
const UpdateController = require('./controllers/update-controller') const UpdateController = require('./controllers/update-controller')
return new UpdateController(state) return new UpdateController(state)
}),
folderWatcher: createGetter(() => {
const FolderWatcherController = require('./controllers/folder-watcher-controller')
return new FolderWatcherController()
}) })
} }
@@ -127,11 +124,17 @@ function onState (err, _state) {
} }
}) })
// Give global trackers
setGlobalTrackers()
// Restart everything we were torrenting last time the app ran // Restart everything we were torrenting last time the app ran
resumeTorrents() resumeTorrents()
// Initialize ReactDOM // Initialize ReactDOM
app = ReactDOM.render(<App state={state} />, document.querySelector('#body')) ReactDOM.render(
<App state={state} ref={elem => { app = elem }} />,
document.querySelector('#body')
)
// Calling update() updates the UI given the current state // Calling update() updates the UI given the current state
// Do this at least once a second to give every file in every torrentSummary // Do this at least once a second to give every file in every torrentSummary
@@ -150,6 +153,21 @@ function onState (err, _state) {
// ...same thing if you paste a torrent // ...same thing if you paste a torrent
document.addEventListener('paste', onPaste) document.addEventListener('paste', onPaste)
// Add YouTube style hotkey shortcuts
window.addEventListener('keydown', onKeydown)
const debouncedFullscreenToggle = debounce(() => {
dispatch('toggleFullScreen')
}, 1000, true)
document.addEventListener('wheel', 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 // ...focus and blur. Needed to show correct dock icon text ('badge') in OSX
window.addEventListener('focus', onFocus) window.addEventListener('focus', onFocus)
window.addEventListener('blur', onBlur) window.addEventListener('blur', onBlur)
@@ -222,94 +240,112 @@ function updateElectron () {
const dispatchHandlers = { const dispatchHandlers = {
// Torrent list: creating, deleting, selecting torrents // Torrent list: creating, deleting, selecting torrents
'openTorrentFile': () => ipcRenderer.send('openTorrentFile'), openTorrentFile: () => ipcRenderer.send('openTorrentFile'),
'openFiles': () => ipcRenderer.send('openFiles'), /* shows the open file dialog */ openFiles: () => ipcRenderer.send('openFiles'), /* shows the open file dialog */
'openTorrentAddress': () => { state.modal = { id: 'open-torrent-address-modal' } }, openTorrentAddress: () => { state.modal = { id: 'open-torrent-address-modal' } },
'addTorrent': (torrentId) => controllers.torrentList().addTorrent(torrentId), addTorrent: (torrentId) => controllers.torrentList().addTorrent(torrentId),
'showCreateTorrent': (paths) => controllers.torrentList().showCreateTorrent(paths), showCreateTorrent: (paths) => controllers.torrentList().showCreateTorrent(paths),
'createTorrent': (options) => controllers.torrentList().createTorrent(options), createTorrent: (options) => controllers.torrentList().createTorrent(options),
'toggleTorrent': (infoHash) => controllers.torrentList().toggleTorrent(infoHash), toggleTorrent: (infoHash) => controllers.torrentList().toggleTorrent(infoHash),
'toggleTorrentFile': (infoHash, index) => pauseAllTorrents: () => controllers.torrentList().pauseAllTorrents(),
resumeAllTorrents: () => controllers.torrentList().resumeAllTorrents(),
toggleTorrentFile: (infoHash, index) =>
controllers.torrentList().toggleTorrentFile(infoHash, index), controllers.torrentList().toggleTorrentFile(infoHash, index),
'confirmDeleteTorrent': (infoHash, deleteData) => confirmDeleteTorrent: (infoHash, deleteData) =>
controllers.torrentList().confirmDeleteTorrent(infoHash, deleteData), controllers.torrentList().confirmDeleteTorrent(infoHash, deleteData),
'deleteTorrent': (infoHash, deleteData) => deleteTorrent: (infoHash, deleteData) =>
controllers.torrentList().deleteTorrent(infoHash, deleteData), controllers.torrentList().deleteTorrent(infoHash, deleteData),
'toggleSelectTorrent': (infoHash) => openTorrentListContextMenu: () => onPaste(),
confirmDeleteAllTorrents: (deleteData) =>
controllers.torrentList().confirmDeleteAllTorrents(deleteData),
deleteAllTorrents: (deleteData) =>
controllers.torrentList().deleteAllTorrents(deleteData),
toggleSelectTorrent: (infoHash) =>
controllers.torrentList().toggleSelectTorrent(infoHash), controllers.torrentList().toggleSelectTorrent(infoHash),
'openTorrentContextMenu': (infoHash) => openTorrentContextMenu: (infoHash) =>
controllers.torrentList().openTorrentContextMenu(infoHash), controllers.torrentList().openTorrentContextMenu(infoHash),
'startTorrentingSummary': (torrentKey) => startTorrentingSummary: (torrentKey) =>
controllers.torrentList().startTorrentingSummary(torrentKey), controllers.torrentList().startTorrentingSummary(torrentKey),
'saveTorrentFileAs': (torrentKey) => saveTorrentFileAs: (torrentKey) =>
controllers.torrentList().saveTorrentFileAs(torrentKey), controllers.torrentList().saveTorrentFileAs(torrentKey),
prioritizeTorrent: (infoHash) => controllers.torrentList().prioritizeTorrent(infoHash),
resumePausedTorrents: () => controllers.torrentList().resumePausedTorrents(),
// Playback // Playback
'playFile': (infoHash, index) => controllers.playback().playFile(infoHash, index), playFile: (infoHash, index) => controllers.playback().playFile(infoHash, index),
'playPause': () => controllers.playback().playPause(), playPause: () => controllers.playback().playPause(),
'nextTrack': () => controllers.playback().nextTrack(), nextTrack: () => controllers.playback().nextTrack(),
'previousTrack': () => controllers.playback().previousTrack(), previousTrack: () => controllers.playback().previousTrack(),
'skip': (time) => controllers.playback().skip(time), skip: (time) => controllers.playback().skip(time),
'skipTo': (time) => controllers.playback().skipTo(time), skipTo: (time) => controllers.playback().skipTo(time),
'changePlaybackRate': (dir) => controllers.playback().changePlaybackRate(dir), preview: (x) => controllers.playback().preview(x),
'changeVolume': (delta) => controllers.playback().changeVolume(delta), clearPreview: () => controllers.playback().clearPreview(),
'setVolume': (vol) => controllers.playback().setVolume(vol), changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir),
'openItem': (infoHash, index) => controllers.playback().openItem(infoHash, index), changeVolume: (delta) => controllers.playback().changeVolume(delta),
setVolume: (vol) => controllers.playback().setVolume(vol),
openPath: (infoHash, index) => controllers.playback().openPath(infoHash, index),
// Subtitles // Subtitles
'openSubtitles': () => controllers.subtitles().openSubtitles(), openSubtitles: () => controllers.subtitles().openSubtitles(),
'selectSubtitle': (index) => controllers.subtitles().selectSubtitle(index), selectSubtitle: (index) => controllers.subtitles().selectSubtitle(index),
'toggleSubtitlesMenu': () => controllers.subtitles().toggleSubtitlesMenu(), toggleSubtitlesMenu: () => controllers.subtitles().toggleSubtitlesMenu(),
'checkForSubtitles': () => controllers.subtitles().checkForSubtitles(), checkForSubtitles: () => controllers.subtitles().checkForSubtitles(),
'addSubtitles': (files, autoSelect) => controllers.subtitles().addSubtitles(files, autoSelect), 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 // Local media: <video>, <audio>, external players
'mediaStalled': () => controllers.media().mediaStalled(), mediaStalled: () => controllers.media().mediaStalled(),
'mediaError': (err) => controllers.media().mediaError(err), mediaError: (err) => controllers.media().mediaError(err),
'mediaSuccess': () => controllers.media().mediaSuccess(), mediaSuccess: () => controllers.media().mediaSuccess(),
'mediaTimeUpdate': () => controllers.media().mediaTimeUpdate(), mediaTimeUpdate: () => controllers.media().mediaTimeUpdate(),
'mediaMouseMoved': () => controllers.media().mediaMouseMoved(), mediaMouseMoved: () => controllers.media().mediaMouseMoved(),
'mediaControlsMouseEnter': () => controllers.media().controlsMouseEnter(), mediaControlsMouseEnter: () => controllers.media().controlsMouseEnter(),
'mediaControlsMouseLeave': () => controllers.media().controlsMouseLeave(), mediaControlsMouseLeave: () => controllers.media().controlsMouseLeave(),
'openExternalPlayer': () => controllers.media().openExternalPlayer(), openExternalPlayer: () => controllers.media().openExternalPlayer(),
'externalPlayerNotFound': () => controllers.media().externalPlayerNotFound(), externalPlayerNotFound: () => controllers.media().externalPlayerNotFound(),
// Remote casting: Chromecast, Airplay, etc // Remote casting: Chromecast, Airplay, etc
'toggleCastMenu': (deviceType) => lazyLoadCast().toggleMenu(deviceType), toggleCastMenu: (deviceType) => lazyLoadCast().toggleMenu(deviceType),
'selectCastDevice': (index) => lazyLoadCast().selectDevice(index), selectCastDevice: (index) => lazyLoadCast().selectDevice(index),
'stopCasting': () => lazyLoadCast().stop(), stopCasting: () => lazyLoadCast().stop(),
// Preferences screen // Preferences screen
'preferences': () => controllers.prefs().show(), preferences: () => controllers.prefs().show(),
'updatePreferences': (key, value) => controllers.prefs().update(key, value), updatePreferences: (key, value) => controllers.prefs().update(key, value),
'checkDownloadPath': checkDownloadPath, checkDownloadPath,
updateGlobalTrackers: (trackers) => setGlobalTrackers(trackers),
startFolderWatcher: () => controllers.folderWatcher().start(),
stopFolderWatcher: () => controllers.folderWatcher().stop(),
// Update (check for new versions on Linux, where there's no auto updater) // Update (check for new versions on Linux, where there's no auto updater)
'updateAvailable': (version) => controllers.update().updateAvailable(version), updateAvailable: (version) => controllers.update().updateAvailable(version),
'skipVersion': (version) => controllers.update().skipVersion(version), skipVersion: (version) => controllers.update().skipVersion(version),
// Navigation between screens (back, forward, ESC, etc) // Navigation between screens (back, forward, ESC, etc)
'exitModal': () => { state.modal = null }, exitModal: () => { state.modal = null },
'backToList': backToList, backToList,
'escapeBack': escapeBack, escapeBack,
'back': () => state.location.back(), back: () => state.location.back(),
'forward': () => state.location.forward(), forward: () => state.location.forward(),
'cancel': () => state.location.cancel(), cancel: () => state.location.cancel(),
// Controlling the window // Controlling the window
'setDimensions': setDimensions, setDimensions,
'toggleFullScreen': (setTo) => ipcRenderer.send('toggleFullScreen', setTo), toggleFullScreen: (setTo) => ipcRenderer.send('toggleFullScreen', setTo),
'setTitle': (title) => { state.window.title = title }, setTitle: (title) => { state.window.title = title },
'resetTitle': () => { state.window.title = config.APP_WINDOW_TITLE }, resetTitle: () => { state.window.title = config.APP_WINDOW_TITLE },
// Everything else // Everything else
'onOpen': onOpen, onOpen,
'error': onError, error: onError,
'uncaughtError': (proc, err) => telemetry.logUncaughtError(proc, err), uncaughtError: (proc, err) => telemetry.logUncaughtError(proc, err),
'stateSave': () => State.save(state), stateSave: () => State.save(state),
'stateSaveImmediate': () => State.saveImmediate(state), stateSaveImmediate: () => State.saveImmediate(state),
'update': () => {} // No-op, just trigger an update update: () => {} // No-op, just trigger an update
} }
// Events from the UI never modify state directly. Instead they call dispatch() // Events from the UI never modify state directly. Instead they call dispatch()
@@ -341,9 +377,10 @@ function setupIpc () {
ipcRenderer.on('windowBoundsChanged', onWindowBoundsChanged) ipcRenderer.on('windowBoundsChanged', onWindowBoundsChanged)
const tc = controllers.torrent() 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-metadata', (e, ...args) => tc.torrentMetadata(...args))
ipcRenderer.on('wt-done', (e, ...args) => tc.torrentDone(...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-warning', (e, ...args) => tc.torrentWarning(...args))
ipcRenderer.on('wt-error', (e, ...args) => tc.torrentError(...args)) ipcRenderer.on('wt-error', (e, ...args) => tc.torrentError(...args))
@@ -365,7 +402,7 @@ function setupIpc () {
function backToList () { function backToList () {
// Exit any modals and screens with a back button // Exit any modals and screens with a back button
state.modal = null state.modal = null
state.location.backToFirst(function () { state.location.backToFirst(() => {
// If we were already on the torrent list, scroll to the top // If we were already on the torrent list, scroll to the top
const contentTag = document.querySelector('.content') const contentTag = document.querySelector('.content')
if (contentTag) contentTag.scrollTop = 0 if (contentTag) contentTag.scrollTop = 0
@@ -383,6 +420,10 @@ function escapeBack () {
} }
} }
function setGlobalTrackers () {
controllers.torrentList().setGlobalTrackers(state.getGlobalTrackers())
}
// Starts all torrents that aren't paused on program startup // Starts all torrents that aren't paused on program startup
function resumeTorrents () { function resumeTorrents () {
state.saved.torrents state.saved.torrents
@@ -431,14 +472,21 @@ function setDimensions (dimensions) {
) )
ipcRenderer.send('setAspectRatio', aspectRatio) 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 state.playing.aspectRatio = aspectRatio
} }
// Called when the user adds files (.torrent, files to seed, subtitles) to the app // 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) // via any method (drag-drop, drag to app icon, command line)
function onOpen (files) { 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 url = state.location.url()
const allTorrents = files.every(TorrentPlayer.isTorrent) const allTorrents = files.every(TorrentPlayer.isTorrent)
@@ -475,13 +523,46 @@ function onError (err) {
update() update()
} }
const editableHtmlTags = new Set(['input', 'textarea'])
function onPaste (e) { function onPaste (e) {
if (e.target.tagName.toLowerCase() === 'input') return if (e && editableHtmlTags.has(e.target.tagName.toLowerCase())) return
controllers.torrentList().addTorrent(electron.clipboard.readText()) controllers.torrentList().addTorrent(electron.clipboard.readText())
update() update()
} }
function onKeydown (e) {
// prevent event fire on user input elements
if (editableHtmlTags.has(e.target.tagName.toLowerCase())) return
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) { function onFocus (e) {
state.window.isFocused = true state.window.isFocused = true
state.dock.badge = 0 state.dock.badge = 0
@@ -494,7 +575,7 @@ function onBlur () {
} }
function onVisibilityChange () { function onVisibilityChange () {
state.window.isVisible = !document.webkitHidden state.window.isVisible = !document.hidden
} }
function onFullscreenChanged (e, isFullScreen) { function onFullscreenChanged (e, isFullScreen) {
@@ -515,7 +596,7 @@ function onWindowBoundsChanged (e, newBounds) {
} }
function checkDownloadPath () { function checkDownloadPath () {
fs.stat(state.saved.prefs.downloadPath, function (err, stat) { fs.stat(state.saved.prefs.downloadPath, (err, stat) => {
if (err) { if (err) {
state.downloadPathStatus = 'missing' state.downloadPathStatus = 'missing'
return console.error(err) return console.error(err)

View File

@@ -12,10 +12,10 @@ const Header = require('../components/header')
const TorrentListPage = require('./torrent-list-page') const TorrentListPage = require('./torrent-list-page')
const Views = { const Views = {
'home': createGetter(() => TorrentListPage), home: createGetter(() => TorrentListPage),
'player': createGetter(() => require('./player-page')), player: createGetter(() => require('./player-page')),
'create-torrent': createGetter(() => require('./create-torrent-page')), 'create-torrent': createGetter(() => require('./create-torrent-page')),
'preferences': createGetter(() => require('./preferences-page')) preferences: createGetter(() => require('./preferences-page'))
} }
const Modals = { const Modals = {
@@ -24,7 +24,9 @@ const Modals = {
), ),
'remove-torrent-modal': createGetter(() => require('../components/remove-torrent-modal')), 'remove-torrent-modal': createGetter(() => require('../components/remove-torrent-modal')),
'update-available-modal': createGetter(() => require('../components/update-available-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' const fontFamily = process.platform === 'win32'
@@ -84,12 +86,12 @@ class App extends React.Component {
const recentErrors = state.errors.filter((x) => now - x.time < 5000) const recentErrors = state.errors.filter((x) => now - x.time < 5000)
const hasErrors = recentErrors.length > 0 const hasErrors = recentErrors.length > 0
const errorElems = recentErrors.map(function (error, i) { const errorElems = recentErrors.map((error, i) => <div key={i} className='error'>{error.message}</div>)
return (<div key={i} className='error'>{error.message}</div>)
})
return ( return (
<div key='errors' <div
className={'error-popover ' + (hasErrors ? 'visible' : 'hidden')}> key='errors'
className={'error-popover ' + (hasErrors ? 'visible' : 'hidden')}
>
<div key='title' className='title'>Error</div> <div key='title' className='title'>Error</div>
{errorElems} {errorElems}
</div> </div>

View File

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

View File

@@ -1,12 +1,14 @@
/* globals MediaMetadata */
const React = require('react') const React = require('react')
const Bitfield = require('bitfield') const BitField = require('bitfield').default
const prettyBytes = require('prettier-bytes') const prettyBytes = require('prettier-bytes')
const zeroFill = require('zero-fill')
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist') const Playlist = require('../lib/playlist')
const {dispatch, dispatcher} = require('../lib/dispatcher') const { dispatch, dispatcher } = require('../lib/dispatcher')
const config = require('../../config') const config = require('../../config')
const { calculateEta } = require('../lib/time')
// Shows a streaming video player. Standard features + Chromecast + Airplay // Shows a streaming video player. Standard features + Chromecast + Airplay
module.exports = class Player extends React.Component { module.exports = class Player extends React.Component {
@@ -20,7 +22,8 @@ module.exports = class Player extends React.Component {
<div <div
className='player' className='player'
onWheel={handleVolumeWheel} onWheel={handleVolumeWheel}
onMouseMove={dispatcher('mediaMouseMoved')}> onMouseMove={dispatcher('mediaMouseMoved')}
>
{showVideo ? renderMedia(state) : renderCastScreen(state)} {showVideo ? renderMedia(state) : renderCastScreen(state)}
{showControls ? renderPlayerControls(state) : null} {showControls ? renderPlayerControls(state) : null}
</div> </div>
@@ -34,6 +37,7 @@ module.exports = class Player extends React.Component {
tag.pause() tag.pause()
tag.src = '' tag.src = ''
tag.load() tag.load()
navigator.mediaSession.metadata = null
} }
} }
@@ -50,6 +54,28 @@ function renderMedia (state) {
// Get the <video> or <audio> tag // Get the <video> or <audio> tag
const mediaElement = document.querySelector(state.playing.type) const mediaElement = document.querySelector(state.playing.type)
if (mediaElement !== null) { if (mediaElement !== null) {
if (navigator.mediaSession.metadata === null && mediaElement.played.length !== 0) {
navigator.mediaSession.metadata = new MediaMetadata({
title: state.playing.fileName
})
navigator.mediaSession.setActionHandler('pause', () => {
dispatch('playPause')
})
navigator.mediaSession.setActionHandler('play', () => {
dispatch('playPause')
})
if (Playlist.hasNext(state)) {
navigator.mediaSession.setActionHandler('nexttrack', () => {
dispatch('nextTrack')
})
}
if (Playlist.hasPrevious(state)) {
navigator.mediaSession.setActionHandler('previoustrack', () => {
dispatch('previousTrack')
})
}
}
if (state.playing.isPaused && !mediaElement.paused) { if (state.playing.isPaused && !mediaElement.paused) {
mediaElement.pause() mediaElement.pause()
} else if (!state.playing.isPaused && mediaElement.paused) { } else if (!state.playing.isPaused && mediaElement.paused) {
@@ -95,24 +121,31 @@ function renderMedia (state) {
delete file.selectedSubtitle 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 state.playing.volume = mediaElement.volume
} }
// Add subtitles to the <video> tag // Add subtitles to the <video> tag
const trackTags = [] const trackTags = []
if (state.playing.subtitles.selectedIndex >= 0) { if (state.playing.subtitles.selectedIndex >= 0) {
for (let i = 0; i < state.playing.subtitles.tracks.length; i++) { state.playing.subtitles.tracks.forEach((track, i) => {
const track = state.playing.subtitles.tracks[i]
const isSelected = state.playing.subtitles.selectedIndex === i const isSelected = state.playing.subtitles.selectedIndex === i
trackTags.push( trackTags.push(
<track <track
key={i} key={i}
default={isSelected ? 'default' : ''} default={isSelected}
label={track.label} label={track.label}
type='subtitles' kind='subtitles'
src={track.buffer} /> src={track.buffer}
/>
) )
} })
} }
// Create the <audio> or <video> tag // Create the <audio> or <video> tag
@@ -121,13 +154,14 @@ function renderMedia (state) {
<MediaTagName <MediaTagName
src={Playlist.getCurrentLocalURL(state)} src={Playlist.getCurrentLocalURL(state)}
onDoubleClick={dispatcher('toggleFullScreen')} onDoubleClick={dispatcher('toggleFullScreen')}
onClick={dispatcher('playPause')}
onLoadedMetadata={onLoadedMetadata} onLoadedMetadata={onLoadedMetadata}
onEnded={onEnded} onEnded={onEnded}
onStalled={dispatcher('mediaStalled')} onStalled={dispatcher('mediaStalled')}
onError={dispatcher('mediaError')} onError={dispatcher('mediaError')}
onTimeUpdate={dispatcher('mediaTimeUpdate')} onTimeUpdate={dispatcher('mediaTimeUpdate')}
onEncrypted={dispatcher('mediaEncrypted')} onEncrypted={dispatcher('mediaEncrypted')}
onCanPlay={onCanPlay}> >
{trackTags} {trackTags}
</MediaTagName> </MediaTagName>
) )
@@ -137,42 +171,66 @@ function renderMedia (state) {
<div <div
key='letterbox' key='letterbox'
className='letterbox' className='letterbox'
onMouseMove={dispatcher('mediaMouseMoved')}> onMouseMove={dispatcher('mediaMouseMoved')}
>
{mediaTag} {mediaTag}
{renderOverlay(state)} {renderOverlay(state)}
</div> </div>
) )
// As soon as we know the video dimensions, resize the window
function onLoadedMetadata (e) { function onLoadedMetadata (e) {
if (state.playing.type !== 'video') return const mediaElement = e.target
const video = e.target
const dimensions = { // check if we can decode video and audio track
width: video.videoWidth, if (state.playing.type === 'video') {
height: video.videoHeight 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) { function onEnded () {
if (Playlist.hasNext(state)) { if (Playlist.hasNext(state)) {
dispatch('nextTrack') dispatch('nextTrack')
} else { } else {
// When the last video completes, pause the video instead of looping // When the last video completes, pause the video instead of looping
state.playing.isPaused = true state.playing.isPaused = true
} if (state.window.isFullScreen) dispatch('toggleFullScreen')
}
function onCanPlay (e) {
const elem = e.target
if (state.playing.type === 'video' &&
elem.webkitVideoDecodedByteCount === 0) {
dispatch('mediaError', 'Video codec unsupported')
} else if (elem.webkitAudioDecodedByteCount === 0) {
dispatch('mediaError', 'Audio codec unsupported')
} else {
dispatch('mediaSuccess')
elem.play()
} }
} }
} }
@@ -203,28 +261,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) { function renderAudioMetadata (state) {
const fileSummary = state.getPlayingFileSummary() const fileSummary = state.getPlayingFileSummary()
if (!fileSummary.audioInfo) return if (!fileSummary.audioInfo) return
const info = fileSummary.audioInfo const common = fileSummary.audioInfo.common || {}
// Get audio track info // Get audio track info
let title = info.title const title = common.title ? common.title : fileSummary.name
if (!title) {
title = fileSummary.name
}
let artist = info.artist && info.artist[0]
let album = info.album
if (album && info.year && !album.includes(info.year)) {
album += ' (' + info.year + ')'
}
let track
if (info.track && info.track.no && info.track.of) {
track = info.track.no + ' of ' + info.track.of
}
// Show a small info box in the middle of the screen with title/album/etc // Show a small info box in the middle of the screen with title/album/etc
const elems = [] const elems = []
// Audio metadata: artist(s)
const artist = common.artist || common.albumartist
if (artist) { if (artist) {
elems.push(( elems.push((
<div key='artist' className='audio-artist'> <div key='artist' className='audio-artist'>
@@ -232,17 +303,90 @@ function renderAudioMetadata (state) {
</div> </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(( elems.push((
<div key='album' className='audio-album'> <div key='album' className='audio-album'>
<label>Album</label>{album} <label>Album</label>{common.album}
</div> </div>
)) ))
} }
if (track) {
// Audio metadata: year
if (common.year) {
elems.push(( elems.push((
<div key='track' className='audio-track'> <div key='year' className='audio-year'>
<label>Track</label>{track} <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) + ' kbit/s') // 128 kbit/s
}
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> </div>
)) ))
} }
@@ -273,11 +417,11 @@ function renderLoadingSpinner (state) {
return ( return (
<div key='loading' className='media-stalled'> <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'> <div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded, <span><span className='progress'>{fileProgress}%</span> downloaded</span>
<span> {prettyBytes(prog.downloadSpeed || 0)}/s</span> <span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> {prettyBytes(prog.uploadSpeed || 0)}/s</span> <span> ↑ {prettyBytes(prog.uploadSpeed || 0)}/s</span>
</div> </div>
</div> </div>
) )
@@ -303,28 +447,76 @@ function renderCastScreen (state) {
isCast = false isCast = false
} else if (state.playing.location === 'error') { } else if (state.playing.location === 'error') {
castIcon = 'error_outline' castIcon = 'error_outline'
castType = 'Error' castType = 'Unable to Play'
isCast = false isCast = false
} }
const isStarting = state.playing.location.endsWith('-pending') const isStarting = state.playing.location.endsWith('-pending')
const castName = state.playing.castName const castName = state.playing.castName
const fileName = state.getPlayingFileSummary().name || ''
let castStatus let castStatus
if (isCast && isStarting) castStatus = 'Connecting to ' + castName + '...' if (isCast && isStarting) castStatus = 'Connecting to ' + castName + '...'
else if (isCast && !isStarting) castStatus = 'Connected to ' + castName else if (isCast && !isStarting) castStatus = 'Connected to ' + castName
else castStatus = '' else castStatus = ''
const prog = state.getPlayingTorrentSummary().progress || {}
// Show a nice title image, if possible // Show a nice title image, if possible
const style = { const style = {
backgroundImage: cssBackgroundImagePoster(state) backgroundImage: cssBackgroundImagePoster(state)
} }
function renderEta (total, downloaded) {
const missing = (total || 0) - (downloaded || 0)
const downloadSpeed = prog.downloadSpeed || 0
if (downloadSpeed === 0 || missing === 0) return
const etaStr = calculateEta(missing, downloadSpeed)
return (<span>{etaStr}</span>)
}
function renderDownloadProgress () {
if (!prog.files) return
const fileProg = prog.files[state.playing.fileIndex]
const fileProgress = fileProg.numPiecesPresent / fileProg.numPieces
const fileLength = state.getPlayingFileSummary().length
const fileDownloaded = fileProgress * fileLength
const progress = Math.round(100 * fileProgress)
const total = prettyBytes(fileLength)
const completed = prettyBytes(fileDownloaded)
const downloadSpeed = prettyBytes(prog.downloadSpeed || 0)
const uploadSpeed = prettyBytes(prog.uploadSpeed || 0)
let sizes
if (fileProgress < 1) {
sizes = <span> | {completed} / {total}</span>
} else {
sizes = <span> | {completed}</span>
}
return (
<div key='download-progress'>
<span className='progress'>{progress}% downloaded {sizes}</span>
<br />
<span>↓ {downloadSpeed}/s ↑ {uploadSpeed}/s | {prog.numPeers || 0} peer(s)</span>
<br />
{renderEta(fileLength, fileDownloaded)}
</div>
)
}
return ( return (
<div key='cast' className='letterbox' style={style}> <div key='cast' className='letterbox' style={style}>
<div className='cast-screen'> <div className='cast-screen'>
<i className='icon'>{castIcon}</i> <i className='icon'>{castIcon}</i>
<div key='type' className='cast-type'>{castType}</div> <div key='type' className='cast-type'>{castType}</div>
<div key='status' className='cast-status'>{castStatus}</div> <div key='status' className='cast-status'>{castStatus}</div>
<div key='name' className='name'>{fileName}</div>
{renderDownloadProgress()}
</div> </div>
</div> </div>
) )
@@ -333,15 +525,16 @@ function renderCastScreen (state) {
function renderCastOptions (state) { function renderCastOptions (state) {
if (!state.devices.castMenu) return if (!state.devices.castMenu) return
const {location, devices} = state.devices.castMenu const { location, devices } = state.devices.castMenu
const player = state.devices[location] const player = state.devices[location]
const items = devices.map(function (device, ix) { const items = devices.map((device, ix) => {
const isSelected = player.device === device const isSelected = player.device === device
const name = device.name const name = device.name
return ( return (
<li key={ix} onClick={dispatcher('selectCastDevice', ix)}> <li key={ix} onClick={dispatcher('selectCastDevice', ix)}>
<i className='icon'>{isSelected ? 'radio_button_checked' : 'radio_button_unchecked'}</i> <i className='icon'>{isSelected ? 'radio_button_checked' : 'radio_button_unchecked'}</i>
{' '}
{name} {name}
</li> </li>
) )
@@ -358,7 +551,7 @@ function renderSubtitleOptions (state) {
const subtitles = state.playing.subtitles const subtitles = state.playing.subtitles
if (!subtitles.tracks.length || !subtitles.showMenu) return if (!subtitles.tracks.length || !subtitles.showMenu) return
const items = subtitles.tracks.map(function (track, ix) { const items = subtitles.tracks.map((track, ix) => {
const isSelected = state.playing.subtitles.selectedIndex === ix const isSelected = state.playing.subtitles.selectedIndex === ix
return ( return (
<li key={ix} onClick={dispatcher('selectSubtitle', ix)}> <li key={ix} onClick={dispatcher('selectSubtitle', ix)}>
@@ -381,6 +574,27 @@ function renderSubtitleOptions (state) {
) )
} }
function renderAudioTrackOptions (state) {
const audioTracks = state.playing.audioTracks
if (!audioTracks.tracks.length || !audioTracks.showMenu) return
const items = audioTracks.tracks.map((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) { function renderPlayerControls (state) {
const positionPercent = 100 * state.playing.currentTime / state.playing.duration const positionPercent = 100 * state.playing.currentTime / state.playing.duration
const playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 3px)' } const playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 3px)' }
@@ -389,50 +603,71 @@ function renderPlayerControls (state) {
: state.playing.subtitles.selectedIndex >= 0 : state.playing.subtitles.selectedIndex >= 0
? 'active' ? 'active'
: '' : ''
const multiAudioClass = state.playing.audioTracks.tracks.length > 1
? 'active'
: 'disabled'
const prevClass = Playlist.hasPrevious(state) ? '' : 'disabled' const prevClass = Playlist.hasPrevious(state) ? '' : 'disabled'
const nextClass = Playlist.hasNext(state) ? '' : 'disabled' const nextClass = Playlist.hasNext(state) ? '' : 'disabled'
const elements = [ const elements = [
renderPreview(state),
<div key='playback-bar' className='playback-bar'> <div key='playback-bar' className='playback-bar'>
{renderLoadingBar(state)} {renderLoadingBar(state)}
<div <div
key='cursor' key='cursor'
className='playback-cursor' className='playback-cursor'
style={playbackCursorStyle} /> style={playbackCursorStyle}
/>
<div <div
key='scrub-bar' key='scrub-bar'
className='scrub-bar' className='scrub-bar'
draggable='true' draggable='true'
onMouseMove={handleScrubPreview}
onMouseOut={clearPreview}
onDragStart={handleDragStart} onDragStart={handleDragStart}
onClick={handleScrub} onClick={handleScrub}
onDrag={handleScrub} /> onDrag={handleScrub}
/>
</div>, </div>,
<i <i
key='skip-previous' key='skip-previous'
className={'icon skip-previous float-left ' + prevClass} className={'icon skip-previous float-left ' + prevClass}
onClick={dispatcher('previousTrack')}> onClick={dispatcher('previousTrack')}
role='button'
aria-label='Previous track'
>
skip_previous skip_previous
</i>, </i>,
<i <i
key='play' key='play'
className='icon play-pause float-left' className='icon play-pause float-left'
onClick={dispatcher('playPause')}> onClick={dispatcher('playPause')}
role='button'
aria-label={state.playing.isPaused ? 'Play' : 'Pause'}
>
{state.playing.isPaused ? 'play_arrow' : 'pause'} {state.playing.isPaused ? 'play_arrow' : 'pause'}
</i>, </i>,
<i <i
key='skip-next' key='skip-next'
className={'icon skip-next float-left ' + nextClass} className={'icon skip-next float-left ' + nextClass}
onClick={dispatcher('nextTrack')}> onClick={dispatcher('nextTrack')}
role='button'
aria-label='Next track'
>
skip_next skip_next
</i>, </i>,
<i <i
key='fullscreen' key='fullscreen'
className='icon fullscreen float-right' className='icon fullscreen float-right'
onClick={dispatcher('toggleFullScreen')}> onClick={dispatcher('toggleFullScreen')}
role='button'
aria-label={state.window.isFullScreen ? 'Exit full screen' : 'Enter full screen'}
>
{state.window.isFullScreen ? 'fullscreen_exit' : 'fullscreen'} {state.window.isFullScreen ? 'fullscreen_exit' : 'fullscreen'}
</i> </i>
] ]
@@ -443,9 +678,20 @@ function renderPlayerControls (state) {
<i <i
key='subtitles' key='subtitles'
className={'icon closed-caption float-right ' + captionsClass} className={'icon closed-caption float-right ' + captionsClass}
onClick={handleSubtitles}> onClick={handleSubtitles}
role='button'
aria-label='Closed captions'
>
closed_caption closed_caption
</i> </i>
), (
<i
key='audio-tracks'
className={'icon multi-audio float-right ' + multiAudioClass}
onClick={handleAudioTracks}
>
library_music
</i>
)) ))
} }
@@ -456,11 +702,11 @@ function renderPlayerControls (state) {
// Add the cast buttons. Icons for each cast type, connected/disconnected: // Add the cast buttons. Icons for each cast type, connected/disconnected:
const buttonIcons = { const buttonIcons = {
'chromecast': {true: 'cast_connected', false: 'cast'}, chromecast: { true: 'cast_connected', false: 'cast' },
'airplay': {true: 'airplay', false: 'airplay'}, airplay: { true: 'airplay', false: 'airplay' },
'dlna': {true: 'tv', false: 'tv'} dlna: { true: 'tv', false: 'tv' }
} }
castTypes.forEach(function (castType) { castTypes.forEach(castType => {
// Do we show this button (eg. the Chromecast button) at all? // Do we show this button (eg. the Chromecast button) at all?
const isCasting = state.playing.location.startsWith(castType) const isCasting = state.playing.location.startsWith(castType)
const player = state.devices[castType] const player = state.devices[castType]
@@ -487,7 +733,8 @@ function renderPlayerControls (state) {
<i <i
key={castType} key={castType}
className={'icon device float-right ' + buttonClass} className={'icon device float-right ' + buttonClass}
onClick={buttonHandler}> onClick={buttonHandler}
>
{buttonIcon} {buttonIcon}
</i> </i>
)) ))
@@ -496,10 +743,14 @@ function renderPlayerControls (state) {
// Render volume slider // Render volume slider
const volume = state.playing.volume const volume = state.playing.volume
const volumeIcon = 'volume_' + ( const volumeIcon = 'volume_' + (
volume === 0 ? 'off' volume === 0
: volume < 0.3 ? 'mute' ? 'off'
: volume < 0.6 ? 'down' : volume < 0.3
: 'up') ? 'mute'
: volume < 0.6
? 'down'
: 'up'
)
const volumeStyle = { const volumeStyle = {
background: '-webkit-gradient(linear, left top, right top, ' + background: '-webkit-gradient(linear, left top, right top, ' +
'color-stop(' + (volume * 100) + '%, #eee), ' + 'color-stop(' + (volume * 100) + '%, #eee), ' +
@@ -510,7 +761,10 @@ function renderPlayerControls (state) {
<div key='volume' className='volume float-left'> <div key='volume' className='volume float-left'>
<i <i
className='icon volume-icon float-left' className='icon volume-icon float-left'
onMouseDown={handleVolumeMute}> onMouseDown={handleVolumeMute}
role='button'
aria-label='Mute'
>
{volumeIcon} {volumeIcon}
</i> </i>
<input <input
@@ -518,13 +772,14 @@ function renderPlayerControls (state) {
type='range' min='0' max='1' step='0.05' type='range' min='0' max='1' step='0.05'
value={volume} value={volume}
onChange={handleVolumeScrub} onChange={handleVolumeScrub}
style={volumeStyle} /> style={volumeStyle}
/>
</div> </div>
)) ))
// Show video playback progress // Show video playback progress
const currentTimeStr = formatTime(state.playing.currentTime) const currentTimeStr = formatTime(state.playing.currentTime, state.playing.duration)
const durationStr = formatTime(state.playing.duration) const durationStr = formatTime(state.playing.duration, state.playing.duration)
elements.push(( elements.push((
<span key='time' className='time float-left'> <span key='time' className='time float-left'>
{currentTimeStr} / {durationStr} {currentTimeStr} / {durationStr}
@@ -540,24 +795,31 @@ function renderPlayerControls (state) {
)) ))
} }
return ( const emptyImage = new window.Image(0, 0)
<div key='controls' className='controls' emptyImage.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D'
onMouseEnter={dispatcher('mediaControlsMouseEnter')}
onMouseLeave={dispatcher('mediaControlsMouseLeave')}>
{elements}
{renderCastOptions(state)}
{renderSubtitleOptions(state)}
</div>
)
function handleDragStart (e) { function handleDragStart (e) {
// Prevent the cursor from changing, eg to a green + icon on Mac
if (e.dataTransfer) { if (e.dataTransfer) {
const dt = e.dataTransfer const dt = e.dataTransfer
// Prevent the cursor from changing, eg to a green + icon on Mac
dt.effectAllowed = 'none' dt.effectAllowed = 'none'
// Prevent ghost image
dt.setDragImage(emptyImage, 0, 0)
} }
} }
// Handles a scrub hover (preview another position in the video)
function handleScrubPreview (e) {
// Only show for videos
if (!e.clientX || state.playing.type !== 'video') return
dispatch('mediaMouseMoved')
dispatch('preview', e.clientX)
}
function clearPreview () {
if (state.playing.type !== 'video') return
dispatch('clearPreview')
}
// Handles a click or drag to scrub (jump to another position in the video) // Handles a click or drag to scrub (jump to another position in the video)
function handleScrub (e) { function handleScrub (e) {
if (!e.clientX) return if (!e.clientX) return
@@ -569,7 +831,7 @@ function renderPlayerControls (state) {
} }
// Handles volume muting and Unmuting // Handles volume muting and Unmuting
function handleVolumeMute (e) { function handleVolumeMute () {
if (state.playing.volume === 0.0) { if (state.playing.volume === 0.0) {
dispatch('setVolume', 1.0) dispatch('setVolume', 1.0)
} else { } else {
@@ -590,6 +852,73 @@ function renderPlayerControls (state) {
dispatch('toggleSubtitlesMenu') dispatch('toggleSubtitlesMenu')
} }
} }
function handleAudioTracks () {
dispatch('toggleAudioTracksMenu')
}
return (
<div
key='controls' className='controls'
onMouseEnter={dispatcher('mediaControlsMouseEnter')}
onMouseLeave={dispatcher('mediaControlsMouseLeave')}
>
{elements}
{renderCastOptions(state)}
{renderSubtitleOptions(state)}
{renderAudioTrackOptions(state)}
</div>
)
}
function renderPreview (state) {
const { previewXCoord = null } = state.playing
// Calculate time from x-coord as fraction of track width
const windowWidth = document.querySelector('body').clientWidth
const fraction = previewXCoord / windowWidth
const time = fraction * state.playing.duration /* seconds */
const height = 70
let width = 0
const previewEl = document.querySelector('video#preview')
if (previewEl !== null && previewXCoord !== null) {
previewEl.currentTime = time
// Auto adjust width to maintain video aspect ratio
width = Math.floor((previewEl.videoWidth / previewEl.videoHeight) * height)
}
// Center preview window on mouse cursor,
// while avoiding falling off the left or right edges
const xPos = Math.min(Math.max(previewXCoord - (width / 2), 5), windowWidth - width - 5)
return (
<div
key='preview' style={{
position: 'absolute',
bottom: 50,
left: xPos,
display: previewXCoord == null && 'none' // Hide preview when XCoord unset
}}
>
<div style={{ width, height, backgroundColor: 'black' }}>
<video
src={Playlist.getCurrentLocalURL(state)}
id='preview'
style={{ border: '1px solid lightgrey', borderRadius: 2 }}
/>
</div>
<p
style={{
textAlign: 'center', margin: 5, textShadow: '0 0 2px rgba(0,0,0,.5)', color: '#eee'
}}
>
{formatTime(time, state.playing.duration)}
</p>
</div>
)
} }
// Renders the loading bar. Shows which parts of the torrent are loaded, which // Renders the loading bar. Shows which parts of the torrent are loaded, which
@@ -599,18 +928,21 @@ function renderLoadingBar (state) {
const torrentSummary = state.getPlayingTorrentSummary() const torrentSummary = state.getPlayingTorrentSummary()
if (!torrentSummary.progress) { if (!torrentSummary.progress) {
return [] return null
} }
// Find all contiguous parts of the torrent which are loaded // Find all contiguous parts of the torrent which are loaded
const prog = torrentSummary.progress const prog = torrentSummary.progress
const fileProg = prog.files[state.playing.fileIndex] const fileProg = prog.files[state.playing.fileIndex]
if (!fileProg) return null
const parts = [] const parts = []
let lastPiecePresent = false let lastPiecePresent = false
for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) { for (let i = fileProg.startPiece; i <= fileProg.endPiece; i++) {
const partPresent = Bitfield.prototype.get.call(prog.bitfield, i) const partPresent = BitField.prototype.get.call(prog.bitfield, i)
if (partPresent && !lastPiecePresent) { if (partPresent && !lastPiecePresent) {
parts.push({start: i - fileProg.startPiece, count: 1}) parts.push({ start: i - fileProg.startPiece, count: 1 })
} else if (partPresent) { } else if (partPresent) {
parts[parts.length - 1].count++ parts[parts.length - 1].count++
} }
@@ -618,7 +950,7 @@ function renderLoadingBar (state) {
} }
// Output some bars to show which parts of the file are loaded // Output some bars to show which parts of the file are loaded
const loadingBarElems = parts.map(function (part, i) { const loadingBarElems = parts.map((part, i) => {
const style = { const style = {
left: (100 * part.start / fileProg.numPieces) + '%', left: (100 * part.start / fileProg.numPieces) + '%',
width: (100 * part.count / fileProg.numPieces) + '%' width: (100 * part.count / fileProg.numPieces) + '%'
@@ -626,6 +958,7 @@ function renderLoadingBar (state) {
return (<div key={i} className='loading-bar-part' style={style} />) return (<div key={i} className='loading-bar-part' style={style} />)
}) })
return (<div key='loading-bar' className='loading-bar'>{loadingBarElems}</div>) return (<div key='loading-bar' className='loading-bar'>{loadingBarElems}</div>)
} }
@@ -642,17 +975,19 @@ function cssBackgroundImageDarkGradient () {
'rgba(0,0,0,0.4) 0%, rgba(0,0,0,1) 100%)' '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)) { if (typeof time !== 'number' || Number.isNaN(time)) {
return '0:00' 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) let minutes = Math.floor(time % 3600 / 60)
if (hours > 0) { if (totalMinutes > 9 && minutes < 10) {
minutes = zeroFill(2, minutes) 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,13 +1,14 @@
const path = require('path')
const React = require('react') const React = require('react')
const PropTypes = require('prop-types')
const colors = require('material-ui/styles/colors') const colors = require('material-ui/styles/colors')
const Checkbox = require('material-ui/Checkbox').default const Checkbox = require('material-ui/Checkbox').default
const RaisedButton = require('material-ui/RaisedButton').default const RaisedButton = require('material-ui/RaisedButton').default
const TextField = require('material-ui/TextField').default
const Heading = require('../components/heading') const Heading = require('../components/heading')
const PathSelector = require('../components/path-selector') const PathSelector = require('../components/path-selector')
const {dispatch} = require('../lib/dispatcher') const { dispatch } = require('../lib/dispatcher')
const config = require('../../config') const config = require('../../config')
class PreferencesPage extends React.Component { class PreferencesPage extends React.Component {
@@ -25,6 +26,18 @@ class PreferencesPage extends React.Component {
this.handleStartupChange = this.handleStartupChange =
this.handleStartupChange.bind(this) this.handleStartupChange.bind(this)
this.handleSoundNotificationsChange =
this.handleSoundNotificationsChange.bind(this)
this.handleSetGlobalTrackers =
this.handleSetGlobalTrackers.bind(this)
const globalTrackers = this.props.state.getGlobalTrackers().join('\n')
this.state = {
globalTrackers
}
} }
downloadPathSelector () { downloadPathSelector () {
@@ -33,11 +46,12 @@ class PreferencesPage extends React.Component {
<PathSelector <PathSelector
dialog={{ dialog={{
title: 'Select download directory', title: 'Select download directory',
properties: [ 'openDirectory' ] properties: ['openDirectory']
}} }}
onChange={this.handleDownloadPathChange} onChange={this.handleDownloadPathChange}
title='Download location' title='Download location'
value={this.props.state.unsaved.prefs.downloadPath} /> value={this.props.state.saved.prefs.downloadPath}
/>
</Preference> </Preference>
) )
} }
@@ -51,9 +65,10 @@ class PreferencesPage extends React.Component {
<Preference> <Preference>
<Checkbox <Checkbox
className='control' className='control'
checked={!this.props.state.unsaved.prefs.openExternalPlayer} checked={!this.props.state.saved.prefs.openExternalPlayer}
label={'Play torrent media files using WebTorrent'} label='Play torrent media files using WebTorrent'
onCheck={this.handleOpenExternalPlayerChange} /> onCheck={this.handleOpenExternalPlayerChange}
/>
</Preference> </Preference>
) )
} }
@@ -62,11 +77,29 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'openExternalPlayer', !isChecked) 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 () { externalPlayerPathSelector () {
const playerPath = this.props.state.unsaved.prefs.externalPlayerPath const playerPath = this.props.state.saved.prefs.externalPlayerPath
const playerName = this.props.state.getExternalPlayerName() 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 always play in ${playerName}.`
: `Torrent media files will play in ${playerName} if WebTorrent cannot play them.` : `Torrent media files will play in ${playerName} if WebTorrent cannot play them.`
@@ -76,12 +109,12 @@ class PreferencesPage extends React.Component {
<PathSelector <PathSelector
dialog={{ dialog={{
title: 'Select media player app', title: 'Select media player app',
properties: [ 'openFile' ] properties: ['openFile']
}} }}
displayValue={playerName}
onChange={this.handleExternalPlayerPathChange} onChange={this.handleExternalPlayerPathChange}
title='External player' title='External player'
value={playerPath ? path.dirname(playerPath) : null} /> value={playerPath}
/>
</Preference> </Preference>
) )
} }
@@ -90,8 +123,61 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'externalPlayerPath', filePath) 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 () { setDefaultAppButton () {
const isFileHandler = this.props.state.unsaved.prefs.isFileHandler const isFileHandler = this.props.state.saved.prefs.isFileHandler
if (isFileHandler) { if (isFileHandler) {
return ( return (
<Preference> <Preference>
@@ -105,7 +191,8 @@ class PreferencesPage extends React.Component {
<RaisedButton <RaisedButton
className='control' className='control'
onClick={this.handleSetDefaultApp} onClick={this.handleSetDefaultApp}
label='Make WebTorrent the default' /> label='Make WebTorrent the default'
/>
</Preference> </Preference>
) )
} }
@@ -114,29 +201,77 @@ class PreferencesPage extends React.Component {
dispatch('updatePreferences', 'startup', isChecked) dispatch('updatePreferences', 'startup', isChecked)
} }
setStartupSection () { setStartupCheckbox () {
if (config.IS_PORTABLE) { if (config.IS_PORTABLE) {
return return
} }
return ( return (
<PreferencesSection title='Startup'> <Preference>
<Preference> <Checkbox
<Checkbox className='control'
className='control' checked={this.props.state.saved.prefs.startup}
checked={this.props.state.unsaved.prefs.startup} label='Open WebTorrent on startup'
label={'Open WebTorrent on startup.'} onCheck={this.handleStartupChange}
onCheck={this.handleStartupChange} />
/> </Preference>
</Preference>
</PreferencesSection>
) )
} }
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 () { handleSetDefaultApp () {
dispatch('updatePreferences', 'isFileHandler', true) dispatch('updatePreferences', 'isFileHandler', true)
} }
setGlobalTrackers () {
// Align the text fields
const textFieldStyle = { width: '100%' }
const textareaStyle = { margin: 0 }
return (
<Preference>
<TextField
className='torrent-trackers control'
style={textFieldStyle}
textareaStyle={textareaStyle}
multiLine
rows={2}
rowsMax={10}
value={this.state.globalTrackers}
onChange={this.handleSetGlobalTrackers}
/>
</Preference>
)
}
handleSetGlobalTrackers (e, globalTrackers) {
this.setState({ globalTrackers })
const announceList = globalTrackers
.split('\n')
.map((s) => s.trim())
.filter((s) => s !== '')
dispatch('updatePreferences', 'globalTrackers', announceList)
dispatch('updateGlobalTrackers', announceList)
}
render () { render () {
const style = { const style = {
color: colors.grey400, color: colors.grey400,
@@ -145,17 +280,26 @@ class PreferencesPage extends React.Component {
} }
return ( return (
<div style={style}> <div style={style}>
<PreferencesSection title='Downloads'> <PreferencesSection title='Folders'>
{this.downloadPathSelector()} {this.downloadPathSelector()}
{this.autoAddTorrentsCheckbox()}
{this.torrentsFolderPathSelector()}
</PreferencesSection> </PreferencesSection>
<PreferencesSection title='Playback'> <PreferencesSection title='Playback'>
{this.openExternalPlayerCheckbox()} {this.openExternalPlayerCheckbox()}
{this.externalPlayerPathSelector()} {this.externalPlayerPathSelector()}
{this.highestPlaybackPriorityCheckbox()}
</PreferencesSection> </PreferencesSection>
<PreferencesSection title='Default torrent app'> <PreferencesSection title='Default torrent app'>
{this.setDefaultAppButton()} {this.setDefaultAppButton()}
</PreferencesSection> </PreferencesSection>
{this.setStartupSection()} <PreferencesSection title='General'>
{this.setStartupCheckbox()}
{this.soundNotificationsCheckbox()}
</PreferencesSection>
<PreferencesSection title='Trackers'>
{this.setGlobalTrackers()}
</PreferencesSection>
</div> </div>
) )
} }
@@ -164,7 +308,7 @@ class PreferencesPage extends React.Component {
class PreferencesSection extends React.Component { class PreferencesSection extends React.Component {
static get propTypes () { static get propTypes () {
return { return {
title: React.PropTypes.string title: PropTypes.string
} }
} }

View File

@@ -6,7 +6,8 @@ const LinearProgress = require('material-ui/LinearProgress').default
const TorrentSummary = require('../lib/torrent-summary') const TorrentSummary = require('../lib/torrent-summary')
const TorrentPlayer = require('../lib/torrent-player') const TorrentPlayer = require('../lib/torrent-player')
const {dispatcher} = require('../lib/dispatcher') const { dispatcher } = require('../lib/dispatcher')
const { calculateEta } = require('../lib/time')
module.exports = class TorrentList extends React.Component { module.exports = class TorrentList extends React.Component {
render () { render () {
@@ -35,7 +36,11 @@ module.exports = class TorrentList extends React.Component {
) )
return ( return (
<div key='torrent-list' className='torrent-list'> <div
key='torrent-list'
className='torrent-list'
onContextMenu={dispatcher('openTorrentListContextMenu')}
>
{contents} {contents}
</div> </div>
) )
@@ -67,7 +72,8 @@ module.exports = class TorrentList extends React.Component {
style={style} style={style}
className={classes.join(' ')} className={classes.join(' ')}
onContextMenu={infoHash && dispatcher('openTorrentContextMenu', infoHash)} onContextMenu={infoHash && dispatcher('openTorrentContextMenu', infoHash)}
onClick={infoHash && dispatcher('toggleSelectTorrent', infoHash)}> onClick={infoHash && dispatcher('toggleSelectTorrent', infoHash)}
>
{this.renderTorrentMetadata(torrentSummary)} {this.renderTorrentMetadata(torrentSummary)}
{infoHash ? this.renderTorrentButtons(torrentSummary) : null} {infoHash ? this.renderTorrentButtons(torrentSummary) : null}
{isSelected ? this.renderTorrentDetails(torrentSummary) : null} {isSelected ? this.renderTorrentDetails(torrentSummary) : null}
@@ -130,7 +136,8 @@ module.exports = class TorrentList extends React.Component {
}} }}
checked={isActive} checked={isActive}
onClick={stopPropagation} onClick={stopPropagation}
onCheck={dispatcher('toggleTorrent', infoHash)} /> onCheck={dispatcher('toggleTorrent', infoHash)}
/>
) )
} }
@@ -147,7 +154,7 @@ module.exports = class TorrentList extends React.Component {
} }
} }
return ( return (
<div style={styles.wrapper}> <div key='progress-bar' style={styles.wrapper}>
<LinearProgress style={styles.progress} mode='determinate' value={progress} /> <LinearProgress style={styles.progress} mode='determinate' value={progress} />
</div> </div>
) )
@@ -189,18 +196,9 @@ module.exports = class TorrentList extends React.Component {
const downloadSpeed = prog.downloadSpeed const downloadSpeed = prog.downloadSpeed
if (downloadSpeed === 0 || missing === 0) return if (downloadSpeed === 0 || missing === 0) return
const rawEta = missing / downloadSpeed const etaStr = calculateEta(missing, downloadSpeed)
const hours = Math.floor(rawEta / 3600) % 24
const minutes = Math.floor(rawEta / 60) % 60
const seconds = Math.floor(rawEta % 60)
// Only display hours and minutes if they are greater than 0 but always return (<span key='eta'>{etaStr}</span>)
// display minutes if hours is being displayed
const hoursStr = hours ? hours + 'h' : ''
const minutesStr = (hours || minutes) ? minutes + 'm' : ''
const secondsStr = seconds + 's'
return (<span>{hoursStr} {minutesStr} {secondsStr} remaining</span>)
} }
function renderTorrentStatus () { function renderTorrentStatus () {
@@ -210,13 +208,15 @@ module.exports = class TorrentList extends React.Component {
else if (torrentSummary.progress.progress === 1) status = 'Not seeding' else if (torrentSummary.progress.progress === 1) status = 'Not seeding'
else status = 'Paused' else status = 'Paused'
} else if (torrentSummary.status === 'downloading') { } 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') { } else if (torrentSummary.status === 'seeding') {
status = 'Seeding' status = 'Seeding'
} else { // torrentSummary.status is 'new' or something unexpected } else { // torrentSummary.status is 'new' or something unexpected
status = '' status = ''
} }
return (<span>{status}</span>) return (<span key='torrent-status'>{status}</span>)
} }
} }
@@ -232,8 +232,9 @@ module.exports = class TorrentList extends React.Component {
<i <i
key='play-button' key='play-button'
title='Start streaming' title='Start streaming'
className={'icon play'} className='icon play'
onClick={dispatcher('playFile', infoHash)}> onClick={dispatcher('playFile', infoHash)}
>
play_circle_outline play_circle_outline
</i> </i>
) )
@@ -246,7 +247,8 @@ module.exports = class TorrentList extends React.Component {
key='delete-button' key='delete-button'
className='icon delete' className='icon delete'
title='Remove torrent' title='Remove torrent'
onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}> onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}
>
close close
</i> </i>
</div> </div>
@@ -278,10 +280,17 @@ module.exports = class TorrentList extends React.Component {
) )
} else { } else {
// We do know the files. List them and show download stats for each one // 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/')) .filter((file) => !file.path.includes('/.____padding_file/'))
.map((file, index) => ({ file, index })) .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 = ( filesElement = (
<div key='files' className='files'> <div key='files' className='files'>
@@ -312,7 +321,7 @@ module.exports = class TorrentList extends React.Component {
torrentSummary.progress.files[index]) { torrentSummary.progress.files[index]) {
const fileProg = torrentSummary.progress.files[index] const fileProg = torrentSummary.progress.files[index]
isDone = fileProg.numPiecesPresent === fileProg.numPieces 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 // Second, for media files where we saved our position, show how far we got
@@ -333,7 +342,7 @@ module.exports = class TorrentList extends React.Component {
} else { } else {
icon = 'description' /* file icon, opens in OS default app */ icon = 'description' /* file icon, opens in OS default app */
handleClick = isDone handleClick = isDone
? dispatcher('openItem', infoHash, index) ? dispatcher('openPath', infoHash, index)
: (e) => e.stopPropagation() // noop if file is not ready : (e) => e.stopPropagation() // noop if file is not ready
} }
// TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed // TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed
@@ -355,8 +364,10 @@ module.exports = class TorrentList extends React.Component {
<td className={'col-size ' + rowClass}> <td className={'col-size ' + rowClass}>
{prettyBytes(file.length)} {prettyBytes(file.length)}
</td> </td>
<td className='col-select' <td
onClick={dispatcher('toggleTorrentFile', infoHash, index)}> className='col-select'
onClick={dispatcher('toggleTorrentFile', infoHash, index)}
>
<i className='icon deselect-file'>{isSelected ? 'close' : 'add'}</i> <i className='icon deselect-file'>{isSelected ? 'close' : 'add'}</i>
</td> </td>
</tr> </tr>
@@ -365,21 +376,21 @@ module.exports = class TorrentList extends React.Component {
renderRadialProgressBar (fraction, cssClass) { renderRadialProgressBar (fraction, cssClass) {
const rotation = 360 * fraction const rotation = 360 * fraction
const transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'} const transformFill = { transform: 'rotate(' + (rotation / 2) + 'deg)' }
const transformFix = {transform: 'rotate(' + rotation + 'deg)'} const transformFix = { transform: 'rotate(' + rotation + 'deg)' }
return ( return (
<div key='radial-progress' className={'radial-progress ' + cssClass}> <div key='radial-progress' className={'radial-progress ' + cssClass}>
<div key='circle' className='circle'> <div className='circle'>
<div key='mask-full' className='mask full' style={transformFill}> <div className='mask full' style={transformFill}>
<div key='fill' className='fill' style={transformFill} /> <div className='fill' style={transformFill} />
</div> </div>
<div key='mask-half' className='mask half'> <div className='mask half'>
<div key='fill' className='fill' style={transformFill} /> <div className='fill' style={transformFill} />
<div key='fill-fix' className='fill fix' style={transformFix} /> <div className='fill fix' style={transformFix} />
</div> </div>
</div> </div>
<div key='inset' className='inset' /> <div className='inset' />
</div> </div>
) )
} }
@@ -393,7 +404,7 @@ function getErrorMessage (torrentSummary) {
const err = torrentSummary.error const err = torrentSummary.error
if (err === 'path-missing') { if (err === 'path-missing') {
return ( return (
<span> <span key='path-missing'>
Path missing.<br /> Path missing.<br />
Fix and restart the app, or delete the torrent. Fix and restart the app, or delete the torrent.
</span> </span>

View File

@@ -3,33 +3,18 @@
console.time('init') console.time('init')
const crypto = require('crypto') const crypto = require('crypto')
const deepEqual = require('deep-equal') const util = require('util')
const defaultAnnounceList = require('create-torrent').announceList const { ipcRenderer } = require('electron')
const electron = require('electron')
const fs = require('fs') const fs = require('fs')
const mkdirp = require('mkdirp') const mm = require('music-metadata')
const musicmetadata = require('musicmetadata')
const networkAddress = require('network-address') const networkAddress = require('network-address')
const path = require('path') const path = require('path')
const WebTorrent = require('webtorrent') const WebTorrent = require('webtorrent')
const zeroFill = require('zero-fill')
const crashReporter = require('../crash-reporter')
const config = require('../config') const config = require('../config')
const {TorrentKeyNotFoundError} = require('./lib/errors') const { TorrentKeyNotFoundError } = require('./lib/errors')
const torrentPoster = require('./lib/torrent-poster') const torrentPoster = require('./lib/torrent-poster')
// Report when the process crashes
crashReporter.init()
// Send & receive messages from the main window
const ipc = electron.ipcRenderer
// Force use of webtorrent trackers on all torrents
global.WEBTORRENT_ANNOUNCE = defaultAnnounceList
.map((arr) => arr[0])
.filter((url) => url.indexOf('wss://') === 0 || url.indexOf('ws://') === 0)
/** /**
* WebTorrent version. * WebTorrent version.
*/ */
@@ -41,10 +26,9 @@ const VERSION = require('../../package.json').version
* '0.16.1' -> '0016' * '0.16.1' -> '0016'
* '1.2.5' -> '0102' * '1.2.5' -> '0102'
*/ */
const VERSION_STR = VERSION.match(/([0-9]+)/g) const VERSION_STR = VERSION
.slice(0, 2) .replace(/\d*./g, v => `0${v % 100}`.slice(-2))
.map((v) => zeroFill(2, v)) .slice(0, 4)
.join('')
/** /**
* Version prefix string (used in peer ID). WebTorrent uses the Azureus-style * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
@@ -75,38 +59,45 @@ init()
function init () { function init () {
listenToClientEvents() listenToClientEvents()
ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) => ipcRenderer.on('wt-set-global-trackers', (e, globalTrackers) =>
setGlobalTrackers(globalTrackers))
ipcRenderer.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) =>
startTorrenting(torrentKey, torrentID, path, fileModtimes, selections)) startTorrenting(torrentKey, torrentID, path, fileModtimes, selections))
ipc.on('wt-stop-torrenting', (e, infoHash) => ipcRenderer.on('wt-stop-torrenting', (e, infoHash) =>
stopTorrenting(infoHash)) stopTorrenting(infoHash))
ipc.on('wt-create-torrent', (e, torrentKey, options) => ipcRenderer.on('wt-create-torrent', (e, torrentKey, options) =>
createTorrent(torrentKey, options)) createTorrent(torrentKey, options))
ipc.on('wt-save-torrent-file', (e, torrentKey) => ipcRenderer.on('wt-save-torrent-file', (e, torrentKey) =>
saveTorrentFile(torrentKey)) saveTorrentFile(torrentKey))
ipc.on('wt-generate-torrent-poster', (e, torrentKey) => ipcRenderer.on('wt-generate-torrent-poster', (e, torrentKey) =>
generateTorrentPoster(torrentKey)) generateTorrentPoster(torrentKey))
ipc.on('wt-get-audio-metadata', (e, infoHash, index) => ipcRenderer.on('wt-get-audio-metadata', (e, infoHash, index) =>
getAudioMetadata(infoHash, index)) getAudioMetadata(infoHash, index))
ipc.on('wt-start-server', (e, infoHash) => ipcRenderer.on('wt-start-server', (e, infoHash) =>
startServer(infoHash)) startServer(infoHash))
ipc.on('wt-stop-server', (e) => ipcRenderer.on('wt-stop-server', () =>
stopServer()) stopServer())
ipc.on('wt-select-files', (e, infoHash, selections) => ipcRenderer.on('wt-select-files', (e, infoHash, selections) =>
selectFiles(infoHash, selections)) selectFiles(infoHash, selections))
ipc.send('ipcReadyWebTorrent') ipcRenderer.send('ipcReadyWebTorrent')
window.addEventListener('error', (e) => window.addEventListener('error', (e) =>
ipc.send('wt-uncaught-error', {message: e.error.message, stack: e.error.stack}), ipcRenderer.send('wt-uncaught-error', { message: e.error.message, stack: e.error.stack }),
true) true)
setInterval(updateTorrentProgress, 1000) setInterval(updateTorrentProgress, 1000)
console.timeEnd('init') console.timeEnd('init')
} }
function listenToClientEvents () { function listenToClientEvents () {
client.on('warning', (err) => ipc.send('wt-warning', null, err.message)) client.on('warning', (err) => ipcRenderer.send('wt-warning', null, err.message))
client.on('error', (err) => ipc.send('wt-error', null, err.message)) client.on('error', (err) => ipcRenderer.send('wt-error', null, err.message))
}
// Sets the default trackers
function setGlobalTrackers (globalTrackers) {
globalThis.WEBTORRENT_ANNOUNCE = globalTrackers
} }
// Starts a given TorrentID, which can be an infohash, magnet URI, etc. // Starts a given TorrentID, which can be an infohash, magnet URI, etc.
@@ -115,8 +106,8 @@ function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections)
console.log('starting torrent %s: %s', torrentKey, torrentID) console.log('starting torrent %s: %s', torrentKey, torrentID)
const torrent = client.add(torrentID, { const torrent = client.add(torrentID, {
path: path, path,
fileModtimes: fileModtimes fileModtimes
}) })
torrent.key = torrentKey torrent.key = torrentKey
@@ -128,6 +119,7 @@ function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections)
} }
function stopTorrenting (infoHash) { function stopTorrenting (infoHash) {
console.log('--- STOP TORRENTING: ', infoHash)
const torrent = client.get(infoHash) const torrent = client.get(infoHash)
if (torrent) torrent.destroy() if (torrent) torrent.destroy()
} }
@@ -139,44 +131,44 @@ function createTorrent (torrentKey, options) {
const torrent = client.seed(paths, options) const torrent = client.seed(paths, options)
torrent.key = torrentKey torrent.key = torrentKey
addTorrentEvents(torrent) addTorrentEvents(torrent)
ipc.send('wt-new-torrent') ipcRenderer.send('wt-new-torrent')
} }
function addTorrentEvents (torrent) { function addTorrentEvents (torrent) {
torrent.on('warning', (err) => torrent.on('warning', (err) =>
ipc.send('wt-warning', torrent.key, err.message)) ipcRenderer.send('wt-warning', torrent.key, err.message))
torrent.on('error', (err) => torrent.on('error', (err) =>
ipc.send('wt-error', torrent.key, err.message)) ipcRenderer.send('wt-error', torrent.key, err.message))
torrent.on('infoHash', () => torrent.on('infoHash', () =>
ipc.send('wt-infohash', torrent.key, torrent.infoHash)) ipcRenderer.send('wt-parsed', torrent.key, torrent.infoHash, torrent.magnetURI))
torrent.on('metadata', torrentMetadata) torrent.on('metadata', torrentMetadata)
torrent.on('ready', torrentReady) torrent.on('ready', torrentReady)
torrent.on('done', torrentDone) torrent.on('done', torrentDone)
function torrentMetadata () { function torrentMetadata () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipc.send('wt-metadata', torrent.key, info) ipcRenderer.send('wt-metadata', torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
} }
function torrentReady () { function torrentReady () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipc.send('wt-ready', torrent.key, info) ipcRenderer.send('wt-ready', torrent.key, info)
ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info) ipcRenderer.send('wt-ready-' + torrent.infoHash, torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
} }
function torrentDone () { function torrentDone () {
const info = getTorrentInfo(torrent) const info = getTorrentInfo(torrent)
ipc.send('wt-done', torrent.key, info) ipcRenderer.send('wt-done', torrent.key, info)
updateTorrentProgress() updateTorrentProgress()
torrent.getFileModtimes(function (err, fileModtimes) { torrent.getFileModtimes((err, fileModtimes) => {
if (err) return onError(err) if (err) return onError(err)
ipc.send('wt-file-modtimes', torrent.key, fileModtimes) ipcRenderer.send('wt-file-modtimes', torrent.key, fileModtimes)
}) })
} }
} }
@@ -209,19 +201,19 @@ function saveTorrentFile (torrentKey) {
const torrent = getTorrent(torrentKey) const torrent = getTorrent(torrentKey)
const torrentPath = path.join(config.TORRENT_PATH, torrent.infoHash + '.torrent') const torrentPath = path.join(config.TORRENT_PATH, torrent.infoHash + '.torrent')
fs.access(torrentPath, fs.constants.R_OK, function (err) { fs.access(torrentPath, fs.constants.R_OK, err => {
const fileName = torrent.infoHash + '.torrent' const fileName = torrent.infoHash + '.torrent'
if (!err) { if (!err) {
// We've already saved the file // We've already saved the file
return ipc.send('wt-file-saved', torrentKey, fileName) return ipcRenderer.send('wt-file-saved', torrentKey, fileName)
} }
// Otherwise, save the .torrent file, under the app config folder // Otherwise, save the .torrent file, under the app config folder
mkdirp(config.TORRENT_PATH, function (_) { fs.mkdir(config.TORRENT_PATH, { recursive: true }, _ => {
fs.writeFile(torrentPath, torrent.torrentFile, function (err) { fs.writeFile(torrentPath, torrent.torrentFile, err => {
if (err) return console.log('error saving torrent file %s: %o', torrentPath, err) if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
console.log('saved torrent file %s', torrentPath) console.log('saved torrent file %s', torrentPath)
return ipc.send('wt-file-saved', torrentKey, fileName) return ipcRenderer.send('wt-file-saved', torrentKey, fileName)
}) })
}) })
}) })
@@ -231,17 +223,17 @@ function saveTorrentFile (torrentKey) {
// Auto chooses either a frame from a video file, an image, etc // Auto chooses either a frame from a video file, an image, etc
function generateTorrentPoster (torrentKey) { function generateTorrentPoster (torrentKey) {
const torrent = getTorrent(torrentKey) const torrent = getTorrent(torrentKey)
torrentPoster(torrent, function (err, buf, extension) { torrentPoster(torrent, (err, buf, extension) => {
if (err) return console.log('error generating poster: %o', err) if (err) return console.log('error generating poster: %o', err)
// save it for next time // save it for next time
mkdirp(config.POSTER_PATH, function (err) { fs.mkdir(config.POSTER_PATH, { recursive: true }, err => {
if (err) return console.log('error creating poster dir: %o', err) if (err) return console.log('error creating poster dir: %o', err)
const posterFileName = torrent.infoHash + extension const posterFileName = torrent.infoHash + extension
const posterFilePath = path.join(config.POSTER_PATH, posterFileName) const posterFilePath = path.join(config.POSTER_PATH, posterFileName)
fs.writeFile(posterFilePath, buf, function (err) { fs.writeFile(posterFilePath, buf, err => {
if (err) return console.log('error saving poster: %o', err) if (err) return console.log('error saving poster: %o', err)
// show the poster // show the poster
ipc.send('wt-poster', torrentKey, posterFileName) ipcRenderer.send('wt-poster', torrentKey, posterFileName)
}) })
}) })
}) })
@@ -250,25 +242,23 @@ function generateTorrentPoster (torrentKey) {
function updateTorrentProgress () { function updateTorrentProgress () {
const progress = getTorrentProgress() const progress = getTorrentProgress()
// TODO: diff torrent-by-torrent, not once for the whole update // 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 */ return /* don't send heavy object if it hasn't changed */
} }
ipc.send('wt-progress', progress) ipcRenderer.send('wt-progress', progress)
prevProgress = progress prevProgress = progress
} }
function getTorrentProgress () { function getTorrentProgress () {
// First, track overall progress // First, track overall progress
const progress = client.progress const progress = client.progress
const hasActiveTorrents = client.torrents.some(function (torrent) { const hasActiveTorrents = client.torrents.some(torrent => torrent.progress !== 1)
return torrent.progress !== 1
})
// Track progress for every file in each torrent // Track progress for every file in each torrent
// TODO: ideally this would be tracked by WebTorrent, which could do it // TODO: ideally this would be tracked by WebTorrent, which could do it
// more efficiently than looping over torrent.bitfield // more efficiently than looping over torrent.bitfield
const torrentProg = client.torrents.map(function (torrent) { const torrentProg = client.torrents.map(torrent => {
const fileProg = torrent.files && torrent.files.map(function (file, index) { const fileProg = torrent.files && torrent.files.map(file => {
const numPieces = file._endPiece - file._startPiece + 1 const numPieces = file._endPiece - file._startPiece + 1
let numPiecesPresent = 0 let numPiecesPresent = 0
for (let piece = file._startPiece; piece <= file._endPiece; piece++) { for (let piece = file._startPiece; piece <= file._endPiece; piece++) {
@@ -308,22 +298,23 @@ function startServer (infoHash) {
else torrent.once('ready', () => startServerFromReadyTorrent(torrent)) else torrent.once('ready', () => startServerFromReadyTorrent(torrent))
} }
function startServerFromReadyTorrent (torrent, cb) { function startServerFromReadyTorrent (torrent) {
if (server) return if (server) return
// start the streaming torrent-to-http server // start the streaming torrent-to-http server
server = torrent.createServer() server = torrent.createServer()
server.listen(0, function () { server.listen(0, () => {
const port = server.address().port const port = server.address().port
const urlSuffix = ':' + port const urlSuffix = ':' + port
const info = { const info = {
torrentKey: torrent.key, torrentKey: torrent.key,
localURL: 'http://localhost' + urlSuffix, localURL: 'http://localhost' + urlSuffix,
networkURL: 'http://' + networkAddress() + urlSuffix networkURL: 'http://' + networkAddress() + urlSuffix,
networkAddress: networkAddress()
} }
ipc.send('wt-server-running', info) ipcRenderer.send('wt-server-running', info)
ipc.send('wt-server-' + torrent.infoHash, info) ipcRenderer.send('wt-server-' + torrent.infoHash, info)
}) })
} }
@@ -333,16 +324,46 @@ function stopServer () {
server = null server = null
} }
console.log('Initializing...')
function getAudioMetadata (infoHash, index) { function getAudioMetadata (infoHash, index) {
const torrent = client.get(infoHash) const torrent = client.get(infoHash)
const file = torrent.files[index] const file = torrent.files[index]
musicmetadata(file.createReadStream(), function (err, info) {
if (err) return console.log('error getting audio metadata for ' + infoHash + ':' + index, err) // Set initial matadata to display the filename first.
const { artist, album, albumartist, title, year, track, disk, genre } = info const metadata = { title: file.name }
const importantInfo = { artist, album, albumartist, title, year, track, disk, genre } ipcRenderer.send('wt-audio-metadata', infoHash, index, metadata)
console.log('got audio metadata for %s: %o', file.name, importantInfo)
ipc.send('wt-audio-metadata', infoHash, index, importantInfo) const options = {
}) native: false,
skipCovers: true,
fileSize: file.length,
observer: () => {
ipcRenderer.send('wt-audio-metadata', infoHash, index, {
common: metadata.common,
format: metadata.format
})
}
}
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 => {
ipcRenderer.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) { function selectFiles (torrentOrInfoHash, selections) {
@@ -362,7 +383,7 @@ function selectFiles (torrentOrInfoHash, selections) {
// selection with individual selections for each file, so we can // selection with individual selections for each file, so we can
// select/deselect files later on // select/deselect files later on
if (!selections) { if (!selections) {
selections = torrent.files.map((x) => true) selections = new Array(torrent.files.length).fill(true)
} }
// Selections specified incorrectly? // Selections specified incorrectly?
@@ -375,15 +396,15 @@ function selectFiles (torrentOrInfoHash, selections) {
torrent.deselect(0, torrent.pieces.length - 1, false) torrent.deselect(0, torrent.pieces.length - 1, false)
// Add selections (individual files) // Add selections (individual files)
for (let i = 0; i < selections.length; i++) { selections.forEach((selection, i) => {
const file = torrent.files[i] const file = torrent.files[i]
if (selections[i]) { if (selection) {
file.select() file.select()
} else { } else {
console.log('deselecting file ' + i + ' of torrent ' + torrent.name) console.log('deselecting file ' + i + ' of torrent ' + torrent.name)
file.deselect() file.deselect()
} }
} })
} }
// Gets a WebTorrent handle by torrentKey // Gets a WebTorrent handle by torrentKey
@@ -401,7 +422,7 @@ function onError (err) {
// TODO: remove this once the following bugs are fixed: // TODO: remove this once the following bugs are fixed:
// https://bugs.chromium.org/p/chromium/issues/detail?id=490143 // https://bugs.chromium.org/p/chromium/issues/detail?id=490143
// https://github.com/electron/electron/issues/7212 // https://github.com/electron/electron/issues/7212
window.testOfflineMode = function () { window.testOfflineMode = () => {
console.log('Test, going OFFLINE') console.log('Test, going OFFLINE')
client = window.client = new WebTorrent({ client = window.client = new WebTorrent({
peerId: PEER_ID, peerId: PEER_ID,

View File

@@ -32,8 +32,8 @@
<p> <p>
Version <script>document.write(require('../package.json').version)</script> Version <script>document.write(require('../package.json').version)</script>
(<script>document.write(require('webtorrent/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>
<p><script>document.write(require('../config').APP_COPYRIGHT)</script></p> <p><script>document.write(require('../build/config').APP_COPYRIGHT)</script></p>
</body> </body>
</html> </html>

Binary file not shown.

Binary file not shown.

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,25 @@
[Desktop Entry]
Type=Application
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
StartupWMClass=<%= productName %>
[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*/%>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -71,7 +71,6 @@ table {
.app { .app {
-webkit-user-select: none; -webkit-user-select: none;
-webkit-app-region: drag;
height: 100%; height: 100%;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
@@ -143,6 +142,7 @@ table {
*/ */
.header { .header {
-webkit-app-region: drag;
background: rgb(40, 40, 40); background: rgb(40, 40, 40);
border-bottom: 1px solid rgb(30, 30, 30); border-bottom: 1px solid rgb(30, 30, 30);
height: 38px; /* vertically center OS menu buttons (OS X) */ height: 38px; /* vertically center OS menu buttons (OS X) */
@@ -322,6 +322,10 @@ textarea,
* TORRENT LIST * TORRENT LIST
*/ */
.torrent-list {
height: 100%;
}
.torrent { .torrent {
background: linear-gradient(to bottom right, #4B79A1, #283E51); background: linear-gradient(to bottom right, #4B79A1, #283E51);
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -558,6 +562,13 @@ body.drag .app::after {
width: 100%; width: 100%;
} }
.player .name {
font-size: 20px;
font-weight: bold;
line-height: 1.5em;
margin-bottom: 6px;
}
/* /*
* PLAYER CONTROLS * PLAYER CONTROLS
*/ */
@@ -615,6 +626,7 @@ body.drag .app::after {
font-size: 13px; font-size: 13px;
margin: 9px 8px 8px 8px; margin: 9px 8px 8px 8px;
opacity: 0.8; opacity: 0.8;
font-variant-numeric: tabular-nums;
} }
.player .controls .icon.closed-caption { .player .controls .icon.closed-caption {
@@ -622,6 +634,11 @@ body.drag .app::after {
margin-top: 6px; margin-top: 6px;
} }
.player .controls .icon.multi-audio {
font-size: 26px;
margin-top: 6px;
}
.player .controls .icon.fullscreen { .player .controls .icon.fullscreen {
font-size: 26px; font-size: 26px;
margin-right: 15px; margin-right: 15px;
@@ -800,6 +817,23 @@ video::-webkit-media-text-track-container {
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: 40px auto; 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 { .media-stalled .loading-status {
@@ -819,12 +853,17 @@ video::-webkit-media-text-track-container {
.audio-metadata label { .audio-metadata label {
display:inline-block; display:inline-block;
width: 100px; width: 120px;
text-align: right; text-align: right;
font-weight: normal; font-weight: normal;
margin-right: 25px; margin-right: 25px;
} }
.audio-metadata .audio-format,
.audio-metadata .audio-comments {
font-weight: normal;
}
/* /*
* ERRORS * ERRORS
*/ */

Binary file not shown.

Binary file not shown.

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