Issue #92: Merge with master & exclude some of the proposed changes.

This commit is contained in:
Borewit
2018-05-23 20:19:28 +02:00
68 changed files with 495 additions and 159 deletions

19
.github/config.yml vendored Normal file
View File

@@ -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. <br><br> ![](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.
<br><br> ![](https://feross.net/s/cats/6.gif)
firstPRMergeComment: >
Congrats on getting your first pull request landed! <br><br>
![](https://feross.net/s/cats/29.gif)
# ProBot WIP (https://probot.github.io/apps/wip/)
# (Requires no configuration)

4
.github/lock.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# ProBot Lock (https://probot.github.io/apps/lock/)
daysUntilLock: 180
lockComment: false

11
.github/no-response.yml vendored Normal file
View File

@@ -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. <br><br>
![](https://feross.net/s/cats/7.gif)

14
.github/stale.yml vendored Normal file
View File

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

View File

@@ -1,4 +1,3 @@
language: node_js language: node_js
node_js: node_js:
- 'node' - lts/*
install: npm install standard depcheck walk-sync

View File

@@ -37,5 +37,15 @@
- Alexey Romanov (romanalexey@gmail.com) - Alexey Romanov (romanalexey@gmail.com)
- Karan Thakkar (karanjthakkar@gmail.com) - Karan Thakkar (karanjthakkar@gmail.com)
- Nuno Campos (nuno.campos@me.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. #### Generated by bin/update-authors.sh.

View File

@@ -1,6 +1,41 @@
# WebTorrent Desktop Version History # 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 ### Added
- Add a new "Transfers" menu for pausing or resuming all torrents (#1027) - Add a new "Transfers" menu for pausing or resuming all torrents (#1027)
@@ -67,7 +102,7 @@
## v0.16.0 - 2016-09-18 ## v0.16.0 - 2016-09-18
### Added ### 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 - Existing 32-bit users will update to 64-bit automatically in next release
- 64-bit reduces likelihood of out-of-memory errors by increasing the address space - 64-bit reduces likelihood of out-of-memory errors by increasing the address space

View File

@@ -13,20 +13,33 @@
<p align="center"> <p align="center">
<a href="https://gitter.im/webtorrent/webtorrent"><img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg" alt="gitter"></a> <a href="https://gitter.im/webtorrent/webtorrent"><img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg" alt="gitter"></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"></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://travis-ci.org/webtorrent/webtorrent-desktop"><img src="https://img.shields.io/travis/webtorrent/webtorrent-desktop/master.svg" alt="travis"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/release/webtorrent/webtorrent-desktop.svg" alt="github release version"></a>
<a href="https://github.com/webtorrent/webtorrent-desktop/releases"><img src="https://img.shields.io/github/downloads/webtorrent/webtorrent-desktop/total.svg" alt="github release downloads"></a>
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Standard - JavaScript Style Guide"></a> <a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Standard - JavaScript Style Guide"></a>
</p> </p>
## Install ## Install
Download the latest version of WebTorrent Desktop from ### Recommended Install
[the official website](https://webtorrent.io/desktop/) or the
[GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page.
**WebTorrent Desktop** is under very active development. You can try out the Download the latest version of WebTorrent Desktop from
current (unstable) development version by cloning the Git repo. See the [the official website](https://webtorrent.io/desktop/):
instructions below in the ["How to Contribute"](#how-to-contribute) section.
### [✨ Download WebTorrent Desktop ✨](https://webtorrent.io/desktop/)
### Advanced Install
- Download specific installer files from the [GitHub releases](https://github.com/webtorrent/webtorrent-desktop/releases) page.
- Use [Homebrew-Cask](https://github.com/caskroom/homebrew-cask) to install from the command line:
```
$ brew cask install webtorrent
```
- Try the (unstable) development version by cloning the Git repository. See the
["How to Contribute"](#how-to-contribute) instructions.
## Screenshots ## Screenshots

View File

@@ -449,51 +449,51 @@ function buildWin32 (cb) {
usePackageJson: false, usePackageJson: false,
version: pkg.version version: pkg.version
}) })
.then(function () { .then(function () {
console.log(`Windows: Created ${destArch} installer.`) console.log(`Windows: Created ${destArch} installer.`)
/** /**
* Delete extraneous Squirrel files (i.e. *.nupkg delta files for older * Delete extraneous Squirrel files (i.e. *.nupkg delta files for older
* versions of the app) * versions of the app)
*/ */
fs.readdirSync(DIST_PATH) fs.readdirSync(DIST_PATH)
.filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version)) .filter((name) => name.endsWith('.nupkg') && !name.includes(pkg.version))
.forEach((filename) => { .forEach((filename) => {
fs.unlinkSync(path.join(DIST_PATH, filename)) fs.unlinkSync(path.join(DIST_PATH, filename))
}) })
if (destArch === 'ia32') { if (destArch === 'ia32') {
console.log('Windows: Renaming ia32 installer files...') console.log('Windows: Renaming ia32 installer files...')
// RELEASES -> RELEASES-ia32 // RELEASES -> RELEASES-ia32
const relPath = path.join(DIST_PATH, 'RELEASES-ia32') const relPath = path.join(DIST_PATH, 'RELEASES-ia32')
fs.renameSync( fs.renameSync(
path.join(DIST_PATH, 'RELEASES'), path.join(DIST_PATH, 'RELEASES'),
relPath relPath
) )
// WebTorrent-vX.X.X-full.nupkg -> WebTorrent-vX.X.X-ia32-full.nupkg // WebTorrent-vX.X.X-full.nupkg -> WebTorrent-vX.X.X-ia32-full.nupkg
fs.renameSync( fs.renameSync(
path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-full.nupkg`), 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`) path.join(DIST_PATH, `${config.APP_NAME}-${config.APP_VERSION}-ia32-full.nupkg`)
) )
// Change file name inside RELEASES-ia32 to match renamed file // Change file name inside RELEASES-ia32 to match renamed file
const relContent = fs.readFileSync(relPath, 'utf8') const relContent = fs.readFileSync(relPath, 'utf8')
const relContent32 = relContent.replace('full.nupkg', 'ia32-full.nupkg') const relContent32 = relContent.replace('full.nupkg', 'ia32-full.nupkg')
fs.writeFileSync(relPath, relContent32) fs.writeFileSync(relPath, relContent32)
if (relContent === relContent32) { if (relContent === relContent32) {
// Sanity check // 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) { function packagePortable (filesPath, destArch, cb) {

View File

@@ -1,7 +1,7 @@
{ {
"name": "webtorrent-desktop", "name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.", "description": "WebTorrent, the streaming torrent client. For Mac, Windows, and Linux.",
"version": "0.18.0", "version": "0.20.0",
"author": { "author": {
"name": "WebTorrent, LLC", "name": "WebTorrent, LLC",
"email": "feross@webtorrent.io", "email": "feross@webtorrent.io",
@@ -16,15 +16,15 @@
"arch": "^2.0.0", "arch": "^2.0.0",
"auto-launch": "^4.0.1", "auto-launch": "^4.0.1",
"bitfield": "^1.0.2", "bitfield": "^1.0.2",
"capture-frame": "^1.0.0", "capture-frame": "^2.0.0",
"chokidar": "^1.6.1", "chokidar": "^1.6.1",
"chromecasts": "^1.8.0", "chromecasts": "^1.9.1",
"cp-file": "^4.0.1", "cp-file": "^6.0.0",
"create-torrent": "^3.24.5", "create-torrent": "^3.24.5",
"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",
"drag-drop": "^2.12.1", "drag-drop": "^4.1.0",
"es6-error": "^4.0.0", "es6-error": "^4.0.0",
"fn-getter": "^1.0.0", "fn-getter": "^1.0.0",
"iso-639-1": "^1.2.1", "iso-639-1": "^1.2.1",
@@ -32,7 +32,7 @@
"location-history": "^1.0.0", "location-history": "^1.0.0",
"material-ui": "^0.17.0", "material-ui": "^0.17.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"musicmetadata": "^2.0.2", "music-metadata": "^1.0.0",
"network-address": "^1.1.0", "network-address": "^1.1.0",
"parse-torrent": "^5.7.3", "parse-torrent": "^5.7.3",
"prettier-bytes": "^1.0.1", "prettier-bytes": "^1.0.1",
@@ -51,14 +51,15 @@
"zero-fill": "^2.2.3" "zero-fill": "^2.2.3"
}, },
"devDependencies": { "devDependencies": {
"buble": "^0.15.2", "babel-eslint": "^8.2.3",
"buble": "^0.19.3",
"cross-zip": "^2.0.1", "cross-zip": "^2.0.1",
"depcheck": "^0.6.4", "depcheck": "^0.6.4",
"electron": "1.6.0", "electron": "1.6.16",
"electron-osx-sign": "0.4.3", "electron-osx-sign": "0.4.3",
"electron-packager": "~8.5.1", "electron-packager": "~8.5.1",
"electron-winstaller": "~2.5.2", "electron-winstaller": "~2.5.2",
"gh-release": "^2.0.3", "gh-release": "^3.2.1",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"nobin-debian-installer": "0.0.10", "nobin-debian-installer": "0.0.10",
"nodemon": "^1.10.2", "nodemon": "^1.10.2",
@@ -108,5 +109,8 @@
"test-integration": "npm run build && node ./test", "test-integration": "npm run build && node ./test",
"update-authors": "./bin/update-authors.sh", "update-authors": "./bin/update-authors.sh",
"watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/" "watch": "nodemon --exec \"npm run start\" --ext js,css --ignore build/ --ignore dist/"
},
"standard": {
"parser": "babel-eslint"
} }
} }

View File

@@ -23,7 +23,7 @@ module.exports = {
CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report', CRASH_REPORT_URL: 'https://webtorrent.io/desktop/crash-report',
TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry', TELEMETRY_URL: 'https://webtorrent.io/desktop/telemetry',
APP_COPYRIGHT: 'Copyright © 2014-2017 ' + APP_TEAM, APP_COPYRIGHT: 'Copyright © 2014-2018 ' + APP_TEAM,
APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'), APP_FILE_ICON: path.join(__dirname, '..', 'static', 'WebTorrentFile'),
APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'), APP_ICON: path.join(__dirname, '..', 'static', 'WebTorrent'),
APP_NAME: APP_NAME, APP_NAME: APP_NAME,

View File

@@ -7,9 +7,8 @@ const electron = require('electron')
const config = require('../config') const config = require('../config')
const log = require('./log') const log = require('./log')
const ANNOUNCEMENT_URL = config.ANNOUNCEMENT_URL + const ANNOUNCEMENT_URL =
'?version=' + config.APP_VERSION + `${config.ANNOUNCEMENT_URL}?version=${config.APP_VERSION}&platform=${process.platform}`
'&platform=' + process.platform
/** /**
* In certain situations, the WebTorrent team may need to show an announcement to * In certain situations, the WebTorrent team may need to show an announcement to

View File

@@ -29,7 +29,6 @@ function spawn (playerPath, url, title) {
if (err) return windows.main.dispatch('externalPlayerNotFound') if (err) return windows.main.dispatch('externalPlayerNotFound')
const args = [ const args = [
'--play-and-exit', '--play-and-exit',
'--video-on-top',
'--quiet', '--quiet',
`--meta-title=${JSON.stringify(title)}`, `--meta-title=${JSON.stringify(title)}`,
url url
@@ -40,13 +39,13 @@ function spawn (playerPath, url, title) {
function kill () { function kill () {
if (!proc) return if (!proc) return
log('Killing external player, pid ' + proc.pid) log(`Killing external player, pid ${proc.pid}`)
proc.kill('SIGKILL') // kill -9 proc.kill('SIGKILL') // kill -9
proc = null proc = null
} }
function spawnExternal (playerPath, args) { function spawnExternal (playerPath, args) {
log('Running external media player:', playerPath + ' ' + args.join(' ')) log('Running external media player:', `${playerPath} ${args.join(' ')}`)
if (process.platform === 'darwin' && path.extname(playerPath) === '.app') { if (process.platform === 'darwin' && path.extname(playerPath) === '.app') {
// Mac: Use executable in packaged .app bundle // Mac: Use executable in packaged .app bundle

View File

@@ -40,9 +40,9 @@ function installDarwin () {
// File handlers are defined in `Info.plist`. // 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) { if (!config.IS_PRODUCTION) {
EXEC_COMMAND.push(config.ROOT_PATH) EXEC_COMMAND.push(config.ROOT_PATH)
@@ -312,7 +312,7 @@ function installLinux () {
'webtorrent-desktop.desktop' 'webtorrent-desktop.desktop'
) )
fs.mkdirp(path.dirname(desktopFilePath)) fs.mkdirp(path.dirname(desktopFilePath))
fs.writeFile(desktopFilePath, desktopFile, (err) => { fs.writeFile(desktopFilePath, desktopFile, err => {
if (err) return log.error(err.message) if (err) return log.error(err.message)
}) })
} }
@@ -334,9 +334,9 @@ function installLinux () {
'icons', 'icons',
'webtorrent-desktop.png' 'webtorrent-desktop.png'
) )
mkdirp(path.dirname(iconFilePath), (err) => { mkdirp(path.dirname(iconFilePath), err => {
if (err) return log.error(err.message) if (err) return log.error(err.message)
fs.writeFile(iconFilePath, iconFile, (err) => { fs.writeFile(iconFilePath, iconFile, err => {
if (err) log.error(err.message) if (err) log.error(err.message)
}) })
}) })

View File

@@ -188,7 +188,7 @@ function onAppOpen (newArgv) {
function sliceArgv (argv) { function sliceArgv (argv) {
return argv.slice(config.IS_PRODUCTION ? 1 return argv.slice(config.IS_PRODUCTION ? 1
: config.IS_TEST ? 4 : config.IS_TEST ? 4
: 2) : 2)
} }
function processArgv (argv) { function processArgv (argv) {

View File

@@ -138,7 +138,7 @@ function setAspectRatio (aspectRatio) {
function setBounds (bounds, maximize) { function setBounds (bounds, maximize) {
// Do nothing in fullscreen // Do nothing in fullscreen
if (!main.win || main.win.isFullScreen()) { if (!main.win || main.win.isFullScreen()) {
log('setBounds: not setting bounds because we\'re in full screen') log(`setBounds: not setting bounds because we're in full screen`)
return return
} }
@@ -162,13 +162,13 @@ function setBounds (bounds, maximize) {
// Assuming we're not maximized or maximizing, set the window size // Assuming we're not maximized or maximizing, set the window size
if (!willBeMaximized) { if (!willBeMaximized) {
log('setBounds: setting bounds to ' + JSON.stringify(bounds)) log(`setBounds: setting bounds to ${JSON.stringify(bounds)}`)
if (bounds.x === null && bounds.y === null) { if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen // X and Y not specified? By default, center on current screen
const scr = electron.screen.getDisplayMatching(main.win.getBounds()) const scr = electron.screen.getDisplayMatching(main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2)) bounds.x = Math.round(scr.bounds.x + (scr.bounds.width / 2) - (bounds.width / 2))
bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2)) bounds.y = Math.round(scr.bounds.y + (scr.bounds.height / 2) - (bounds.height / 2))
log('setBounds: centered to ' + JSON.stringify(bounds)) log(`setBounds: centered to ${JSON.stringify(bounds)}`)
} }
// Resize the window's content area (so window border doesn't need to be taken // Resize the window's content area (so window border doesn't need to be taken
// into account) // into account)

View File

@@ -102,10 +102,10 @@ module.exports = class PlaybackController {
const state = this.state const state = this.state
if (Playlist.hasNext(state) && state.playing.location !== 'external') { if (Playlist.hasNext(state) && state.playing.location !== 'external') {
this.updatePlayer( this.updatePlayer(
state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => { state.playing.infoHash, Playlist.getNextIndex(state), false, (err) => {
if (err) dispatch('error', err) if (err) dispatch('error', err)
else this.play() else this.play()
}) })
} }
} }
@@ -271,7 +271,7 @@ module.exports = class PlaybackController {
state.playing.fileIndex = index state.playing.fileIndex = index
state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video' state.playing.type = TorrentPlayer.isVideo(fileSummary) ? 'video'
: TorrentPlayer.isAudio(fileSummary) ? 'audio' : TorrentPlayer.isAudio(fileSummary) ? 'audio'
: 'other' : 'other'
// pick up where we left off // pick up where we left off
let jumpToTime = 0 let jumpToTime = 0

View File

@@ -157,23 +157,24 @@ module.exports = class TorrentListController {
prioritizeTorrent (infoHash) { prioritizeTorrent (infoHash) {
this.state.saved.torrents this.state.saved.torrents
.filter((torrent) => { // We're interested in active torrents only. .filter((torrent) => { // We're interested in active torrents only.
return (['downloading', 'seeding'].indexOf(torrent.status) !== -1) return (['downloading', 'seeding'].indexOf(torrent.status) !== -1)
}) })
.map((torrent) => { // Pause all active torrents except the one that started playing. .map((torrent) => { // Pause all active torrents except the one that started playing.
if (infoHash === torrent.infoHash) return if (infoHash === torrent.infoHash) return
// Pause torrent without playing sounds. // Pause torrent without playing sounds.
this.pauseTorrent(torrent, false) 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) console.log('Playback Priority: paused torrents: ', this.state.saved.torrentsToResume)
} }
resumePausedTorrents () { resumePausedTorrents () {
console.log('Playback Priority: resuming paused torrents') 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.state.saved.torrentsToResume.map((infoHash) => {
this.toggleTorrent(infoHash) this.toggleTorrent(infoHash)
}) })

View File

@@ -123,7 +123,8 @@ function setupStateSaved (cb) {
externalPlayerPath: null, externalPlayerPath: null,
startup: false, startup: false,
autoAddTorrents: false, autoAddTorrents: false,
torrentsFolderPath: '' torrentsFolderPath: '',
highestPlaybackPriority: true
}, },
torrents: config.DEFAULT_TORRENTS.map(createTorrentObject), torrents: config.DEFAULT_TORRENTS.map(createTorrentObject),
torrentsToResume: [], torrentsToResume: [],

View File

@@ -33,12 +33,18 @@ function isVideo (file) {
function isAudio (file) { function isAudio (file) {
return [ return [
'.aac', '.aac',
'.aiff',
'.ape',
'.ac3', '.ac3',
'.mp3',
'.ogg',
'.wav',
'.flac', '.flac',
'.m4a' '.m4a',
'.mp2',
'.mp3',
'.oga',
'.ogg',
'.opus',
'.wav',
'.wma'
].includes(getFileExtension(file)) ].includes(getFileExtension(file))
} }

View File

@@ -3,39 +3,146 @@ module.exports = torrentPoster
const captureFrame = require('capture-frame') const captureFrame = require('capture-frame')
const path = require('path') 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) { function torrentPoster (torrent, cb) {
// First, try to use a poster image if available // First, try to use a poster image if available
const posterFile = torrent.files.filter(function (file) { const posterFile = torrent.files.filter(function (file) {
return /^poster\.(jpg|png|gif)$/.test(file.name) return /^poster\.(jpg|png|gif)$/.test(file.name)
})[0] })[0]
if (posterFile) return torrentPosterFromImage(posterFile, torrent, cb) if (posterFile) return extractPoster(posterFile, cb)
// Second, try to use the largest video file // 'score' each media type based on total size present in torrent
// Filter out file formats that the <video> tag definitely can't play const bestScore = ['audio', 'video', 'image'].map(mediaType => {
const videoFile = getLargestFileByExtension(torrent, ['.mp4', '.m4v', '.webm', '.mov', '.mkv']) return {
if (videoFile) return torrentPosterFromVideo(videoFile, torrent, cb) type: mediaType,
size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType])}
}).sort((a, b) => { // sort descending on size
return b.size - a.size
})[0]
// Third, try to use the largest image file if (bestScore.size === 0) {
const imgFile = getLargestFileByExtension(torrent, ['.gif', '.jpg', '.jpeg', '.png']) // Admit defeat, no video, audio or image had a significant presence
if (imgFile) return torrentPosterFromImage(imgFile, torrent, cb) return cb(new Error('Cannot generate a poster from any files in the torrent'))
}
// TODO: generate a waveform from the largest sound file // Based on which media type is dominant we select the corresponding poster function
// Finally, admit defeat switch (bestScore.type) {
return cb(new Error('Cannot generate a poster from any files in the torrent')) case 'audio':
return torrentPosterFromAudio(torrent, cb)
case 'image':
return torrentPosterFromImage(torrent, cb)
case 'video':
return torrentPosterFromVideo(torrent, cb)
}
} }
/**
* Calculate the total data size of file matching one of the provided extensions
* @param torrent
* @param extensions List of extension to match
* @returns {number} total size, of matches found (>= 0)
*/
function calculateDataLengthByExtension (torrent, extensions) {
const files = filterOnExtension(torrent, extensions)
if (files.length === 0) return 0
return files
.map(file => file.length)
.reduce((a, b) => {
return a + b
})
}
/**
* Get the largest file of a given torrent, filtered by provided extension
* @param torrent Torrent to search in
* @param extensions Extension whitelist filter
* @returns Torrent file object
*/
function getLargestFileByExtension (torrent, extensions) { function getLargestFileByExtension (torrent, extensions) {
const files = torrent.files.filter(function (file) { const files = filterOnExtension(torrent, extensions)
const extname = path.extname(file.name).toLowerCase()
return extensions.indexOf(extname) !== -1
})
if (files.length === 0) return undefined if (files.length === 0) return undefined
return files.reduce(function (a, b) { return files.reduce((a, b) => {
return a.length > b.length ? a : b return a.length > b.length ? a : b
}) })
} }
function torrentPosterFromVideo (file, torrent, cb) { /**
* Filter file on a list extension, can be used to find al image files
* @param torrent Torrent to filter files from
* @param extensions File extensions to filter on
* @returns {number} Array of torrent file objects matching one of the given extensions
*/
function filterOnExtension (torrent, extensions) {
return torrent.files.filter(file => {
const extname = path.extname(file.name).toLowerCase()
return extensions.indexOf(extname) !== -1
})
}
/**
* Returns a score how likely the file is suitable as a poster
* @param imgFile File object of an image
* @returns {number} Score, higher score is a better match
*/
function scoreAudioCoverFile (imgFile) {
const fileName = path.basename(imgFile.name, path.extname(imgFile.name)).toLowerCase()
const relevanceScore = {
cover: 80,
folder: 80,
album: 80,
front: 80,
back: 20,
spectrogram: -80
}
for (let keyword in relevanceScore) {
if (fileName === keyword) {
return relevanceScore[keyword]
}
if (fileName.indexOf(keyword) !== -1) {
return relevanceScore[keyword]
}
}
return 0
}
function torrentPosterFromAudio (torrent, cb) {
const imageFiles = filterOnExtension(torrent, mediaExtensions.image)
const bestCover = imageFiles.map(file => {
return {
file: file,
score: scoreAudioCoverFile(file)
}
}).reduce((a, b) => {
if (a.score > b.score) {
return a
}
if (b.score > a.score) {
return b
}
// If score is equal, pick the largest file, aiming for highest resolution
if (a.file.length > b.file.length) {
return a
}
return b
})
if (!bestCover) return cb(new Error('Generated poster contains no data'))
const extname = path.extname(bestCover.file.name)
bestCover.file.getBuffer((err, buf) => cb(err, buf, extname))
}
function torrentPosterFromVideo (torrent, cb) {
const file = getLargestFileByExtension(torrent, mediaExtensions.video)
const index = torrent.files.indexOf(file) const index = torrent.files.indexOf(file)
const server = torrent.createServer(0) const server = torrent.createServer(0)
@@ -77,7 +184,12 @@ function torrentPosterFromVideo (file, torrent, cb) {
} }
} }
function torrentPosterFromImage (file, torrent, cb) { function torrentPosterFromImage (torrent, cb) {
const extname = path.extname(file.name) const file = getLargestFileByExtension(torrent, mediaExtensions.image)
file.getBuffer((err, buf) => cb(err, buf, extname)) extractPoster(file, cb)
}
function extractPoster (file, cb) {
const extname = path.extname(file.name)
file.getBuffer((err, buf) => { return cb(err, buf, extname) })
} }

View File

@@ -459,6 +459,13 @@ function setDimensions (dimensions) {
function onOpen (files) { function onOpen (files) {
if (!Array.isArray(files)) files = [ files ] if (!Array.isArray(files)) files = [ files ]
// File API seems to transform "magnet:?foo" in "magnet:///?foo"
// this is a sanitization
files = files.map(file => {
if (typeof file !== 'string') return file
return file.replace(/^magnet:\/+\?/i, 'magnet:?')
})
const url = state.location.url() const url = state.location.url()
const allTorrents = files.every(TorrentPlayer.isTorrent) const allTorrents = files.every(TorrentPlayer.isTorrent)
const allSubtitles = files.every(controllers.subtitles().isSubtitle) const allSubtitles = files.every(controllers.subtitles().isSubtitle)

View File

@@ -160,6 +160,7 @@ function renderMedia (state) {
} else { } else {
// When the last video completes, pause the video instead of looping // When the last video completes, pause the video instead of looping
state.playing.isPaused = true state.playing.isPaused = true
if (state.window.isFullScreen) dispatch('toggleFullScreen')
} }
} }
@@ -206,25 +207,18 @@ function renderOverlay (state) {
function renderAudioMetadata (state) { function renderAudioMetadata (state) {
const fileSummary = state.getPlayingFileSummary() const fileSummary = state.getPlayingFileSummary()
if (!fileSummary.audioInfo) return if (!fileSummary.audioInfo) return
const info = fileSummary.audioInfo const common = fileSummary.audioInfo.common || {}
// Get audio track info // Get audio track info
let title = info.title const title = common.title ? common.title : fileSummary.name
if (!title) {
title = fileSummary.name
}
let artist = info.artist && info.artist[0]
let album = info.album
if (album && info.year && !album.includes(info.year)) {
album += ' (' + info.year + ')'
}
let track
if (info.track && info.track.no && info.track.of) {
track = info.track.no + ' of ' + info.track.of
}
// Show a small info box in the middle of the screen with title/album/etc // Show a small info box in the middle of the screen with title/album/etc
const elems = [] const elems = []
// Audio metadata: artist(s)
const artist = common.albumartist || common.artist ||
(common.artists && common.artists.filter(function (a) { return a }).join(', ')) ||
'(Unknown Artist)'
if (artist) { if (artist) {
elems.push(( elems.push((
<div key='artist' className='audio-artist'> <div key='artist' className='audio-artist'>
@@ -232,14 +226,52 @@ function renderAudioMetadata (state) {
</div> </div>
)) ))
} }
if (album) {
// Audio metadata: album
if (common.album) {
elems.push(( elems.push((
<div key='album' className='audio-album'> <div key='album' className='audio-album'>
<label>Album</label>{album} <label>Album</label>{common.album}
</div> </div>
)) ))
} }
if (track) {
// Audio metadata: year
if (common.year) {
elems.push((
<div key='year' className='audio-year'>
<label>Year</label>{common.year}
</div>
))
}
// Audio metadata: release information (label & catalog-number)
if (common.label || common.catalognumber) {
const releaseInfo = []
if (common.label && common.catalognumber &&
common.label.length === common.catalognumber.length) {
// Assume labels & catalog-numbers are pairs
for (let n = 0; n < common.label.length; ++n) {
releaseInfo.push(common.label[0] + ' / ' + common.catalognumber[n])
}
} else {
if (common.label) {
releaseInfo.push(...common.label)
}
if (common.catalognumber) {
releaseInfo.push(...common.catalognumber)
}
}
elems.push((
<div key='release' className='audio-release'>
<label>Release</label>{ releaseInfo.join(', ') }
</div>
))
}
// Audio metadata: track-number
if (common.track && common.track.no && common.track.of) {
const track = common.track.no + ' of ' + common.track.of
elems.push(( elems.push((
<div key='track' className='audio-track'> <div key='track' className='audio-track'>
<label>Track</label>{track} <label>Track</label>{track}
@@ -247,6 +279,38 @@ function renderAudioMetadata (state) {
)) ))
} }
// Audio metadata: format
const format = []
fileSummary.audioInfo.format = fileSummary.audioInfo.format || ''
if (fileSummary.audioInfo.format.dataformat) {
format.push(fileSummary.audioInfo.format.dataformat)
}
if (fileSummary.audioInfo.format.bitrate) {
format.push(fileSummary.audioInfo.format.bitrate / 1000 + ' kbps')
}
if (fileSummary.audioInfo.format.sampleRate) {
format.push(fileSummary.audioInfo.format.sampleRate / 1000 + ' kHz')
}
if (fileSummary.audioInfo.format.bitsPerSample) {
format.push(fileSummary.audioInfo.format.bitsPerSample + ' bit')
}
if (format.length > 0) {
elems.push((
<div key='format' className='audio-format'>
<label>Format</label>{ format.join(', ') }
</div>
))
}
// Audio metadata: comments
if (common.comment) {
elems.push((
<div key='comments' className='audio-comments'>
<label>Comments</label>{common.comment.join(' / ')}
</div>
))
}
// Align the title with the other info, if available. Otherwise, center title // Align the title with the other info, if available. Otherwise, center title
const emptyLabel = (<label />) const emptyLabel = (<label />)
elems.unshift(( elems.unshift((
@@ -273,7 +337,7 @@ function renderLoadingSpinner (state) {
return ( return (
<div key='loading' className='media-stalled'> <div key='loading' className='media-stalled'>
<div key='loading-spinner' className='loading-spinner'>&nbsp;</div> <div key='loading-spinner' className='loading-spinner' />
<div key='loading-progress' className='loading-status ellipsis'> <div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded <span className='progress'>{fileProgress}%</span> downloaded
<span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span> <span> ↓ {prettyBytes(prog.downloadSpeed || 0)}/s</span>
@@ -303,7 +367,7 @@ function renderCastScreen (state) {
isCast = false isCast = false
} else if (state.playing.location === 'error') { } else if (state.playing.location === 'error') {
castIcon = 'error_outline' castIcon = 'error_outline'
castType = 'Error' castType = 'Unable to Play'
isCast = false isCast = false
} }
@@ -497,9 +561,9 @@ function renderPlayerControls (state) {
const volume = state.playing.volume const volume = state.playing.volume
const volumeIcon = 'volume_' + ( const volumeIcon = 'volume_' + (
volume === 0 ? 'off' volume === 0 ? 'off'
: volume < 0.3 ? 'mute' : volume < 0.3 ? 'mute'
: volume < 0.6 ? 'down' : volume < 0.6 ? 'down'
: 'up') : 'up')
const volumeStyle = { const volumeStyle = {
background: '-webkit-gradient(linear, left top, right top, ' + background: '-webkit-gradient(linear, left top, right top, ' +
'color-stop(' + (volume * 100) + '%, #eee), ' + 'color-stop(' + (volume * 100) + '%, #eee), ' +

View File

@@ -216,7 +216,7 @@ module.exports = class TorrentList extends React.Component {
} else { // torrentSummary.status is 'new' or something unexpected } else { // torrentSummary.status is 'new' or something unexpected
status = '' status = ''
} }
return (<span>{status}</span>) return (<span key='torrent-status'>{status}</span>)
} }
} }

View File

@@ -8,7 +8,7 @@ const defaultAnnounceList = require('create-torrent').announceList
const electron = require('electron') const electron = require('electron')
const fs = require('fs') const fs = require('fs')
const mkdirp = require('mkdirp') const mkdirp = require('mkdirp')
const musicmetadata = require('musicmetadata') const mm = require('music-metadata')
const networkAddress = require('network-address') const networkAddress = require('network-address')
const path = require('path') const path = require('path')
const WebTorrent = require('webtorrent') const WebTorrent = require('webtorrent')
@@ -98,7 +98,7 @@ function init () {
window.addEventListener('error', (e) => window.addEventListener('error', (e) =>
ipc.send('wt-uncaught-error', {message: e.error.message, stack: e.error.stack}), ipc.send('wt-uncaught-error', {message: e.error.message, stack: e.error.stack}),
true) true)
setInterval(updateTorrentProgress, 1000) setInterval(updateTorrentProgress, 1000)
console.timeEnd('init') console.timeEnd('init')
@@ -334,16 +334,30 @@ function stopServer () {
server = null server = null
} }
console.log('Initializing...')
function getAudioMetadata (infoHash, index) { function getAudioMetadata (infoHash, index) {
const torrent = client.get(infoHash) const torrent = client.get(infoHash)
const file = torrent.files[index] const file = torrent.files[index]
musicmetadata(file.createReadStream(), function (err, info) {
if (err) return console.log('error getting audio metadata for ' + infoHash + ':' + index, err) // Set initial matadata to display the filename first.
const { artist, album, albumartist, title, year, track, disk, genre } = info const metadata = { title: file.name }
const importantInfo = { artist, album, albumartist, title, year, track, disk, genre } ipc.send('wt-audio-metadata', infoHash, index, metadata)
console.log('got audio metadata for %s: %o', file.name, importantInfo)
ipc.send('wt-audio-metadata', infoHash, index, importantInfo) const options = {native: false, skipCovers: true, fileSize: file.length}
}) const onMetaData = file.done
// If completed; use direct file access
? mm.parseFile(path.join(torrent.path, file.path), options)
// otherwise stream
: mm.parseStream(file.createReadStream(), file.name, options)
onMetaData
.then(function (metadata) {
console.log('got audio metadata for %s (length=%s): %o', file.name, file.length, metadata)
ipc.send('wt-audio-metadata', infoHash, index, metadata)
}).catch(function (err) {
return console.log('error getting audio metadata for ' + infoHash + ':' + index, err)
})
} }
function selectFiles (torrentOrInfoHash, selections) { function selectFiles (torrentOrInfoHash, selections) {

View File

@@ -820,12 +820,17 @@ video::-webkit-media-text-track-container {
.audio-metadata label { .audio-metadata label {
display:inline-block; display:inline-block;
width: 100px; width: 120px;
text-align: right; text-align: right;
font-weight: normal; font-weight: normal;
margin-right: 25px; margin-right: 25px;
} }
.audio-metadata .audio-format,
.audio-metadata .audio-comments {
font-weight: normal;
}
/* /*
* ERRORS * ERRORS
*/ */

View File

@@ -10,7 +10,7 @@ test('app runs', function (t) {
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => setup.screenshotCreateOrCompare(app, t, 'app-basic')) .then(() => setup.screenshotCreateOrCompare(app, t, 'app-basic'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })
require('./test-torrent-list') require('./test-torrent-list')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

After

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 KiB

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 KiB

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 844 KiB

After

Width:  |  Height:  |  Size: 870 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1024 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1016 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

After

Width:  |  Height:  |  Size: 725 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 239 KiB

View File

@@ -30,7 +30,7 @@ test('add-torrent', function (t) {
.then(() => app.client.moveToObject('.torrent')) .then(() => app.client.moveToObject('.torrent'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-100-percent')) .then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-100-percent'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })
test('create-torrent', function (t) { test('create-torrent', function (t) {
@@ -40,10 +40,9 @@ test('create-torrent', function (t) {
announce: [ announce: [
'udp://explodie.org:6969', 'udp://explodie.org:6969',
'udp://tracker.coppersurfer.tk:6969', 'udp://tracker.coppersurfer.tk:6969',
'udp://tracker.empire-js.us:1337',
'udp://tracker.leechers-paradise.org:6969', 'udp://tracker.leechers-paradise.org:6969',
'udp://tracker.openbittorrent.com:80',
'udp://tracker.opentrackr.org:1337', 'udp://tracker.opentrackr.org:1337',
'udp://zer0day.ch:1337',
'wss://tracker.btorrent.xyz', 'wss://tracker.btorrent.xyz',
'wss://tracker.fastcast.nz', 'wss://tracker.fastcast.nz',
'wss://tracker.openwebtorrent.com' 'wss://tracker.openwebtorrent.com'
@@ -84,5 +83,5 @@ test('create-torrent', function (t) {
config.SAVED_TORRENT_FILE, config.SAVED_TORRENT_FILE,
expectedTorrent)) expectedTorrent))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })

View File

@@ -53,5 +53,5 @@ test('audio-streaming', function (t) {
.then(() => app.webContents.executeJavaScript('dispatch("skipTo", 2)')) .then(() => app.webContents.executeJavaScript('dispatch("skipTo", 2)'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-wired-5')) .then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-wired-5'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })

View File

@@ -0,0 +1,25 @@
const test = require('tape')
const fs = require('fs')
const path = require('path')
const WebTorrent = require('webtorrent')
const torrentPoster = require('../build/renderer/lib/torrent-poster')
const client = new WebTorrent()
test("get cover from: 'wiredCd.torrent'", (t) => {
const torrentPath = path.join(__dirname, '..', 'static', 'wiredCd.torrent')
const torrentData = fs.readFileSync(torrentPath)
client.add(torrentData, (torrent) => {
torrentPoster(torrent, (err, buf, extension) => {
if (err) {
t.fail(err)
} else {
t.equals(extension, '.jpg')
t.end()
}
})
})
})

View File

@@ -22,7 +22,7 @@ test('torrent-list: show download path missing', function (t) {
.then((windowTitle) => t.equal(windowTitle, 'Preferences', 'window title')) .then((windowTitle) => t.equal(windowTitle, 'Preferences', 'window title'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'prefs-basic')) .then(() => setup.screenshotCreateOrCompare(app, t, 'prefs-basic'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })
test('torrent-list: start, stop, and delete torrents', function (t) { test('torrent-list: start, stop, and delete torrents', function (t) {
@@ -31,22 +31,20 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny')) .then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Mouse over the first torrent .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list'))
.then(() => app.client.moveToObject('.torrent'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover'))
// Click download on the first torrent, start downloading // Click download on the first torrent, start downloading
.then(() => app.client.click('.download input')) .then(() => app.client.click('.download input'))
.then(() => app.client.waitUntilTextExists('.torrent-list', '276 MB')) .then(() => app.client.waitUntilTextExists('.torrent-list', '276 MB'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-start-download')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-start-download'))
// Click download on the first torrent again, stop downloading // Click download on the first torrent again, stop downloading
.then(() => app.client.click('.download input')) .then(() => app.client.click('.download input'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover-download')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-download'))
// Click delete on the first torrent // Click delete on the first torrent
.then(() => app.client.click('.icon.delete')) .then(() => app.client.click('.icon.delete'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt'))
// Click cancel on the resulting confirmation dialog. Should be same as before. // Click cancel on the resulting confirmation dialog. Should be same as before.
.then(() => app.client.click('.control.cancel')) .then(() => app.client.click('.control.cancel'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list'))
// Click delete on the first torrent again // Click delete on the first torrent again
.then(() => app.client.click('.icon.delete')) .then(() => app.client.click('.icon.delete'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-delete-prompt'))
@@ -54,7 +52,7 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
.then(() => app.client.click('.control.ok')) .then(() => app.client.click('.control.ok'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-deleted')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-deleted'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })
test('torrent-list: expand torrent, unselect file', function (t) { test('torrent-list: expand torrent, unselect file', function (t) {
@@ -63,9 +61,6 @@ test('torrent-list: expand torrent, unselect file', function (t) {
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny')) .then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Mouse over the torrent
.then(() => app.client.moveToObject('#torrent-cosmos'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-hover'))
// Click on the torrent, expand // Click on the torrent, expand
.then(() => app.client.click('#torrent-cosmos')) .then(() => app.client.click('#torrent-cosmos'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-expand')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-cosmos-expand'))
@@ -101,5 +96,5 @@ test('torrent-list: expand torrent, unselect file', function (t) {
// Make sure that all the files are gone // Make sure that all the files are gone
.then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', null)) .then(() => setup.compareDownloadFolder(t, 'CosmosLaundromatFirstCycle', null))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })

View File

@@ -31,5 +31,5 @@ test('video-streaming', function (t) {
// Take another screenshot to verify that the window resized correctly // Take another screenshot to verify that the window resized correctly
.then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-return')) .then(() => setup.screenshotCreateOrCompare(app, t, 'play-torrent-return'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })