Compare commits

..

26 Commits

Author SHA1 Message Date
Feross Aboukhadijeh
d1806d9503 0.2.0 2016-03-29 03:58:38 -07:00
Feross Aboukhadijeh
1ce894c134 changelog 2016-03-29 03:57:29 -07:00
Feross Aboukhadijeh
8b1d7e5394 Changelog v0.2.0 2016-03-29 03:52:19 -07:00
DC
39a6832631 Minimize to tray
Fixes #150
2016-03-29 03:51:15 -07:00
Feross Aboukhadijeh
9694a9f5fd CHANGELOG 2016-03-29 03:16:23 -07:00
Feross Aboukhadijeh
0683255281 webtorrent@0.88.1 2016-03-29 02:53:08 -07:00
DC
3a76629f09 UX polish: highlight drag-drop even when placeholder isn't visible
Before, if you scrolled so that the bottom placeholder wasn't visible, there was no indication that the app is still a drag target.
2016-03-29 02:37:25 -07:00
DC
630e8611ba Add Cosmos Laundromat as a default torrent 2016-03-29 00:41:48 -07:00
DC
cc273e7312 Loading spinner for videos
Only worked for audio before
2016-03-29 00:41:42 -07:00
DC
c8da083526 Make npm run package work on Mac and Linux 2016-03-28 23:09:07 -07:00
DC
840966c7f0 Remove global shortcuts when player isn't active 2016-03-28 22:57:40 -07:00
Feross Aboukhadijeh
8ce7235c2b webtorrent@0.88
Lots of perf fixes for #256
2016-03-28 22:55:23 -07:00
DC
f70cef2cee Loading spinner: center, text-overflow ellipsis 2016-03-28 22:36:45 -07:00
DC
dc2e2a82e7 Loading spinner: show download speed 2016-03-28 21:23:52 -07:00
DC
c70fef3feb Show spinner when audio/video is stalled
Fixes #243
2016-03-28 21:16:06 -07:00
DC
1afedac12f Fix version, my bad 2016-03-28 20:10:02 -07:00
DC
b8ff4b378b Linux updater: better message 2016-03-28 18:52:09 -07:00
DC
86069a7173 Linux update notifications
Fixes #257
2016-03-28 16:16:43 -07:00
Feross Aboukhadijeh
25db4eec9d Update package.js for electron-packager v6 2016-03-28 13:55:18 -07:00
Feross Aboukhadijeh
9080a69e3c Merge pull request #258 from feross/greenkeeper-electron-packager-6.0.0
Update electron-packager to version 6.0.0 🚀
2016-03-28 13:53:28 -07:00
Nate Goldman
986fbf5418 app -> desktop 2016-03-28 13:50:27 -07:00
Feross Aboukhadijeh
df04363f7c WebTorrent Desktop 2016-03-28 13:36:51 -07:00
greenkeeperio-bot
57117e9043 chore(package): update electron-packager to version 6.0.0
http://greenkeeper.io/
2016-03-28 07:59:53 -07:00
Feross Aboukhadijeh
5dd104a588 update 0.1.1 changelog 2016-03-28 01:18:13 -07:00
Feross Aboukhadijeh
849365f839 package .zip files for Linux 2016-03-28 01:17:57 -07:00
Feross Aboukhadijeh
e3e32f154c Fixes for PR #250 2016-03-28 00:48:14 -07:00
22 changed files with 431 additions and 99 deletions

View File

@@ -1,6 +1,29 @@
# WebTorrent.app Version History
# WebTorrent Desktop Version History
## v0.1.1
## v0.2.0 - 2016-03-29
Added
- Minimise to tray (Windows, Linux)
- Show spinner and download speed when player is stalled waiting for data
- Highlight window on drag-and-drop
- Show notification to update to new app version (Linux)
- We have an auto-updater for Windows and Mac. We don't have one for Linux yet, so
Linux users need to download new versions manually.
Changed
- Renamed WebTorrent.app to WebTorrent Desktop
- Add Cosmos Laundromat as a default torrent
Fixed
- Only capture media keys when player is active
- Update WebTorrent to 0.88.1 for performance improvements
- When seeding, do not proactively connect to new peers
- When seeding, do not accept new peers from peer exchange (ut_pex)
- Fixed leaks, and other improvements that result in less garbage collection
Thanks to @dcposch, @ungoldman, and @feross for contributing to this release.
## v0.1.1 - 2016-03-28
- Performance improvements
- Improve app startup time by over 100%
@@ -17,8 +40,13 @@
- Fixed
- Crash when ".local/share/{applications,icons}" path did not exist (Linux)
- WebTorrent executable can be moved without breaking torrents in the client
- Video progress bar shows progress for current file, not full torrent
- Video player window shows file title instead of torrent title
## v0.1.0
Thanks to @dcposch, @ungoldman, @rom1504, @grunjol, @Flet, and @feross for contributing to
this release.
## v0.1.0 - 2016-03-25
- **Windows support!**
- Includes auto-updater, just like the OS X version.
@@ -33,18 +61,18 @@
**NOTE:** OS X users must install v0.1.0 manually because the app bundle ID was changed in this release, and the auto-updater cannot handle this condition.
Thanks to @dcposch, @ngoldman, and @feross for contributing to this release.
Thanks to @dcposch, @ungoldman, and @feross for contributing to this release.
## v0.0.1
## v0.0.1 - 2016-03-21
- Wait 10 seconds (instead of 60 seconds) after app launch before checking for updates.
## v0.0.0
## v0.0.0 - 2016-03-21
The first official release of WebTorrent.app, the streaming torrent client for OS X,
The first official release of WebTorrent Desktop, the streaming torrent client for OS X,
Windows, and Linux. For now, we're only releasing binaries for OS X.
WebTorrent.app is in ALPHA and under very active development  expect lots more polish in
WebTorrent Desktop is in ALPHA and under very active development  expect lots more polish in
the coming weeks! If you know JavaScript and want to help us out, there's
[lots to do](https://github.com/feross/webtorrent-app/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+contribution%22)!

View File

@@ -2,7 +2,7 @@
<br>
<a href="https://webtorrent.io"><img src="https://webtorrent.io/img/WebTorrent.png" alt="WebTorrent" width="200"></a>
<br>
WebTorrent.app
WebTorrent Desktop
<br>
<br>
</h1>
@@ -26,7 +26,7 @@
## Install
**WebTorrent.app** is still under very active development. You can download the latest version from the [releases](https://github.com/feross/webtorrent-app/releases) page.
**WebTorrent Desktop** is still under very active development. You can download the latest version from the [releases](https://github.com/feross/webtorrent-app/releases) page.
## Screenshot

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env node
/**
* Remove all traces of WebTorrent.app from the system (config and temp files).
* Remove all traces of WebTorrent Desktop from the system (config and temp files).
* Useful for developers.
*/

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env node
var electron = require('electron-prebuilt')
var proc = require('child_process')
var cp = require('child_process')
var path = require('path')
var child = proc.spawn(electron, [path.join(__dirname, '..')], {stdio: 'inherit'})
var child = cp.spawn(electron, [path.join(__dirname, '..')], {stdio: 'inherit'})
child.on('close', function (code) {
process.exit(code)
})

View File

@@ -37,8 +37,8 @@ var all = {
// Build 64 bit binaries only.
arch: 'x64',
// The application source directory.
dir: config.ROOT_PATH,
// The human-readable copyright line for the app.
'app-copyright': config.APP_COPYRIGHT,
// The release version of the application. Maps to the `ProductVersion` metadata
// property on Windows, and `CFBundleShortVersionString` on OS X.
@@ -58,6 +58,9 @@ var all = {
// Windows requires the build version to start with a number :/ so we stick on a prefix
'build-version': '0-' + cp.execSync('git rev-parse --short HEAD').toString().replace('\n', ''),
// The application source directory.
dir: config.ROOT_PATH,
// Pattern which specifies which files to ignore when copying files to create the
// package(s).
ignore: /^\/dist|\/(appveyor.yml|.appveyor.yml|appdmg|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^\/]*|.*\.md|.*\.markdown)$/,
@@ -105,9 +108,6 @@ var win32 = {
// Company that produced the file.
CompanyName: config.APP_NAME,
// Copyright notices that apply to the file.
LegalCopyright: config.APP_COPYRIGHT,
// Name of the program, displayed to users
FileDescription: config.APP_NAME,
@@ -149,6 +149,8 @@ function buildDarwin (cb) {
var infoPlistPath = path.join(contentsPath, 'Info.plist')
var infoPlist = plist.parse(fs.readFileSync(infoPlistPath, 'utf8'))
// TODO: Use new `extend-info` and `extra-resource` opts to electron-packager,
// available as of v6.
infoPlist.CFBundleDocumentTypes = [
{
CFBundleTypeExtensions: [ 'torrent' ],
@@ -176,8 +178,6 @@ function buildDarwin (cb) {
}
]
infoPlist.NSHumanReadableCopyright = config.APP_COPYRIGHT
fs.writeFileSync(infoPlistPath, plist.build(infoPlist))
// Copy torrent file icon into app bundle
@@ -206,12 +206,14 @@ function buildDarwin (cb) {
verbose: true
}
// TODO: Use the built-in `sign` opt to electron-packager that takes an options
// object as of v6.
sign(signOpts, function (err) {
if (err) return cb(err)
// Create .zip file (used by the auto-updater)
var zipPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '.zip')
cp.execSync(`pushd ${buildPath[0]} && zip -r -y ${zipPath} ${config.APP_NAME + '.app'} && popd`)
cp.execSync(`cd ${buildPath[0]} && zip -r -y ${zipPath} ${config.APP_NAME + '.app'}`)
console.log('Created OS X .zip file.')
var targetPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '.dmg')
@@ -284,7 +286,16 @@ function buildWin32 (cb) {
}
function buildLinux (cb) {
electronPackager(Object.assign({}, all, linux), cb)
electronPackager(Object.assign({}, all, linux), function (err, buildPath) {
if (err) return cb(err)
// Create .zip file for Linux
var distPath = path.join(config.ROOT_PATH, 'dist')
var zipPath = path.join(config.ROOT_PATH, 'dist', BUILD_NAME + '-linux.zip')
var appFolderName = path.basename(buildPath[0])
cp.execSync(`cd ${distPath} && zip -r -y ${zipPath} ${appFolderName}`)
console.log('Created Linux .zip file.')
})
}
function printDone (err, buildPath) {

View File

@@ -3,9 +3,11 @@ module.exports = {
}
var electron = require('electron')
var get = require('simple-get')
var config = require('../config')
var log = require('./log')
var windows = require('./windows')
var autoUpdater = electron.autoUpdater
@@ -20,7 +22,7 @@ function init () {
* We always check for updates on app startup. To keep app startup fast, we delay this
* first check so it happens when there is less going on.
*/
setTimeout(() => autoUpdater.checkForUpdates(), config.AUTO_UPDATE_CHECK_STARTUP_DELAY)
setTimeout(checkForUpdates, config.AUTO_UPDATE_CHECK_STARTUP_DELAY)
autoUpdater.on('checking-for-update', () => log('Checking for app update'))
autoUpdater.on('update-available', () => log('App update available'))
@@ -29,3 +31,20 @@ function init () {
log('App update downloaded: ', releaseName, updateURL)
})
}
function checkForUpdates () {
// Electron's built-in auto updater only supports Mac and Windows, for now
if (process.platform !== 'linux') {
return autoUpdater.checkForUpdates()
}
// If we're on Linux, we have to do it ourselves
get.concat(config.AUTO_UPDATE_URL, function (err, res, data) {
if (err) return log('Error checking for app update: ' + err.message)
if (![200, 204].includes(res.statusCode)) return log('Error checking for app update, got HTTP ' + res.statusCode)
if (res.statusCode !== 200) return
var obj = JSON.parse(data)
windows.main.send('dispatch', 'updateAvailable', obj.version)
})
}

View File

@@ -11,6 +11,7 @@ var menu = require('./menu')
var shortcuts = require('./shortcuts')
var squirrelWin32 = require('./squirrel-win32')
var windows = require('./windows')
var tray = require('./tray')
var shouldQuit = false
var argv = sliceArgv(process.argv)
@@ -52,6 +53,7 @@ function init () {
menu.init()
windows.createMainWindow()
shortcuts.init()
tray.init()
if (process.platform !== 'win32') handlers.init()
})
@@ -67,12 +69,6 @@ function init () {
app.on('activate', function () {
windows.createMainWindow()
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
app.quit()
}
})
}
function onOpen (e, torrentId) {

View File

@@ -12,6 +12,7 @@ var powerSaveBlocker = electron.powerSaveBlocker
var log = require('./log')
var menu = require('./menu')
var windows = require('./windows')
var shortcuts = require('./shortcuts')
// has to be a number, not a boolean, and undefined throws an error
var powerSaveBlockID = 0
@@ -61,8 +62,14 @@ function init () {
ipcMain.on('blockPowerSave', blockPowerSave)
ipcMain.on('unblockPowerSave', unblockPowerSave)
ipcMain.on('onPlayerOpen', menu.onPlayerOpen)
ipcMain.on('onPlayerClose', menu.onPlayerClose)
ipcMain.on('onPlayerOpen', function () {
menu.onPlayerOpen()
shortcuts.registerPlayerShortcuts()
})
ipcMain.on('onPlayerClose', function () {
menu.onPlayerClose()
shortcuts.unregisterPlayerShortcuts()
})
}
function setBounds (bounds, maximize) {

View File

@@ -1,5 +1,7 @@
module.exports = {
init
init,
registerPlayerShortcuts,
unregisterPlayerShortcuts
}
var electron = require('electron')
@@ -11,11 +13,17 @@ var menu = require('./menu')
var windows = require('./windows')
function init () {
// Special "media key" for play/pause, available on some keyboards
globalShortcut.register('MediaPlayPause', () => windows.main.send('dispatch', 'playPause'))
// ⌘+Shift+F is an alternative fullscreen shortcut to the ones defined in menu.js.
// Electron does not support multiple accelerators for a single menu item, so this
// is registered separately here.
localShortcut.register('CmdOrCtrl+Shift+F', menu.toggleFullScreen)
}
function registerPlayerShortcuts () {
// Special "media key" for play/pause, available on some keyboards
globalShortcut.register('MediaPlayPause', () => windows.main.send('dispatch', 'playPause'))
}
function unregisterPlayerShortcuts () {
globalShortcut.unregister('MediaPlayPause')
}

31
main/tray.js Normal file
View File

@@ -0,0 +1,31 @@
module.exports = {
init
}
var path = require('path')
var electron = require('electron')
var windows = require('./windows')
function init () {
// No tray icon on OSX
if (process.platform === 'darwin') return
var trayIcon = new electron.Tray(path.join(__dirname, '..', 'static', 'WebTorrentSmall.png'))
// On Windows, left click to open the app, right click for context menu
// On Linux, any click (right or left) opens the context menu
trayIcon.on('click', showApp)
var contextMenu = electron.Menu.buildFromTemplate([
{ label: 'Show', click: showApp },
{ label: 'Quit', click: quitApp }
])
trayIcon.setContextMenu(contextMenu)
}
function showApp () {
windows.main.show()
}
function quitApp () {
electron.app.quit()
}

View File

@@ -8,8 +8,6 @@ var windows = module.exports = {
var electron = require('electron')
var app = electron.app
var config = require('../config')
var menu = require('./menu')
@@ -62,8 +60,8 @@ function createMainWindow () {
title: config.APP_WINDOW_TITLE,
titleBarStyle: 'hidden-inset', // Hide OS chrome, except traffic light buttons (OS X)
useContentSize: true, // Specify web page size without OS chrome
width: 450,
height: 38 + (120 * 4) // header height + 4 torrents
width: 500,
height: 38 + (120 * 5) // header height + 4 torrents
})
win.loadURL(config.WINDOW_MAIN)
@@ -78,7 +76,7 @@ function createMainWindow () {
win.on('leave-full-screen', () => menu.onToggleFullScreen(false))
win.on('close', function (e) {
if (process.platform === 'darwin' && !app.isQuitting) {
if (!electron.app.isQuitting) {
e.preventDefault()
win.send('dispatch', 'pause')
win.hide()

View File

@@ -1,14 +1,14 @@
{
"name": "webtorrent-app",
"name": "webtorrent-desktop",
"description": "WebTorrent, the streaming torrent client. For OS X, Windows, and Linux.",
"version": "0.1.1",
"version": "0.2.0",
"author": {
"name": "Feross Aboukhadijeh",
"email": "feross@feross.org",
"url": "http://feross.org"
},
"bugs": {
"url": "https://github.com/feross/webtorrent-app/issues"
"url": "https://github.com/feross/webtorrent-desktop/issues"
},
"dependencies": {
"airplay-js": "guerrerocarlos/node-airplay-js",
@@ -27,14 +27,15 @@
"network-address": "^1.1.0",
"path-exists": "^2.1.0",
"prettier-bytes": "^1.0.1",
"simple-get": "^2.0.0",
"upload-element": "^1.0.1",
"virtual-dom": "^2.1.1",
"webtorrent": "^0.87.1",
"webtorrent": "^0.88.1",
"winreg": "^1.0.1"
},
"devDependencies": {
"electron-osx-sign": "^0.3.0",
"electron-packager": "^5.0.0",
"electron-packager": "^6.0.0",
"electron-winstaller": "^2.0.5",
"gh-release": "^2.0.3",
"plist": "^1.2.0",
@@ -46,15 +47,17 @@
},
"homepage": "https://webtorrent.io",
"keywords": [
"desktop",
"electron",
"electron-app"
"electron-app",
"webtorrent"
],
"license": "MIT",
"main": "index.js",
"productName": "WebTorrent",
"repository": {
"type": "git",
"url": "git://github.com/feross/webtorrent-app.git"
"url": "git://github.com/feross/webtorrent-desktop.git"
},
"scripts": {
"clean": "node ./bin/clean.js",
@@ -66,6 +69,6 @@
"update-authors": "./bin/update-authors.sh"
},
"bin": {
"webtorrent-app": "./bin/cmd.js"
"webtorrent-desktop": "./bin/cmd.js"
}
}

View File

@@ -242,6 +242,10 @@ i:not(.disabled):hover {
* MODAL POPOVERS
*/
.modal {
z-index: 2;
}
.modal .modal-background {
content: ' ';
position: fixed;
@@ -313,30 +317,6 @@ input {
box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.1);
}
/*
* PLAYER
*/
.player {
position: absolute;
width: 100%;
height: 100%;
background-color: #000;
}
.player .letterbox {
width: 100%;
height: 100%;
display: flex;
background-size: cover;
background-position: center center;
}
.player video {
display: block;
width: 100%;
}
/*
* TORRENT LIST
*/
@@ -432,7 +412,8 @@ input {
padding-top: 8px;
}
.torrent.requested .play {
.torrent.requested .play,
.loading-spinner {
border-top: 6px solid rgba(255, 255, 255, 0.2);
border-right: 6px solid rgba(255, 255, 255, 0.2);
border-bottom: 6px solid rgba(255, 255, 255, 0.2);
@@ -495,6 +476,17 @@ body.drag .torrent-placeholder span {
color: #def;
}
body.drag .app::after {
content: ' ';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 3;
}
/*
* TORRENT LIST: EXPANDED TORRENT DETAILS
*/
@@ -557,6 +549,28 @@ body.drag .torrent-placeholder span {
text-align: right;
}
/*
* PLAYER
*/
.player {
position: absolute;
width: 100%;
height: 100%;
background-color: #000;
}
.player .letterbox {
width: 100%;
height: 100%;
display: flex;
}
.player video {
display: block;
width: 100%;
}
/*
* PLAYER CONTROLS
*/
@@ -704,12 +718,22 @@ body.drag .torrent-placeholder span {
}
/*
* AUDIO DETAILS
* MEDIA OVERLAY / AUDIO DETAILS
*/
.audio-metadata {
width: 500px;
max-width: 100%;
.media-overlay-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
background-size: cover;
background-position: center center;
}
.media-overlay {
max-width: calc(100% - 80px);
white-space: nowrap;
text-overflow: ellipsis;
align-self: center;
@@ -719,6 +743,23 @@ body.drag .torrent-placeholder span {
line-height: 2;
}
.media-stalled .loading-spinner {
width: 40px;
height: 40px;
margin: 40px auto;
}
.media-stalled .loading-status {
font-size: 18px;
font-weight: normal;
text-align: center;
}
.audio-metadata div {
overflow: hidden;
text-overflow: ellipsis;
}
.audio-metadata .audio-title {
font-size: 32px;
}

View File

@@ -143,8 +143,8 @@ function lazyLoadCast () {
function initWebtorrent () {
WebTorrent = require('webtorrent')
// Connect to the WebTorrent and BitTorrent networks
// WebTorrent.app is a hybrid client, as explained here: https://webtorrent.io/faq
// Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid
// client, as explained here: https://webtorrent.io/faq
state.client = new WebTorrent()
state.client.on('warning', onWarning)
state.client.on('error', function (err) {
@@ -258,6 +258,7 @@ function dispatch (action, ...args) {
playPause()
}
if (action === 'play') {
if (state.location.pending()) return
state.location.go({
url: 'player',
onbeforeload: function (cb) {
@@ -269,6 +270,13 @@ function dispatch (action, ...args) {
}
if (action === 'pause') {
playPause(true)
// Work around virtual-dom issue: it doesn't expose its redraw function,
// and only redraws on requestAnimationFrame(). That means when the user
// closes the window (hide window / minimize to tray) and we want to pause
// the video, we update the vdom but it keeps playing until you reopen!
var videoTag = document.querySelector('video')
if (videoTag) videoTag.pause()
}
if (action === 'playbackJump') {
jumpToTime(args[0] /* seconds */)
@@ -284,6 +292,13 @@ function dispatch (action, ...args) {
state.playing.isPaused = true
ipcRenderer.send('unblockPowerSave')
}
if (action === 'mediaStalled') {
state.playing.isStalled = true
}
if (action === 'mediaTimeUpdate') {
state.playing.lastTimeUpdate = new Date().getTime()
state.playing.isStalled = false
}
if (action === 'toggleFullScreen') {
ipcRenderer.send('toggleFullScreen', args[0] /* optional bool */)
}
@@ -293,6 +308,14 @@ function dispatch (action, ...args) {
if (action === 'exitModal') {
state.modal = null
}
if (action === 'updateAvailable') {
updateAvailable(args[0] /* version */)
}
if (action === 'skipVersion') {
if (!state.saved.skippedVersions) state.saved.skippedVersions = []
state.saved.skippedVersions.push(args[0] /* version */)
saveState()
}
// Update the virtual-dom, unless it's just a mouse move event
if (action !== 'mediaMouseMoved') {
@@ -300,6 +323,15 @@ function dispatch (action, ...args) {
}
}
// Shows a modal saying that we have an update
function updateAvailable (version) {
if (state.saved.skippedVersions && state.saved.skippedVersions.includes(version)) {
console.log('new version skipped by user: v' + version)
return
}
state.modal = { id: 'update-available-modal', version: version }
}
// Plays or pauses the video. If isPaused is undefined, acts as a toggle
function playPause (isPaused) {
if (isPaused === state.playing.isPaused) {
@@ -310,7 +342,6 @@ function playPause (isPaused) {
Cast.playPause()
}
state.playing.isPaused = !state.playing.isPaused
update()
}
function jumpToTime (time) {
@@ -318,7 +349,6 @@ function jumpToTime (time) {
Cast.seek(time)
} else {
state.playing.jumpToTime = time
update()
}
}
@@ -334,7 +364,6 @@ function setVolume (volume) {
Cast.setVolume(volume)
} else {
state.playing.setVolume = volume
update()
}
}
@@ -347,7 +376,7 @@ function setupIpc () {
ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args))
ipcRenderer.on('showOpenTorrentAddress', function (e) {
state.modal = 'open-torrent-address-modal'
state.modal = { id: 'open-torrent-address-modal' }
update()
})

View File

@@ -4,6 +4,7 @@ function LocationHistory () {
if (!new.target) return new LocationHistory()
this._history = []
this._forward = []
this._pending = null
}
LocationHistory.prototype.go = function (page) {
@@ -13,9 +14,12 @@ LocationHistory.prototype.go = function (page) {
}
LocationHistory.prototype._go = function (page) {
if (this._pending) return
if (page.onbeforeload) {
this._pending = page
page.onbeforeload((err) => {
if (err) return
this._pending = null
this._history.push(page)
})
} else {
@@ -59,3 +63,7 @@ LocationHistory.prototype.hasBack = function () {
LocationHistory.prototype.hasForward = function () {
return this._forward.length > 0
}
LocationHistory.prototype.pending = function () {
return this._pending
}

View File

@@ -28,6 +28,8 @@ module.exports = {
currentTime: 0, /* seconds */
duration: 1, /* seconds */
isPaused: true,
isStalled: false,
lastTimeUpdate: 0, /* Unix time in ms */
mouseStationarySince: 0 /* Unix time in ms */
},
audioInfo: null, /* set whenever an audio file is playing */
@@ -40,6 +42,7 @@ module.exports = {
badge: 0,
progress: 0
},
modal: null, /* modal popover */
errors: [], /* user-facing errors */
/*
@@ -109,6 +112,70 @@ module.exports = {
'numPieces': 2180
}
]
},
{
status: 'paused',
infoHash: '6a02592d2bbc069628cd5ed8a54f88ee06ac0ba5',
magnetURI: 'magnet:?xt=urn:btih:6a02592d2bbc069628cd5ed8a54f88ee06ac0ba5&dn=CosmosLaundromatFirstCycle&tr=http%3A%2F%2Fbt1.archive.org%3A6969%2Fannounce&tr=http%3A%2F%2Fbt2.archive.org%3A6969%2Fannounce&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&tr=wss%3A%2F%2Ftracker.webtorrent.io&ws=http%3A%2F%2Fia601508.us.archive.org%2F14%2Fitems%2F&ws=http%3A%2F%2Fia801508.us.archive.org%2F14%2Fitems%2F&ws=https%3A%2F%2Farchive.org%2Fdownload%2F',
displayName: 'Cosmos Laundromat (Preview)',
posterURL: 'cosmosLaundromat.jpg',
torrentPath: 'cosmosLaundromat.torrent',
files: [
{
'name': 'Cosmos Laundromat - First Cycle (1080p).gif',
'length': 223580,
'numPiecesPresent': 0,
'numPieces': 1
},
{
'name': 'Cosmos Laundromat - First Cycle (1080p).mp4',
'length': 220087570,
'numPiecesPresent': 0,
'numPieces': 421
},
{
'name': 'Cosmos Laundromat - First Cycle (1080p).ogv',
'length': 56832560,
'numPiecesPresent': 0,
'numPieces': 109
},
{
'name': 'CosmosLaundromat-FirstCycle1080p.en.srt',
'length': 3949,
'numPiecesPresent': 0,
'numPieces': 1
},
{
'name': 'CosmosLaundromat-FirstCycle1080p.es.srt',
'length': 3907,
'numPiecesPresent': 0,
'numPieces': 1
},
{
'name': 'CosmosLaundromat-FirstCycle1080p.fr.srt',
'length': 4119,
'numPiecesPresent': 0,
'numPieces': 1
},
{
'name': 'CosmosLaundromat-FirstCycle1080p.it.srt',
'length': 3941,
'numPiecesPresent': 0,
'numPieces': 1
},
{
'name': 'CosmosLaundromatFirstCycle_meta.sqlite',
'length': 11264,
'numPiecesPresent': 0,
'numPieces': 1
},
{
'name': 'CosmosLaundromatFirstCycle_meta.xml',
'length': 1204,
'numPiecesPresent': 0,
'numPieces': 1
}
]
}
],
downloadPath: path.join(os.homedir(), 'Downloads')

View File

@@ -8,7 +8,8 @@ var Header = require('./header')
var Player = require('./player')
var TorrentList = require('./torrent-list')
var Modals = {
'open-torrent-address-modal': require('./open-torrent-address-modal')
'open-torrent-address-modal': require('./open-torrent-address-modal'),
'update-available-modal': require('./update-available-modal')
}
function App (state, dispatch) {
@@ -62,7 +63,7 @@ function App (state, dispatch) {
function getModal () {
if (state.modal) {
var contents = Modals[state.modal](state, dispatch)
var contents = Modals[state.modal.id](state, dispatch)
return hx`
<div class='modal'>
<div class='modal-background'></div>

View File

@@ -4,6 +4,8 @@ var h = require('virtual-dom/h')
var hyperx = require('hyperx')
var hx = hyperx(h)
var prettyBytes = require('prettier-bytes')
var util = require('../util')
var {dispatch, dispatcher} = require('../lib/dispatcher')
@@ -40,7 +42,7 @@ function renderMedia (state) {
mediaElement.currentTime = state.playing.jumpToTime
state.playing.jumpToTime = null
}
// set volume
// Set volume
if (state.playing.setVolume !== null && isFinite(state.playing.setVolume)) {
mediaElement.volume = state.playing.setVolume
state.playing.setVolume = null
@@ -60,25 +62,20 @@ function renderMedia (state) {
onended=${onEnded}
onplay=${dispatcher('mediaPlaying')}
onpause=${dispatcher('mediaPaused')}
onstalling=${dispatcher('mediaStalled')}
ontimeupdate=${dispatcher('mediaTimeUpdate')}
autoplay>
</div>
`
mediaTag.tagName = mediaType
// Show the media.
// Video fills the window, centered with black bars if necessary
// Audio gets a static poster image and a summary of the file metadata.
var isAudio = mediaType === 'audio'
var style = {
backgroundImage: isAudio ? cssBackgroundImagePoster(state) : ''
}
return hx`
<div
class='letterbox'
style=${style}
onmousemove=${dispatcher('mediaMouseMoved')}>
${mediaTag}
${renderAudioMetadata(state)}
${renderOverlay(state)}
</div>
`
@@ -99,6 +96,31 @@ function renderMedia (state) {
}
}
function renderOverlay (state) {
var elems = []
var audioMetadataElem = renderAudioMetadata(state)
var spinnerElem = renderLoadingSpinner(state)
if (audioMetadataElem) elems.push(audioMetadataElem)
if (spinnerElem) elems.push(spinnerElem)
// Video fills the window, centered with black bars if necessary
// Audio gets a static poster image and a summary of the file metadata.
var style
if (state.playing.type === 'audio') {
style = { backgroundImage: cssBackgroundImagePoster(state) }
} else if (elems.length !== 0) {
style = { backgroundImage: cssBackgroundImageDarkGradient() }
} else {
return /* Video, not audio, and it isn't stalled, so no spinner. No overlay needed. */
}
return hx`
<div class='media-overlay-background' style=${style}>
<div class='media-overlay'>${elems}</div>
</div>
`
}
function renderAudioMetadata (state) {
if (!state.playing.audioInfo) return
var info = state.playing.audioInfo
@@ -119,14 +141,42 @@ function renderAudioMetadata (state) {
track = info.track.no + ' of ' + info.track.of
}
// Show a small info box in the middle of the screen
var elems = [hx`<div class='audio-title'><label></label>${title}</div>`]
// Show a small info box in the middle of the screen with title/album/artist/etc
var elems = []
if (artist) elems.push(hx`<div class='audio-artist'><label>Artist</label>${artist}</div>`)
if (album) elems.push(hx`<div class='audio-album'><label>Album</label>${album}</div>`)
if (track) elems.push(hx`<div class='audio-track'><label>Track</label>${track}</div>`)
// Align the title with the artist/etc info, if available. Otherwise, center the title
var emptyLabel = hx`<label></label>`
elems.unshift(hx`<div class='audio-title'>${elems.length ? emptyLabel : undefined}${title}</div>`)
return hx`<div class='audio-metadata'>${elems}</div>`
}
function renderLoadingSpinner (state) {
if (state.playing.isPaused) return
var isProbablyStalled = state.playing.isStalled ||
(new Date().getTime() - state.playing.lastTimeUpdate > 2000)
if (!isProbablyStalled) return
var torrentSummary = getPlayingTorrentSummary(state)
var torrent = state.client.get(torrentSummary.infoHash)
var file = torrentSummary.files[state.playing.fileIndex]
var progress = Math.floor(100 * file.numPiecesPresent / file.numPieces)
return hx`
<div class='media-stalled'>
<div class='loading-spinner'>&nbsp;</div>
<div class='loading-status ellipsis'>
<span class='progress'>${progress}%</span> downloaded,
<span>↓ ${prettyBytes(torrent.downloadSpeed || 0)}/s</span>
<span>↑ ${prettyBytes(torrent.uploadSpeed || 0)}/s</span>
</div>
</div>
`
}
function renderCastScreen (state) {
var isChromecast = state.playing.location.startsWith('chromecast')
var isAirplay = state.playing.location.startsWith('airplay')
@@ -157,9 +207,12 @@ function cssBackgroundImagePoster (state) {
if (!torrentSummary || !torrentSummary.posterURL) return ''
var posterURL = util.getAbsoluteStaticPath(torrentSummary.posterURL)
var cleanURL = posterURL.replace(/\\/g, '/')
return cssBackgroundImageDarkGradient() + `, url(${cleanURL})`
}
function cssBackgroundImageDarkGradient () {
return 'radial-gradient(circle at center, ' +
'rgba(0,0,0,0.4) 0%, rgba(0,0,0,1) 100%)' +
`, url(${cleanURL})`
'rgba(0,0,0,0.4) 0%, rgba(0,0,0,1) 100%)'
}
function getPlayingTorrentSummary (state) {
@@ -282,7 +335,7 @@ function renderLoadingBar (state) {
lastPartPresent = partPresent
}
// Output an list of rectangles to show loading progress
// Output some bars to show which parts of the file are loaded
return hx`
<div class='loading-bar'>
${parts.map(function (part) {

View File

@@ -0,0 +1,32 @@
module.exports = UpdateAvailableModal
var h = require('virtual-dom/h')
var hyperx = require('hyperx')
var hx = hyperx(h)
var electron = require('electron')
var {dispatch} = require('../lib/dispatcher')
function UpdateAvailableModal (state) {
return hx`
<div class='update-available-modal'>
<p><strong>A new version of WebTorrent is available: v${state.modal.version}</strong></p>
<p>We have an auto-updater for Windows and Mac. We don't have one for Linux yet, so you'll have to download the new version manually.</p>
<p>
<button class='primary' onclick=${handleOK}>Show Download Page</button>
<button class='cancel' onclick=${handleCancel}>Skip This Release</button>
</p>
</div>
`
function handleOK () {
electron.shell.openExternal('https://github.com/feross/webtorrent-desktop/releases')
dispatch('exitModal')
}
function handleCancel () {
dispatch('skipVersion', state.modal.version)
dispatch('exitModal')
}
}

BIN
static/WebTorrentSmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
static/cosmosLaundromat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.