Merge branch 'master' into update-electron

# Conflicts:
#	package-lock.json
#	package.json
This commit is contained in:
Borewit
2019-07-21 10:42:32 +02:00
15 changed files with 408 additions and 499 deletions

View File

@@ -158,6 +158,18 @@ The Mac app can only be packaged from **macOS**.
The Linux app can be packaged from **any** platform. The Linux app can be packaged from **any** platform.
#### Recommended readings to start working in the app
Electron (Framework to make native apps for Windows, OSX and Linux in Javascript):
https://electronjs.org/docs/tutorial/quick-start
React.js (Framework to work with Frontend UI):
https://reactjs.org/docs/getting-started.html
Material UI (React components that implement Google's Material Design.):
https://material-ui.com/getting-started
### Privacy ### Privacy
WebTorrent Desktop collects some basic usage stats to help us make the app better. WebTorrent Desktop collects some basic usage stats to help us make the app better.

687
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,11 +16,11 @@
"arch": "^2.0.0", "arch": "^2.0.0",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"bitfield": "^1.0.2", "bitfield": "^1.0.2",
"capture-frame": "^2.0.0", "capture-frame": "^3.0.0",
"chokidar": "^2.0.4", "chokidar": "^2.0.4",
"chromecasts": "^1.9.1", "chromecasts": "^1.9.1",
"cp-file": "^7.0.0", "cp-file": "^7.0.0",
"create-torrent": "^3.33.0", "create-torrent": "^4.0.0",
"debounce": "^1.0.0", "debounce": "^1.0.0",
"deep-equal": "^1.0.1", "deep-equal": "^1.0.1",
"dlnacasts": "^0.1.0", "dlnacasts": "^0.1.0",
@@ -34,7 +34,7 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"music-metadata": "^3.6.1", "music-metadata": "^3.6.1",
"network-address": "^1.1.0", "network-address": "^1.1.0",
"parse-torrent": "^6.0.1", "parse-torrent": "^7.0.0",
"prettier-bytes": "^1.0.1", "prettier-bytes": "^1.0.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.5.2", "react": "^16.5.2",
@@ -51,10 +51,10 @@
"zero-fill": "^2.2.3" "zero-fill": "^2.2.3"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^9.0.0", "babel-eslint": "^10.0.2",
"buble": "^0.19.6", "buble": "^0.19.6",
"cross-zip": "^2.0.1", "cross-zip": "^2.0.1",
"depcheck": "^0.7.2", "depcheck": "^0.8.0",
"electron": "^4.0.0", "electron": "^4.0.0",
"electron-osx-sign": "^0.4.11", "electron-osx-sign": "^0.4.11",
"electron-packager": "~8.5.1", "electron-packager": "~8.5.1",
@@ -70,7 +70,7 @@
"spectron": "^3.3.0", "spectron": "^3.3.0",
"standard": "*", "standard": "*",
"tape": "^4.9.1", "tape": "^4.9.1",
"walk-sync": "^1.1.3" "walk-sync": "^2.0.2"
}, },
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0"

View File

@@ -235,100 +235,100 @@ function updateElectron () {
const dispatchHandlers = { const dispatchHandlers = {
// Torrent list: creating, deleting, selecting torrents // Torrent list: creating, deleting, selecting torrents
'openTorrentFile': () => ipcRenderer.send('openTorrentFile'), openTorrentFile: () => ipcRenderer.send('openTorrentFile'),
'openFiles': () => ipcRenderer.send('openFiles'), /* shows the open file dialog */ openFiles: () => ipcRenderer.send('openFiles'), /* shows the open file dialog */
'openTorrentAddress': () => { state.modal = { id: 'open-torrent-address-modal' } }, openTorrentAddress: () => { state.modal = { id: 'open-torrent-address-modal' } },
'addTorrent': (torrentId) => controllers.torrentList().addTorrent(torrentId), addTorrent: (torrentId) => controllers.torrentList().addTorrent(torrentId),
'showCreateTorrent': (paths) => controllers.torrentList().showCreateTorrent(paths), showCreateTorrent: (paths) => controllers.torrentList().showCreateTorrent(paths),
'createTorrent': (options) => controllers.torrentList().createTorrent(options), createTorrent: (options) => controllers.torrentList().createTorrent(options),
'toggleTorrent': (infoHash) => controllers.torrentList().toggleTorrent(infoHash), toggleTorrent: (infoHash) => controllers.torrentList().toggleTorrent(infoHash),
'pauseAllTorrents': () => controllers.torrentList().pauseAllTorrents(), pauseAllTorrents: () => controllers.torrentList().pauseAllTorrents(),
'resumeAllTorrents': () => controllers.torrentList().resumeAllTorrents(), resumeAllTorrents: () => controllers.torrentList().resumeAllTorrents(),
'toggleTorrentFile': (infoHash, index) => toggleTorrentFile: (infoHash, index) =>
controllers.torrentList().toggleTorrentFile(infoHash, index), controllers.torrentList().toggleTorrentFile(infoHash, index),
'confirmDeleteTorrent': (infoHash, deleteData) => confirmDeleteTorrent: (infoHash, deleteData) =>
controllers.torrentList().confirmDeleteTorrent(infoHash, deleteData), controllers.torrentList().confirmDeleteTorrent(infoHash, deleteData),
'deleteTorrent': (infoHash, deleteData) => deleteTorrent: (infoHash, deleteData) =>
controllers.torrentList().deleteTorrent(infoHash, deleteData), controllers.torrentList().deleteTorrent(infoHash, deleteData),
'toggleSelectTorrent': (infoHash) => toggleSelectTorrent: (infoHash) =>
controllers.torrentList().toggleSelectTorrent(infoHash), controllers.torrentList().toggleSelectTorrent(infoHash),
'openTorrentContextMenu': (infoHash) => openTorrentContextMenu: (infoHash) =>
controllers.torrentList().openTorrentContextMenu(infoHash), controllers.torrentList().openTorrentContextMenu(infoHash),
'startTorrentingSummary': (torrentKey) => startTorrentingSummary: (torrentKey) =>
controllers.torrentList().startTorrentingSummary(torrentKey), controllers.torrentList().startTorrentingSummary(torrentKey),
'saveTorrentFileAs': (torrentKey) => saveTorrentFileAs: (torrentKey) =>
controllers.torrentList().saveTorrentFileAs(torrentKey), controllers.torrentList().saveTorrentFileAs(torrentKey),
'prioritizeTorrent': (infoHash) => controllers.torrentList().prioritizeTorrent(infoHash), prioritizeTorrent: (infoHash) => controllers.torrentList().prioritizeTorrent(infoHash),
'resumePausedTorrents': () => controllers.torrentList().resumePausedTorrents(), resumePausedTorrents: () => controllers.torrentList().resumePausedTorrents(),
// Playback // Playback
'playFile': (infoHash, index) => controllers.playback().playFile(infoHash, index), playFile: (infoHash, index) => controllers.playback().playFile(infoHash, index),
'playPause': () => controllers.playback().playPause(), playPause: () => controllers.playback().playPause(),
'nextTrack': () => controllers.playback().nextTrack(), nextTrack: () => controllers.playback().nextTrack(),
'previousTrack': () => controllers.playback().previousTrack(), previousTrack: () => controllers.playback().previousTrack(),
'skip': (time) => controllers.playback().skip(time), skip: (time) => controllers.playback().skip(time),
'skipTo': (time) => controllers.playback().skipTo(time), skipTo: (time) => controllers.playback().skipTo(time),
'changePlaybackRate': (dir) => controllers.playback().changePlaybackRate(dir), changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir),
'changeVolume': (delta) => controllers.playback().changeVolume(delta), changeVolume: (delta) => controllers.playback().changeVolume(delta),
'setVolume': (vol) => controllers.playback().setVolume(vol), setVolume: (vol) => controllers.playback().setVolume(vol),
'openItem': (infoHash, index) => controllers.playback().openItem(infoHash, index), openItem: (infoHash, index) => controllers.playback().openItem(infoHash, index),
// Subtitles // Subtitles
'openSubtitles': () => controllers.subtitles().openSubtitles(), openSubtitles: () => controllers.subtitles().openSubtitles(),
'selectSubtitle': (index) => controllers.subtitles().selectSubtitle(index), selectSubtitle: (index) => controllers.subtitles().selectSubtitle(index),
'toggleSubtitlesMenu': () => controllers.subtitles().toggleSubtitlesMenu(), toggleSubtitlesMenu: () => controllers.subtitles().toggleSubtitlesMenu(),
'checkForSubtitles': () => controllers.subtitles().checkForSubtitles(), checkForSubtitles: () => controllers.subtitles().checkForSubtitles(),
'addSubtitles': (files, autoSelect) => controllers.subtitles().addSubtitles(files, autoSelect), addSubtitles: (files, autoSelect) => controllers.subtitles().addSubtitles(files, autoSelect),
// Local media: <video>, <audio>, external players // Local media: <video>, <audio>, external players
'mediaStalled': () => controllers.media().mediaStalled(), mediaStalled: () => controllers.media().mediaStalled(),
'mediaError': (err) => controllers.media().mediaError(err), mediaError: (err) => controllers.media().mediaError(err),
'mediaSuccess': () => controllers.media().mediaSuccess(), mediaSuccess: () => controllers.media().mediaSuccess(),
'mediaTimeUpdate': () => controllers.media().mediaTimeUpdate(), mediaTimeUpdate: () => controllers.media().mediaTimeUpdate(),
'mediaMouseMoved': () => controllers.media().mediaMouseMoved(), mediaMouseMoved: () => controllers.media().mediaMouseMoved(),
'mediaControlsMouseEnter': () => controllers.media().controlsMouseEnter(), mediaControlsMouseEnter: () => controllers.media().controlsMouseEnter(),
'mediaControlsMouseLeave': () => controllers.media().controlsMouseLeave(), mediaControlsMouseLeave: () => controllers.media().controlsMouseLeave(),
'openExternalPlayer': () => controllers.media().openExternalPlayer(), openExternalPlayer: () => controllers.media().openExternalPlayer(),
'externalPlayerNotFound': () => controllers.media().externalPlayerNotFound(), externalPlayerNotFound: () => controllers.media().externalPlayerNotFound(),
// Remote casting: Chromecast, Airplay, etc // Remote casting: Chromecast, Airplay, etc
'toggleCastMenu': (deviceType) => lazyLoadCast().toggleMenu(deviceType), toggleCastMenu: (deviceType) => lazyLoadCast().toggleMenu(deviceType),
'selectCastDevice': (index) => lazyLoadCast().selectDevice(index), selectCastDevice: (index) => lazyLoadCast().selectDevice(index),
'stopCasting': () => lazyLoadCast().stop(), stopCasting: () => lazyLoadCast().stop(),
// Preferences screen // Preferences screen
'preferences': () => controllers.prefs().show(), preferences: () => controllers.prefs().show(),
'updatePreferences': (key, value) => controllers.prefs().update(key, value), updatePreferences: (key, value) => controllers.prefs().update(key, value),
'checkDownloadPath': checkDownloadPath, checkDownloadPath: checkDownloadPath,
'startFolderWatcher': () => controllers.folderWatcher().start(), startFolderWatcher: () => controllers.folderWatcher().start(),
'stopFolderWatcher': () => controllers.folderWatcher().stop(), stopFolderWatcher: () => controllers.folderWatcher().stop(),
// Update (check for new versions on Linux, where there's no auto updater) // Update (check for new versions on Linux, where there's no auto updater)
'updateAvailable': (version) => controllers.update().updateAvailable(version), updateAvailable: (version) => controllers.update().updateAvailable(version),
'skipVersion': (version) => controllers.update().skipVersion(version), skipVersion: (version) => controllers.update().skipVersion(version),
// Navigation between screens (back, forward, ESC, etc) // Navigation between screens (back, forward, ESC, etc)
'exitModal': () => { state.modal = null }, exitModal: () => { state.modal = null },
'backToList': backToList, backToList: backToList,
'escapeBack': escapeBack, escapeBack: escapeBack,
'back': () => state.location.back(), back: () => state.location.back(),
'forward': () => state.location.forward(), forward: () => state.location.forward(),
'cancel': () => state.location.cancel(), cancel: () => state.location.cancel(),
// Controlling the window // Controlling the window
'setDimensions': setDimensions, setDimensions: setDimensions,
'toggleFullScreen': (setTo) => ipcRenderer.send('toggleFullScreen', setTo), toggleFullScreen: (setTo) => ipcRenderer.send('toggleFullScreen', setTo),
'setTitle': (title) => { state.window.title = title }, setTitle: (title) => { state.window.title = title },
'resetTitle': () => { state.window.title = config.APP_WINDOW_TITLE }, resetTitle: () => { state.window.title = config.APP_WINDOW_TITLE },
// Everything else // Everything else
'onOpen': onOpen, onOpen: onOpen,
'error': onError, error: onError,
'uncaughtError': (proc, err) => telemetry.logUncaughtError(proc, err), uncaughtError: (proc, err) => telemetry.logUncaughtError(proc, err),
'stateSave': () => State.save(state), stateSave: () => State.save(state),
'stateSaveImmediate': () => State.saveImmediate(state), stateSaveImmediate: () => State.saveImmediate(state),
'update': () => {} // No-op, just trigger an update update: () => {} // No-op, just trigger an update
} }
// Events from the UI never modify state directly. Instead they call dispatch() // Events from the UI never modify state directly. Instead they call dispatch()

View File

@@ -12,10 +12,10 @@ const Header = require('../components/header')
const TorrentListPage = require('./torrent-list-page') const TorrentListPage = require('./torrent-list-page')
const Views = { const Views = {
'home': createGetter(() => TorrentListPage), home: createGetter(() => TorrentListPage),
'player': createGetter(() => require('./player-page')), player: createGetter(() => require('./player-page')),
'create-torrent': createGetter(() => require('./create-torrent-page')), 'create-torrent': createGetter(() => require('./create-torrent-page')),
'preferences': createGetter(() => require('./preferences-page')) preferences: createGetter(() => require('./preferences-page'))
} }
const Modals = { const Modals = {

View File

@@ -539,9 +539,9 @@ function renderPlayerControls (state) {
// Add the cast buttons. Icons for each cast type, connected/disconnected: // Add the cast buttons. Icons for each cast type, connected/disconnected:
const buttonIcons = { const buttonIcons = {
'chromecast': { true: 'cast_connected', false: 'cast' }, chromecast: { true: 'cast_connected', false: 'cast' },
'airplay': { true: 'airplay', false: 'airplay' }, airplay: { true: 'airplay', false: 'airplay' },
'dlna': { true: 'tv', false: 'tv' } dlna: { true: 'tv', false: 'tv' }
} }
castTypes.forEach(function (castType) { castTypes.forEach(function (castType) {
// Do we show this button (eg. the Chromecast button) at all? // Do we show this button (eg. the Chromecast button) at all?

View File

@@ -205,7 +205,7 @@ function compareTorrentFile (t, pathActual, fieldsExpected) {
function extractImportantFields (parsedTorrent) { function extractImportantFields (parsedTorrent) {
const { infoHash, name, announce, urlList, comment } = parsedTorrent const { infoHash, name, announce, urlList, comment } = parsedTorrent
const priv = parsedTorrent.private // private is a reserved word in JS const priv = parsedTorrent.private // private is a reserved word in JS
return { infoHash, name, announce, urlList, comment, 'private': priv } return { infoHash, name, announce, urlList, comment, private: priv }
} }
function copy (pathFrom, pathTo) { function copy (pathFrom, pathTo) {