fix(download): show failed downloads + don't crash when one dl fails (#474)
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import React, { useContext, createContext, useState, useRef } from 'react';
|
||||
import { remote } from 'electron';
|
||||
import { connect } from 'react-redux';
|
||||
import ElectronLog from 'electron-log';
|
||||
|
||||
import { useDownloadHistory } from '../HistoryProvider';
|
||||
import { downloadMany, download, setSavePath, cancel, cancelCurrent, pause, pauseResume, clearQueue } from './ipc/send';
|
||||
@@ -22,6 +23,8 @@ const DownloadManagerProvider = props => {
|
||||
queue: [],
|
||||
currentDownload: { beatmapSetId: null, progressPercent: null, downloadSpeed: null, status: null },
|
||||
overallProgress: 0,
|
||||
failedDownloads: [],
|
||||
failedIds: [],
|
||||
});
|
||||
const stateRef = useRef(state);
|
||||
stateRef.current = state;
|
||||
@@ -53,6 +56,10 @@ const DownloadManagerProvider = props => {
|
||||
taskManager.add({ name: 'download', status: 'running', description: 'initializing', section: 'Downloads' });
|
||||
}
|
||||
|
||||
const failedItems = stateRef.current.beatmapSetsInQueue
|
||||
.filter(item => stateRef.current.failedIds.includes(item.id))
|
||||
.map(item => ({ ...item, failed: true }));
|
||||
|
||||
const beatmapSetsInQueue = stateRef.current.beatmapSetsInQueue.filter(item =>
|
||||
queue.some(itm => itm.beatmapSetId === item.id),
|
||||
);
|
||||
@@ -68,7 +75,13 @@ const DownloadManagerProvider = props => {
|
||||
beatmapSetsInQueue.push(...missingItems);
|
||||
}
|
||||
|
||||
setState(prevState => ({ ...prevState, queue, beatmapSetsInQueue }));
|
||||
setState(prevState => ({
|
||||
...prevState,
|
||||
queue,
|
||||
beatmapSetsInQueue,
|
||||
failedIds: [],
|
||||
failedDownloads: [...stateRef.current.failedDownloads, ...failedItems],
|
||||
}));
|
||||
};
|
||||
|
||||
const updateCurrentDowload = item => {
|
||||
@@ -132,12 +145,36 @@ const DownloadManagerProvider = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const discardFailedDownload = beatmapSetId => {
|
||||
setState(oldState => ({
|
||||
...oldState,
|
||||
failedDownloads: oldState.failedDownloads.filter(failedItm => failedItm.id !== beatmapSetId),
|
||||
}));
|
||||
};
|
||||
|
||||
const downloadFail = ({ beatmapSetId }) => {
|
||||
ElectronLog.error('Download manager: download failed for beatmapsset = ', beatmapSetId);
|
||||
setState(oldState => ({
|
||||
...oldState,
|
||||
failedIds: [...oldState.failedIds, beatmapSetId],
|
||||
}));
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
clearQueue();
|
||||
setState(oldState => ({
|
||||
...oldState,
|
||||
failedDownloads: [],
|
||||
}));
|
||||
};
|
||||
|
||||
useDownloadMangerIPC({
|
||||
onDownloadManagerReady: initSaveLocation,
|
||||
onDownloadProgress: updateCurrentDowload,
|
||||
onDownloadPaused: downloadPaused,
|
||||
onQueueUpdate: updateQueue,
|
||||
onDownloadSucceed: downloadSucceeded,
|
||||
onDownloadFail: downloadFail,
|
||||
});
|
||||
|
||||
const value = {
|
||||
@@ -146,10 +183,11 @@ const DownloadManagerProvider = props => {
|
||||
pauseResumeDownload: pauseResume,
|
||||
cancelDownload: cancelCurrent,
|
||||
removeItemfromQueue: cancel,
|
||||
clear: clearQueue,
|
||||
clear,
|
||||
push,
|
||||
pushMany,
|
||||
setPath,
|
||||
discardFailedDownload,
|
||||
};
|
||||
const { children } = props;
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ import Header from './components/Header';
|
||||
import Empty from './components/Empty';
|
||||
|
||||
const Downloads = ({ setHeaderContent, windowSize }) => {
|
||||
const { removeItemfromQueue, beatmapSetsInQueue } = useDownloadQueue();
|
||||
const { removeItemfromQueue, beatmapSetsInQueue, failedDownloads, discardFailedDownload } = useDownloadQueue();
|
||||
|
||||
const items = [...beatmapSetsInQueue, ...failedDownloads];
|
||||
|
||||
useEffect(() => {
|
||||
setHeaderContent(<Header />);
|
||||
@@ -19,13 +21,18 @@ const Downloads = ({ setHeaderContent, windowSize }) => {
|
||||
const listHeight = windowSize.height;
|
||||
return (
|
||||
<div className="menuContainer Downloads" style={{ transition: 'background 0ms', overflow: 'hidden' }}>
|
||||
{beatmapSetsInQueue.length ? (
|
||||
{items.length ? (
|
||||
<List
|
||||
height={listHeight}
|
||||
itemCount={beatmapSetsInQueue.length}
|
||||
itemCount={items.length}
|
||||
itemSize={50}
|
||||
width={listWidth}
|
||||
itemData={{ items: beatmapSetsInQueue, downloadSection: true, removeItemfromQueue }}
|
||||
itemData={{
|
||||
items,
|
||||
itemMode: 'download',
|
||||
removeItemfromQueue,
|
||||
discardFailedDownload,
|
||||
}}
|
||||
>
|
||||
{BeatmapListItem}
|
||||
</List>
|
||||
|
||||
@@ -25,6 +25,7 @@ const useStyle = createUseStyles({
|
||||
width: 'calc(100% - 3rem)',
|
||||
borderRadius: '5px',
|
||||
paddingRight: '10px',
|
||||
backgroundColor: ({failed}) => failed ? 'rgba(255, 0,0,.2)' : 'none',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||
},
|
||||
@@ -99,12 +100,12 @@ const useStyle = createUseStyles({
|
||||
});
|
||||
|
||||
const BeatmapListItem = ({ index, style, data }) => {
|
||||
const { removeItemfromQueue = () => {}, items, itemMode = 'pack' || 'download' || 'library', collectionName } = data;
|
||||
const { removeItemfromQueue = () => {}, items, itemMode = 'pack' || 'download' || 'library', collectionName, discardFailedDownload = () => {} } = data;
|
||||
const isPackMode = itemMode === 'pack';
|
||||
const isDownloadMode = itemMode === 'download';
|
||||
const isLibraryMode = itemMode === 'library';
|
||||
const item = items[index];
|
||||
const { id, title, artist, creator, songDuration } = item;
|
||||
const { id, title, artist, creator, songDuration, failed } = item;
|
||||
|
||||
const osuPath = useSelector(getOsuPath);
|
||||
const [artworkURL, setArtWorkURL] = useState('');
|
||||
@@ -118,7 +119,7 @@ const BeatmapListItem = ({ index, style, data }) => {
|
||||
|
||||
const downloadProgress = useCurrentDownloadItem(id);
|
||||
|
||||
const classes = useStyle({ downloadProgress: downloadProgress === -1 && !isDownloadMode ? 0.5 : downloadProgress });
|
||||
const classes = useStyle({ downloadProgress: downloadProgress === -1 && !isDownloadMode ? 0.5 : downloadProgress, failed });
|
||||
|
||||
const { status } = currentDownload || {};
|
||||
const isDownloading = downloadProgress >= 0;
|
||||
@@ -132,6 +133,12 @@ const BeatmapListItem = ({ index, style, data }) => {
|
||||
if (isLibraryMode) playPreview();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
if (isDownloading) cancelDownload()
|
||||
else if (failed) discardFailedDownload(items[index].id)
|
||||
else removeItemfromQueue(items[index].id)
|
||||
}
|
||||
|
||||
const wrapperStyle = {
|
||||
backgroundColor: isSelected && 'rgba(255,255,255,.05)',
|
||||
};
|
||||
@@ -197,7 +204,7 @@ const BeatmapListItem = ({ index, style, data }) => {
|
||||
{isDownloadMode && (
|
||||
<NewButton
|
||||
iconName="Cancel"
|
||||
onClick={() => (isDownloading ? cancelDownload() : removeItemfromQueue(items[index].id))}
|
||||
onClick={handleCancel}
|
||||
borderless
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -104,11 +104,8 @@ class BeatmapDownloader {
|
||||
this.currentDownload.item = item;
|
||||
}
|
||||
|
||||
clearCurrentDownload(skip) {
|
||||
const downloadState = this.currentDownload.item && this.currentDownload.item.getState();
|
||||
if ((downloadState === 'progressing' || downloadState === 'interrupted') && !skip) {
|
||||
throw new Error('downloadNotStopped');
|
||||
}
|
||||
clearCurrentDownload() {
|
||||
this.currentDownload.item.cancel();
|
||||
this.deleteFromQueue(this.currentDownload.beatmapSetInfos);
|
||||
this.currentDownload = { item: null, beatmapSetInfos: { beatmapSetId: null, uniqId: null, beatmapSetInfos: null } };
|
||||
}
|
||||
@@ -192,7 +189,7 @@ class BeatmapDownloader {
|
||||
this.onCancel(item, beatmapSetId);
|
||||
break;
|
||||
default:
|
||||
this.onFailed(state, beatmapSetId);
|
||||
this.onFailed(undefined, state, beatmapSetId);
|
||||
warn(`unhandled download item state ${state}`);
|
||||
break;
|
||||
}
|
||||
@@ -278,6 +275,8 @@ class BeatmapDownloader {
|
||||
onFailed(_item, _state, beatmapSetId) {
|
||||
error('Download manager onFailed ', _item, _state, beatmapSetId);
|
||||
this.sendToWin('download-failed', { beatmapSetId });
|
||||
global.tracking.visitor.exception('Beatmap download failed');
|
||||
this.trackEvent('beatmapDownload', 'failed', this.currentDownload.beatmapSetInfos.beatmapSetId);
|
||||
this.clearCurrentDownload();
|
||||
this.executeQueue();
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ const main = async () => {
|
||||
});
|
||||
|
||||
// init ga tracking and set tracking methods on global
|
||||
const { trackEvent, trackNavigation } = makeTracker(mainWindow.webContents.session.getUserAgent());
|
||||
global.tracking = { trackEvent, trackNavigation };
|
||||
const { trackEvent, trackNavigation, visitor } = makeTracker(mainWindow.webContents.session.getUserAgent());
|
||||
global.tracking = { trackEvent, trackNavigation, visitor };
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
try {
|
||||
|
||||
@@ -27,6 +27,7 @@ export function makeServer() {
|
||||
});
|
||||
|
||||
this.passthrough('https://osu.ppy.sh/api/**');
|
||||
this.passthrough('https://beatconnect.io/api/beatmap/**');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user