Compare commits

..

231 Commits

Author SHA1 Message Date
renovate[bot]
bc6f732cec chore(deps): update dependency electron to v35 [security] 2026-04-09 21:43:20 +00:00
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
76 changed files with 9506 additions and 7570 deletions

View File

@@ -12,8 +12,8 @@ jobs:
node:
- '16'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: npm install

View File

@@ -5,11 +5,11 @@ jobs:
package_linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -17,7 +17,7 @@ jobs:
${{ runner.os }}-node-
- run: npm install
- run: npm run package -- linux
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: linux
path: |
@@ -27,11 +27,11 @@ jobs:
package_macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -39,7 +39,7 @@ jobs:
${{ runner.os }}-node-
- run: npm install
- run: npm run package -- darwin
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: macos
path: |
@@ -48,11 +48,11 @@ jobs:
package_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -60,7 +60,7 @@ jobs:
${{ runner.os }}-node-
- run: npm install
- run: npm run package -- win32
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: windows
path: |

View File

@@ -12,12 +12,12 @@ jobs:
pull-requests: write
steps:
- uses: actions/stale@v4
- 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,bug,dependency,enhancement,'help wanted',question,security,meta
exempt-pr-labels: accepted,blocked,bug,dependency,enhancement,'help wanted',question,security,meta
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'

View File

@@ -13,7 +13,7 @@
<p align="center">
<a href="https://discord.gg/cnXkm4Z"><img src="https://img.shields.io/discord/612575111718895616" alt="discord"></a>
<a href="https://travis-ci.org/webtorrent/webtorrent-desktop"><img src="https://img.shields.io/travis/webtorrent/webtorrent-desktop/master.svg" alt="travis"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/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/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>
@@ -147,7 +147,7 @@ to be installed. For example on Mac, first install
[XQuartz](http://www.xquartz.org/), then run:
```
brew install wine mono
$ brew install wine mono
```
(Requires the [Homebrew](http://brew.sh/) package manager.)

View File

@@ -1 +0,0 @@
module.exports = require('electron')

View File

@@ -1 +0,0 @@
import('./build/main/index.js')

1
index.js Normal file
View File

@@ -0,0 +1 @@
require('./build/main')

14782
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
{
"name": "webtorrent-desktop",
"type": "module",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.24.0",
"author": {
@@ -22,68 +21,68 @@
"url": "https://github.com/webtorrent/webtorrent-desktop/issues"
},
"dependencies": {
"@electron/remote": "2.0.1",
"@electron/remote": "2.1.3",
"airplayer": "github:webtorrent/airplayer#fix-security",
"application-config": "2.0.0",
"arch": "2.2.0",
"auto-launch": "5.0.5",
"bitfield": "4.0.0",
"bitfield": "4.1.0",
"capture-frame": "4.0.0",
"chokidar": "3.5.2",
"chokidar": "3.5.3",
"chromecasts": "1.10.2",
"create-torrent": "5.0.1",
"create-torrent": "5.0.9",
"debounce": "1.2.1",
"dlnacasts": "0.1.0",
"drag-drop": "7.2.0",
"es6-error": "4.1.1",
"fn-getter": "1.0.0",
"iso-639-1": "2.1.9",
"iso-639-1": "2.1.15",
"languagedetect": "2.0.0",
"location-history": "1.1.2",
"material-ui": "0.20.2",
"music-metadata": "7.11.4",
"music-metadata": "7.14.0",
"network-address": "1.1.2",
"parse-torrent": "9.1.4",
"parse-torrent": "9.1.5",
"prettier-bytes": "1.0.4",
"prop-types": "15.7.2",
"prop-types": "15.8.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"rimraf": "3.0.2",
"rimraf": "4.4.0",
"run-parallel": "1.2.0",
"semver": "7.3.5",
"semver": "7.3.8",
"simple-concat": "1.0.1",
"simple-get": "4.0.0",
"simple-get": "4.0.1",
"srt-to-vtt": "1.1.3",
"vlc-command": "1.2.0",
"webtorrent": "1.5.6",
"webtorrent": "1.9.7",
"winreg": "1.2.4"
},
"devDependencies": {
"@babel/cli": "7.15.7",
"@babel/core": "7.15.8",
"@babel/eslint-parser": "7.15.8",
"@babel/plugin-transform-react-jsx": "7.14.9",
"@babel/cli": "7.28.6",
"@babel/core": "7.29.0",
"@babel/eslint-parser": "7.28.6",
"@babel/plugin-transform-react-jsx": "7.28.6",
"cross-zip": "4.0.0",
"depcheck": "1.4.2",
"electron": "15.2.0",
"electron-notarize": "1.1.1",
"electron-osx-sign": "0.5.0",
"electron-packager": "15.4.0",
"electron-winstaller": "5.0.0",
"gh-release": "6.0.1",
"minimist": "1.2.5",
"nodemon": "2.0.13",
"open": "8.3.0",
"plist": "3.0.4",
"pngjs": "6.0.0",
"depcheck": "1.4.7",
"electron": "35.7.5",
"electron-notarize": "1.2.2",
"electron-osx-sign": "0.6.0",
"electron-packager": "17.1.2",
"electron-winstaller": "5.4.0",
"gh-release": "7.0.2",
"minimist": "1.2.8",
"nodemon": "2.0.22",
"open": "8.4.2",
"plist": "3.1.0",
"pngjs": "7.0.0",
"run-series": "1.1.9",
"spectron": "15.0.0",
"standard": "16.0.4",
"tape": "5.3.1"
"spectron": "19.0.0",
"standard": "17.0.0",
"tape": "5.9.0"
},
"engines": {
"node": "^16.0.0",
"npm": "^7.10.0 || ^8.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",
"keywords": [
@@ -97,11 +96,11 @@
"webtorrent"
],
"license": "MIT",
"main": "index.cjs",
"main": "index.js",
"optionalDependencies": {
"appdmg": "^0.6.0",
"electron-installer-debian": "^3.1.0",
"electron-installer-redhat": "^3.3.0"
"electron-installer-debian": "^3.2.0",
"electron-installer-redhat": "^3.4.0"
},
"private": true,
"productName": "WebTorrent",

View File

@@ -1,13 +1,12 @@
import applicationConfig from 'application-config'
import path from 'path'
import arch from 'arch'
import fs from 'fs'
import electron from '../electron.cjs'
const appConfig = require('application-config')('WebTorrent')
const path = require('path')
const { app } = require('electron')
const arch = require('arch')
const appConfig = applicationConfig('WebTorrent')
const APP_NAME = 'WebTorrent'
const APP_TEAM = 'WebTorrent, LLC'
const APP_VERSION = JSON.parse(fs.readFileSync('package.json').toString()).version
const APP_VERSION = require('../package.json').version
const IS_TEST = isTest()
const PORTABLE_PATH = IS_TEST
? path.join(process.platform === 'win32' ? 'C:\\Windows\\Temp' : '/tmp', 'WebTorrentTest')
@@ -18,6 +17,93 @@ const IS_PORTABLE = isPortable()
const UI_HEADER_HEIGHT = 38
const UI_TORRENT_HEIGHT = 100
module.exports = {
ANNOUNCEMENT_URL: 'https://webtorrent.io/desktop/announcement',
AUTO_UPDATE_URL: 'https://webtorrent.io/desktop/update',
CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report',
TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry',
APP_COPYRIGHT: `Copyright © 2014-${new Date().getFullYear()} ${APP_TEAM}`,
APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'),
APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'),
APP_NAME,
APP_TEAM,
APP_VERSION,
APP_WINDOW_TITLE: APP_NAME,
CONFIG_PATH: getConfigPath(),
DEFAULT_TORRENTS: [
{
testID: 'bbb',
name: 'Big Buck Bunny',
posterFileName: 'bigBuckBunny.jpg',
torrentFileName: 'bigBuckBunny.torrent'
},
{
testID: 'cosmos',
name: 'Cosmos Laundromat (Preview)',
posterFileName: 'cosmosLaundromat.jpg',
torrentFileName: 'cosmosLaundromat.torrent'
},
{
testID: 'sintel',
name: 'Sintel',
posterFileName: 'sintel.jpg',
torrentFileName: 'sintel.torrent'
},
{
testID: 'tears',
name: 'Tears of Steel',
posterFileName: 'tearsOfSteel.jpg',
torrentFileName: 'tearsOfSteel.torrent'
},
{
testID: 'wired',
name: 'The WIRED CD - Rip. Sample. Mash. Share',
posterFileName: 'wiredCd.jpg',
torrentFileName: 'wiredCd.torrent'
}
],
DELAYED_INIT: 3000 /* 3 seconds */,
DEFAULT_DOWNLOAD_PATH: getDefaultDownloadPath(),
GITHUB_URL: 'https://github.com/webtorrent/webtorrent-desktop',
GITHUB_URL_ISSUES: 'https://github.com/webtorrent/webtorrent-desktop/issues',
GITHUB_URL_RAW: 'https://raw.githubusercontent.com/webtorrent/webtorrent-desktop/master',
GITHUB_URL_RELEASES: 'https://github.com/webtorrent/webtorrent-desktop/releases',
HOME_PAGE_URL: 'https://webtorrent.io',
TWITTER_PAGE_URL: 'https://twitter.com/WebTorrentApp',
IS_PORTABLE,
IS_PRODUCTION,
IS_TEST,
OS_SYSARCH: arch() === 'x64' ? 'x64' : 'ia32',
POSTER_PATH: path.join(getConfigPath(), 'Posters'),
ROOT_PATH: path.join(__dirname, '..'),
STATIC_PATH: path.join(__dirname, '..', 'static'),
TORRENT_PATH: path.join(getConfigPath(), 'Torrents'),
WINDOW_ABOUT: 'file://' + path.join(__dirname, '..', 'static', 'about.html'),
WINDOW_MAIN: 'file://' + path.join(__dirname, '..', 'static', 'main.html'),
WINDOW_WEBTORRENT: 'file://' + path.join(__dirname, '..', 'static', 'webtorrent.html'),
WINDOW_INITIAL_BOUNDS: {
width: 500,
height: UI_HEADER_HEIGHT + (UI_TORRENT_HEIGHT * 6) // header + 6 torrents
},
WINDOW_MIN_HEIGHT: UI_HEADER_HEIGHT + (UI_TORRENT_HEIGHT * 2), // header + 2 torrents
WINDOW_MIN_WIDTH: 425,
UI_HEADER_HEIGHT,
UI_TORRENT_HEIGHT
}
function getConfigPath () {
if (IS_PORTABLE) {
return PORTABLE_PATH
@@ -35,12 +121,16 @@ function getDefaultDownloadPath () {
}
function getPath (key) {
if (!process.versions.electron || process.type !== 'browser') {
if (!process.versions.electron) {
// Node.js process
return ''
} else if (process.type === 'renderer') {
// Electron renderer process
return require('@electron/remote').app.getPath(key)
} else {
// Electron main process
return app.getPath(key)
}
// Electron main process
return electron.app.getPath(key)
}
function isTest () {
@@ -57,6 +147,8 @@ function isPortable () {
return false
}
const fs = require('fs')
try {
// This line throws if the "Portable Settings" folder does not exist, and does
// nothing otherwise.
@@ -82,117 +174,3 @@ function isProduction () {
return !/\/electron$/.test(process.execPath)
}
}
export const ANNOUNCEMENT_URL = 'https://webtorrent.io/desktop/announcement'
export const AUTO_UPDATE_URL = 'https://webtorrent.io/desktop/update'
export const CRASH_REPORT_URL = 'https://webtorrent.io/desktop/crash-report'
export const TELEMETRY_URL = 'https://webtorrent.io/desktop/telemetry'
export const APP_COPYRIGHT = `Copyright © 2014-${new Date().getFullYear()} ${APP_TEAM}`
export const APP_FILE_ICON = new URL('../static/WebTorrentFile', import.meta.url).pathname // path.join(__dirname, '..',
// 'static', 'WebTorrentFile')
export const APP_ICON = new URL('../static/WebTorrent', import.meta.url).pathname // path.join(__dirname, '..',
// 'static',
// 'WebTorrent')
export const CONFIG_PATH = getConfigPath()
export const DEFAULT_TORRENTS = [
{
testID: 'bbb',
name: 'Big Buck Bunny',
posterFileName: 'bigBuckBunny.jpg',
torrentFileName: 'bigBuckBunny.torrent'
},
{
testID: 'cosmos',
name: 'Cosmos Laundromat (Preview)',
posterFileName: 'cosmosLaundromat.jpg',
torrentFileName: 'cosmosLaundromat.torrent'
},
{
testID: 'sintel',
name: 'Sintel',
posterFileName: 'sintel.jpg',
torrentFileName: 'sintel.torrent'
},
{
testID: 'tears',
name: 'Tears of Steel',
posterFileName: 'tearsOfSteel.jpg',
torrentFileName: 'tearsOfSteel.torrent'
},
{
testID: 'wired',
name: 'The WIRED CD - Rip. Sample. Mash. Share',
posterFileName: 'wiredCd.jpg',
torrentFileName: 'wiredCd.torrent'
}
]
export const DELAYED_INIT = 3000 /* 3 seconds */
export const DEFAULT_DOWNLOAD_PATH = getDefaultDownloadPath()
export const GITHUB_URL = 'https://github.com/webtorrent/webtorrent-desktop'
export const GITHUB_URL_ISSUES = 'https://github.com/webtorrent/webtorrent-desktop/issues'
export const GITHUB_URL_RAW = 'https://raw.githubusercontent.com/webtorrent/webtorrent-desktop/master'
export const GITHUB_URL_RELEASES = 'https://github.com/webtorrent/webtorrent-desktop/releases'
export const HOME_PAGE_URL = 'https://webtorrent.io'
export const TWITTER_PAGE_URL = 'https://twitter.com/WebTorrentApp'
export const OS_SYSARCH = arch() === 'x64' ? 'x64' : 'ia32'
export const POSTER_PATH = path.join(getConfigPath(), 'Posters')
export const ROOT_PATH = new URL('../', import.meta.url).pathname
export const STATIC_PATH = new URL('../static', import.meta.url).pathname
export const TORRENT_PATH = path.join(getConfigPath(), 'Torrents')
export const WINDOW_ABOUT = 'file://' + new URL('../static/about.html', import.meta.url).pathname
export const WINDOW_MAIN = 'file://' + new URL('../static/main.html', import.meta.url).pathname
export const WINDOW_WEBTORRENT = 'file://' + new URL('../static/webtorrent.html', import.meta.url).pathname
export const WINDOW_INITIAL_BOUNDS = {
width: 500,
height: UI_HEADER_HEIGHT + (UI_TORRENT_HEIGHT * 6) // header + 6 torrents
}
export const WINDOW_MIN_HEIGHT = UI_HEADER_HEIGHT + (UI_TORRENT_HEIGHT * 2)
export const WINDOW_MIN_WIDTH = 425
export { APP_NAME }
export { APP_TEAM }
export { APP_VERSION }
export { APP_NAME as APP_WINDOW_TITLE }
export { IS_PORTABLE }
export { IS_PRODUCTION }
export { IS_TEST }
export { UI_HEADER_HEIGHT }
export { UI_TORRENT_HEIGHT }
export default {
ANNOUNCEMENT_URL,
AUTO_UPDATE_URL,
CRASH_REPORT_URL,
TELEMETRY_URL,
APP_COPYRIGHT,
APP_FILE_ICON,
APP_ICON,
APP_NAME,
APP_TEAM,
APP_VERSION,
APP_WINDOW_TITLE: APP_NAME,
CONFIG_PATH,
DEFAULT_TORRENTS,
DELAYED_INIT,
DEFAULT_DOWNLOAD_PATH,
GITHUB_URL,
GITHUB_URL_ISSUES,
GITHUB_URL_RAW,
GITHUB_URL_RELEASES,
HOME_PAGE_URL,
TWITTER_PAGE_URL,
IS_PORTABLE,
IS_PRODUCTION,
IS_TEST,
OS_SYSARCH,
POSTER_PATH,
ROOT_PATH,
STATIC_PATH,
TORRENT_PATH,
WINDOW_ABOUT,
WINDOW_MAIN,
WINDOW_WEBTORRENT,
WINDOW_INITIAL_BOUNDS,
WINDOW_MIN_HEIGHT,
WINDOW_MIN_WIDTH,
UI_HEADER_HEIGHT,
UI_TORRENT_HEIGHT
}

View File

@@ -1,13 +1,15 @@
import electron from '../electron.cjs'
module.exports = {
init
}
async function init () {
const config = await import('./config.js')
electron.crashReporter.start({
function init () {
const config = require('./config')
const { crashReporter } = require('electron')
crashReporter.start({
productName: config.APP_NAME,
submitURL: config.CRASH_REPORT_URL,
globalExtra: { _companyName: config.APP_NAME },
compress: true
})
}
export default { init }

View File

@@ -1,6 +1,11 @@
import electron from '../../electron.cjs'
import config from '../config.js'
import log from './log.js'
module.exports = {
init
}
const { dialog } = require('electron')
const config = require('../config')
const log = require('./log')
const ANNOUNCEMENT_URL =
`${config.ANNOUNCEMENT_URL}?version=${config.APP_VERSION}&platform=${process.platform}`
@@ -19,8 +24,8 @@ const ANNOUNCEMENT_URL =
* "detail": "Please update to v0.xx as soon as possible..."
* }
*/
async function init () {
const { default: get } = await import('simple-get')
function init () {
const get = require('simple-get')
get.concat(ANNOUNCEMENT_URL, onResponse)
}
@@ -39,7 +44,7 @@ function onResponse (err, res, data) {
}
}
electron.dialog.showMessageBox({
dialog.showMessageBox({
type: 'info',
buttons: ['OK'],
title: data.title,
@@ -47,5 +52,3 @@ function onResponse (err, res, data) {
detail: data.detail
})
}
export default { init }

View File

@@ -1,13 +1,20 @@
import electron from '../../electron.cjs'
import log from './log.js'
import * as windows from './windows/index.js'
module.exports = {
openSeedFile,
openSeedDirectory,
openTorrentFile,
openTorrentAddress,
openFiles
}
const { dialog } = electron
const { dialog } = require('electron')
const log = require('./log')
const windows = require('./windows')
/**
* Show open dialog to create a single-file torrent.
*/
export function openSeedFile () {
function openSeedFile () {
if (!windows.main.win) return
log('openSeedFile')
const opts = {
@@ -22,7 +29,7 @@ export function openSeedFile () {
* Windows and Linux, open dialogs are for files *or* directories only, not both,
* so this function shows a directory dialog on those platforms.
*/
export function openSeedDirectory () {
function openSeedDirectory () {
if (!windows.main.win) return
log('openSeedDirectory')
const opts = process.platform === 'darwin'
@@ -41,7 +48,7 @@ export function openSeedDirectory () {
* Show flexible open dialog that supports selecting .torrent files to add, or
* a file or folder to create a single-file or single-directory torrent.
*/
export function openFiles () {
function openFiles () {
if (!windows.main.win) return
log('openFiles')
const opts = process.platform === 'darwin'
@@ -63,7 +70,7 @@ export function openFiles () {
/*
* Show open dialog to open a .torrent file.
*/
export function openTorrentFile () {
function openTorrentFile () {
if (!windows.main.win) return
log('openTorrentFile')
const opts = {
@@ -83,7 +90,7 @@ export function openTorrentFile () {
/*
* Show modal dialog to open a torrent URL (magnet uri, http torrent link, etc.)
*/
export function openTorrentAddress () {
function openTorrentAddress () {
log('openTorrentAddress')
windows.main.dispatch('openTorrentAddress')
}

View File

@@ -1,8 +1,13 @@
import electron from '../../electron.cjs'
import * as dialog from './dialog.js'
import log from './log.js'
module.exports = {
downloadFinished,
init,
setBadge
}
const { app, Menu } = electron
const { app, Menu } = require('electron')
const dialog = require('./dialog')
const log = require('./log')
/**
* Add a right-click menu to the dock icon. (Mac)
@@ -52,5 +57,3 @@ function getMenuTemplate () {
}
]
}
export default { downloadFinished, init, setBadge }

View File

@@ -1,19 +1,27 @@
import cp from 'child_process'
import path from 'path'
import vlcCommand from 'vlc-command'
import log from './log.js'
import * as windows from './windows'
module.exports = {
spawn,
kill,
checkInstall
}
const cp = require('child_process')
const path = require('path')
const vlcCommand = require('vlc-command')
const log = require('./log')
const windows = require('./windows')
// holds a ChildProcess while we're playing a video in an external player, null otherwise
let proc = null
export function checkInstall (playerPath, cb) {
function checkInstall (playerPath, cb) {
// check for VLC if external player has not been specified by the user
// otherwise assume the player is installed
if (!playerPath) return vlcCommand(cb)
process.nextTick(() => cb(null))
}
export function spawn (playerPath, url, title) {
function spawn (playerPath, url, title) {
if (playerPath) return spawnExternal(playerPath, [url])
// Try to find and use VLC if external player is not specified
@@ -29,7 +37,7 @@ export function spawn (playerPath, url, title) {
})
}
export function kill () {
function kill () {
if (!proc) return
log(`Killing external player, pid ${proc.pid}`)
proc.kill('SIGKILL') // kill -9

View File

@@ -1,7 +1,7 @@
import * as chokidar from 'chokidar'
import log from './log.js'
const chokidar = require('chokidar')
const log = require('./log')
export class FolderWatcher {
class FolderWatcher {
constructor ({ window, state }) {
this.window = window
this.state = state
@@ -46,3 +46,5 @@ export class FolderWatcher {
this.watching = false
}
}
module.exports = FolderWatcher

View File

@@ -1,7 +1,12 @@
import config from '../config.js'
import path from 'path'
module.exports = {
install,
uninstall
}
export function install () {
const config = require('../config')
const path = require('path')
function install () {
switch (process.platform) {
case 'darwin': installDarwin()
break
@@ -10,7 +15,7 @@ export function install () {
}
}
export function uninstall () {
function uninstall () {
switch (process.platform) {
case 'darwin': uninstallDarwin()
break
@@ -19,8 +24,9 @@ export function uninstall () {
}
}
async function installDarwin () {
const { app } = await import('electron')
function installDarwin () {
const { app } = require('electron')
// On Mac, only protocols that are listed in `Info.plist` can be set as the
// default handler at runtime.
app.setAsDefaultProtocolClient('magnet')
@@ -37,14 +43,33 @@ if (!config.IS_PRODUCTION) {
EXEC_COMMAND.push(config.ROOT_PATH)
}
async function installWin32 () {
const Registry = await import('wingreg')
const log = await import('./log')
function installWin32 () {
const Registry = require('winreg')
const iconPath = path.join(process.resourcesPath, 'app.asar.unpacked', 'static', 'WebTorrentFile.ico')
registerProtocolHandlerWin32('magnet', 'URL:BitTorrent Magnet URL', iconPath, EXEC_COMMAND)
registerProtocolHandlerWin32('stream-magnet', 'URL:BitTorrent Stream-Magnet URL', iconPath, EXEC_COMMAND)
registerFileHandlerWin32('.torrent', 'io.webtorrent.torrent', 'BitTorrent Document', iconPath, EXEC_COMMAND)
const log = require('./log')
const iconPath = path.join(
process.resourcesPath, 'app.asar.unpacked', 'static', 'WebTorrentFile.ico'
)
registerProtocolHandlerWin32(
'magnet',
'URL:BitTorrent Magnet URL',
iconPath,
EXEC_COMMAND
)
registerProtocolHandlerWin32(
'stream-magnet',
'URL:BitTorrent Stream-Magnet URL',
iconPath,
EXEC_COMMAND
)
registerFileHandlerWin32(
'.torrent',
'io.webtorrent.torrent',
'BitTorrent Document',
iconPath,
EXEC_COMMAND
)
/**
* To add a protocol handler, the following keys must be added to the Windows registry:
@@ -172,8 +197,9 @@ async function installWin32 () {
}
}
async function uninstallWin32 () {
const Registry = await import('winreg')
function uninstallWin32 () {
const Registry = require('winreg')
unregisterProtocolHandlerWin32('magnet', EXEC_COMMAND)
unregisterProtocolHandlerWin32('stream-magnet', EXEC_COMMAND)
unregisterFileHandlerWin32('.torrent', 'io.webtorrent.torrent', EXEC_COMMAND)

View File

@@ -1,25 +1,22 @@
/* eslint-disable import/first */
console.time('init')
import * as RemoteMain from '@electron/remote/main/index.js'
RemoteMain.initialize()
import electron from '../../electron.cjs'
const { app, ipcMain } = electron
require('@electron/remote/main').initialize()
const { app, ipcMain } = require('electron')
// Start crash reporter early, so it takes effect for child processes
import crashReporter from '../crash-reporter.js'
const crashReporter = require('../crash-reporter')
crashReporter.init()
import fs from 'fs'
import parallel from 'run-parallel'
import config from '../config.js'
import ipc from './ipc.js'
import log from './log.js'
import menu from './menu.js'
import State from '../renderer/lib/state.js'
import * as windows from './windows/index.js'
const parallel = require('run-parallel')
const WEBTORRENT_VERSION = JSON.parse(fs.readFileSync('node_modules/webtorrent/package.json').toString()).version
const config = require('../config')
const ipc = require('./ipc')
const log = require('./log')
const menu = require('./menu')
const State = require('../renderer/lib/state')
const windows = require('./windows')
const WEBTORRENT_VERSION = require('webtorrent/package.json').version
let shouldQuit = false
let argv = sliceArgv(process.argv)
@@ -57,18 +54,13 @@ if (!shouldQuit && !config.IS_PORTABLE) {
if (shouldQuit) {
app.quit()
} else {
init().catch(console.error)
init()
}
async function init () {
console.log('index init')
app.whenReady().then(() => {
console.log('readyyyy')
})
function init () {
app.on('second-instance', (event, commandLine, workingDirectory) => onAppOpen(commandLine))
if (config.IS_PORTABLE) {
console.log('is portable')
const path = await import('path')
const path = require('path')
// Put all user data into the "Portable Settings" folder
app.setPath('userData', config.CONFIG_PATH)
// Put Electron crash files, etc. into the "Portable Settings\Temp" folder
@@ -80,7 +72,7 @@ async function init () {
app.isQuitting = false
parallel({
appReady: (cb) => app.whenReady().then(cb),
appReady: (cb) => app.on('ready', () => cb(null)),
state: (cb) => State.load(cb)
}, onReady)
@@ -138,18 +130,17 @@ async function init () {
})
app.on('activate', () => {
console.log('activate')
if (isReady) windows.main.show()
})
}
async function delayedInit (state) {
function delayedInit (state) {
if (app.isQuitting) return
const { default: announcement } = await import('./announcement.js')
const { default: dock } = await import('./dock.js')
const { default: updater } = await import('./updater.js')
const { FolderWatcher } = await import('./folder-watcher.js')
const announcement = require('./announcement')
const dock = require('./dock')
const updater = require('./updater')
const FolderWatcher = require('./folder-watcher')
const folderWatcher = new FolderWatcher({ window: windows.main, state })
announcement.init()
@@ -162,13 +153,13 @@ async function delayedInit (state) {
}
if (process.platform === 'win32') {
const userTasks = await import('./user-tasks.js')
const userTasks = require('./user-tasks')
userTasks.init()
}
if (process.platform !== 'darwin') {
const { init: trayInit } = await import('./tray.js')
trayInit()
const tray = require('./tray')
tray.init()
}
}
@@ -214,12 +205,12 @@ function sliceArgv (argv) {
)
}
async function processArgv (argv) {
function processArgv (argv) {
const torrentIds = []
await Promise.all(argv.forEach(async arg => {
argv.forEach(arg => {
if (arg === '-n' || arg === '-o' || arg === '-u') {
// Critical path: Only load the 'dialog' package if it is needed
const dialog = await import('./dialog')
const dialog = require('./dialog')
if (arg === '-n') {
dialog.openSeedDirectory()
} else if (arg === '-o') {
@@ -242,7 +233,7 @@ async function processArgv (argv) {
// running.
torrentIds.push(arg)
}
}))
})
if (torrentIds.length > 0) {
windows.main.dispatch('onOpen', torrentIds)
}

View File

@@ -1,10 +1,13 @@
import electron from '../../electron.cjs'
module.exports = {
init,
setModule
}
import log from './log.js'
import menu from './menu.js'
import * as windows from './windows/index.js'
const { app, ipcMain } = require('electron')
const { app, ipcMain } = electron
const log = require('./log')
const menu = require('./menu')
const windows = require('./windows')
// Messages from the main process, to be sent once the WebTorrent process starts
const messageQueueMainToWebTorrent = []
@@ -37,12 +40,12 @@ function init () {
* Dialog
*/
ipcMain.on('openTorrentFile', async () => {
const dialog = await import('./dialog')
ipcMain.on('openTorrentFile', () => {
const dialog = require('./dialog')
dialog.openTorrentFile()
})
ipcMain.on('openFiles', async () => {
const dialog = await import('./dialog')
ipcMain.on('openFiles', () => {
const dialog = require('./dialog')
dialog.openFiles()
})
@@ -50,12 +53,12 @@ function init () {
* Dock
*/
ipcMain.on('setBadge', async (e, ...args) => {
const dock = await import('./dock')
ipcMain.on('setBadge', (e, ...args) => {
const dock = require('./dock')
dock.setBadge(...args)
})
ipcMain.on('downloadFinished', async (e, ...args) => {
const dock = await import('./dock')
ipcMain.on('downloadFinished', (e, ...args) => {
const dock = require('./dock')
dock.downloadFinished(...args)
})
@@ -63,10 +66,10 @@ function init () {
* Player Events
*/
ipcMain.on('onPlayerOpen', async () => {
const powerSaveBlocker = await import('./power-save-blocker')
const shortcuts = await import('./shortcuts')
const thumbar = await import('./thumbar')
ipcMain.on('onPlayerOpen', () => {
const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar')
menu.togglePlaybackControls(true)
powerSaveBlocker.enable()
@@ -74,17 +77,17 @@ function init () {
thumbar.enable()
})
ipcMain.on('onPlayerUpdate', async (e, ...args) => {
const thumbar = await import('./thumbar')
ipcMain.on('onPlayerUpdate', (e, ...args) => {
const thumbar = require('./thumbar')
menu.onPlayerUpdate(...args)
thumbar.onPlayerUpdate(...args)
})
ipcMain.on('onPlayerClose', async () => {
const powerSaveBlocker = await import('./power-save-blocker')
const shortcuts = await import('./shortcuts')
const thumbar = await import('./thumbar')
ipcMain.on('onPlayerClose', () => {
const powerSaveBlocker = require('./power-save-blocker')
const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar')
menu.togglePlaybackControls(false)
powerSaveBlocker.disable()
@@ -92,17 +95,17 @@ function init () {
thumbar.disable()
})
ipcMain.on('onPlayerPlay', async () => {
const powerSaveBlocker = await import('./power-save-blocker')
const thumbar = await import('./thumbar')
ipcMain.on('onPlayerPlay', () => {
const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar')
powerSaveBlocker.enable()
thumbar.onPlayerPlay()
})
ipcMain.on('onPlayerPause', async () => {
const powerSaveBlocker = await import('./power-save-blocker')
const thumbar = await import('./thumbar')
ipcMain.on('onPlayerPause', () => {
const powerSaveBlocker = require('./power-save-blocker')
const thumbar = require('./thumbar')
powerSaveBlocker.disable()
thumbar.onPlayerPause()
@@ -134,16 +137,16 @@ function init () {
* Shell
*/
ipcMain.on('openPath', async (e, ...args) => {
const shell = await import('./shell')
ipcMain.on('openPath', (e, ...args) => {
const shell = require('./shell')
shell.openPath(...args)
})
ipcMain.on('showItemInFolder', async (e, ...args) => {
const shell = await import('./shell')
ipcMain.on('showItemInFolder', (e, ...args) => {
const shell = require('./shell')
shell.showItemInFolder(...args)
})
ipcMain.on('moveItemToTrash', async (e, ...args) => {
const shell = await import('./shell')
ipcMain.on('moveItemToTrash', (e, ...args) => {
const shell = require('./shell')
shell.moveItemToTrash(...args)
})
@@ -151,8 +154,8 @@ function init () {
* File handlers
*/
ipcMain.on('setDefaultFileHandler', async (e, flag) => {
const handlers = await import('./handlers')
ipcMain.on('setDefaultFileHandler', (e, flag) => {
const handlers = require('./handlers')
if (flag) handlers.install()
else handlers.uninstall()
@@ -162,8 +165,8 @@ function init () {
* Auto start on login
*/
ipcMain.on('setStartup', async (e, flag) => {
const startup = await import('./startup')
ipcMain.on('setStartup', (e, flag) => {
const startup = require('./startup')
if (flag) startup.install()
else startup.uninstall()
@@ -187,18 +190,18 @@ function init () {
* External Media Player
*/
ipcMain.on('checkForExternalPlayer', async (e, path) => {
const externalPlayer = await import('./external-player')
ipcMain.on('checkForExternalPlayer', (e, path) => {
const externalPlayer = require('./external-player')
externalPlayer.checkInstall(path, err => {
windows.main.send('checkForExternalPlayer', !err)
})
})
ipcMain.on('openExternalPlayer', async (e, ...args) => {
const externalPlayer = await import('./external-player')
const shortcuts = await import('./shortcuts')
const thumbar = await import('./thumbar')
ipcMain.on('openExternalPlayer', (e, ...args) => {
const externalPlayer = require('./external-player')
const shortcuts = require('./shortcuts')
const thumbar = require('./thumbar')
menu.togglePlaybackControls(false)
shortcuts.disable()
@@ -206,8 +209,8 @@ function init () {
externalPlayer.spawn(...args)
})
ipcMain.on('quitExternalPlayer', async () => {
const externalPlayer = await import('./external-player')
ipcMain.on('quitExternalPlayer', () => {
const externalPlayer = require('./external-player')
externalPlayer.kill()
})
@@ -243,10 +246,3 @@ function init () {
oldEmit.call(ipcMain, name, e, ...args)
}
}
export { init }
export { setModule }
export default {
init,
setModule
}

View File

@@ -1,7 +1,5 @@
import electron from '../../electron.cjs'
import * as windows from './windows/index.js'
const { app } = electron
module.exports = log
module.exports.error = error
/**
* In the main electron process, we do not use console.log() statements because they do
@@ -9,7 +7,11 @@ const { app } = electron
* version of the app. Instead use this module, which sends the logs to the main window
* where they can be viewed in Developer Tools.
*/
export default function log (...args) {
const { app } = require('electron')
const windows = require('./windows')
function log (...args) {
if (app.ipcReady) {
windows.main.send('log', ...args)
} else {
@@ -17,7 +19,7 @@ export default function log (...args) {
}
}
export function error (...args) {
function error (...args) {
if (app.ipcReady) {
windows.main.send('error', ...args)
} else {

View File

@@ -1,14 +1,23 @@
import electron from '../../electron.cjs'
import config from '../config.js'
import * as windows from './windows/index.js'
import shell from './shell.js'
module.exports = {
init,
togglePlaybackControls,
setWindowFocus,
setAllowNav,
onPlayerUpdate,
onToggleAlwaysOnTop,
onToggleFullScreen
}
const { app, Menu } = require('electron')
const config = require('../config')
const windows = require('./windows')
let menu = null
function init () {
console.log('menu init')
menu = electron.Menu.buildFromTemplate(getMenuTemplate())
electron.Menu.setApplicationMenu(menu)
menu = Menu.buildFromTemplate(getMenuTemplate())
Menu.setApplicationMenu(menu)
}
function togglePlaybackControls (flag) {
@@ -76,24 +85,24 @@ function getMenuTemplate () {
? 'Create New Torrent...'
: 'Create New Torrent from Folder...',
accelerator: 'CmdOrCtrl+N',
click: async () => {
const dialog = await import('./dialog')
click: () => {
const dialog = require('./dialog')
dialog.openSeedDirectory()
}
},
{
label: 'Open Torrent File...',
accelerator: 'CmdOrCtrl+O',
click: async () => {
const dialog = await import('./dialog')
click: () => {
const dialog = require('./dialog')
dialog.openTorrentFile()
}
},
{
label: 'Open Torrent Address...',
accelerator: 'CmdOrCtrl+U',
click: async () => {
const dialog = await import('./dialog')
click: () => {
const dialog = require('./dialog')
dialog.openTorrentAddress()
}
},
@@ -294,18 +303,21 @@ function getMenuTemplate () {
{
label: 'Learn more about ' + config.APP_NAME,
click: () => {
const shell = require('./shell')
shell.openExternal(config.HOME_PAGE_URL)
}
},
{
label: 'Release Notes',
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_RELEASES)
}
},
{
label: 'Contribute on GitHub',
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL)
}
},
@@ -315,18 +327,21 @@ function getMenuTemplate () {
{
label: 'Report an Issue...',
click: () => {
const shell = require('./shell')
shell.openExternal(config.GITHUB_URL_ISSUES)
}
},
{
label: 'Follow us on Twitter',
click: () => {
const shell = require('./shell')
shell.openExternal(config.TWITTER_PAGE_URL)
}
}
]
}
]
if (process.platform === 'darwin') {
// WebTorrent menu (Mac)
template.unshift({
@@ -411,8 +426,8 @@ function getMenuTemplate () {
// File menu (Windows, Linux)
template[0].submenu.unshift({
label: 'Create New Torrent from File...',
click: async () => {
const dialog = await import('./dialog')
click: () => {
const dialog = require('./dialog')
dialog.openSeedFile()
}
})
@@ -445,26 +460,9 @@ function getMenuTemplate () {
// File menu (Linux)
template[0].submenu.push({
label: 'Quit',
click: () => electron.app.quit()
click: () => app.quit()
})
}
return template
}
export { init }
export { togglePlaybackControls }
export { setWindowFocus }
export { setAllowNav }
export { onPlayerUpdate }
export { onToggleAlwaysOnTop }
export { onToggleFullScreen }
export default {
init,
togglePlaybackControls,
setWindowFocus,
setAllowNav,
onPlayerUpdate,
onToggleAlwaysOnTop,
onToggleFullScreen
}

View File

@@ -1,5 +1,10 @@
import { powerSaveBlocker } from 'electron'
import log from './log.js'
module.exports = {
enable,
disable
}
const { powerSaveBlocker } = require('electron')
const log = require('./log')
let blockId = 0
@@ -7,7 +12,7 @@ let blockId = 0
* Block the system from entering low-power (sleep) mode or turning off the
* display.
*/
export function enable () {
function enable () {
if (powerSaveBlocker.isStarted(blockId)) {
// If a power saver block already exists, do nothing.
return
@@ -19,7 +24,7 @@ export function enable () {
/**
* Stop blocking the system from entering low-power mode.
*/
export function disable () {
function disable () {
if (!powerSaveBlocker.isStarted(blockId)) {
// If a power saver block does not exist, do nothing.
return

View File

@@ -1,7 +1,12 @@
import electron from '../../electron.cjs'
import log from './log.js'
module.exports = {
openExternal,
openPath,
showItemInFolder,
moveItemToTrash
}
const { shell } = electron
const { shell } = require('electron')
const log = require('./log')
/**
* Open the given external protocol URL in the desktops default manner.
@@ -14,6 +19,7 @@ function openExternal (url) {
/**
* Open the given file in the desktops default manner.
*/
function openPath (path) {
log(`openPath: ${path}`)
shell.openPath(path)
@@ -34,10 +40,3 @@ function moveItemToTrash (path) {
log(`moveItemToTrash: ${path}`)
shell.trashItem(path)
}
export default {
openExternal,
openPath,
showItemInFolder,
moveItemToTrash
}

View File

@@ -1,7 +1,12 @@
import { globalShortcut } from 'electron'
import * as windows from './windows'
module.exports = {
disable,
enable
}
export function enable () {
const { globalShortcut } = require('electron')
const windows = require('./windows')
function enable () {
// Register play/pause media key, available on some keyboards.
globalShortcut.register(
'MediaPlayPause',
@@ -17,7 +22,7 @@ export function enable () {
)
}
export function disable () {
function disable () {
// Return the media key to the OS, so other apps can use it.
globalShortcut.unregister('MediaPlayPause')
globalShortcut.unregister('MediaNextTrack')

View File

@@ -1,16 +1,23 @@
import { app } from 'electron'
import path from 'path'
import { spawn } from 'child_process'
import handlers from './handlers.js'
module.exports = {
handleEvent
}
const { app } = require('electron')
const path = require('path')
const spawn = require('child_process').spawn
const handlers = require('./handlers')
const EXE_NAME = path.basename(process.execPath)
const UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe')
const run = (args, done) => {
spawn(UPDATE_EXE, args, { detached: true }).on('close', done)
spawn(UPDATE_EXE, args, { detached: true })
.on('close', done)
}
export function handleEvent (cmd) {
function handleEvent (cmd) {
if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
run([`--createShortcut=${EXE_NAME}`], app.quit)
return true

View File

@@ -1,12 +1,17 @@
import { APP_NAME } from '../config.js'
import AutoLaunch from 'auto-launch'
module.exports = {
install,
uninstall
}
const { APP_NAME } = require('../config')
const AutoLaunch = require('auto-launch')
const appLauncher = new AutoLaunch({
name: APP_NAME,
isHidden: true
})
export function install () {
function install () {
return appLauncher
.isEnabled()
.then(enabled => {
@@ -14,7 +19,7 @@ export function install () {
})
}
export function uninstall () {
function uninstall () {
return appLauncher
.isEnabled()
.then(enabled => {

View File

@@ -1,6 +1,21 @@
import path from 'path'
import config from '../config.js'
import * as windows from './windows'
module.exports = {
disable,
enable,
onPlayerPause,
onPlayerPlay,
onPlayerUpdate
}
/**
* On Windows, add a "thumbnail toolbar" with a play/pause button in the taskbar.
* This provides users a way to access play/pause functionality without restoring
* or activating the window.
*/
const path = require('path')
const config = require('../config')
const windows = require('./windows')
const PREV_ICON = path.join(config.STATIC_PATH, 'PreviousTrackThumbnailBarButton.png')
const PLAY_ICON = path.join(config.STATIC_PATH, 'PlayThumbnailBarButton.png')
@@ -17,7 +32,7 @@ let buttons = []
/**
* Show the Windows thumbnail toolbar buttons.
*/
export function enable () {
function enable () {
buttons = [
{
tooltip: 'Previous Track',
@@ -41,26 +56,26 @@ export function enable () {
/**
* Hide the Windows thumbnail toolbar buttons.
*/
export function disable () {
function disable () {
buttons = []
update()
}
export function onPlayerPause () {
function onPlayerPause () {
if (!isEnabled()) return
buttons[PLAY_PAUSE].tooltip = 'Play'
buttons[PLAY_PAUSE].icon = PLAY_ICON
update()
}
export function onPlayerPlay () {
function onPlayerPlay () {
if (!isEnabled()) return
buttons[PLAY_PAUSE].tooltip = 'Pause'
buttons[PLAY_PAUSE].icon = PAUSE_ICON
update()
}
export function onPlayerUpdate (state) {
function onPlayerUpdate (state) {
if (!isEnabled()) return
buttons[PREV].flags = [state.hasPrevious ? 'enabled' : 'disabled']
buttons[NEXT].flags = [state.hasNext ? 'enabled' : 'disabled']

View File

@@ -1,10 +1,17 @@
import { app, Tray, Menu } from 'electron'
import config from '../config.js'
import * as windows from './windows'
module.exports = {
hasTray,
init,
setWindowFocus
}
const { app, Tray, Menu } = require('electron')
const config = require('../config')
const windows = require('./windows')
let tray
export function init () {
function init () {
if (process.platform === 'linux') {
initLinux()
}
@@ -17,11 +24,11 @@ export function init () {
/**
* Returns true if there a tray icon is active.
*/
export function hasTray () {
function hasTray () {
return !!tray
}
export function setWindowFocus (flag) {
function setWindowFocus (flag) {
if (!tray) return
updateTrayMenu()
}
@@ -39,8 +46,8 @@ function initWin32 () {
/**
* Check for libappindicator support before creating tray icon.
*/
async function checkLinuxTraySupport (cb) {
const cp = await import('child_process')
function checkLinuxTraySupport (cb) {
const cp = require('child_process')
// Check that libappindicator libraries are installed in system.
cp.exec('ldconfig -p | grep libappindicator', (err, stdout) => {

View File

@@ -1,10 +1,13 @@
import electron from '../../electron.cjs'
import get from 'simple-get'
import config from '../config.js'
import log from './log.js'
import * as windows from './windows/index.js'
module.exports = {
init
}
const { autoUpdater } = electron
const { autoUpdater } = require('electron')
const get = require('simple-get')
const config = require('../config')
const log = require('./log')
const windows = require('./windows')
const AUTO_UPDATE_URL = config.AUTO_UPDATE_URL +
'?version=' + config.APP_VERSION +
@@ -19,8 +22,6 @@ function init () {
}
}
export default { init }
// The Electron auto-updater does not support Linux yet, so manually check for
// updates and show the user a modal notification.
function initLinux () {
@@ -48,7 +49,7 @@ function onResponse (err, res, data) {
function initDarwinWin32 () {
autoUpdater.on(
'error',
(err) => log(`Update error: ${err.message}`)
(err) => log.error(`Update error: ${err.message}`)
)
autoUpdater.on(

View File

@@ -1,9 +1,13 @@
import { app } from 'electron'
module.exports = {
init
}
const { app } = require('electron')
/**
* Add a user task menu to the app icon on right-click. (Windows)
*/
export function init () {
function init () {
if (process.platform !== 'win32') return
app.setUserTasks(getUserTasks())
}

View File

@@ -1,18 +1,17 @@
import config from '../../config.js'
import electron from '../../../electron.cjs'
import * as RemoteMain from '@electron/remote/main/index.js'
export const about = {
const about = module.exports = {
init,
win: null
}
const config = require('../../config')
const { BrowserWindow } = require('electron')
function init () {
if (about.win) {
return about.win.show()
}
const win = about.win = new electron.BrowserWindow({
const win = about.win = new BrowserWindow({
backgroundColor: '#ECECEC',
center: true,
fullscreen: false,
@@ -34,7 +33,8 @@ function init () {
},
width: 300
})
RemoteMain.enable(win.webContents)
require('@electron/remote/main').enable(win.webContents)
win.loadURL(config.WINDOW_ABOUT)
win.once('ready-to-show', () => {

View File

@@ -1,3 +1,3 @@
export * from './about.js'
export * from './main.js'
export * from './webtorrent.js'
exports.about = require('./about')
exports.main = require('./main')
exports.webtorrent = require('./webtorrent')

View File

@@ -1,13 +1,4 @@
import debounce from 'debounce'
import electron from '../../../electron.cjs'
import config from '../../config.js'
import log from '../log.js'
import menu from '../menu.js'
import * as RemoteMain from '@electron/remote/main/index.js'
const { app, BrowserWindow, screen } = electron
export const main = {
const main = module.exports = {
dispatch,
hide,
init,
@@ -23,6 +14,13 @@ export const main = {
win: null
}
const { app, BrowserWindow, screen } = require('electron')
const debounce = require('debounce')
const config = require('../../config')
const log = require('../log')
const menu = require('../menu')
function init (state, options) {
if (main.win) {
return main.win.show()
@@ -52,8 +50,8 @@ function init (state, options) {
x: initialBounds.x,
y: initialBounds.y
})
RemoteMain.enable(win.webContents)
console.log(config.WINDOW_MAIN)
require('@electron/remote/main').enable(win.webContents)
win.loadURL(config.WINDOW_MAIN)
win.once('ready-to-show', () => {
@@ -93,17 +91,17 @@ function init (state, options) {
})
win.on('move', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds())
send('windowBoundsChanged', main.win.getBounds())
}, 1000))
win.on('resize', debounce(e => {
send('windowBoundsChanged', e.sender.getBounds())
send('windowBoundsChanged', main.win.getBounds())
}, 1000))
win.on('close', async e => {
win.on('close', e => {
if (process.platform !== 'darwin') {
const { hasTray } = await import('../tray.js')
if (!hasTray()) {
const tray = require('../tray')
if (!tray.hasTray()) {
app.quit()
return
}

View File

@@ -1,8 +1,4 @@
import electron from '../../../electron.cjs'
import config from '../../config.js'
import * as RemoteMain from '@electron/remote/main/index.js'
export const webtorrent = {
const webtorrent = module.exports = {
init,
send,
show,
@@ -10,8 +6,12 @@ export const webtorrent = {
win: null
}
const { app, BrowserWindow } = require('electron')
const config = require('../../config')
function init () {
const win = webtorrent.win = new electron.BrowserWindow({
const win = webtorrent.win = new BrowserWindow({
backgroundColor: '#1E1E1E',
center: true,
fullscreen: false,
@@ -33,11 +33,13 @@ function init () {
},
width: 150
})
RemoteMain.enable(win.webContents)
require('@electron/remote/main').enable(win.webContents)
win.loadURL(config.WINDOW_WEBTORRENT)
// Prevent killing the WebTorrent process
win.on('close', e => {
if (electron.app.isQuitting) {
if (app.isQuitting) {
return
}
e.preventDefault()

View File

@@ -1,7 +1,8 @@
import * as React from 'react'
import { dispatcher } from '../lib/dispatcher.js'
const React = require('react')
export default class CreateTorrentErrorPage extends React.Component {
const { dispatcher } = require('../lib/dispatcher')
module.exports = class CreateTorrentErrorPage extends React.Component {
render () {
return (
<div className='create-torrent'>

View File

@@ -1,8 +1,9 @@
import * as React from 'react'
import ModalOKCancel from './modal-ok-cancel.js'
import { dispatch, dispatcher } from '../lib/dispatcher.js'
const React = require('react')
export default class DeleteAllTorrentsModal extends React.Component {
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

View File

@@ -1,7 +1,8 @@
import * as React from 'react'
import { dispatcher } from '../lib/dispatcher.js'
const React = require('react')
export default class Header extends React.Component {
const { dispatcher } = require('../lib/dispatcher')
class Header extends React.Component {
render () {
const loc = this.props.state.location
return (
@@ -10,6 +11,7 @@ export default class Header extends React.Component {
onMouseMove={dispatcher('mediaMouseMoved')}
onMouseEnter={dispatcher('mediaControlsMouseEnter')}
onMouseLeave={dispatcher('mediaControlsMouseLeave')}
role='navigation'
>
{this.getTitle()}
<div className='nav left float-left'>
@@ -17,6 +19,9 @@ export default class Header extends React.Component {
className={'icon back ' + (loc.hasBack() ? '' : 'disabled')}
title='Back'
onClick={dispatcher('back')}
role='button'
aria-disabled={!loc.hasBack()}
aria-label='Back'
>
chevron_left
</i>
@@ -24,6 +29,9 @@ export default class Header extends React.Component {
className={'icon forward ' + (loc.hasForward() ? '' : 'disabled')}
title='Forward'
onClick={dispatcher('forward')}
role='button'
aria-disabled={!loc.hasForward()}
aria-label='Forward'
>
chevron_right
</i>
@@ -49,9 +57,12 @@ export default class Header extends React.Component {
className='icon add'
title='Add torrent'
onClick={dispatcher('openFiles')}
role='button'
>
add
</i>
)
}
}
module.exports = Header

View File

@@ -1,8 +1,9 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import * as colors from 'material-ui/styles/colors'
const React = require('react')
const PropTypes = require('prop-types')
export default class Heading extends React.Component {
const colors = require('material-ui/styles/colors')
class Heading extends React.Component {
static get propTypes () {
return {
level: PropTypes.number
@@ -30,3 +31,5 @@ export default class Heading extends React.Component {
)
}
}
module.exports = Heading

View File

@@ -1,10 +1,8 @@
import * as React from 'react'
import flatButton from 'material-ui/FlatButton'
import raisedButton from 'material-ui/RaisedButton'
const React = require('react')
const FlatButton = require('material-ui/FlatButton').default
const RaisedButton = require('material-ui/RaisedButton').default
const FlatButton = flatButton.default
const RaisedButton = raisedButton.default
export default class ModalOKCancel extends React.Component {
module.exports = class ModalOKCancel extends React.Component {
render () {
const cancelStyle = { marginRight: 10, color: 'black' }
const { cancelText, onCancel, okText, onOK } = this.props

View File

@@ -1,14 +1,12 @@
import * as React from 'react'
import textField from 'material-ui/TextField'
import electron from 'electron'
import ModalOKCancel from './modal-ok-cancel.js'
import { dispatch, dispatcher } from '../lib/dispatcher.js'
import { isMagnetLink } from '../lib/torrent-player.js'
const React = require('react')
const TextField = require('material-ui/TextField').default
const { clipboard } = require('electron')
const TextField = textField.default
const { clipboard } = electron
const ModalOKCancel = require('./modal-ok-cancel')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const { isMagnetLink } = require('../lib/torrent-player')
export default class OpenTorrentAddressModal extends React.Component {
module.exports = class OpenTorrentAddressModal extends React.Component {
render () {
return (
<div className='open-torrent-address-modal'>

View File

@@ -1,17 +1,17 @@
import path from 'path'
import * as colors from 'material-ui/styles/colors'
import remote from '@electron/remote'
import * as React from 'react'
import PropTypes from 'prop-types'
import raisedButton from 'material-ui/RaisedButton'
import textField from 'material-ui/TextField'
const path = require('path')
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 TextField = require('material-ui/TextField').default
const RaisedButton = raisedButton.default
const TextField = textField.default
// Lets you pick a file or directory.
// Uses the system Open File dialog.
// You can't edit the text field directly.
export default class PathSelector extends React.Component {
class PathSelector extends React.Component {
static propTypes () {
return {
className: PropTypes.string,
@@ -81,3 +81,5 @@ export default class PathSelector extends React.Component {
)
}
}
module.exports = PathSelector

View File

@@ -1,8 +1,9 @@
import * as React from 'react'
import ModalOKCancel from './modal-ok-cancel.js'
import { dispatch, dispatcher } from '../lib/dispatcher.js'
const React = require('react')
export default class RemoveTorrentModal extends React.Component {
const ModalOKCancel = require('./modal-ok-cancel')
const { dispatch, dispatcher } = require('../lib/dispatcher')
module.exports = class RemoveTorrentModal extends React.Component {
render () {
const state = this.props.state
const message = state.modal.deleteData

View File

@@ -1,10 +1,9 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import raisedButton from 'material-ui/RaisedButton'
const React = require('react')
const PropTypes = require('prop-types')
const RaisedButton = raisedButton.default
const RaisedButton = require('material-ui/RaisedButton').default
export default class ShowMore extends React.Component {
class ShowMore extends React.Component {
static get propTypes () {
return {
defaultExpanded: PropTypes.bool,
@@ -22,9 +21,11 @@ export default class ShowMore extends React.Component {
constructor (props) {
super(props)
this.state = {
expanded: !!this.props.defaultExpanded
}
this.handleClick = this.handleClick.bind(this)
}
@@ -50,3 +51,5 @@ export default class ShowMore extends React.Component {
)
}
}
module.exports = ShowMore

View File

@@ -1,9 +1,10 @@
import * as React from 'react'
import { shell } from 'electron'
import ModalOKCancel from './modal-ok-cancel.js'
import { dispatcher } from '../lib/dispatcher.js'
const React = require('react')
const { shell } = require('electron')
export default class UnsupportedMediaModal extends React.Component {
const ModalOKCancel = require('./modal-ok-cancel')
const { dispatcher } = require('../lib/dispatcher')
module.exports = class UnsupportedMediaModal extends React.Component {
render () {
const state = this.props.state
const err = state.modal.error

View File

@@ -1,9 +1,10 @@
import * as React from 'react'
import { shell } from 'electron'
import ModalOKCancel from './modal-ok-cancel.js'
import { dispatch } from '../lib/dispatcher.js'
const React = require('react')
const { shell } = require('electron')
export default class UpdateAvailableModal extends React.Component {
const ModalOKCancel = require('./modal-ok-cancel')
const { dispatch } = require('../lib/dispatcher')
module.exports = class UpdateAvailableModal extends React.Component {
render () {
const state = this.props.state
return (

View File

@@ -1,6 +1,6 @@
import { dispatch } from '../lib/dispatcher.js'
const { dispatch } = require('../lib/dispatcher')
export default class AudioTracksController {
module.exports = class AudioTracksController {
constructor (state) {
this.state = state
}

View File

@@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron'
const { ipcRenderer } = require('electron')
export default class FolderWatcherController {
module.exports = class FolderWatcherController {
start () {
console.log('-- IPC: start folder watcher')
ipcRenderer.send('startFolderWatcher')

View File

@@ -1,8 +1,10 @@
import { ipcRenderer } from 'electron'
import telemetry from '../lib/telemetry.js'
import Playlist from '../lib/playlist.js'
const { ipcRenderer } = require('electron')
const telemetry = require('../lib/telemetry')
const Playlist = require('../lib/playlist')
export default class MediaController {
// Controls local play back: the <video>/<audio> tag and VLC
// Does not control remote casting (Chromecast etc)
module.exports = class MediaController {
constructor (state) {
this.state = state
}

View File

@@ -1,29 +1,19 @@
import { ipcRenderer } from 'electron'
import path from 'path'
import Cast from '../lib/cast.js'
import { dispatch } from '../lib/dispatcher.js'
import telemetry from '../lib/telemetry.js'
import { UnplayableFileError, UnplayableTorrentError } from '../lib/errors.js'
import sound from '../lib/sound.js'
import TorrentPlayer from '../lib/torrent-player.js'
import TorrentSummary from '../lib/torrent-summary.js'
import Playlist from '../lib/playlist.js'
import State from '../lib/state.js'
// Checks whether we are connected and already casting
// Returns false if we not casting (state.playing.location === 'local')
// or if we're trying to connect but haven't yet ('chromecast-pending', etc)
function isCasting (state) {
return state.playing.location === 'chromecast' ||
state.playing.location === 'airplay' ||
state.playing.location === 'dlna'
}
function restoreBounds (state) {
ipcRenderer.send('setAspectRatio', 0)
if (state.window.bounds) {
ipcRenderer.send('setBounds', state.window.bounds, false)
}
}
export default class PlaybackController {
const { ipcRenderer } = require('electron')
const path = require('path')
const Cast = require('../lib/cast')
const { dispatch } = require('../lib/dispatcher')
const telemetry = require('../lib/telemetry')
const { UnplayableFileError, UnplayableTorrentError } = require('../lib/errors')
const sound = require('../lib/sound')
const TorrentPlayer = require('../lib/torrent-player')
const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist')
const State = require('../lib/state')
// Controls playback of torrents and files within torrents
// both local (<video>,<audio>,external player) and remote (cast)
module.exports = class PlaybackController {
constructor (state, config, update) {
this.state = state
this.config = config
@@ -36,10 +26,12 @@ export default class PlaybackController {
// * If no file index is provided, restore the most recently viewed file or autoplay the first
playFile (infoHash, index /* optional */) {
this.pauseActiveTorrents(infoHash)
const state = this.state
if (state.location.url() === 'player') {
this.updatePlayer(infoHash, index, false, (err) => {
if (err) { dispatch('error', err) } else { this.play() }
if (err) dispatch('error', err)
else this.play()
})
} else {
let initialized = false
@@ -47,10 +39,13 @@ export default class PlaybackController {
url: 'player',
setup: (cb) => {
const torrentSummary = TorrentSummary.getByKey(state, infoHash)
if (index === undefined || initialized) index = torrentSummary.mostRecentFileIndex
if (index === undefined) index = torrentSummary.files.findIndex(TorrentPlayer.isPlayable)
if (index === undefined) return cb(new UnplayableTorrentError())
initialized = true
this.openPlayer(infoHash, index, (err) => {
if (!err) this.play()
cb(err)
@@ -58,7 +53,7 @@ export default class PlaybackController {
},
destroy: () => this.closePlayer()
}, (err) => {
if (err) { dispatch('error', err) }
if (err) dispatch('error', err)
})
}
}
@@ -66,7 +61,9 @@ export default class PlaybackController {
// Open a file in OS default app.
openPath (infoHash, index) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
const filePath = path.join(torrentSummary.path, torrentSummary.files[index].path)
const filePath = path.join(
torrentSummary.path,
torrentSummary.files[index].path)
ipcRenderer.send('openPath', filePath)
}
@@ -74,21 +71,27 @@ export default class PlaybackController {
playPause () {
const state = this.state
if (state.location.url() !== 'player') return
// force rerendering if window is hidden,
// in order to bypass `raf` and play/pause media immediately
const mediaTag = document.querySelector('video,audio')
if (!state.window.isVisible && mediaTag) {
if (state.playing.isPaused) { mediaTag.play() } else { mediaTag.pause() }
if (state.playing.isPaused) mediaTag.play()
else mediaTag.pause()
}
if (state.playing.isPaused) { this.play() } else { this.pause() }
if (state.playing.isPaused) this.play()
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)
}
@@ -96,9 +99,11 @@ export default class PlaybackController {
nextTrack () {
const state = this.state
if (Playlist.hasNext(state) && state.playing.location !== 'external') {
this.updatePlayer(state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) { dispatch('error', err) } else { this.play() }
})
this.updatePlayer(
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err)
else this.play()
})
}
}
@@ -106,9 +111,11 @@ export default class PlaybackController {
previousTrack () {
const state = this.state
if (Playlist.hasPrevious(state) && state.playing.location !== 'external') {
this.updatePlayer(state.playing.infoHash, Playlist.getPreviousIndex(state), false, (err) => {
if (err) { dispatch('error', err) } else { this.play() }
})
this.updatePlayer(
state.playing.infoHash, Playlist.getPreviousIndex(state), false, (err) => {
if (err) dispatch('error', err)
else this.play()
})
}
}
@@ -145,7 +152,8 @@ export default class PlaybackController {
console.error('Tried to skip to a non-finite time ' + time)
return console.trace()
}
if (isCasting(this.state)) { Cast.seek(time) } else { this.state.playing.jumpToTime = time }
if (isCasting(this.state)) Cast.seek(time)
else this.state.playing.jumpToTime = time
}
// Show video preview
@@ -197,6 +205,7 @@ export default class PlaybackController {
setVolume (volume) {
// check if its in [0.0 - 1.0] range
volume = Math.max(0, Math.min(1, volume))
const state = this.state
if (isCasting(state)) {
Cast.setVolume(volume)
@@ -213,6 +222,7 @@ export default class PlaybackController {
showOrHidePlayerControls () {
const state = this.state
const hideControls = state.shouldHidePlayerControls()
if (hideControls !== state.playing.hideControls) {
state.playing.hideControls = hideControls
return true
@@ -224,10 +234,13 @@ export default class PlaybackController {
openPlayer (infoHash, index, cb) {
const state = this.state
const torrentSummary = TorrentSummary.getByKey(state, infoHash)
state.playing.infoHash = torrentSummary.infoHash
state.playing.isReady = false
// update UI to show pending playback
sound.play('PLAY')
this.startServer(torrentSummary)
ipcRenderer.send('onPlayerOpen')
this.updatePlayer(infoHash, index, true, cb)
@@ -236,12 +249,15 @@ export default class PlaybackController {
// Starts WebTorrent server for media streaming
startServer (torrentSummary) {
const state = this.state
if (torrentSummary.status === 'paused') {
dispatch('startTorrentingSummary', torrentSummary.torrentKey)
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash, () => onTorrentReady())
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash,
() => onTorrentReady())
} else {
onTorrentReady()
}
function onTorrentReady () {
ipcRenderer.send('wt-start-server', torrentSummary.infoHash)
ipcRenderer.once('wt-server-running', () => { state.playing.isReady = true })
@@ -251,13 +267,17 @@ export default class PlaybackController {
// Called each time the current file changes
updatePlayer (infoHash, index, resume, cb) {
const state = this.state
const torrentSummary = TorrentSummary.getByKey(state, infoHash)
const fileSummary = torrentSummary.files[index]
if (!TorrentPlayer.isPlayable(fileSummary)) {
torrentSummary.mostRecentFileIndex = undefined
return cb(new UnplayableFileError())
}
torrentSummary.mostRecentFileIndex = index
// update state
state.playing.infoHash = infoHash
state.playing.fileIndex = index
@@ -267,7 +287,8 @@ export default class PlaybackController {
: TorrentPlayer.isAudio(fileSummary)
? 'audio'
: 'other'
// pick up where we left off
// pick up where we left off
let jumpToTime = 0
if (resume && fileSummary.currentTime) {
const fraction = fileSummary.currentTime / fileSummary.duration
@@ -277,24 +298,30 @@ export default class PlaybackController {
}
}
state.playing.jumpToTime = jumpToTime
// if it's audio, parse out the metadata (artist, title, etc)
if (torrentSummary.status === 'paused') {
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash, getAudioMetadata)
} else {
getAudioMetadata()
}
function getAudioMetadata () {
if (state.playing.type === 'audio') {
ipcRenderer.send('wt-get-audio-metadata', torrentSummary.infoHash, index)
}
}
// if it's video, check for subtitles files that are done downloading
dispatch('checkForSubtitles')
// enable previously selected subtitle track
if (fileSummary.selectedSubtitle) {
dispatch('addSubtitles', [fileSummary.selectedSubtitle], true)
}
state.window.title = fileSummary.name
// play in VLC if set as default player (Preferences / Playback / Play in VLC)
if (this.state.saved.prefs.openExternalPlayer) {
dispatch('openExternalPlayer')
@@ -302,8 +329,10 @@ export default class PlaybackController {
cb()
return
}
// otherwise, play the video
this.update()
ipcRenderer.send('onPlayerUpdate', Playlist.hasNext(state), Playlist.hasPrevious(state))
cb()
}
@@ -334,13 +363,33 @@ export default class PlaybackController {
dispatch('toggleFullScreen', false)
}
restoreBounds(state)
// Tell the WebTorrent process to kill the torrent-to-HTTP server
ipcRenderer.send('wt-stop-server')
ipcRenderer.send('onPlayerClose')
// Playback Priority: resume previously paused downloads.
if (this.state.saved.prefs.highestPlaybackPriority) {
dispatch('resumePausedTorrents')
}
this.update()
}
}
// Checks whether we are connected and already casting
// Returns false if we not casting (state.playing.location === 'local')
// or if we're trying to connect but haven't yet ('chromecast-pending', etc)
function isCasting (state) {
return state.playing.location === 'chromecast' ||
state.playing.location === 'airplay' ||
state.playing.location === 'dlna'
}
function restoreBounds (state) {
ipcRenderer.send('setAspectRatio', 0)
if (state.window.bounds) {
ipcRenderer.send('setBounds', state.window.bounds, false)
}
}

View File

@@ -1,7 +1,8 @@
import { dispatch } from '../lib/dispatcher.js'
import { ipcRenderer } from 'electron'
const { dispatch } = require('../lib/dispatcher')
const { ipcRenderer } = require('electron')
export default class PrefsController {
// Controls the Preferences screen
module.exports = class PrefsController {
constructor (state, config) {
this.state = state
this.config = config

View File

@@ -1,10 +1,11 @@
import remote from '@electron/remote'
import fs from 'fs'
import path from 'path'
import parallel from 'run-parallel'
import { dispatch } from '../lib/dispatcher.js'
const remote = require('@electron/remote')
const fs = require('fs')
const path = require('path')
const parallel = require('run-parallel')
export default class SubtitlesController {
const { dispatch } = require('../lib/dispatcher')
module.exports = class SubtitlesController {
constructor (state) {
this.state = state
}
@@ -81,11 +82,11 @@ export default class SubtitlesController {
}
}
async function loadSubtitle (file, cb) {
function loadSubtitle (file, cb) {
// Lazy load to keep startup fast
const concat = await import('simple-concat')
const LanguageDetect = await import('languagedetect')
const srtToVtt = await import('srt-to-vtt')
const concat = require('simple-concat')
const LanguageDetect = require('languagedetect')
const srtToVtt = require('srt-to-vtt')
// Read the .SRT or .VTT file, parse it, add subtitle track
const filePath = file.path || file
@@ -114,8 +115,8 @@ async function loadSubtitle (file, cb) {
// Checks whether a language name like 'English' or 'German' matches the system
// language, aka the current locale
async function isSystemLanguage (language) {
const iso639 = await import('iso-639-1')
function isSystemLanguage (language) {
const iso639 = require('iso-639-1')
const osLangISO = window.navigator.language.split('-')[0] // eg 'en'
const langIso = iso639.getCode(language) // eg 'de' if language is 'German'
return langIso === osLangISO

View File

@@ -1,10 +1,11 @@
import path from 'path'
import { ipcRenderer } from 'electron'
import TorrentSummary from '../lib/torrent-summary.js'
import sound from '../lib/sound.js'
import { dispatch } from '../lib/dispatcher.js'
const path = require('path')
const { ipcRenderer } = require('electron')
export default class TorrentController {
const TorrentSummary = require('../lib/torrent-summary')
const sound = require('../lib/sound')
const { dispatch } = require('../lib/dispatcher')
module.exports = class TorrentController {
constructor (state) {
this.state = state
}

View File

@@ -1,18 +1,17 @@
import fs from 'fs'
import path from 'path'
import electron from 'electron'
import remote from '@electron/remote'
const fs = require('fs')
const path = require('path')
const { ipcRenderer, clipboard } = require('electron')
const remote = require('@electron/remote')
import { dispatch } from '../lib/dispatcher.js'
import { TorrentKeyNotFoundError } from '../lib/errors.js'
import sound from '../lib/sound.js'
import TorrentSummary from '../lib/torrent-summary.js'
const { ipcRenderer, clipboard } = electron
const { dispatch } = require('../lib/dispatcher')
const { TorrentKeyNotFoundError } = require('../lib/errors')
const sound = require('../lib/sound')
const TorrentSummary = require('../lib/torrent-summary')
const instantIoRegex = /^(https:\/\/)?instant\.io\/#/
export default class TorrentListController {
// Controls the torrent list: creating, adding, deleting, & manipulating torrents
module.exports = class TorrentListController {
constructor (state) {
this.state = state
}
@@ -114,6 +113,10 @@ export default class TorrentListController {
}
}
setGlobalTrackers (globalTrackers) {
ipcRenderer.send('wt-set-global-trackers', globalTrackers)
}
// TODO: use torrentKey, not infoHash
toggleTorrent (infoHash) {
const torrentSummary = TorrentSummary.getByKey(this.state, infoHash)

View File

@@ -1,6 +1,7 @@
import { dispatch } from '../lib/dispatcher.js'
const { dispatch } = require('../lib/dispatcher')
export default class UpdateController {
// Controls the UI checking for new versions of the app, prompting install
module.exports = class UpdateController {
constructor (state) {
this.state = state
}

View File

@@ -1,18 +1,40 @@
import http from 'http'
import config from '../src/config.js'
import { CastingError } from './errors.js'
// The Cast module talks to Airplay and Chromecast
// * Modifies state when things change
// * Starts and stops casting, provides remote video controls
module.exports = {
init,
toggleMenu,
selectDevice,
stop,
play,
pause,
seek,
setVolume,
setRate
}
const http = require('http')
const config = require('../../config')
const { CastingError } = require('./errors')
// Lazy load these for a ~300ms improvement in startup time
let airplayer, chromecasts, dlnacasts
// App state. Cast modifies state.playing and state.errors in response to events
let state
// Callback to notify module users when state has changed
let update
// setInterval() for updating cast status
let statusInterval = null
// Start looking for cast devices on the local network
function init (appState, callback) {
state = appState
update = callback
// Don't actually cast during integration tests
// (Otherwise you'd need a physical Chromecast + AppleTV + DLNA TV to run them.)
if (config.IS_TEST) {
@@ -21,26 +43,32 @@ function init (appState, callback) {
state.devices.dlna = testPlayer('dlna')
return
}
// Load modules, scan the network for devices
airplayer = require('airplayer')()
chromecasts = require('chromecasts')()
dlnacasts = require('dlnacasts')()
state.devices.chromecast = chromecastPlayer()
state.devices.dlna = dlnaPlayer()
state.devices.airplay = airplayPlayer()
// Listen for devices: Chromecast, DLNA and Airplay
chromecasts.on('update', device => {
// TODO: how do we tell if there are *no longer* any Chromecasts available?
// From looking at the code, chromecasts.players only grows, never shrinks
state.devices.chromecast.addDevice(device)
})
dlnacasts.on('update', device => {
state.devices.dlna.addDevice(device)
})
airplayer.on('update', device => {
state.devices.airplay.addDevice(device)
})
}
// integration test player implementation
function testPlayer (type) {
return {
@@ -53,17 +81,20 @@ function testPlayer (type) {
seek,
volume
}
function getDevices () {
return [{ name: type + '-1' }, { name: type + '-2' }]
}
function open () { }
function play () { }
function pause () { }
function stop () { }
function status () { }
function seek () { }
function volume () { }
function open () {}
function play () {}
function pause () {}
function stop () {}
function status () {}
function seek () {}
function volume () {}
}
// chromecast player implementation
function chromecastPlayer () {
const ret = {
@@ -79,9 +110,11 @@ function chromecastPlayer () {
volume
}
return ret
function getDevices () {
return chromecasts.players
}
function addDevice (device) {
device.on('error', err => {
if (device !== ret.device) return
@@ -98,6 +131,7 @@ function chromecastPlayer () {
update()
})
}
function serveSubtitles (callback) {
const subtitles = state.playing.subtitles
const selectedSubtitle = subtitles.tracks[subtitles.selectedIndex]
@@ -110,7 +144,7 @@ function chromecastPlayer () {
'Access-Control-Allow-Origin': '*',
'Transfer-Encoding': 'chunked'
})
res.end(Buffer.from(selectedSubtitle.buffer.substr(21), 'base64'))
res.end(Buffer.from(selectedSubtitle.buffer.slice(21), 'base64'))
}).listen(0, () => {
const port = ret.subServer.address().port
const subtitlesUrl = 'http://' + state.server.networkAddress + ':' + port + '/'
@@ -118,6 +152,7 @@ function chromecastPlayer () {
})
}
}
function open () {
const torrentSummary = state.saved.torrents.find((x) => x.infoHash === state.playing.infoHash)
serveSubtitles(subtitlesUrl => {
@@ -140,28 +175,35 @@ function chromecastPlayer () {
})
})
}
function play (callback) {
ret.device.play(null, null, callback)
}
function pause (callback) {
ret.device.pause(callback)
}
function stop (callback) {
ret.device.stop(callback)
if (ret.subServer) {
ret.subServer.close()
}
}
function status () {
ret.device.status(handleStatus)
}
function seek (time, callback) {
ret.device.seek(time, callback)
}
function volume (volume, callback) {
ret.device.volume(volume, callback)
}
}
// airplay player implementation
function airplayPlayer () {
const ret = {
@@ -177,6 +219,7 @@ function airplayPlayer () {
volume
}
return ret
function addDevice (player) {
player.on('event', event => {
switch (event.state) {
@@ -194,9 +237,11 @@ function airplayPlayer () {
update()
})
}
function getDevices () {
return airplayer.players
}
function open () {
ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, (err, res) => {
if (err) {
@@ -211,15 +256,19 @@ function airplayPlayer () {
update()
})
}
function play (callback) {
ret.device.resume(callback)
}
function pause (callback) {
ret.device.pause(callback)
}
function stop (callback) {
ret.device.stop(callback)
}
function status () {
ret.device.playbackInfo((err, res, status) => {
if (err) {
@@ -235,15 +284,18 @@ function airplayPlayer () {
}
})
}
function seek (time, callback) {
ret.device.scrub(time, callback)
}
function volume (volume, callback) {
// AirPlay doesn't support volume
// TODO: We should just disable the volume slider
state.playing.volume = volume
}
}
// DLNA player implementation
function dlnaPlayer (player) {
const ret = {
@@ -259,9 +311,11 @@ function dlnaPlayer (player) {
volume
}
return ret
function getDevices () {
return dlnacasts.players
}
function addDevice (device) {
device.on('error', err => {
if (device !== ret.device) return
@@ -278,6 +332,7 @@ function dlnaPlayer (player) {
update()
})
}
function open () {
const torrentSummary = state.saved.torrents.find((x) => x.infoHash === state.playing.infoHash)
ret.device.play(state.server.networkURL + '/' + state.playing.fileIndex, {
@@ -297,21 +352,27 @@ function dlnaPlayer (player) {
update()
})
}
function play (callback) {
ret.device.play(null, null, callback)
}
function pause (callback) {
ret.device.pause(callback)
}
function stop (callback) {
ret.device.stop(callback)
}
function status () {
ret.device.status(handleStatus)
}
function seek (time, callback) {
ret.device.seek(time, callback)
}
function volume (volume, callback) {
ret.device.volume(volume, err => {
// quick volume update
@@ -320,15 +381,19 @@ function dlnaPlayer (player) {
})
}
}
function handleStatus (err, status) {
if (err || !status) {
return console.log('error getting %s status: %o', state.playing.location, err || 'missing response')
return console.log('error getting %s status: %o',
state.playing.location,
err || 'missing response')
}
state.playing.isPaused = status.playerState === 'PAUSED'
state.playing.currentTime = status.currentTime
state.playing.volume = status.volume.muted ? 0 : status.volume.level
update()
}
// Start polling cast device state, whenever we're connected
function startStatusInterval () {
statusInterval = setInterval(() => {
@@ -336,6 +401,7 @@ function startStatusInterval () {
if (player) player.status()
}, 1000)
}
/*
* Shows the device menu for a given cast type ('chromecast', 'airplay', etc)
* The menu lists eg. all Chromecasts detected; the user can click one to cast.
@@ -347,33 +413,43 @@ function toggleMenu (location) {
state.devices.castMenu = null
return
}
// Never cast to two devices at the same time
if (state.playing.location !== 'local') {
throw new CastingError(`You can't connect to ${location} when already connected to another device`)
throw new CastingError(
`You can't connect to ${location} when already connected to another device`
)
}
// Find all cast devices of the given type
const player = getPlayer(location)
const devices = player ? player.getDevices() : []
if (devices.length === 0) {
throw new CastingError(`No ${location} devices available`)
}
// Show a menu
state.devices.castMenu = { location, devices }
}
function selectDevice (index) {
const { location, devices } = state.devices.castMenu
// Start casting
const player = getPlayer(location)
player.device = devices[index]
player.open()
// Poll the casting device's status every few seconds
startStatusInterval()
// Show the Connecting... screen
state.devices.castMenu = null
state.playing.castName = devices[index].name
state.playing.location = location + '-pending'
update()
}
// Stops casting, move video back to local screen
function stop () {
const player = getPlayer()
@@ -387,6 +463,7 @@ function stop () {
stoppedCasting()
}
}
function stoppedCasting () {
state.playing.location = 'local'
state.playing.jumpToTime = Number.isFinite(state.playing.currentTime)
@@ -394,6 +471,7 @@ function stoppedCasting () {
: 0
update()
}
function getPlayer (location) {
if (location) {
return state.devices[location]
@@ -407,14 +485,17 @@ function getPlayer (location) {
return null
}
}
function play () {
const player = getPlayer()
if (player) player.play(castCallback)
}
function pause () {
const player = getPlayer()
if (player) player.pause(castCallback)
}
function setRate (rate) {
let player
let result = true
@@ -430,34 +511,17 @@ function setRate (rate) {
}
return result
}
function seek (time) {
const player = getPlayer()
if (player) { player.seek(time, castCallback) }
if (player) player.seek(time, castCallback)
}
function setVolume (volume) {
const player = getPlayer()
if (player) { player.volume(volume, castCallback) }
if (player) player.volume(volume, castCallback)
}
function castCallback (...args) {
console.log('%s callback: %o', state.playing.location, args)
}
export { init }
export { toggleMenu }
export { selectDevice }
export { stop }
export { play }
export { pause }
export { seek }
export { setVolume }
export { setRate }
export default {
init,
toggleMenu,
selectDevice,
stop,
play,
pause,
seek,
setVolume,
setRate
}

View File

@@ -1,11 +1,20 @@
module.exports = {
dispatch,
dispatcher,
setDispatch
}
const dispatchers = {}
let _dispatch = () => { }
let _dispatch = () => {}
function setDispatch (dispatch) {
_dispatch = dispatch
}
function dispatch (...args) {
_dispatch(...args)
}
// Most DOM event handlers are trivial functions like `() => dispatch(<args>)`.
// For these, `dispatcher(<args>)` is preferred because it memoizes the handler
// function. This prevents React from updating the listener functions on
@@ -17,20 +26,14 @@ function dispatcher (...args) {
handler = dispatchers[str] = e => {
// Do not propagate click to elements below the button
e.stopPropagation()
if (e.currentTarget.classList.contains('disabled')) {
// Ignore clicks on disabled elements
return
}
dispatch(...args)
}
}
return handler
}
export { dispatch }
export { dispatcher }
export { setDispatch }
export default {
dispatch,
dispatcher,
setDispatch
}

View File

@@ -1,37 +1,35 @@
import ExtendableError from 'es6-error'
const ExtendableError = require('es6-error')
/* Generic errors */
class CastingError extends ExtendableError {
}
class PlaybackError extends ExtendableError {
}
class SoundError extends ExtendableError {
}
class TorrentError extends ExtendableError {
}
class CastingError extends ExtendableError {}
class PlaybackError extends ExtendableError {}
class SoundError extends ExtendableError {}
class TorrentError extends ExtendableError {}
/* Playback */
class UnplayableTorrentError extends PlaybackError {
constructor () { super('Can\'t play any files in torrent') }
}
class UnplayableFileError extends PlaybackError {
constructor () { super('Can\'t play that file') }
}
/* Sound */
class InvalidSoundNameError extends SoundError {
constructor (name) { super(`Invalid sound name: ${name}`) }
}
/* Torrent */
class TorrentKeyNotFoundError extends TorrentError {
constructor (torrentKey) { super(`Can't resolve torrent key ${torrentKey}`) }
}
export { CastingError }
export { PlaybackError }
export { SoundError }
export { TorrentError }
export { UnplayableTorrentError }
export { UnplayableFileError }
export { InvalidSoundNameError }
export { TorrentKeyNotFoundError }
export default {
module.exports = {
CastingError,
PlaybackError,
SoundError,

View File

@@ -2,12 +2,11 @@ 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'
],
'.ogg', '.opus', '.spx', '.wma', '.wav', '.wv', '.wvp'],
video: [
'.avi', '.mp4', '.m4v', '.webm', '.mov', '.mkv', '.mpg', '.mpeg',
'.ogv', '.webm', '.wmv', '.m2ts'
],
'.ogv', '.webm', '.wmv', '.m2ts'],
image: ['.gif', '.jpg', '.jpeg', '.png']
}
export default mediaExtensions
module.exports = mediaExtensions

View File

@@ -1,8 +1,14 @@
/* eslint-disable camelcase */
import fs, { copyFileSync } from 'fs'
import path from 'path'
import semver from 'semver'
import config from '../../config.js'
module.exports = {
run
}
const fs = require('fs')
const path = require('path')
const semver = require('semver')
const config = require('../../config')
// Change `state.saved` (which will be saved back to config.json on exit) as
// needed, for example to deal with config.json format changes across versions
@@ -35,14 +41,17 @@ function run (state) {
// Whenever the app is updated, re-install default handlers if the user has
// enabled them.
async function installHandlers (saved) {
function installHandlers (saved) {
if (saved.prefs.isFileHandler) {
const { ipcRenderer } = await import('electron')
const ipcRenderer = require('electron').ipcRenderer
ipcRenderer.send('setDefaultFileHandler', true)
}
}
function migrate_0_7_0 (saved) {
const { copyFileSync } = require('fs')
const path = require('path')
saved.torrents.forEach(ts => {
const infoHash = ts.infoHash
@@ -82,6 +91,7 @@ function migrate_0_7_0 (saved) {
delete ts.posterURL
ts.posterFileName = infoHash + extension
}
// Fix exception caused by incorrect file ordering.
// https://github.com/webtorrent/webtorrent-desktop/pull/604#issuecomment-222805214
delete ts.defaultPlayFileIndex
@@ -106,8 +116,8 @@ function migrate_0_11_0 (saved) {
}
}
async function migrate_0_12_0 (saved) {
const TorrentSummary = await import('./torrent-summary')
function migrate_0_12_0 (saved) {
const TorrentSummary = require('./torrent-summary')
if (saved.prefs.openExternalPlayer == null && saved.prefs.playInVlc != null) {
saved.prefs.openExternalPlayer = saved.prefs.playInVlc
@@ -159,6 +169,10 @@ function migrate_0_17_2 (saved) {
// Remove the trailing dot (.) from the Wired CD torrent name, since
// folders/files that end in a trailing dot (.) or space are not deletable from
// Windows Explorer. See: https://github.com/webtorrent/webtorrent-desktop/issues/905
const { copyFileSync } = require('fs')
const rimraf = require('rimraf')
const OLD_NAME = 'The WIRED CD - Rip. Sample. Mash. Share.'
const NEW_NAME = 'The WIRED CD - Rip. Sample. Mash. Share'
@@ -188,7 +202,7 @@ function migrate_0_17_2 (saved) {
} catch (err) {}
ts.posterFileName = NEW_HASH + '.jpg'
fs.rmdirSync(path.join(config.TORRENT_PATH, ts.torrentFileName))
rimraf.sync(path.join(config.TORRENT_PATH, ts.torrentFileName))
copyFileSync(
path.join(config.STATIC_PATH, 'wiredCd.torrent'),
path.join(config.TORRENT_PATH, NEW_HASH + '.torrent')
@@ -218,7 +232,3 @@ function migrate_0_22_0 (saved) {
saved.prefs.externalPlayerPath = ''
}
}
export default {
run
}

View File

@@ -1,36 +1,52 @@
import TorrentSummary from './torrent-summary.js'
import TorrentPlayer from './torrent-player.js'
module.exports = {
hasNext,
getNextIndex,
hasPrevious,
getPreviousIndex,
getCurrentLocalURL
}
const TorrentSummary = require('./torrent-summary')
const TorrentPlayer = require('./torrent-player')
const cache = {
infoHash: null,
previousIndex: null,
currentIndex: null,
nextIndex: null
}
function hasNext (state) {
updateCache(state)
return cache.nextIndex !== null
}
function getNextIndex (state) {
updateCache(state)
return cache.nextIndex
}
function hasPrevious (state) {
updateCache(state)
return cache.previousIndex !== null
}
function getPreviousIndex (state) {
updateCache(state)
return cache.previousIndex
}
function getCurrentLocalURL (state) {
return state.server
? state.server.localURL + '/' + state.playing.fileIndex + '/' +
encodeURIComponent(state.playing.fileName)
encodeURIComponent(state.playing.fileName)
: ''
}
function updateCache (state) {
const infoHash = state.playing.infoHash
const fileIndex = state.playing.fileIndex
if (infoHash === cache.infoHash) {
switch (fileIndex) {
case cache.currentIndex:
@@ -49,10 +65,12 @@ function updateCache (state) {
} else {
cache.infoHash = infoHash
}
cache.previousIndex = findPreviousIndex(state)
cache.currentIndex = fileIndex
cache.nextIndex = findNextIndex(state)
}
function findPreviousIndex (state) {
const files = TorrentSummary.getByKey(state, state.playing.infoHash).files
for (let i = state.playing.fileIndex - 1; i >= 0; i--) {
@@ -60,6 +78,7 @@ function findPreviousIndex (state) {
}
return null
}
function findNextIndex (state) {
const files = TorrentSummary.getByKey(state, state.playing.infoHash).files
for (let i = state.playing.fileIndex + 1; i < files.length; i++) {
@@ -67,15 +86,3 @@ function findNextIndex (state) {
}
return null
}
export { hasNext }
export { getNextIndex }
export { hasPrevious }
export { getPreviousIndex }
export { getCurrentLocalURL }
export default {
hasNext,
getNextIndex,
hasPrevious,
getPreviousIndex,
getCurrentLocalURL
}

View File

@@ -1,11 +1,20 @@
import config from '../src/config.js'
import { InvalidSoundNameError } from './errors.js'
import path from 'path'
module.exports = {
init,
play
}
const config = require('../../config')
const { InvalidSoundNameError } = require('./errors')
const path = require('path')
const VOLUME = 0.25
// App state to access the soundNotifications preference
let state
/* Cache of Audio elements, for instant playback */
const cache = {}
const sounds = {
ADD: {
url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'add.wav'),
@@ -40,16 +49,20 @@ const sounds = {
volume: VOLUME
}
}
function init (appState) {
state = appState
}
function play (name) {
if (state == null) {
return
}
if (!state.saved.prefs.soundNotifications) {
return
}
let audio = cache[name]
if (!audio) {
const sound = sounds[name]
@@ -63,9 +76,3 @@ function play (name) {
audio.currentTime = 0
audio.play()
}
export { init }
export { play }
export default {
init,
play
}

View File

@@ -1,21 +1,21 @@
import applicationConfig from 'application-config'
import path from 'path'
import { EventEmitter } from 'events'
const appConfig = require('application-config')('WebTorrent')
const path = require('path')
const { EventEmitter } = require('events')
import config from '../../config.js'
const config = require('../../config')
const defaultAnnounceList = require('create-torrent').announceList.map((arr) => arr[0])
const appConfig = applicationConfig('WebTorrent')
const SAVE_DEBOUNCE_INTERVAL = 1000
appConfig.filePath = path.join(config.CONFIG_PATH, 'config.json')
const State = Object.assign(new EventEmitter(), {
const State = module.exports = Object.assign(new EventEmitter(), {
getDefaultPlayState,
load,
// state.save() calls are rate-limited. Use state.saveImmediate() to skip limit.
async save (...args) {
save (...args) {
// Perf optimization: Lazy-require debounce (and it's dependencies)
const debounce = await import('debounce')
const debounce = require('debounce')
// After first State.save() invokation, future calls go straight to the
// debounced function
State.save = debounce(saveImmediate, SAVE_DEBOUNCE_INTERVAL)
@@ -24,10 +24,8 @@ const State = Object.assign(new EventEmitter(), {
saveImmediate
})
export default State
async function getDefaultState () {
const { default: LocationHistory } = await import('location-history')
function getDefaultState () {
const LocationHistory = require('location-history')
return {
/*
@@ -82,6 +80,7 @@ async function getDefaultState () {
getPlayingTorrentSummary,
getPlayingFileSummary,
getExternalPlayerName,
getGlobalTrackers,
shouldHidePlayerControls
}
}
@@ -118,9 +117,9 @@ function getDefaultPlayState () {
}
/* If the saved state file doesn't exist yet, here's what we use instead */
async function setupStateSaved () {
const { copyFileSync, mkdirSync, readFileSync } = await import('fs')
const { default: parseTorrent } = await import('parse-torrent')
function setupStateSaved () {
const { copyFileSync, mkdirSync, readFileSync } = require('fs')
const parseTorrent = require('parse-torrent')
const saved = {
prefs: {
@@ -132,7 +131,8 @@ async function setupStateSaved () {
soundNotifications: true,
autoAddTorrents: false,
torrentsFolderPath: '',
highestPlaybackPriority: true
highestPlaybackPriority: true,
globalTrackers: defaultAnnounceList
},
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
torrentsToResume: [],
@@ -201,18 +201,24 @@ function shouldHidePlayerControls () {
new Date().getTime() - this.playing.mouseStationarySince > 2000 &&
!this.playing.mouseInControls &&
!this.playing.isPaused &&
this.playing.location === 'local' &&
this.playing.playbackRate === 1
this.playing.location === 'local'
}
function getGlobalTrackers () {
const trackers = this.saved.prefs.globalTrackers
if (!trackers) {
return defaultAnnounceList
}
return trackers
}
async function load (cb) {
console.log('state load')
let saved = await appConfig.read()
if (!saved || !saved.version) {
console.log('Missing config file: Creating new one')
try {
saved = await setupStateSaved()
saved = setupStateSaved()
} catch (err) {
onSavedState(err)
return
@@ -221,9 +227,9 @@ async function load (cb) {
onSavedState(null, saved)
async function onSavedState (err, saved) {
function onSavedState (err, saved) {
if (err) return cb(err)
const state = await getDefaultState()
const state = getDefaultState()
state.saved = saved
if (process.type === 'renderer') {
@@ -232,7 +238,6 @@ async function load (cb) {
migrations.run(state)
}
console.log('calling cb')
cb(null, state)
}
}

View File

@@ -1,14 +1,24 @@
import remote from '@electron/remote'
import config from '../../config.js'
// Collects anonymous usage stats and uncaught errors
// Reports back so that we can improve WebTorrent Desktop
module.exports = {
init,
send,
logUncaughtError,
logPlayAttempt
}
const remote = require('@electron/remote')
const config = require('../../config')
let telemetry
async function init (state) {
function init (state) {
telemetry = state.saved.telemetry
// First app run
if (!telemetry) {
const crypto = await import('crypto')
const crypto = require('crypto')
telemetry = state.saved.telemetry = {
userID: crypto.randomBytes(32).toString('hex') // 256-bit random ID
}
@@ -16,7 +26,7 @@ async function init (state) {
}
}
async function send (state) {
function send (state) {
const now = new Date()
telemetry.version = config.APP_VERSION
telemetry.timestamp = now.toISOString()
@@ -32,7 +42,7 @@ async function send (state) {
return reset()
}
const get = await import('simple-get')
const get = require('simple-get')
const opts = {
url: config.TELEMETRY_URL,
@@ -72,8 +82,8 @@ function getScreenInfo () {
}
// Track basic system info like OS version and amount of RAM
async function getSystemInfo () {
const os = await import('os')
function getSystemInfo () {
const os = require('os')
return {
osPlatform: process.platform,
osRelease: os.type() + ' ' + os.release(),
@@ -210,14 +220,3 @@ function logPlayAttempt (result) {
attempts.total = (attempts.total || 0) + 1
attempts[result] = (attempts[result] || 0) + 1
}
export { init }
export { send }
export { logUncaughtError }
export { logPlayAttempt }
export default {
init,
send,
logUncaughtError,
logPlayAttempt
}

View File

@@ -1,17 +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`
}
export { calculateEta }
export default {
calculateEta
}

View File

@@ -1,45 +1,4 @@
import path from 'path'
import mediaExtensions from './media-extensions.js'
// Checks whether a fileSummary or file path is audio/video that we can play,
// based on the file extension
function isPlayable (file) {
return isVideo(file) || isAudio(file)
}
// Checks whether a fileSummary or file path is playable video
function isVideo (file) {
return mediaExtensions.video.includes(getFileExtension(file))
}
// Checks whether a fileSummary or file path is playable audio
function isAudio (file) {
return mediaExtensions.audio.includes(getFileExtension(file))
}
// Checks if the argument is either:
// - a string that's a valid filename ending in .torrent
// - a file object where obj.name is ends in .torrent
// - a string that's a magnet link (magnet://...)
function isTorrent (file) {
return isTorrentFile(file) || isMagnetLink(file)
}
function isTorrentFile (file) {
return getFileExtension(file) === '.torrent'
}
function isMagnetLink (link) {
return typeof link === 'string' && /^(stream-)?magnet:/.test(link)
}
function getFileExtension (file) {
const name = typeof file === 'string' ? file : file.name
return path.extname(name).toLowerCase()
}
function isPlayableTorrentSummary (torrentSummary) {
return torrentSummary.files && torrentSummary.files.some(isPlayable)
}
export { isPlayable }
export { isVideo }
export { isAudio }
export { isTorrent }
export { isMagnetLink }
export { isPlayableTorrentSummary }
export default {
module.exports = {
isPlayable,
isVideo,
isAudio,
@@ -47,3 +6,48 @@ export default {
isMagnetLink,
isPlayableTorrentSummary
}
const path = require('path')
const mediaExtensions = require('./media-extensions')
// Checks whether a fileSummary or file path is audio/video that we can play,
// based on the file extension
function isPlayable (file) {
return isVideo(file) || isAudio(file)
}
// Checks whether a fileSummary or file path is playable video
function isVideo (file) {
return mediaExtensions.video.includes(getFileExtension(file))
}
// Checks whether a fileSummary or file path is playable audio
function isAudio (file) {
return mediaExtensions.audio.includes(getFileExtension(file))
}
// Checks if the argument is either:
// - a string that's a valid filename ending in .torrent
// - a file object where obj.name is ends in .torrent
// - a string that's a magnet link (magnet://...)
function isTorrent (file) {
return isTorrentFile(file) || isMagnetLink(file)
}
function isTorrentFile (file) {
return getFileExtension(file) === '.torrent'
}
function isMagnetLink (link) {
return typeof link === 'string' && /^(stream-)?magnet:/.test(link)
}
function getFileExtension (file) {
const name = typeof file === 'string' ? file : file.name
return path.extname(name).toLowerCase()
}
function isPlayableTorrentSummary (torrentSummary) {
return torrentSummary.files && torrentSummary.files.some(isPlayable)
}

View File

@@ -1,20 +1,28 @@
import captureFrame from 'capture-frame'
import path from 'path'
import mediaExtensions from './media-extensions.js'
module.exports = torrentPoster
const captureFrame = require('capture-frame')
const path = require('path')
const mediaExtensions = require('./media-extensions')
const msgNoSuitablePoster = 'Cannot generate a poster from any files in the torrent'
function torrentPoster (torrent, cb) {
// First, try to use a poster image if available
const posterFile = torrent.files.filter(file => /^poster\.(jpg|png|gif)$/.test(file.name))[0]
if (posterFile) return extractPoster(posterFile, cb)
// 'score' each media type based on total size present in torrent
const bestScore = ['audio', 'video', 'image'].map(mediaType => ({
type: mediaType,
size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType])
})).sort((a, b) => b.size - a.size)[0] // sort descending on size
if (bestScore.size === 0) {
// Admit defeat, no video, audio or image had a significant presence
return cb(new Error(msgNoSuitablePoster))
}
// Based on which media type is dominant we select the corresponding poster function
switch (bestScore.type) {
case 'audio':
@@ -25,6 +33,7 @@ function torrentPoster (torrent, cb) {
return torrentPosterFromVideo(torrent, cb)
}
}
/**
* Calculate the total data size of file matching one of the provided extensions
* @param torrent
@@ -38,6 +47,7 @@ function calculateDataLengthByExtension (torrent, extensions) {
.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
@@ -49,6 +59,7 @@ function getLargestFileByExtension (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
@@ -61,6 +72,7 @@ function filterOnExtension (torrent, extensions) {
return extensions.indexOf(extname) !== -1
})
}
/**
* Returns a score how likely the file is suitable as a poster
* @param imgFile File object of an image
@@ -76,6 +88,7 @@ function scoreAudioCoverFile (imgFile) {
back: 20,
spectrogram: -80
}
for (const keyword in relevanceScore) {
if (fileName === keyword) {
return relevanceScore[keyword]
@@ -86,9 +99,12 @@ function scoreAudioCoverFile (imgFile) {
}
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)
@@ -105,47 +121,62 @@ function torrentPosterFromAudio (torrent, cb) {
}
return b
})
const extname = path.extname(bestCover.file.name)
bestCover.file.getBuffer((err, buf) => cb(err, buf, extname))
}
function torrentPosterFromVideo (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.video)
const index = torrent.files.indexOf(file)
const server = torrent.createServer(0)
server.listen(0, onListening)
function onListening () {
const port = server.address().port
const url = 'http://localhost:' + port + '/' + index
const video = document.createElement('video')
video.addEventListener('canplay', onCanPlay)
video.volume = 0
video.src = url
video.play()
function onCanPlay () {
video.removeEventListener('canplay', onCanPlay)
video.addEventListener('seeked', onSeeked)
video.currentTime = Math.min((video.duration || 600) * 0.03, 60)
}
function onSeeked () {
video.removeEventListener('seeked', onSeeked)
const frame = captureFrame(video)
const buf = frame && frame.image
// unload video element
video.pause()
video.src = ''
video.load()
server.destroy()
if (buf.length === 0) return cb(new Error(msgNoSuitablePoster))
cb(null, buf, '.jpg')
}
}
}
function torrentPosterFromImage (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.image)
extractPoster(file, cb)
}
function extractPoster (file, cb) {
const extname = path.extname(file.name)
file.getBuffer((err, buf) => cb(err, buf, extname))
}
export default torrentPoster

View File

@@ -1,11 +1,21 @@
import path from 'path'
import config from '../src/config.js'
module.exports = {
getPosterPath,
getTorrentPath,
getByKey,
getTorrentId,
getFileOrFolder
}
const path = require('path')
const config = require('../../config')
// Expects a torrentSummary
// Returns an absolute path to the torrent file, or null if unavailable
function getTorrentPath (torrentSummary) {
if (!torrentSummary || !torrentSummary.torrentFileName) return null
return path.join(config.TORRENT_PATH, torrentSummary.torrentFileName)
}
// Expects a torrentSummary
// Returns an absolute path to the poster image, or null if unavailable
function getPosterPath (torrentSummary) {
@@ -15,6 +25,7 @@ function getPosterPath (torrentSummary) {
// Backslashes in URLS in CSS cause bizarre string encoding issues
return posterPath.replace(/\\/g, '/')
}
// Expects a torrentSummary
// Returns a torrentID: filename, magnet URI, or infohash
function getTorrentId (torrentSummary) {
@@ -25,12 +36,15 @@ function getTorrentId (torrentSummary) {
return s.magnetURI || s.infoHash
}
}
// Expects a torrentKey or infoHash
// Returns the corresponding torrentSummary, or undefined
function getByKey (state, torrentKey) {
if (!torrentKey) return undefined
return state.saved.torrents.find((x) => x.torrentKey === torrentKey || x.infoHash === torrentKey)
return state.saved.torrents.find((x) =>
x.torrentKey === torrentKey || x.infoHash === torrentKey)
}
// Returns the path to either the file (in a single-file torrent) or the root
// folder (in multi-file torrent)
// WARNING: assumes that multi-file torrents consist of a SINGLE folder.
@@ -42,15 +56,3 @@ function getFileOrFolder (torrentSummary) {
const dirname = ts.files[0].path.split(path.sep)[0]
return path.join(ts.path, dirname)
}
export { getPosterPath }
export { getTorrentPath }
export { getByKey }
export { getTorrentId }
export { getFileOrFolder }
export default {
getPosterPath,
getTorrentPath,
getByKey,
getTorrentId,
getFileOrFolder
}

View File

@@ -1,43 +1,51 @@
/* eslint-disable import/first */
/**
* Perf optimization: Hook into require() to modify how certain modules load:
*
* - `inline-style-prefixer` (used by `material-ui`) takes ~40ms. It is not
* actually used because auto-prefixing is disabled with
* `darkBaseTheme.userAgent = false`. Return a fake object.
*/
const Module = require('module')
const _require = Module.prototype.require
Module.prototype.require = function (id) {
if (id === 'inline-style-prefixer') return {}
return _require.apply(this, arguments)
}
console.time('init')
// Perf optimization: Start asynchronously read on config file before all the
// blocking import() calls below.
// blocking require() calls below.
import State from './lib/state'
const State = require('./lib/state')
State.load(onState)
import createGetter from 'fn-getter'
import remote from '@electron/remote'
import debounce from 'debounce'
import dragDrop from 'drag-drop'
import electron from 'electron'
import fs from 'fs'
import * as React from 'react'
import * as ReactDOM from 'react-dom'
const createGetter = require('fn-getter')
const debounce = require('debounce')
const dragDrop = require('drag-drop')
const electron = require('electron')
const fs = require('fs')
const React = require('react')
const ReactDOM = require('react-dom')
import config from '../config.js'
import telemetry from './lib/telemetry.js'
import sound from './lib/sound.js'
import TorrentPlayer from './lib/torrent-player.js'
import App from './pages/app.js'
import { setDispatch } from './lib/dispatcher.js'
const config = require('../config')
const telemetry = require('./lib/telemetry')
const sound = require('./lib/sound')
const TorrentPlayer = require('./lib/torrent-player')
import TorrentController from './controllers/torrent-controller.js'
import AudioTracksController from './controllers/audio-tracks-controller.js'
import SubtitlesController from './controllers/subtitles-controller.js'
import PrefsController from './controllers/prefs-controller.js'
import PlaybackController from './controllers/playback-controller.js'
import MediaController from './controllers/media-controller.js'
import TorrentListController from './controllers/torrent-list-controller.js'
import UpdateController from './controllers/update-controller.js'
import FolderWatcherController from './controllers/folder-watcher-controller.js'
// Perf optimization: Needed immediately, so do not lazy load it below
const TorrentListController = require('./controllers/torrent-list-controller')
const { clipboard, ipcRenderer } = electron
const App = require('./pages/app')
// Electron apps have two processes: a main process (node) runs first and starts
// a renderer process (essentially a Chrome window). We're in the renderer process,
// and this IPC channel receives from and sends messages to the main process
const ipcRenderer = electron.ipcRenderer
// Yo-yo pattern: state object lives here and percolates down thru all the views.
// Events come back up from the views via dispatch(...)
setDispatch(dispatch)
require('./lib/dispatcher').setDispatch(dispatch)
// From dispatch(...), events are sent to one of the controllers
let controllers = null
@@ -72,15 +80,39 @@ function onState (err, _state) {
// Create controllers
controllers = {
media: createGetter(() => new MediaController(state)),
playback: createGetter(() => new PlaybackController(state, config, update)),
prefs: createGetter(() => new PrefsController(state, config)),
subtitles: createGetter(() => new SubtitlesController(state)),
audioTracks: createGetter(() => new AudioTracksController(state)),
torrent: createGetter(() => new TorrentController(state)),
media: createGetter(() => {
const MediaController = require('./controllers/media-controller')
return new MediaController(state)
}),
playback: createGetter(() => {
const PlaybackController = require('./controllers/playback-controller')
return new PlaybackController(state, config, update)
}),
prefs: createGetter(() => {
const PrefsController = require('./controllers/prefs-controller')
return new PrefsController(state, config)
}),
subtitles: createGetter(() => {
const SubtitlesController = require('./controllers/subtitles-controller')
return new SubtitlesController(state)
}),
audioTracks: createGetter(() => {
const AudioTracksController = require('./controllers/audio-tracks-controller')
return new AudioTracksController(state)
}),
torrent: createGetter(() => {
const TorrentController = require('./controllers/torrent-controller')
return new TorrentController(state)
}),
torrentList: createGetter(() => new TorrentListController(state)),
update: createGetter(() => new UpdateController(state)),
folderWatcher: createGetter(() => new FolderWatcherController())
update: createGetter(() => {
const UpdateController = require('./controllers/update-controller')
return new UpdateController(state)
}),
folderWatcher: createGetter(() => {
const FolderWatcherController = require('./controllers/folder-watcher-controller')
return new FolderWatcherController()
})
}
// Add first page to location history
@@ -92,6 +124,9 @@ function onState (err, _state) {
}
})
// Give global trackers
setGlobalTrackers()
// Restart everything we were torrenting last time the app ran
resumeTorrents()
@@ -137,7 +172,7 @@ function onState (err, _state) {
window.addEventListener('focus', onFocus)
window.addEventListener('blur', onBlur)
if (remote.getCurrentWindow().isVisible()) {
if (electron.remote.getCurrentWindow().isVisible()) {
sound.play('STARTUP')
}
@@ -167,9 +202,9 @@ function delayedInit () {
}
// Lazily loads Chromecast and Airplay support
async function lazyLoadCast () {
function lazyLoadCast () {
if (!Cast) {
Cast = (await import('./lib/cast')).default
Cast = require('./lib/cast')
Cast.init(state, update) // Search the local network for Chromecast and Airplays
}
return Cast
@@ -282,6 +317,7 @@ const dispatchHandlers = {
preferences: () => controllers.prefs().show(),
updatePreferences: (key, value) => controllers.prefs().update(key, value),
checkDownloadPath,
updateGlobalTrackers: (trackers) => setGlobalTrackers(trackers),
startFolderWatcher: () => controllers.folderWatcher().start(),
stopFolderWatcher: () => controllers.folderWatcher().stop(),
@@ -384,6 +420,10 @@ function escapeBack () {
}
}
function setGlobalTrackers () {
controllers.torrentList().setGlobalTrackers(state.getGlobalTrackers())
}
// Starts all torrents that aren't paused on program startup
function resumeTorrents () {
state.saved.torrents
@@ -400,7 +440,7 @@ function resumeTorrents () {
// Set window dimensions to match video dimensions or fill the screen
function setDimensions (dimensions) {
// Don't modify the window size if it's already maximized
if (remote.getCurrentWindow().isMaximized()) {
if (electron.remote.getCurrentWindow().isMaximized()) {
state.window.bounds = null
return
}
@@ -412,7 +452,7 @@ function setDimensions (dimensions) {
width: window.outerWidth,
height: window.outerHeight
}
state.window.wasMaximized = remote.getCurrentWindow().isMaximized
state.window.wasMaximized = electron.remote.getCurrentWindow().isMaximized
// Limit window size to screen size
const screenWidth = window.screen.width
@@ -487,7 +527,7 @@ const editableHtmlTags = new Set(['input', 'textarea'])
function onPaste (e) {
if (e && editableHtmlTags.has(e.target.tagName.toLowerCase())) return
controllers.torrentList().addTorrent(clipboard.readText())
controllers.torrentList().addTorrent(electron.clipboard.readText())
update()
}

View File

@@ -1,35 +1,32 @@
import * as colors from 'material-ui/styles/colors'
import createGetter from 'fn-getter'
import * as React from 'react'
import darkBaseTheme$0 from 'material-ui/styles/baseThemes/darkBaseTheme'
import getMuiTheme$0 from 'material-ui/styles/getMuiTheme'
import muiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import Header from '../components/header.js'
import TorrentListPage from './torrent-list-page.js'
import playerPage from './player-page.js'
import createTorrentPage from './create-torrent-page.js'
import preferencesPage from './preferences-page.js'
import openTorrentAddressModal from '../components/open-torrent-address-modal.js'
import removeTorrentModal from '../components/remove-torrent-modal.js'
import updateAvailableModal from '../components/update-available-modal.js'
import unsupportedMediaModal from '../components/unsupported-media-modal.js'
import deleteAllTorrentsModal from '../components/delete-all-torrents-modal.js'
const darkBaseTheme = { default: darkBaseTheme$0 }.default
const getMuiTheme = getMuiTheme$0.default
const MuiThemeProvider = muiThemeProvider.default
const colors = require('material-ui/styles/colors')
const createGetter = require('fn-getter')
const React = require('react')
const darkBaseTheme = require('material-ui/styles/baseThemes/darkBaseTheme').default
const getMuiTheme = require('material-ui/styles/getMuiTheme').default
const MuiThemeProvider = require('material-ui/styles/MuiThemeProvider').default
const Header = require('../components/header')
// Perf optimization: Needed immediately, so do not lazy load it below
const TorrentListPage = require('./torrent-list-page')
const Views = {
home: createGetter(() => TorrentListPage),
player: createGetter(() => playerPage),
'create-torrent': createGetter(() => createTorrentPage),
preferences: createGetter(() => preferencesPage)
player: createGetter(() => require('./player-page')),
'create-torrent': createGetter(() => require('./create-torrent-page')),
preferences: createGetter(() => require('./preferences-page'))
}
const Modals = {
'open-torrent-address-modal': createGetter(() => openTorrentAddressModal),
'remove-torrent-modal': createGetter(() => removeTorrentModal),
'update-available-modal': createGetter(() => updateAvailableModal),
'unsupported-media-modal': createGetter(() => unsupportedMediaModal),
'delete-all-torrents-modal': createGetter(() => deleteAllTorrentsModal)
'open-torrent-address-modal': createGetter(
() => require('../components/open-torrent-address-modal')
),
'remove-torrent-modal': createGetter(() => require('../components/remove-torrent-modal')),
'update-available-modal': createGetter(() => require('../components/update-available-modal')),
'unsupported-media-modal': createGetter(() => require('../components/unsupported-media-modal')),
'delete-all-torrents-modal':
createGetter(() => require('../components/delete-all-torrents-modal'))
}
const fontFamily = process.platform === 'win32'
@@ -48,7 +45,7 @@ darkBaseTheme.palette.accent3Color = colors.redA100
let darkMuiTheme
let lightMuiTheme
export default class App extends React.Component {
class App extends React.Component {
render () {
const state = this.props.state
@@ -101,12 +98,12 @@ export default class App extends React.Component {
)
}
async getModal () {
getModal () {
const state = this.props.state
if (!state.modal) return
if (!lightMuiTheme) {
const lightBaseTheme = (await import('material-ui/styles/baseThemes/lightBaseTheme')).default
const lightBaseTheme = require('material-ui/styles/baseThemes/lightBaseTheme').default
lightBaseTheme.fontFamily = fontFamily
lightBaseTheme.userAgent = false
lightMuiTheme = getMuiTheme(lightBaseTheme)
@@ -131,3 +128,5 @@ export default class App extends React.Component {
return (<View state={state} />)
}
}
module.exports = App

View File

@@ -1,25 +1,22 @@
import createTorrent from 'create-torrent'
import path from 'path'
import prettyBytes from 'prettier-bytes'
import * as React from 'react'
import flatButton from 'material-ui/FlatButton'
import raisedButton from 'material-ui/RaisedButton'
import textField from 'material-ui/TextField'
import checkbox from 'material-ui/Checkbox'
const createTorrent = require('create-torrent')
const path = require('path')
const prettyBytes = require('prettier-bytes')
const React = require('react')
import { dispatch, dispatcher } from '../lib/dispatcher.js'
import CreateTorrentErrorPage from '../components/create-torrent-error-page.js'
import Heading from '../components/heading.js'
import ShowMore from '../components/show-more.js'
const { dispatch, dispatcher } = require('../lib/dispatcher')
const FlatButton = flatButton.default
const RaisedButton = raisedButton.default
const TextField = textField.default
const Checkbox = checkbox.default
const FlatButton = require('material-ui/FlatButton').default
const RaisedButton = require('material-ui/RaisedButton').default
const TextField = require('material-ui/TextField').default
const Checkbox = require('material-ui/Checkbox').default
const CreateTorrentErrorPage = require('../components/create-torrent-error-page')
const Heading = require('../components/heading')
const ShowMore = require('../components/show-more')
// Shows a basic UI to create a torrent, from an already-selected file or folder.
// Includes a "Show Advanced..." button and more advanced UI.
export default class CreateTorrentPage extends React.Component {
class CreateTorrentPage extends React.Component {
constructor (props) {
super(props)
@@ -226,3 +223,5 @@ function containsDots (path, pathPrefix) {
const suffix = path.substring(pathPrefix.length).replace(/\\/g, '/')
return ('/' + suffix).includes('/.')
}
module.exports = CreateTorrentPage

View File

@@ -1,16 +1,17 @@
import * as React from 'react'
import bitfield from 'bitfield'
import prettyBytes from 'prettier-bytes'
/* globals MediaMetadata */
import TorrentSummary from '../lib/torrent-summary.js'
import Playlist from '../lib/playlist.js'
import { dispatch, dispatcher } from '../lib/dispatcher.js'
import config from '../../config.js'
import { calculateEta } from '../lib/time.js'
const React = require('react')
const BitField = require('bitfield').default
const prettyBytes = require('prettier-bytes')
const BitField = bitfield.default
const TorrentSummary = require('../lib/torrent-summary')
const Playlist = require('../lib/playlist')
const { dispatch, dispatcher } = require('../lib/dispatcher')
const config = require('../../config')
const { calculateEta } = require('../lib/time')
export default class Player extends React.Component {
// Shows a streaming video player. Standard features + Chromecast + Airplay
module.exports = class Player extends React.Component {
render () {
// Show the video as large as will fit in the window, play immediately
// If the video is on Chromecast or Airplay, show a title screen instead
@@ -36,6 +37,7 @@ export default class Player extends React.Component {
tag.pause()
tag.src = ''
tag.load()
navigator.mediaSession.metadata = null
}
}
@@ -52,6 +54,28 @@ function renderMedia (state) {
// Get the <video> or <audio> tag
const mediaElement = document.querySelector(state.playing.type)
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) {
mediaElement.pause()
} else if (!state.playing.isPaused && mediaElement.paused) {
@@ -611,6 +635,8 @@ function renderPlayerControls (state) {
key='skip-previous'
className={'icon skip-previous float-left ' + prevClass}
onClick={dispatcher('previousTrack')}
role='button'
aria-label='Previous track'
>
skip_previous
</i>,
@@ -619,6 +645,8 @@ function renderPlayerControls (state) {
key='play'
className='icon play-pause float-left'
onClick={dispatcher('playPause')}
role='button'
aria-label={state.playing.isPaused ? 'Play' : 'Pause'}
>
{state.playing.isPaused ? 'play_arrow' : 'pause'}
</i>,
@@ -627,6 +655,8 @@ function renderPlayerControls (state) {
key='skip-next'
className={'icon skip-next float-left ' + nextClass}
onClick={dispatcher('nextTrack')}
role='button'
aria-label='Next track'
>
skip_next
</i>,
@@ -635,6 +665,8 @@ function renderPlayerControls (state) {
key='fullscreen'
className='icon fullscreen float-right'
onClick={dispatcher('toggleFullScreen')}
role='button'
aria-label={state.window.isFullScreen ? 'Exit full screen' : 'Enter full screen'}
>
{state.window.isFullScreen ? 'fullscreen_exit' : 'fullscreen'}
</i>
@@ -647,6 +679,8 @@ function renderPlayerControls (state) {
key='subtitles'
className={'icon closed-caption float-right ' + captionsClass}
onClick={handleSubtitles}
role='button'
aria-label='Closed captions'
>
closed_caption
</i>
@@ -728,6 +762,8 @@ function renderPlayerControls (state) {
<i
className='icon volume-icon float-left'
onMouseDown={handleVolumeMute}
role='button'
aria-label='Mute'
>
{volumeIcon}
</i>

View File

@@ -1,18 +1,17 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import * as colors from 'material-ui/styles/colors'
import checkbox from 'material-ui/Checkbox'
import raisedButton from 'material-ui/RaisedButton'
const React = require('react')
const PropTypes = require('prop-types')
import Heading from '../components/heading.js'
import PathSelector from '../components/path-selector.js'
import { dispatch } from '../lib/dispatcher.js'
import config from '../../config.js'
const colors = require('material-ui/styles/colors')
const Checkbox = require('material-ui/Checkbox').default
const RaisedButton = require('material-ui/RaisedButton').default
const TextField = require('material-ui/TextField').default
const Heading = require('../components/heading')
const PathSelector = require('../components/path-selector')
const Checkbox = checkbox.default
const RaisedButton = raisedButton.default
const { dispatch } = require('../lib/dispatcher')
const config = require('../../config')
export default class PreferencesPage extends React.Component {
class PreferencesPage extends React.Component {
constructor (props) {
super(props)
@@ -30,6 +29,15 @@ export default class PreferencesPage extends React.Component {
this.handleSoundNotificationsChange =
this.handleSoundNotificationsChange.bind(this)
this.handleSetGlobalTrackers =
this.handleSetGlobalTrackers.bind(this)
const globalTrackers = this.props.state.getGlobalTrackers().join('\n')
this.state = {
globalTrackers
}
}
downloadPathSelector () {
@@ -231,6 +239,39 @@ export default class PreferencesPage extends React.Component {
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 () {
const style = {
color: colors.grey400,
@@ -256,6 +297,9 @@ export default class PreferencesPage extends React.Component {
{this.setStartupCheckbox()}
{this.soundNotificationsCheckbox()}
</PreferencesSection>
<PreferencesSection title='Trackers'>
{this.setGlobalTrackers()}
</PreferencesSection>
</div>
)
}
@@ -288,3 +332,5 @@ class Preference extends React.Component {
return (<div style={style}>{this.props.children}</div>)
}
}
module.exports = PreferencesPage

View File

@@ -1,17 +1,15 @@
import * as React from 'react'
import prettyBytes from 'prettier-bytes'
import checkbox from 'material-ui/Checkbox'
import linearProgress from 'material-ui/LinearProgress'
const React = require('react')
const prettyBytes = require('prettier-bytes')
import TorrentSummary from '../lib/torrent-summary.js'
import TorrentPlayer from '../lib/torrent-player.js'
import { dispatcher } from '../lib/dispatcher.js'
import { calculateEta } from '../lib/time.js'
const Checkbox = require('material-ui/Checkbox').default
const LinearProgress = require('material-ui/LinearProgress').default
const Checkbox = checkbox.default
const LinearProgress = linearProgress.default
const TorrentSummary = require('../lib/torrent-summary')
const TorrentPlayer = require('../lib/torrent-player')
const { dispatcher } = require('../lib/dispatcher')
const { calculateEta } = require('../lib/time')
export default class TorrentList extends React.Component {
module.exports = class TorrentList extends React.Component {
render () {
const state = this.props.state

View File

@@ -1,33 +1,24 @@
/* eslint-disable import/first */
// To keep the UI snappy, we run WebTorrent in its own hidden window, a separate
// process from the main window.
console.time('init')
import crypto from 'crypto'
import util from 'util'
import { announceList as defaultAnnounceList } from 'create-torrent'
import electron from 'electron'
import fs from 'fs'
import * as mm from 'music-metadata'
import networkAddress from 'network-address'
import path from 'path'
import WebTorrent from 'webtorrent'
const crypto = require('crypto')
const util = require('util')
const { ipcRenderer } = require('electron')
const fs = require('fs')
const mm = require('music-metadata')
const networkAddress = require('network-address')
const path = require('path')
const WebTorrent = require('webtorrent')
import config from '../config.js'
import { TorrentKeyNotFoundError } from './lib/errors.js'
import torrentPoster from './lib/torrent-poster.js'
const { ipcRenderer } = electron
// Force use of webtorrent trackers on all torrents
globalThis.WEBTORRENT_ANNOUNCE = defaultAnnounceList
.map((arr) => arr[0])
.filter((url) => url.indexOf('wss://') === 0 || url.indexOf('ws://') === 0)
const config = require('../config')
const { TorrentKeyNotFoundError } = require('./lib/errors')
const torrentPoster = require('./lib/torrent-poster')
/**
* WebTorrent version.
*/
const VERSION = JSON.parse(fs.readFileSync('package.json').toString()).version
const VERSION = require('../../package.json').version
/**
* Version number in Azureus-style. Generated from major and minor semver version.
@@ -68,6 +59,8 @@ init()
function init () {
listenToClientEvents()
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))
ipcRenderer.on('wt-stop-torrenting', (e, infoHash) =>
@@ -102,6 +95,11 @@ function listenToClientEvents () {
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.
// Returns a WebTorrent object. See https://git.io/vik9M
function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections) {

View File

@@ -71,7 +71,6 @@ table {
.app {
-webkit-user-select: none;
-webkit-app-region: drag;
height: 100%;
display: flex;
flex-flow: column;
@@ -143,6 +142,7 @@ table {
*/
.header {
-webkit-app-region: drag;
background: rgb(40, 40, 40);
border-bottom: 1px solid rgb(30, 30, 30);
height: 38px; /* vertically center OS menu buttons (OS X) */

View File

@@ -8,6 +8,6 @@
</head>
<body>
<div id='body'></div>
<script type="module" src="../build/renderer/main.js"></script>
<script>require('../build/renderer/main.js')</script>
</body>
</html>