diff --git a/bin/package.js b/bin/package.js index b7f06f38..5e3321d8 100755 --- a/bin/package.js +++ b/bin/package.js @@ -15,6 +15,7 @@ var rimraf = require('rimraf') var BUILD_NAME = config.APP_NAME + '-v' + config.APP_VERSION function build () { + rimraf.sync(path.join(config.ROOT_PATH, 'dist')) var platform = process.argv[2] var packageType = process.argv.length > 3 ? process.argv[3] : 'all' if (platform === 'darwin') { diff --git a/config.js b/config.js index 4100d152..7cecb1fe 100644 --- a/config.js +++ b/config.js @@ -32,39 +32,6 @@ module.exports = { ROOT_PATH: __dirname, STATIC_PATH: path.join(__dirname, 'static'), - SOUND_ADD: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'add.wav'), - volume: 0.2 - }, - SOUND_DELETE: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'delete.wav'), - volume: 0.1 - }, - SOUND_DISABLE: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'disable.wav'), - volume: 0.2 - }, - SOUND_DONE: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'done.wav'), - volume: 0.2 - }, - SOUND_ENABLE: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'enable.wav'), - volume: 0.2 - }, - SOUND_ERROR: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'error.wav'), - volume: 0.2 - }, - SOUND_PLAY: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'play.wav'), - volume: 0.2 - }, - SOUND_STARTUP: { - url: 'file://' + path.join(__dirname, 'static', 'sound', 'startup.wav'), - volume: 0.4 - }, - WINDOW_ABOUT: 'file://' + path.join(__dirname, 'renderer', 'about.html'), WINDOW_MAIN: 'file://' + path.join(__dirname, 'renderer', 'main.html'), WINDOW_WEBTORRENT: 'file://' + path.join(__dirname, 'renderer', 'webtorrent.html'), diff --git a/renderer/index.js b/renderer/index.js index 8dd3151f..7dbde4a5 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -8,7 +8,6 @@ var EventEmitter = require('events') var fs = require('fs') var mainLoop = require('main-loop') var path = require('path') -var remote = require('remote') var srtToVtt = require('srt-to-vtt') var createElement = require('virtual-dom/create-element') @@ -16,25 +15,30 @@ var diff = require('virtual-dom/diff') var patch = require('virtual-dom/patch') var App = require('./views/app') -var errors = require('./lib/errors') var config = require('../config') var crashReporter = require('../crash-reporter') +var errors = require('./lib/errors') +var sound = require('./lib/sound') +var State = require('./state') var TorrentPlayer = require('./lib/torrent-player') var util = require('./util') + var {setDispatch} = require('./lib/dispatcher') setDispatch(dispatch) -var State = require('./state') - -// This dependency is the slowest-loading, so we lazy load it -var Cast = null // Electron apps have two processes: a main process (node) runs first and starts // a renderer process (essentially a Chrome window). We're in the renderer process, // and this IPC channel receives from and sends messages to the main process var ipcRenderer = electron.ipcRenderer - var clipboard = electron.clipboard -var dialog = remote.require('dialog') + +var dialog = electron.remote.dialog +var Menu = electron.remote.Menu +var MenuItem = electron.remote.MenuItem +var remote = electron.remote + +// This dependency is the slowest-loading, so we lazy load it +var Cast = null // For easy debugging in Developer Tools var state = global.state = State.getInitialState() @@ -60,8 +64,7 @@ function init () { initWebTorrent() - // Lazily load the Chromecast/Airplay/DLNA modules - window.setTimeout(lazyLoadCast, 5000) + window.setTimeout(delayedInit, 5000) // The UI is built with virtual-dom, a minimalist library extracted from React // The concepts--one way data flow, a pure function that renders state to a @@ -112,11 +115,16 @@ function init () { setupIpc() // Done! Ideally we want to get here <100ms after the user clicks the app - playInterfaceSound('STARTUP') + sound.play('STARTUP') console.timeEnd('init') } +function delayedInit () { + lazyLoadCast() + sound.preload() +} + // Lazily loads Chromecast and Airplay support function lazyLoadCast () { if (!Cast) { @@ -651,7 +659,7 @@ function torrentInfoHash (torrentKey, infoHash) { status: 'new' } state.saved.torrents.push(torrentSummary) - playInterfaceSound('ADD') + sound.play('ADD') } torrentSummary.infoHash = infoHash @@ -800,13 +808,13 @@ function openPlayer (infoHash, index, cb) { if (index === undefined) return cb(new errors.UnplayableError()) // update UI to show pending playback - if (torrentSummary.progress !== 1) playInterfaceSound('PLAY') + if (torrentSummary.progress !== 1) sound.play('PLAY') torrentSummary.playStatus = 'requested' update() var timeout = setTimeout(function () { torrentSummary.playStatus = 'timeout' /* no seeders available? */ - playInterfaceSound('ERROR') + sound.play('ERROR') cb(new Error('playback timed out')) update() }, 10000) /* give it a few seconds */ @@ -903,11 +911,11 @@ function toggleTorrent (infoHash) { if (torrentSummary.status === 'paused') { torrentSummary.status = 'new' startTorrentingSummary(torrentSummary) - playInterfaceSound('ENABLE') + sound.play('ENABLE') } else { torrentSummary.status = 'paused' ipcRenderer.send('wt-stop-torrenting', torrentSummary.infoHash) - playInterfaceSound('DISABLE') + sound.play('DISABLE') } } @@ -919,7 +927,7 @@ function deleteTorrent (infoHash) { if (index > -1) state.saved.torrents.splice(index, 1) saveStateThrottled() state.location.clearForward() // prevent user from going forward to a deleted torrent - playInterfaceSound('DELETE') + sound.play('DELETE') } function toggleSelectTorrent (infoHash) { @@ -930,18 +938,18 @@ function toggleSelectTorrent (infoHash) { function openTorrentContextMenu (infoHash) { var torrentSummary = getTorrentSummary(infoHash) - var menu = new remote.Menu() - menu.append(new remote.MenuItem({ + var menu = new Menu() + menu.append(new MenuItem({ label: 'Save Torrent File As...', click: () => saveTorrentFileAs(torrentSummary) })) - menu.append(new remote.MenuItem({ + menu.append(new MenuItem({ label: 'Copy Instant.io Link to Clipboard', click: () => clipboard.writeText(`https://instant.io/#${torrentSummary.infoHash}`) })) - menu.append(new remote.MenuItem({ + menu.append(new MenuItem({ label: 'Copy Magnet Link to Clipboard', click: () => clipboard.writeText(torrentSummary.magnetURI) })) @@ -959,7 +967,7 @@ function saveTorrentFileAs (torrentSummary) { { name: 'All Files', extensions: ['*'] } ] } - dialog.showSaveDialog(remote.getCurrentWindow(), opts, (savePath) => { + dialog.showSaveDialog(remote.getCurrentWindow(), opts, function (savePath) { var torrentPath = util.getAbsoluteStaticPath(torrentSummary.torrentPath) fs.readFile(torrentPath, function (err, torrentFile) { if (err) return onError(err) @@ -1021,7 +1029,7 @@ function restoreBounds () { function onError (err) { console.error(err.stack || err) - playInterfaceSound('ERROR') + sound.play('ERROR') state.errors.push({ time: new Date().getTime(), message: err.message || err @@ -1043,17 +1051,7 @@ function showDoneNotification (torrent) { ipcRenderer.send('focusWindow', 'main') } - playInterfaceSound('DONE') -} - -function playInterfaceSound (name) { - var sound = config[`SOUND_${name}`] - if (!sound) throw new Error('Invalid sound name') - - var audio = new window.Audio() - audio.volume = sound.volume - audio.src = sound.url - audio.play() + sound.play('DONE') } // Finds the longest common prefix diff --git a/renderer/lib/sound.js b/renderer/lib/sound.js new file mode 100644 index 00000000..a497e0ce --- /dev/null +++ b/renderer/lib/sound.js @@ -0,0 +1,75 @@ +module.exports = { + preload, + play +} + +var config = require('../../config') +var path = require('path') + +/* Cache of Audio elements, for instant playback */ +var cache = {} + +var sounds = { + ADD: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'add.wav'), + volume: 0.2 + }, + DELETE: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'delete.wav'), + volume: 0.1 + }, + DISABLE: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'disable.wav'), + volume: 0.2 + }, + DONE: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'done.wav'), + volume: 0.2 + }, + ENABLE: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'enable.wav'), + volume: 0.2 + }, + ERROR: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'error.wav'), + volume: 0.2 + }, + POP: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'pop.wav'), + volume: 0.2 + }, + PLAY: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'play.wav'), + volume: 0.2 + }, + STARTUP: { + url: 'file://' + path.join(config.STATIC_PATH, 'sound', 'startup.wav'), + volume: 0.4 + } +} + +function preload () { + for (var name in sounds) { + if (!cache[name]) { + var sound = sounds[name] + var audio = cache[name] = new window.Audio() + audio.volume = sound.volume + audio.src = sound.url + } + } +} + +function play (name) { + var audio = cache[name] + if (!audio) { + var sound = sounds[name] + if (!sound) { + throw new Error('Invalid sound name') + } + audio = cache[name] = new window.Audio() + audio.volume = sound.volume + audio.src = sound.url + } + audio.currentTime = 0 + audio.play() +}