diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 00000000..87b5f8dc --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,19 @@ +# ProBot Request Info (https://probot.github.io/apps/request-info/) + +requestInfoReplyComment: > + We would appreciate it if you could provide us with more information about this + issue.

![](https://feross.net/s/cats/14.gif) +requestInfoLabelToAdd: 'need more info' + +# ProBot Welcome (https://probot.github.io/apps/welcome/) + +newPRWelcomeComment: > + Thanks for opening this pull request! You're awesome. +

![](https://feross.net/s/cats/6.gif) +firstPRMergeComment: > + Congrats on getting your first pull request landed!

+ ![](https://feross.net/s/cats/29.gif) + +# ProBot WIP (https://probot.github.io/apps/wip/) + +# (Requires no configuration) diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 00000000..9390a223 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,4 @@ +# ProBot Lock (https://probot.github.io/apps/lock/) + +daysUntilLock: 180 +lockComment: false diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 00000000..1c396cf1 --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,11 @@ +# ProBot No Response (https://probot.github.io/apps/no-response/) + +daysUntilClose: 7 +responseRequiredLabel: 'need more info' +closeComment: > + This issue has been automatically closed because there was no response to the + maintainer's request for more information from the issue opener. With only the + information that is currently in the issue, we don't have enough information to + take action. Please leave a comment or open a new issue if you have the additional + information we need to investigate further.

+ ![](https://feross.net/s/cats/7.gif) diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..df9115c2 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,14 @@ +# ProBot Stale (https://probot.github.io/apps/stale/) + +daysUntilStale: 90 +daysUntilClose: 7 +exemptLabels: + - accepted + - bug + - enhancement + - meta +staleLabel: stale +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. +closeComment: false diff --git a/.travis.yml b/.travis.yml index 33f6e7bd..c159f6ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ language: node_js node_js: - - 'node' -install: npm install standard depcheck walk-sync + - lts/* diff --git a/AUTHORS.md b/AUTHORS.md index bceeb904..fb47452f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -37,5 +37,15 @@ - Alexey Romanov (romanalexey@gmail.com) - Karan Thakkar (karanjthakkar@gmail.com) - Nuno Campos (nuno.campos@me.com) +- Ebrahim Byagowi (ebrahim@gnu.org) +- Josip Janzic (josip@jjanzic.com) +- Emil Bay (github@tixz.dk) +- Borewit (borewit@users.noreply.github.com) +- greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com) +- Auyer (rafa_auyer@icloud.com) +- SimplyAhmazing (ahmad19526@gmail.com) +- Cezar Carneiro (cezargcarneiro@gmail.com) +- Terry Hau (terryhau@gmail.com) +- Vítor Galvão (info@vitorgalvao.com) #### Generated by bin/update-authors.sh. diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9e33f8..67145077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,41 @@ # WebTorrent Desktop Version History -## v0.18.0 +## v0.20.0 - 2018-04-26 + +### Added + +- Added support for additional audio extensions: 'aiff', 'ape', 'mp2', 'oga', 'opus', 'wma' (#1240) + +### Changed + +- Displaying filename while music metadata is being downloaded (#1361) +- Improved the poster selection for audio/music based torrents (#1334) +- Launch VLC player without the `--video-on-top` flag (#1286) + +### Fixed + +- Fix silently failing to open magnets links on Linux (#1367) + +## v0.19.0 - 2018-01-26 + +### Added +- Added watch folder feature: Automatically add new torrent files added to a folder on disk (#1154) +- Added highest playback priority feature: pauses other active torrents when playback starts (#840) +- Add 'Start Speaking' and 'Stop Speaking' menu item (Mac) (#439) +- Add pinch-to-zoom gesture to enter/exit fullscreen (#1148) + +### Changed +- [SECURITY] Mitigate Electron protocol handler issue (Windows) +- Moved project from Feross's GitHub account to the WebTorrent GitHub organization +- Updated to electron@1.6.16 +- Updated to material-ui@0.17 +- Treat .FLAC as playable audio (#1127) + +### Fixed +- Fix time and duration so it doesn't bounce in the UI (#1233) +- Fix 'About WebTorrent' menu location on Windows (#1120) + +## v0.18.0 - 2017-02-03 ### Added - Add a new "Transfers" menu for pausing or resuming all torrents (#1027) @@ -67,7 +102,7 @@ ## v0.16.0 - 2016-09-18 ### Added -- **Windows 64-bit support!** ([#931](https://github.com/webtorrent/webtorrent-desktop/pull/931)) +- **Windows 64-bit support!** (#931) - Existing 32-bit users will update to 64-bit automatically in next release - 64-bit reduces likelihood of out-of-memory errors by increasing the address space diff --git a/README.md b/README.md index 26a517ce..13bc1d01 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,33 @@

gitter - github release travis + github release version + github release downloads Standard - JavaScript Style Guide

## Install -Download the latest version of WebTorrent Desktop from -[the official website](https://webtorrent.io/desktop/) or the -[GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page. +### Recommended Install -**WebTorrent Desktop** is under very active development. You can try out the -current (unstable) development version by cloning the Git repo. See the -instructions below in the ["How to Contribute"](#how-to-contribute) section. +Download the latest version of WebTorrent Desktop from +[the official website](https://webtorrent.io/desktop/): + +### [✨ Download WebTorrent Desktop ✨](https://webtorrent.io/desktop/) + +### Advanced Install + +- Download specific installer files from the [GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page. + +- Use [Homebrew-Cask](https://github.com/caskroom/homebrew-cask) to install from the command line: + + ``` + $ brew cask install webtorrent + ``` + +- Try the (unstable) development version by cloning the Git repository. See the + ["How to Contribute"](#how-to-contribute) instructions. ## Screenshots diff --git a/bin/package.js b/bin/package.js index 9f59a193..edf00dfd 100755 --- a/bin/package.js +++ b/bin/package.js @@ -449,51 +449,51 @@ function buildWin32 (cb) { usePackageJson: false, version: pkg.version }) - .then(function () { - console.log(`Windows: Created ${destArch} installer.`) + .then(function () { + console.log(`Windows: Created ${destArch} installer.`) - /** + /** * Delete extraneous Squirrel files (i.e. *.nupkg delta files for older * versions of the app) */ - fs.readdirSync(DIST_PATH) - .filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version)) - .forEach((filename) => { - fs.unlinkSync(path.join(DIST_PATH, filename)) - }) + fs.readdirSync(DIST_PATH) + .filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version)) + .forEach((filename) => { + fs.unlinkSync(path.join(DIST_PATH, filename)) + }) - if (destArch === 'ia32') { - console.log('Windows: Renaming ia32 installer files...') + if (destArch === 'ia32') { + console.log('Windows: Renaming ia32 installer files...') - // RELEASES -> RELEASES-ia32 - const relPath = path.join(DIST_PATH, 'RELEASES-ia32') - fs.renameSync( - path.join(DIST_PATH, 'RELEASES'), - relPath - ) + // RELEASES -> RELEASES-ia32 + const relPath = path.join(DIST_PATH, 'RELEASES-ia32') + fs.renameSync( + path.join(DIST_PATH, 'RELEASES'), + relPath + ) - // WebTorrent-vX.X.X-full.nupkg -> WebTorrent-vX.X.X-ia32-full.nupkg - fs.renameSync( - path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-full.nupkg`), - path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-ia32-full.nupkg`) - ) + // WebTorrent-vX.X.X-full.nupkg -> WebTorrent-vX.X.X-ia32-full.nupkg + fs.renameSync( + path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-full.nupkg`), + path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-ia32-full.nupkg`) + ) - // Change file name inside RELEASES-ia32 to match renamed file - const relContent = fs.readFileSync(relPath, 'utf8') - const relContent32 = relContent.replace('full.nupkg', 'ia32-full.nupkg') - fs.writeFileSync(relPath, relContent32) + // Change file name inside RELEASES-ia32 to match renamed file + const relContent = fs.readFileSync(relPath, 'utf8') + const relContent32 = relContent.replace('full.nupkg', 'ia32-full.nupkg') + fs.writeFileSync(relPath, relContent32) - if (relContent === relContent32) { + if (relContent === relContent32) { // Sanity check - throw new Error('Fixing RELEASES-ia32 failed. Replacement did not modify the file.') + throw new Error('Fixing RELEASES-ia32 failed. Replacement did not modify the file.') + } + + console.log('Windows: Renamed ia32 installer files.') } - console.log('Windows: Renamed ia32 installer files.') - } - - cb(null) - }) - .catch(cb) + cb(null) + }) + .catch(cb) } function packagePortable (filesPath, destArch, cb) { diff --git a/package.json b/package.json index 6a7f43b6..c4dcfebc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "webtorrent-desktop", "description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.", - "version": "0.18.0", + "version": "0.20.0", "author": { "name": "WebTorrent, LLC", "email": "feross@webtorrent.io", @@ -16,15 +16,15 @@ "arch": "^2.0.0", "auto-launch": "^4.0.1", "bitfield": "^1.0.2", - "capture-frame": "^1.0.0", + "capture-frame": "^2.0.0", "chokidar": "^1.6.1", - "chromecasts": "^1.8.0", - "cp-file": "^4.0.1", + "chromecasts": "^1.9.1", + "cp-file": "^6.0.0", "create-torrent": "^3.24.5", "debounce": "^1.0.0", "deep-equal": "^1.0.1", "dlnacasts": "^0.1.0", - "drag-drop": "^2.12.1", + "drag-drop": "^4.1.0", "es6-error": "^4.0.0", "fn-getter": "^1.0.0", "iso-639-1": "^1.2.1", @@ -32,7 +32,7 @@ "location-history": "^1.0.0", "material-ui": "^0.17.0", "mkdirp": "^0.5.1", - "musicmetadata": "^2.0.2", + "music-metadata": "^1.0.0", "network-address": "^1.1.0", "parse-torrent": "^5.7.3", "prettier-bytes": "^1.0.1", @@ -51,14 +51,15 @@ "zero-fill": "^2.2.3" }, "devDependencies": { - "buble": "^0.15.2", + "babel-eslint": "^8.2.3", + "buble": "^0.19.3", "cross-zip": "^2.0.1", "depcheck": "^0.6.4", - "electron": "1.6.0", + "electron": "1.6.16", "electron-osx-sign": "0.4.3", "electron-packager": "~8.5.1", "electron-winstaller": "~2.5.2", - "gh-release": "^2.0.3", + "gh-release": "^3.2.1", "minimist": "^1.2.0", "nobin-debian-installer": "0.0.10", "nodemon": "^1.10.2", @@ -108,5 +109,8 @@ "test-integration": "npm run build && node ./test", "update-authors": "./bin/update-authors.sh", "watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/" + }, + "standard": { + "parser": "babel-eslint" } } diff --git a/src/config.js b/src/config.js index 43b5c57f..6f478bee 100644 --- a/src/config.js +++ b/src/config.js @@ -23,7 +23,7 @@ module.exports = { CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report', TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry', - APP_COPYRIGHT: 'Copyright © 2014-2017 ' + APP_TEAM, + APP_COPYRIGHT: 'Copyright © 2014-2018 ' + APP_TEAM, APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'), APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'), APP_NAME: APP_NAME, diff --git a/src/main/announcement.js b/src/main/announcement.js index 662ce51c..ca4ab019 100644 --- a/src/main/announcement.js +++ b/src/main/announcement.js @@ -7,9 +7,8 @@ const electron = require('electron') const config = require('../config') const log = require('./log') -const ANNOUNCEMENT_URL = config.ANNOUNCEMENT_URL + - '?version=' + config.APP_VERSION + - '&platform=' + process.platform +const ANNOUNCEMENT_URL = + `${config.ANNOUNCEMENT_URL}?version=${config.APP_VERSION}&platform=${process.platform}` /** * In certain situations, the WebTorrent team may need to show an announcement to diff --git a/src/main/external-player.js b/src/main/external-player.js index 39b80061..c6858120 100644 --- a/src/main/external-player.js +++ b/src/main/external-player.js @@ -29,7 +29,6 @@ function spawn (playerPath, url, title) { if (err) return windows.main.dispatch('externalPlayerNotFound') const args = [ '--play-and-exit', - '--video-on-top', '--quiet', `--meta-title=${JSON.stringify(title)}`, url @@ -40,13 +39,13 @@ function spawn (playerPath, url, title) { function kill () { if (!proc) return - log('Killing external player, pid ' + proc.pid) + log(`Killing external player, pid ${proc.pid}`) proc.kill('SIGKILL') // kill -9 proc = null } function spawnExternal (playerPath, args) { - log('Running external media player:', playerPath + ' ' + args.join(' ')) + log('Running external media player:', `${playerPath} ${args.join(' ')}`) if (process.platform === 'darwin' && path.extname(playerPath) === '.app') { // Mac: Use executable in packaged .app bundle diff --git a/src/main/handlers.js b/src/main/handlers.js index cc5d2820..dfef92c8 100644 --- a/src/main/handlers.js +++ b/src/main/handlers.js @@ -40,9 +40,9 @@ function installDarwin () { // File handlers are defined in `Info.plist`. } -function uninstallDarwin () { } +function uninstallDarwin () {} -const EXEC_COMMAND = [process.execPath] +const EXEC_COMMAND = [ process.execPath, '--' ] if (!config.IS_PRODUCTION) { EXEC_COMMAND.push(config.ROOT_PATH) @@ -312,7 +312,7 @@ function installLinux () { 'webtorrent-desktop.desktop' ) fs.mkdirp(path.dirname(desktopFilePath)) - fs.writeFile(desktopFilePath, desktopFile, (err) => { + fs.writeFile(desktopFilePath, desktopFile, err => { if (err) return log.error(err.message) }) } @@ -334,9 +334,9 @@ function installLinux () { 'icons', 'webtorrent-desktop.png' ) - mkdirp(path.dirname(iconFilePath), (err) => { + mkdirp(path.dirname(iconFilePath), err => { if (err) return log.error(err.message) - fs.writeFile(iconFilePath, iconFile, (err) => { + fs.writeFile(iconFilePath, iconFile, err => { if (err) log.error(err.message) }) }) diff --git a/src/main/index.js b/src/main/index.js index 9d952f87..33e9fb3a 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -188,7 +188,7 @@ function onAppOpen (newArgv) { function sliceArgv (argv) { return argv.slice(config.IS_PRODUCTION ? 1 : config.IS_TEST ? 4 - : 2) + : 2) } function processArgv (argv) { diff --git a/src/main/windows/main.js b/src/main/windows/main.js index 104ac132..6898ebfd 100644 --- a/src/main/windows/main.js +++ b/src/main/windows/main.js @@ -138,7 +138,7 @@ function setAspectRatio (aspectRatio) { function setBounds (bounds, maximize) { // Do nothing in fullscreen if (!main.win || main.win.isFullScreen()) { - log('setBounds: not setting bounds because we\'re in full screen') + log(`setBounds: not setting bounds because we're in full screen`) return } @@ -162,13 +162,13 @@ function setBounds (bounds, maximize) { // Assuming we're not maximized or maximizing, set the window size if (!willBeMaximized) { - log('setBounds: setting bounds to ' + JSON.stringify(bounds)) + log(`setBounds: setting bounds to ${JSON.stringify(bounds)}`) if (bounds.x === null && bounds.y === null) { // X and Y not specified? By default, center on current screen const scr = electron.screen.getDisplayMatching(main.win.getBounds()) bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2)) bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2)) - log('setBounds: centered to ' + JSON.stringify(bounds)) + log(`setBounds: centered to ${JSON.stringify(bounds)}`) } // Resize the window's content area (so window border doesn't need to be taken // into account) diff --git a/src/renderer/controllers/playback-controller.js b/src/renderer/controllers/playback-controller.js index 8288baa3..b26ecf5f 100644 --- a/src/renderer/controllers/playback-controller.js +++ b/src/renderer/controllers/playback-controller.js @@ -102,10 +102,10 @@ module.exports = class PlaybackController { const state = this.state if (Playlist.hasNext(state) && state.playing.location !== 'external') { this.updatePlayer( - state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => { - if (err) dispatch('error', err) - else this.play() - }) + state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => { + if (err) dispatch('error', err) + else this.play() + }) } } @@ -271,7 +271,7 @@ module.exports = class PlaybackController { state.playing.fileIndex = index state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video' : TorrentPlayer.isAudio(fileSummary) ? 'audio' - : 'other' + : 'other' // pick up where we left off let jumpToTime = 0 diff --git a/src/renderer/controllers/torrent-list-controller.js b/src/renderer/controllers/torrent-list-controller.js index 12cee063..984b6fdb 100644 --- a/src/renderer/controllers/torrent-list-controller.js +++ b/src/renderer/controllers/torrent-list-controller.js @@ -157,23 +157,24 @@ module.exports = class TorrentListController { prioritizeTorrent (infoHash) { this.state.saved.torrents - .filter((torrent) => { // We're interested in active torrents only. - return (['downloading', 'seeding'].indexOf(torrent.status) !== -1) - }) - .map((torrent) => { // Pause all active torrents except the one that started playing. - if (infoHash === torrent.infoHash) return + .filter((torrent) => { // We're interested in active torrents only. + return (['downloading', 'seeding'].indexOf(torrent.status) !== -1) + }) + .map((torrent) => { // Pause all active torrents except the one that started playing. + if (infoHash === torrent.infoHash) return - // Pause torrent without playing sounds. - this.pauseTorrent(torrent, false) + // Pause torrent without playing sounds. + this.pauseTorrent(torrent, false) - this.state.saved.torrentsToResume.push(torrent.infoHash) - }) + this.state.saved.torrentsToResume.push(torrent.infoHash) + }) console.log('Playback Priority: paused torrents: ', this.state.saved.torrentsToResume) } resumePausedTorrents () { console.log('Playback Priority: resuming paused torrents') + if (!this.state.saved.torrentsToResume || !this.state.saved.torrentsToResume.length) return this.state.saved.torrentsToResume.map((infoHash) => { this.toggleTorrent(infoHash) }) diff --git a/src/renderer/lib/state.js b/src/renderer/lib/state.js index 68c85bb0..8fc89a4f 100644 --- a/src/renderer/lib/state.js +++ b/src/renderer/lib/state.js @@ -123,7 +123,8 @@ function setupStateSaved (cb) { externalPlayerPath: null, startup: false, autoAddTorrents: false, - torrentsFolderPath: '' + torrentsFolderPath: '', + highestPlaybackPriority: true }, torrents: config.DEFAULT_TORRENTS.map(createTorrentObject), torrentsToResume: [], diff --git a/src/renderer/lib/torrent-player.js b/src/renderer/lib/torrent-player.js index 6a77d25b..723d2cfa 100644 --- a/src/renderer/lib/torrent-player.js +++ b/src/renderer/lib/torrent-player.js @@ -33,12 +33,18 @@ function isVideo (file) { function isAudio (file) { return [ '.aac', + '.aiff', + '.ape', '.ac3', - '.mp3', - '.ogg', - '.wav', '.flac', - '.m4a' + '.m4a', + '.mp2', + '.mp3', + '.oga', + '.ogg', + '.opus', + '.wav', + '.wma' ].includes(getFileExtension(file)) } diff --git a/src/renderer/lib/torrent-poster.js b/src/renderer/lib/torrent-poster.js index f05d1bb5..8672f3fb 100644 --- a/src/renderer/lib/torrent-poster.js +++ b/src/renderer/lib/torrent-poster.js @@ -3,39 +3,146 @@ module.exports = torrentPoster const captureFrame = require('capture-frame') const path = require('path') +const mediaExtensions = { + audio: ['.aac', '.asf', '.flac', '.m2a', '.m4a', '.mp2', '.mp4', '.mp3', '.oga', '.ogg', '.opus', + '.wma', '.wav', '.wv', '.wvp'], + video: ['.mp4', '.m4v', '.webm', '.mov', '.mkv'], + image: ['.gif', '.jpg', '.jpeg', '.png'] +} + function torrentPoster (torrent, cb) { // First, try to use a poster image if available const posterFile = torrent.files.filter(function (file) { return /^poster\.(jpg|png|gif)$/.test(file.name) })[0] - if (posterFile) return torrentPosterFromImage(posterFile, torrent, cb) + if (posterFile) return extractPoster(posterFile, cb) - // Second, try to use the largest video file - // Filter out file formats that the