diff --git a/renderer/controllers/playback-controller.js b/renderer/controllers/playback-controller.js index 126fb5df..d6679ee7 100644 --- a/renderer/controllers/playback-controller.js +++ b/renderer/controllers/playback-controller.js @@ -40,7 +40,7 @@ module.exports = class PlaybackController { // Show a file in the OS, eg in Finder on a Mac openItem (infoHash, index) { - var torrentSummary = torrentSummary.getByKey(this.state, infoHash) + var torrentSummary = TorrentSummary.getByKey(this.state, infoHash) var filePath = path.join( torrentSummary.path, torrentSummary.files[index].path) diff --git a/renderer/controllers/torrent-controller.js b/renderer/controllers/torrent-controller.js new file mode 100644 index 00000000..0225add7 --- /dev/null +++ b/renderer/controllers/torrent-controller.js @@ -0,0 +1,192 @@ +const path = require('path') +const ipcRenderer = require('electron').ipcRenderer + +const TorrentSummary = require('../lib/torrent-summary') +const TorrentPlayer = require('../lib/torrent-player') +const sound = require('../lib/sound') +const {dispatch} = require('../lib/dispatcher') + +module.exports = class TorrentController { + constructor (state) { + this.state = state + } + + torrentInfoHash (torrentKey, infoHash) { + var torrentSummary = this.getTorrentSummary(torrentKey) + console.log('got infohash for %s torrent %s', + torrentSummary ? 'existing' : 'new', torrentKey) + + if (!torrentSummary) { + var torrents = this.state.saved.torrents + + // Check if an existing (non-active) torrent has the same info hash + if (torrents.find((t) => t.infoHash === infoHash)) { + ipcRenderer.send('wt-stop-torrenting', infoHash) + return dispatch('error', 'Cannot add duplicate torrent') + } + + torrentSummary = { + torrentKey: torrentKey, + status: 'new' + } + torrents.unshift(torrentSummary) + sound.play('ADD') + } + + torrentSummary.infoHash = infoHash + dispatch('update') + } + + torrentWarning (torrentKey, message) { + console.log('warning for torrent %s: %s', torrentKey, message) + } + + torrentError (torrentKey, message) { + // TODO: WebTorrent needs semantic errors + if (message.startsWith('Cannot add duplicate torrent')) { + // Remove infohash from the message + message = 'Cannot add duplicate torrent' + } + dispatch('error', message) + + var torrentSummary = this.getTorrentSummary(torrentKey) + if (torrentSummary) { + console.log('Pausing torrent %s due to error: %s', torrentSummary.infoHash, message) + torrentSummary.status = 'paused' + dispatch('update') + } + } + + torrentMetadata (torrentKey, torrentInfo) { + // Summarize torrent + var torrentSummary = this.getTorrentSummary(torrentKey) + torrentSummary.status = 'downloading' + torrentSummary.name = torrentSummary.displayName || torrentInfo.name + torrentSummary.path = torrentInfo.path + torrentSummary.magnetURI = torrentInfo.magnetURI + // TODO: make torrentInfo immutable, save separately as torrentSummary.info + // For now, check whether torrentSummary.files has already been set: + var hasDetailedFileInfo = torrentSummary.files && torrentSummary.files[0].path + if (!hasDetailedFileInfo) { + torrentSummary.files = torrentInfo.files + } + if (!torrentSummary.selections) { + torrentSummary.selections = torrentSummary.files.map((x) => true) + } + torrentSummary.defaultPlayFileIndex = TorrentPlayer.pickFileToPlay(torrentInfo.files) + dispatch('update') + + // Save the .torrent file, if it hasn't been saved already + if (!torrentSummary.torrentFileName) ipcRenderer.send('wt-save-torrent-file', torrentKey) + + // Auto-generate a poster image, if it hasn't been generated already + if (!torrentSummary.posterFileName) ipcRenderer.send('wt-generate-torrent-poster', torrentKey) + } + + torrentDone (torrentKey, torrentInfo) { + // Update the torrent summary + var torrentSummary = this.getTorrentSummary(torrentKey) + torrentSummary.status = 'seeding' + + // Notify the user that a torrent finished, but only if we actually DL'd at least part of it. + // Don't notify if we merely finished verifying data files that were already on disk. + if (torrentInfo.bytesReceived > 0) { + if (!this.state.window.isFocused) { + this.state.dock.badge += 1 + } + showDoneNotification(torrentSummary) + ipcRenderer.send('downloadFinished', getTorrentPath(torrentSummary)) + } + + dispatch('update') + } + + torrentProgress (progressInfo) { + // Overall progress across all active torrents, 0 to 1 + var progress = progressInfo.progress + var hasActiveTorrents = progressInfo.hasActiveTorrents + + // Hide progress bar when client has no torrents, or progress is 100% + // TODO: isn't this equivalent to: if (progress === 1) ? + if (!hasActiveTorrents || progress === 1) { + progress = -1 + } + + // Show progress bar under the WebTorrent taskbar icon, on OSX + this.state.dock.progress = progress + + // Update progress for each individual torrent + progressInfo.torrents.forEach((p) => { + var torrentSummary = this.getTorrentSummary(p.torrentKey) + if (!torrentSummary) { + console.log('warning: got progress for missing torrent %s', p.torrentKey) + return + } + torrentSummary.progress = p + }) + + // TODO: Find an efficient way to re-enable this line, which allows subtitle + // files which are completed after a video starts to play to be added + // dynamically to the list of subtitles. + // checkForSubtitles() + + dispatch('update') + } + + torrentFileModtimes (torrentKey, fileModtimes) { + var torrentSummary = this.getTorrentSummary(torrentKey) + torrentSummary.fileModtimes = fileModtimes + dispatch('saveStateThrottled') + } + + torrentFileSaved (torrentKey, torrentFileName) { + console.log('torrent file saved %s: %s', torrentKey, torrentFileName) + var torrentSummary = this.getTorrentSummary(torrentKey) + torrentSummary.torrentFileName = torrentFileName + dispatch('saveStateThrottled') + } + + torrentPosterSaved (torrentKey, posterFileName) { + var torrentSummary = this.getTorrentSummary(torrentKey) + torrentSummary.posterFileName = posterFileName + dispatch('saveStateThrottled') + } + + torrentAudioMetadata (infoHash, index, info) { + var torrentSummary = this.getTorrentSummary(infoHash) + var fileSummary = torrentSummary.files[index] + fileSummary.audioInfo = info + dispatch('update') + } + + torrentServerRunning (serverInfo) { + this.state.server = serverInfo + } + + // Gets a torrent summary {name, infoHash, status} from state.saved.torrents + // Returns undefined if we don't know that infoHash + getTorrentSummary (torrentKey) { + return TorrentSummary.getByKey(this.state, torrentKey) + } +} + +function getTorrentPath (torrentSummary) { + var itemPath = TorrentSummary.getFileOrFolder(torrentSummary) + if (torrentSummary.files.length > 1) { + itemPath = path.dirname(itemPath) + } + return itemPath +} + +function showDoneNotification (torrent) { + var notif = new window.Notification('Download Complete', { + body: torrent.name, + silent: true + }) + + notif.onclick = function () { + ipcRenderer.send('show') + } + + sound.play('DONE') +} diff --git a/renderer/controllers/torrent-list-controller.js b/renderer/controllers/torrent-list-controller.js index e1e27ff5..c259184d 100644 --- a/renderer/controllers/torrent-list-controller.js +++ b/renderer/controllers/torrent-list-controller.js @@ -112,6 +112,14 @@ module.exports = class TorrentListController { ipcRenderer.send('wt-select-files', infoHash, torrentSummary.selections) } + confirmDeleteTorrent (infoHash, deleteData) { + this.state.modal = { + id: 'remove-torrent-modal', + infoHash, + deleteData + } + } + // TODO: use torrentKey, not infoHash deleteTorrent (infoHash, deleteData) { ipcRenderer.send('wt-stop-torrenting', infoHash) @@ -151,14 +159,12 @@ module.exports = class TorrentListController { menu.append(new electron.remote.MenuItem({ label: 'Remove From List', - click: () => this.deleteTorrent( - torrentSummary.infoHash, false) + click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, false) })) menu.append(new electron.remote.MenuItem({ label: 'Remove Data File', - click: () => this.deleteTorrent( - torrentSummary.infoHash, true) + click: () => dispatch('confirmDeleteTorrent', torrentSummary.infoHash, true) })) menu.append(new electron.remote.MenuItem({ @@ -239,27 +245,23 @@ function findFilesRecursive (paths, cb) { function deleteFile (path) { if (!path) return fs.unlink(path, function (err) { - if (err) dispatch('onError', err) + if (err) dispatch('error', err) }) } -// Delete all files in a torren +// Delete all files in a torrent function moveItemToTrash (torrentSummary) { - // TODO: delete directories, not just files - torrentSummary.files.forEach(function (file) { - var filePath = path.join(torrentSummary.path, file.path) - console.log('DEBUG DELETING ' + filePath) - ipcRenderer.send('moveItemToTrash', filePath) - }) + var filePath = TorrentSummary.getFileOrFolder(torrentSummary) + ipcRenderer.send('moveItemToTrash', filePath) } function showItemInFolder (torrentSummary) { - ipcRenderer.send('showItemInFolder', TorrentSummary.getTorrentPath(torrentSummary)) + ipcRenderer.send('showItemInFolder', TorrentSummary.getFileOrFolder(torrentSummary)) } function saveTorrentFileAs (torrentSummary) { var downloadPath = this.state.saved.prefs.downloadPath - var newFileName = `${path.parse(torrentSummary.name).name}.torrent` + var newFileName = path.parse(torrentSummary.name).name + '.torrent' var opts = { title: 'Save Torrent File', defaultPath: path.join(downloadPath, newFileName), diff --git a/renderer/lib/torrent-player.js b/renderer/lib/torrent-player.js index fe81b020..c2ee4003 100644 --- a/renderer/lib/torrent-player.js +++ b/renderer/lib/torrent-player.js @@ -2,21 +2,21 @@ module.exports = { isPlayable, isVideo, isAudio, - isPlayableTorrent, + isTorrent, + isPlayableTorrentSummary, pickFileToPlay } var path = require('path') -/** - * Determines whether a file in a torrent is audio/video we can play - */ +// Checks whether a fileSummary or file path is audio/video that we can play, +// based on the file extension function isPlayable (file) { return isVideo(file) || isAudio(file) } +// Checks whether a fileSummary or file path is playable video function isVideo (file) { - var ext = path.extname(file.name).toLowerCase() return [ '.avi', '.m4v', @@ -27,21 +27,36 @@ function isVideo (file) { '.ogv', '.webm', '.wmv' - ].includes(ext) + ].includes(getFileExtension(file)) } +// Checks whether a fileSummary or file path is playable audio function isAudio (file) { - var ext = path.extname(file.name).toLowerCase() return [ '.aac', '.ac3', '.mp3', '.ogg', '.wav' - ].includes(ext) + ].includes(getFileExtension(file)) } -function isPlayableTorrent (torrentSummary) { +// Checks if the argument is either: +// - a string that's a valid filename ending in .torrent +// - a file object where obj.name is ends in .torrent +// - a string that's a magnet link (magnet://...) +function isTorrent (file) { + var isTorrentFile = getFileExtension(file) === '.torrent' + var isMagnet = typeof file === 'string' && /^(stream-)?magnet:/.test(file) + return isTorrentFile || isMagnet +} + +function getFileExtension (file) { + var name = typeof file === 'string' ? file : file.name + return path.extname(name).toLowerCase() +} + +function isPlayableTorrentSummary (torrentSummary) { return torrentSummary.files && torrentSummary.files.some(isPlayable) } diff --git a/renderer/lib/torrent-summary.js b/renderer/lib/torrent-summary.js index 56e048ff..fd5b0d80 100644 --- a/renderer/lib/torrent-summary.js +++ b/renderer/lib/torrent-summary.js @@ -2,7 +2,8 @@ module.exports = { getPosterPath, getTorrentPath, getByKey, - getTorrentID + getTorrentID, + getFileOrFolder } var path = require('path') @@ -43,3 +44,13 @@ function getByKey (state, torrentKey) { return state.saved.torrents.find((x) => x.torrentKey === torrentKey || x.infoHash === torrentKey) } + +// Returns the path to either the file (in a single-file torrent) or the root +// folder (in multi-file torrent) +// WARNING: assumes that multi-file torrents consist of a SINGLE folder. +// TODO: make this assumption explicit, enforce it in the `create-torrent` +// module. Store root folder explicitly to avoid hacky path processing below. +function getFileOrFolder (torrentSummary) { + var ts = torrentSummary + return path.join(ts.path, ts.files[0].path.split('/')[0]) +} diff --git a/renderer/main.css b/renderer/main.css index 9f64c957..b4683c43 100644 --- a/renderer/main.css +++ b/renderer/main.css @@ -326,7 +326,6 @@ table { } .create-torrent input.torrent-is-private { - width: initial; margin: 0; } @@ -404,7 +403,7 @@ button.button-raised:active { * OTHER FORM ELEMENT DEFAULTS */ -input { +input[type='text'] { background: transparent; width: 300px; padding: 6px; diff --git a/renderer/main.js b/renderer/main.js index af9c2dbc..6ee63609 100644 --- a/renderer/main.js +++ b/renderer/main.js @@ -6,7 +6,6 @@ crashReporter.init() const dragDrop = require('drag-drop') const electron = require('electron') const mainLoop = require('main-loop') -const path = require('path') const createElement = require('virtual-dom/create-element') const diff = require('virtual-dom/diff') @@ -18,14 +17,14 @@ const telemetry = require('./lib/telemetry') const sound = require('./lib/sound') const State = require('./lib/state') const TorrentPlayer = require('./lib/torrent-player') -const TorrentSummary = require('./lib/torrent-summary') const MediaController = require('./controllers/media-controller') const UpdateController = require('./controllers/update-controller') const PrefsController = require('./controllers/prefs-controller') -const TorrentListController = require('./controllers/torrent-list-controller') const PlaybackController = require('./controllers/playback-controller') const SubtitlesController = require('./controllers/subtitles-controller') +const TorrentListController = require('./controllers/torrent-list-controller') +const TorrentController = require('./controllers/torrent-controller') // Yo-yo pattern: state object lives here and percolates down thru all the views. // Events come back up from the views via dispatch(...) @@ -60,9 +59,10 @@ function onState (err, _state) { media: new MediaController(state), update: new UpdateController(state), prefs: new PrefsController(state, config), - torrentList: new TorrentListController(state), playback: new PlaybackController(state, config, update), - subtitles: new SubtitlesController(state) + subtitles: new SubtitlesController(state), + torrentList: new TorrentListController(state), + torrent: new TorrentController(state) } // Add first page to location history @@ -178,7 +178,8 @@ const dispatchHandlers = { 'createTorrent': (options) => controllers.torrentList.createTorrent(options), 'toggleTorrent': (infoHash) => controllers.torrentList.toggleTorrent(infoHash), 'toggleTorrentFile': (infoHash, index) => controllers.torrentList.toggleTorrentFile(infoHash, index), - 'deleteTorrent': (infoHash) => controllers.torrentList.deleteTorrent(infoHash), + 'confirmDeleteTorrent': (infoHash, deleteData) => controllers.torrentList.confirmDeleteTorrent(infoHash, deleteData), + 'deleteTorrent': (infoHash, deleteData) => controllers.torrentList.deleteTorrent(infoHash, deleteData), 'toggleSelectTorrent': (infoHash) => controllers.torrentList.toggleSelectTorrent(infoHash), 'openTorrentContextMenu': (infoHash) => controllers.torrentList.openTorrentContextMenu(infoHash), 'startTorrentingSummary': (torrentSummary) => @@ -239,7 +240,9 @@ const dispatchHandlers = { 'onOpen': onOpen, 'error': onError, 'uncaughtError': (proc, err) => telemetry.logUncaughtError(proc, err), - 'saveState': () => State.save(state) + 'saveState': () => State.save(state), + 'saveStateThrottled': () => State.saveThrottled(state), + 'update': () => {} // No-op, just trigger an update } // Events from the UI never modify state directly. Instead they call dispatch() @@ -269,18 +272,19 @@ function setupIpc () { ipcRenderer.on('fullscreenChanged', onFullscreenChanged) - ipcRenderer.on('wt-infohash', (e, ...args) => torrentInfoHash(...args)) - ipcRenderer.on('wt-metadata', (e, ...args) => torrentMetadata(...args)) - ipcRenderer.on('wt-done', (e, ...args) => torrentDone(...args)) - ipcRenderer.on('wt-warning', (e, ...args) => torrentWarning(...args)) - ipcRenderer.on('wt-error', (e, ...args) => torrentError(...args)) + var tc = controllers.torrent + ipcRenderer.on('wt-infohash', (e, ...args) => tc.torrentInfoHash(...args)) + ipcRenderer.on('wt-metadata', (e, ...args) => tc.torrentMetadata(...args)) + ipcRenderer.on('wt-done', (e, ...args) => tc.torrentDone(...args)) + ipcRenderer.on('wt-warning', (e, ...args) => tc.torrentWarning(...args)) + ipcRenderer.on('wt-error', (e, ...args) => tc.torrentError(...args)) - ipcRenderer.on('wt-progress', (e, ...args) => torrentProgress(...args)) - ipcRenderer.on('wt-file-modtimes', (e, ...args) => torrentFileModtimes(...args)) - ipcRenderer.on('wt-file-saved', (e, ...args) => torrentFileSaved(...args)) - ipcRenderer.on('wt-poster', (e, ...args) => torrentPosterSaved(...args)) - ipcRenderer.on('wt-audio-metadata', (e, ...args) => torrentAudioMetadata(...args)) - ipcRenderer.on('wt-server-running', (e, ...args) => torrentServerRunning(...args)) + ipcRenderer.on('wt-progress', (e, ...args) => tc.torrentProgress(...args)) + ipcRenderer.on('wt-file-modtimes', (e, ...args) => tc.torrentFileModtimes(...args)) + ipcRenderer.on('wt-file-saved', (e, ...args) => tc.torrentFileSaved(...args)) + ipcRenderer.on('wt-poster', (e, ...args) => tc.torrentPosterSaved(...args)) + ipcRenderer.on('wt-audio-metadata', (e, ...args) => tc.torrentAudioMetadata(...args)) + ipcRenderer.on('wt-server-running', (e, ...args) => tc.torrentServerRunning(...args)) ipcRenderer.on('wt-uncaught-error', (e, err) => telemetry.logUncaughtError('webtorrent', err)) @@ -325,177 +329,6 @@ function resumeTorrents () { .forEach((torrentSummary) => controllers.torrentList.startTorrentingSummary(torrentSummary)) } -function isTorrent (file) { - var name = typeof file === 'string' ? file : file.name - var isTorrentFile = path.extname(name).toLowerCase() === '.torrent' - var isMagnet = typeof file === 'string' && /^(stream-)?magnet:/.test(file) - return isTorrentFile || isMagnet -} - -// Gets a torrent summary {name, infoHash, status} from state.saved.torrents -// Returns undefined if we don't know that infoHash -function getTorrentSummary (torrentKey) { - return TorrentSummary.getByKey(state, torrentKey) -} - -function torrentInfoHash (torrentKey, infoHash) { - var torrentSummary = getTorrentSummary(torrentKey) - console.log('got infohash for %s torrent %s', - torrentSummary ? 'existing' : 'new', torrentKey) - - if (!torrentSummary) { - // Check if an existing (non-active) torrent has the same info hash - if (state.saved.torrents.find((t) => t.infoHash === infoHash)) { - ipcRenderer.send('wt-stop-torrenting', infoHash) - return onError(new Error('Cannot add duplicate torrent')) - } - - torrentSummary = { - torrentKey: torrentKey, - status: 'new' - } - state.saved.torrents.unshift(torrentSummary) - sound.play('ADD') - } - - torrentSummary.infoHash = infoHash - update() -} - -function torrentWarning (torrentKey, message) { - onWarning(message) -} - -function torrentError (torrentKey, message) { - // TODO: WebTorrent needs semantic errors - if (message.startsWith('Cannot add duplicate torrent')) { - // Remove infohash from the message - message = 'Cannot add duplicate torrent' - } - onError(message) - - var torrentSummary = getTorrentSummary(torrentKey) - if (torrentSummary) { - console.log('Pausing torrent %s due to error: %s', torrentSummary.infoHash, message) - torrentSummary.status = 'paused' - update() - } -} - -function torrentMetadata (torrentKey, torrentInfo) { - // Summarize torrent - var torrentSummary = getTorrentSummary(torrentKey) - torrentSummary.status = 'downloading' - torrentSummary.name = torrentSummary.displayName || torrentInfo.name - torrentSummary.path = torrentInfo.path - torrentSummary.magnetURI = torrentInfo.magnetURI - // TODO: make torrentInfo immutable, save separately as torrentSummary.info - // For now, check whether torrentSummary.files has already been set: - var hasDetailedFileInfo = torrentSummary.files && torrentSummary.files[0].path - if (!hasDetailedFileInfo) { - torrentSummary.files = torrentInfo.files - } - if (!torrentSummary.selections) { - torrentSummary.selections = torrentSummary.files.map((x) => true) - } - torrentSummary.defaultPlayFileIndex = TorrentPlayer.pickFileToPlay(torrentInfo.files) - update() - - // Save the .torrent file, if it hasn't been saved already - if (!torrentSummary.torrentFileName) ipcRenderer.send('wt-save-torrent-file', torrentKey) - - // Auto-generate a poster image, if it hasn't been generated already - if (!torrentSummary.posterFileName) ipcRenderer.send('wt-generate-torrent-poster', torrentKey) -} - -function torrentDone (torrentKey, torrentInfo) { - // Update the torrent summary - var torrentSummary = getTorrentSummary(torrentKey) - torrentSummary.status = 'seeding' - - // Notify the user that a torrent finished, but only if we actually DL'd at least part of it. - // Don't notify if we merely finished verifying data files that were already on disk. - if (torrentInfo.bytesReceived > 0) { - if (!state.window.isFocused) { - state.dock.badge += 1 - } - showDoneNotification(torrentSummary) - ipcRenderer.send('downloadFinished', getTorrentPath(torrentSummary)) - } - - update() -} - -function torrentProgress (progressInfo) { - // Overall progress across all active torrents, 0 to 1 - var progress = progressInfo.progress - var hasActiveTorrents = progressInfo.hasActiveTorrents - - // Hide progress bar when client has no torrents, or progress is 100% - // TODO: isn't this equivalent to: if (progress === 1) ? - if (!hasActiveTorrents || progress === 1) { - progress = -1 - } - - // Show progress bar under the WebTorrent taskbar icon, on OSX - state.dock.progress = progress - - // Update progress for each individual torrent - progressInfo.torrents.forEach(function (p) { - var torrentSummary = getTorrentSummary(p.torrentKey) - if (!torrentSummary) { - console.log('warning: got progress for missing torrent %s', p.torrentKey) - return - } - torrentSummary.progress = p - }) - - // TODO: Find an efficient way to re-enable this line, which allows subtitle - // files which are completed after a video starts to play to be added - // dynamically to the list of subtitles. - // checkForSubtitles() - - update() -} - -function torrentFileModtimes (torrentKey, fileModtimes) { - var torrentSummary = getTorrentSummary(torrentKey) - torrentSummary.fileModtimes = fileModtimes - State.saveThrottled(state) -} - -function torrentFileSaved (torrentKey, torrentFileName) { - console.log('torrent file saved %s: %s', torrentKey, torrentFileName) - var torrentSummary = getTorrentSummary(torrentKey) - torrentSummary.torrentFileName = torrentFileName - State.saveThrottled(state) -} - -function torrentPosterSaved (torrentKey, posterFileName) { - var torrentSummary = getTorrentSummary(torrentKey) - torrentSummary.posterFileName = posterFileName - State.saveThrottled(state) -} - -function torrentAudioMetadata (infoHash, index, info) { - var torrentSummary = getTorrentSummary(infoHash) - var fileSummary = torrentSummary.files[index] - fileSummary.audioInfo = info - update() -} - -function torrentServerRunning (serverInfo) { - state.server = serverInfo -} - -function getTorrentPath (torrentSummary) { - var itemPath = path.join(torrentSummary.path, torrentSummary.files[0].path) - if (torrentSummary.files.length > 1) { - itemPath = path.dirname(itemPath) - } - return itemPath -} - // Set window dimensions to match video dimensions or fill the screen function setDimensions (dimensions) { // Don't modify the window size if it's already maximized @@ -535,19 +368,6 @@ function setDimensions (dimensions) { state.playing.aspectRatio = aspectRatio } -function showDoneNotification (torrent) { - var notif = new window.Notification('Download Complete', { - body: torrent.name, - silent: true - }) - - notif.onclick = function () { - ipcRenderer.send('show') - } - - sound.play('DONE') -} - // Called when the user adds files (.torrent, files to seed, subtitles) to the app // via any method (drag-drop, drag to app icon, command line) function onOpen (files) { @@ -560,7 +380,7 @@ function onOpen (files) { var subtitles = files.filter(controllers.subtitles.isSubtitle) if (state.location.url() === 'home' || subtitles.length === 0) { - if (files.every(isTorrent)) { + if (files.every(TorrentPlayer.isTorrent)) { if (state.location.url() !== 'home') { backToList() } @@ -588,10 +408,6 @@ function onError (err) { update() } -function onWarning (err) { - console.log('warning: %s', err.message || err) -} - function onPaste (e) { if (e.target.tagName.toLowerCase() === 'input') return diff --git a/renderer/views/app.js b/renderer/views/app.js index bdd01f75..58c64b2d 100644 --- a/renderer/views/app.js +++ b/renderer/views/app.js @@ -12,6 +12,7 @@ var Views = { var Modals = { 'open-torrent-address-modal': require('./open-torrent-address-modal'), + 'remove-torrent-modal': require('./remove-torrent-modal'), 'update-available-modal': require('./update-available-modal'), 'unsupported-media-modal': require('./unsupported-media-modal') } diff --git a/renderer/views/remove-torrent-modal.js b/renderer/views/remove-torrent-modal.js new file mode 100644 index 00000000..580adac5 --- /dev/null +++ b/renderer/views/remove-torrent-modal.js @@ -0,0 +1,26 @@ +module.exports = RemoveTorrentModal + +var {dispatch, dispatcher} = require('../lib/dispatcher') +var hx = require('../lib/hx') + +function RemoveTorrentModal (state) { + var message = state.modal.deleteData + ? 'Are you sure you want to remove this torrent from the list and delete the data file?' + : 'Are you sure you want to remove this torrent from the list?' + var buttonText = state.modal.deleteData ? 'Remove Data' : 'Remove' + + return hx` +
+

${message}

+

+ + +

+
+ ` + + function handleRemove () { + dispatch('deleteTorrent', state.modal.infoHash, state.modal.deleteData) + dispatch('exitModal') + } +} diff --git a/renderer/views/torrent-list.js b/renderer/views/torrent-list.js index b897ac1e..6b6d68ac 100644 --- a/renderer/views/torrent-list.js +++ b/renderer/views/torrent-list.js @@ -148,7 +148,7 @@ function TorrentList (state) { // Only show the play button for torrents that contain playable media var playButton - if (TorrentPlayer.isPlayableTorrent(torrentSummary)) { + if (TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) { playButton = hx` + onclick=${dispatcher('confirmDeleteTorrent', infoHash, false)}> close