fix(download): show failed downloads + don't crash when one dl fails (#474)

This commit is contained in:
Yannis Petitot
2022-03-26 11:30:34 +01:00
committed by GitHub
parent 735a76e8a6
commit f7c0bde69e
6 changed files with 70 additions and 18 deletions
+40 -2
View File
@@ -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;
+11 -4
View File
@@ -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
/>
)}
+5 -6
View File
@@ -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();
}
+2 -2
View File
@@ -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 {
+1
View File
@@ -27,6 +27,7 @@ export function makeServer() {
});
this.passthrough('https://osu.ppy.sh/api/**');
this.passthrough('https://beatconnect.io/api/beatmap/**');
},
});
}