Check path for each torrent
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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'
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user