Check path for each torrent

This commit is contained in:
DC
2016-08-13 20:28:20 -07:00
parent 1ec305162e
commit 0809e20a6e
6 changed files with 110 additions and 58 deletions

View File

@@ -188,7 +188,7 @@ module.exports = class PlaybackController {
}, 10000) /* give it a few seconds */ }, 10000) /* give it a few seconds */
if (torrentSummary.status === 'paused') { if (torrentSummary.status === 'paused') {
dispatch('startTorrentingSummary', torrentSummary) dispatch('startTorrentingSummary', torrentSummary.torrentKey)
ipcRenderer.once('wt-ready-' + torrentSummary.infoHash, ipcRenderer.once('wt-ready-' + torrentSummary.infoHash,
() => this.openPlayerFromActiveTorrent(torrentSummary, index, timeout, cb)) () => this.openPlayerFromActiveTorrent(torrentSummary, index, timeout, cb))
} else { } else {

View File

@@ -76,21 +76,25 @@ module.exports = class TorrentListController {
} }
// Starts downloading and/or seeding a given torrentSummary. // Starts downloading and/or seeding a given torrentSummary.
startTorrentingSummary (torrentSummary) { startTorrentingSummary (torrentKey) {
var s = torrentSummary var s = TorrentSummary.getByKey(this.state, torrentKey)
if (!s) throw new Error('Missing key: ' + torrentKey)
// Backward compatibility for config files save before we had torrentKey
if (!s.torrentKey) s.torrentKey = this.state.nextTorrentKey++
// Use Downloads folder by default // Use Downloads folder by default
if (!s.path) s.path = this.state.saved.prefs.downloadPath if (!s.path) s.path = this.state.saved.prefs.downloadPath
ipcRenderer.send('wt-start-torrenting', fs.stat(TorrentSummary.getFileOrFolder(s), function (err) {
s.torrentKey, if (err) {
TorrentSummary.getTorrentID(s), s.error = 'path-missing'
s.path, return
s.fileModtimes, }
s.selections) ipcRenderer.send('wt-start-torrenting',
s.torrentKey,
TorrentSummary.getTorrentID(s),
s.path,
s.fileModtimes,
s.selections)
})
} }
// TODO: use torrentKey, not infoHash // TODO: use torrentKey, not infoHash
@@ -98,7 +102,7 @@ module.exports = class TorrentListController {
var torrentSummary = TorrentSummary.getByKey(this.state, infoHash) var torrentSummary = TorrentSummary.getByKey(this.state, infoHash)
if (torrentSummary.status === 'paused') { if (torrentSummary.status === 'paused') {
torrentSummary.status = 'new' torrentSummary.status = 'new'
this.startTorrentingSummary(torrentSummary) this.startTorrentingSummary(torrentSummary.torrentKey)
sound.play('ENABLE') sound.play('ENABLE')
} else { } else {
torrentSummary.status = 'paused' torrentSummary.status = 'paused'

View File

@@ -200,6 +200,9 @@ function save (state, cb) {
if (key === 'playStatus') { if (key === 'playStatus') {
continue // Don't save whether a torrent is playing / pending continue // Don't save whether a torrent is playing / pending
} }
if (key === 'error') {
continue // Don't save error states
}
torrent[key] = x[key] torrent[key] = x[key]
} }
return torrent return torrent

View File

@@ -75,15 +75,15 @@ function onState (err, _state) {
} }
}) })
// Restart everything we were torrenting last time the app ran
resumeTorrents()
// Calling update() updates the UI given the current state // Calling update() updates the UI given the current state
// Do this at least once a second to give every file in every torrentSummary // Do this at least once a second to give every file in every torrentSummary
// a progress bar and to keep the cursor in sync when playing a video // a progress bar and to keep the cursor in sync when playing a video
setInterval(update, 1000) setInterval(update, 1000)
app = ReactDOM.render(<App state={state} />, document.querySelector('#body')) app = ReactDOM.render(<App state={state} />, document.querySelector('#body'))
// Restart everything we were torrenting last time the app ran
resumeTorrents()
// Lazy-load other stuff, like the AppleTV module, later to keep startup fast // Lazy-load other stuff, like the AppleTV module, later to keep startup fast
window.setTimeout(delayedInit, config.DELAYED_INIT) window.setTimeout(delayedInit, config.DELAYED_INIT)
@@ -179,8 +179,7 @@ const dispatchHandlers = {
'deleteTorrent': (infoHash, deleteData) => controllers.torrentList.deleteTorrent(infoHash, deleteData), 'deleteTorrent': (infoHash, deleteData) => controllers.torrentList.deleteTorrent(infoHash, deleteData),
'toggleSelectTorrent': (infoHash) => controllers.torrentList.toggleSelectTorrent(infoHash), 'toggleSelectTorrent': (infoHash) => controllers.torrentList.toggleSelectTorrent(infoHash),
'openTorrentContextMenu': (infoHash) => controllers.torrentList.openTorrentContextMenu(infoHash), 'openTorrentContextMenu': (infoHash) => controllers.torrentList.openTorrentContextMenu(infoHash),
'startTorrentingSummary': (torrentSummary) => 'startTorrentingSummary': (torrentKey) => controllers.torrentList.startTorrentingSummary(torrentKey),
controllers.torrentList.startTorrentingSummary(torrentSummary),
// Playback // Playback
'playFile': (infoHash, index) => controllers.playback.playFile(infoHash, index), 'playFile': (infoHash, index) => controllers.playback.playFile(infoHash, index),
@@ -318,8 +317,14 @@ function escapeBack () {
// Starts all torrents that aren't paused on program startup // Starts all torrents that aren't paused on program startup
function resumeTorrents () { function resumeTorrents () {
state.saved.torrents state.saved.torrents
.filter((torrentSummary) => torrentSummary.status !== 'paused') .map((torrentSummary) => {
.forEach((torrentSummary) => controllers.torrentList.startTorrentingSummary(torrentSummary)) // Torrent keys are ephemeral, reassigned each time the app runs.
// On startup, give all torrents a key, even the ones that are paused.
torrentSummary.torrentKey = state.nextTorrentKey++
return torrentSummary
})
.filter((s) => s.status !== 'paused')
.forEach((s) => controllers.torrentList.startTorrentingSummary(s.torrentKey))
} }
// Set window dimensions to match video dimensions or fill the screen // Set window dimensions to match video dimensions or fill the screen
@@ -440,7 +445,6 @@ function onFullscreenChanged (e, isFullScreen) {
} }
function checkDownloadPath () { function checkDownloadPath () {
state.downloadPathStatus = undefined
fs.stat(state.saved.prefs.downloadPath, function (err, stat) { fs.stat(state.saved.prefs.downloadPath, function (err, stat) {
if (err) { if (err) {
state.downloadPathStatus = 'missing' state.downloadPathStatus = 'missing'

View File

@@ -9,31 +9,27 @@ module.exports = class TorrentList extends React.Component {
render () { render () {
var state = this.props.state var state = this.props.state
var contents var contents = []
if (!state.downloadPathStatus) { if (state.downloadPathStatus === 'missing') {
contents = '' contents.push(
} else if (state.downloadPathStatus === 'missing') { <div key='torrent-missing-path'>
contents = (
<div>
<p>Download path missing: {state.saved.prefs.downloadPath}</p> <p>Download path missing: {state.saved.prefs.downloadPath}</p>
<p>Check that all drives are connected?</p> <p>Check that all drives are connected?</p>
<p>Alternatively, choose a new download path in <p>Alternatively, choose a new download path
<a href='#' onClick={dispatcher('preferences')}>Preferences</a> in <a href='#' onClick={dispatcher('preferences')}>Preferences</a>
</p> </p>
</div> </div>
) )
} else if (state.downloadPathStatus === 'ok') {
contents = state.saved.torrents.map(
(torrentSummary) => this.renderTorrent(torrentSummary)
)
contents.push(
<div key='torrent-placeholder' className='torrent-placeholder'>
<span className='ellipsis'>Drop a torrent file here or paste a magnet link</span>
</div>
)
} else {
throw new Error('Unhandled downloadPathStatus ' + state.downloadPathStatus)
} }
var torrentElems = state.saved.torrents.map(
(torrentSummary) => this.renderTorrent(torrentSummary)
)
contents.push(...torrentElems)
contents.push(
<div key='torrent-placeholder' className='torrent-placeholder'>
<span className='ellipsis'>Drop a torrent file here or paste a magnet link</span>
</div>
)
return ( return (
<div key='torrent-list' className='torrent-list'> <div key='torrent-list' className='torrent-list'>
@@ -64,7 +60,7 @@ module.exports = class TorrentList extends React.Component {
if (torrentSummary.playStatus) classes.push(torrentSummary.playStatus) if (torrentSummary.playStatus) classes.push(torrentSummary.playStatus)
if (isSelected) classes.push('selected') if (isSelected) classes.push('selected')
if (!infoHash) classes.push('disabled') if (!infoHash) classes.push('disabled')
if (torrentSummary.torrrentKey) console.error('Missing torrentKey', torrentSummary) if (!torrentSummary.torrentKey) throw new Error('Missing torrentKey')
return ( return (
<div <div
key={torrentSummary.torrentKey} key={torrentSummary.torrentKey}
@@ -88,8 +84,14 @@ module.exports = class TorrentList extends React.Component {
// If it's downloading/seeding then show progress info // If it's downloading/seeding then show progress info
var prog = torrentSummary.progress var prog = torrentSummary.progress
if (torrentSummary.status !== 'paused' && prog) { if (torrentSummary.error) {
elements.push(( elements.push(
<div key='progress-info' className='ellipsis'>
{getErrorMessage(torrentSummary)}
</div>
)
} else if (torrentSummary.status !== 'paused' && prog) {
elements.push(
<div key='progress-info' className='ellipsis'> <div key='progress-info' className='ellipsis'>
{renderPercentProgress()} {renderPercentProgress()}
{renderTotalProgress()} {renderTotalProgress()}
@@ -98,7 +100,7 @@ module.exports = class TorrentList extends React.Component {
{renderUploadSpeed()} {renderUploadSpeed()}
{renderEta()} {renderEta()}
</div> </div>
)) )
} }
return (<div key='metadata' className='metadata'>{elements}</div>) return (<div key='metadata' className='metadata'>{elements}</div>)
@@ -195,8 +197,9 @@ module.exports = class TorrentList extends React.Component {
} }
// Only show the play button for torrents that contain playable media // Only show the play button for torrents that contain playable media
var playButton var playButton, downloadButton
if (TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) { var noErrors = !torrentSummary.error
if (noErrors && TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) {
playButton = ( playButton = (
<i <i
key='play-button' key='play-button'
@@ -207,11 +210,8 @@ module.exports = class TorrentList extends React.Component {
</i> </i>
) )
} }
if (noErrors) {
return ( downloadButton = (
<div key='buttons' className='buttons'>
{positionElem}
{playButton}
<i <i
key='download-button' key='download-button'
className={'button-round icon download ' + torrentSummary.status} className={'button-round icon download ' + torrentSummary.status}
@@ -219,6 +219,14 @@ module.exports = class TorrentList extends React.Component {
onClick={dispatcher('toggleTorrent', infoHash)}> onClick={dispatcher('toggleTorrent', infoHash)}>
{downloadIcon} {downloadIcon}
</i> </i>
)
}
return (
<div key='buttons' className='buttons'>
{positionElem}
{playButton}
{downloadButton}
<i <i
key='delete-button' key='delete-button'
className='icon delete' className='icon delete'
@@ -233,12 +241,26 @@ module.exports = class TorrentList extends React.Component {
// Show files, per-file download status and play buttons, and so on // Show files, per-file download status and play buttons, and so on
renderTorrentDetails (torrentSummary) { renderTorrentDetails (torrentSummary) {
var filesElement var filesElement
if (!torrentSummary.files) { if (torrentSummary.error || !torrentSummary.files) {
// We don't know what files this torrent contains var message = ''
var message = torrentSummary.status === 'paused' if (torrentSummary.error === 'path-missing') {
? 'Failed to load torrent info. Click the download button to try again...' // Special case error: this torrent's download dir or file is missing
: 'Downloading torrent info...' message = 'Missing path: ' + TorrentSummary.getFileOrFolder(torrentSummary)
filesElement = (<div key='files' className='files warning'>{message}</div>) } else if (torrentSummary.error) {
// General error for this torrent: just show the message
message = torrentSummary.error.message || torrentSummary.error
} else if (torrentSummary.status === 'paused') {
// No file info, no infohash, and we're not trying to download from the DHT
message = 'Failed to load torrent info. Click the download button to try again...'
} else {
// No file info, no infohash, trying to load from the DHT
message = 'Downloading torrent info...'
}
filesElement = (
<div key='files' className='files warning'>
{message}
</div>
)
} else { } else {
// We do know the files. List them and show download stats for each one // We do know the files. List them and show download stats for each one
var fileRows = torrentSummary.files var fileRows = torrentSummary.files
@@ -349,3 +371,16 @@ module.exports = class TorrentList extends React.Component {
) )
} }
} }
function getErrorMessage (torrentSummary) {
var err = torrentSummary.error
if (err === 'path-missing') {
return (
<span>
Path missing.<br />
Fix and restart the app, or delete the torrent.
</span>
)
}
return 'Error'
}

View File

@@ -554,8 +554,14 @@ input[type='text'] {
/* /*
* TORRENT LIST: ERRORS * TORRENT LIST: ERRORS
*/ */
.torrent-list p { .torrent-list p {
padding: 5px 20px; margin: 10px 20px;
}
.torrent-list a {
color: #99f;
text-decoration: none;
} }
/* /*