Skip to content

Commit 3401be8

Browse files
committed
wip: Improve downloader more
1 parent 2309cf4 commit 3401be8

9 files changed

Lines changed: 171 additions & 78 deletions

File tree

‎src/back/Downloader.ts‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,15 @@ export class Downloader extends WrappedEventEmitter {
106106
}
107107

108108
public getTotal(): number {
109-
return Object.values(this.tasks).length;
109+
return this.total;
110+
}
111+
112+
public getDone(): number {
113+
return this.done;
114+
}
115+
116+
public getFailures(): number {
117+
return this.failures;
110118
}
111119

112120
public getTasks(): Record<string, DownloadTask> {
@@ -177,7 +185,6 @@ export class Downloader extends WrappedEventEmitter {
177185

178186
private assignTaskToIdleWorker(task: DownloadTask): void {
179187
if (this.status === 'running' && this.idleWorkers.length > 0) {
180-
log.debug('Downloads', 'Starting task');
181188
const worker = this.idleWorkers.shift(); // Get the first idle worker
182189
if (worker) {
183190
task.errors = [];
@@ -195,11 +202,9 @@ export class Downloader extends WrappedEventEmitter {
195202
if (this.status === 'running') {
196203
this.tasks[gameId].status = status;
197204
this.tasks[gameId].errors = errors;
198-
log.info('Downloader', `Task: ${gameId} - Status: ${status}`);
199205
this.emit('taskChange', this.tasks[gameId]);
200206
} else {
201207
this.tasks[gameId].status = 'waiting';
202-
log.info('Downloader', `Task: ${gameId} - Status: waiting`);
203208
this.emit('taskChange', this.tasks[gameId]);
204209
}
205210
}

‎src/back/GameDataProvider.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ export const GameDataProviderRaw: GameDataProvider = {
1818

1919
const filename = getGameDataFilename(gameData);
2020
const fullUrl = new URL(filename, source.arguments).href;
21-
console.log(fullUrl);
2221
const tempPath = path.join(dataPacksFolderPath, `${filename}.temp`);
2322
await downloadFile(axios, fullUrl, tempPath, abortSignal, onProgress, onDetails);
2423
// Check hash of download
@@ -28,12 +27,10 @@ export const GameDataProviderRaw: GameDataProvider = {
2827
await new Promise<void>((resolve, reject) => {
2928
stream.on('end', async () => {
3029
const sha256 = hash.digest('hex').toUpperCase();
31-
console.log(`hash ${sha256}`);
3230
if (sha256.toLowerCase() !== gameData.sha256.toLowerCase()) {
3331
reject('Hash of download does not match! Download aborted.\n (It may be a corrupted download, try again)');
3432
} else {
3533
try {
36-
log.debug('Game Launcher', 'Validated game data, importing to games folder');
3734
await importGameDataSkipHash(gameData.gameId, tempPath, dataPacksFolderPath, sha256, gameData)
3835
.catch((err) => {
3936
console.log(`Error importing game data ${err}`);

‎src/back/responses.ts‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ export function registerRequestCallbacks(state: BackState, init: () => Promise<v
209209
state: state.downloader.status,
210210
tasks: state.downloader.getTasks(),
211211
workers: state.downloader.getWorkerStates(),
212+
total: state.downloader.getTotal(),
213+
done: state.downloader.getDone(),
214+
failures: state.downloader.getFailures()
212215
};
213216
});
214217

@@ -2060,6 +2063,62 @@ export function registerRequestCallbacks(state: BackState, init: () => Promise<v
20602063
}
20612064
});
20622065

2066+
state.socketServer.register(BackIn.DOWNLOAD_SEARCH_RESULTS, async (event, query) => {
2067+
const openDialog = state.socketServer.showMessageBoxBack(state, event.client);
2068+
const dialogId = openDialog({
2069+
largeMessage: true,
2070+
message: 'Calculating download size of search results...',
2071+
buttons: []
2072+
});
2073+
2074+
const search = fpDatabase.parseUserSearchInput('').search;
2075+
search.limit = 9999999999;
2076+
search.filter = query.filter;
2077+
search.slim = true;
2078+
search.loadRelations.gameData = true;
2079+
const games = await fpDatabase.searchGames(search);
2080+
console.log('Found ' + games.length + ' games');
2081+
2082+
let totalSize = 0;
2083+
for (const game of games) {
2084+
if (game.gameData) {
2085+
for (const gd of game.gameData) {
2086+
if (!gd.presentOnDisk) {
2087+
totalSize += gd.size;
2088+
}
2089+
}
2090+
}
2091+
}
2092+
2093+
state.socketServer.broadcast(BackOut.CANCEL_DIALOG, dialogId);
2094+
console.log('size ' + totalSize);
2095+
2096+
if (totalSize > 0) {
2097+
// Estimated size larger than 0B, ask the user before downloading
2098+
const humanReadableSize = sizeToString(totalSize);
2099+
2100+
const dialogId = state.socketServer.showMessageBoxBack(state, event.client)({
2101+
message: `Downloading these search results requires approximately ${humanReadableSize} of additional space. Continue?`,
2102+
buttons: [state.languageContainer.misc.yes, state.languageContainer.misc.no],
2103+
cancelId: 1,
2104+
largeMessage: true,
2105+
});
2106+
const result = (await awaitDialog(state, dialogId)).buttonIdx;
2107+
2108+
if (result === 1) {
2109+
log.debug('Downloads', 'User aborted search results download at size prompt');
2110+
return;
2111+
}
2112+
} else {
2113+
return;
2114+
}
2115+
2116+
state.downloader.addTasks(games);
2117+
if (state.downloader.status !== 'running') {
2118+
state.downloader.start();
2119+
}
2120+
});
2121+
20632122
state.socketServer.register(BackIn.EXPORT_META_EDIT, async (event, id, properties) => {
20642123
const game = await fpDatabase.findGame(id);
20652124
if (game) {

‎src/renderer/components/SearchBar.tsx‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from '@renderer/hooks/useAppSelector';
33
import { useLocalization } from '@renderer/hooks/useLocalization';
44
import { forceSearch, setAdvancedFilter, setExpanded, setExtOrder, setOrderBy, setOrderReverse, setSearchText } from '@renderer/store/search/slice';
55
import { getPlatformIconURL } from '@renderer/Util';
6+
import { BackIn } from '@shared/back/types';
67
import { getDefaultAdvancedFilter } from '@shared/search/util';
78
import { formatString } from '@shared/utils/StringFormatter';
89
import { AdvancedFilter, AdvancedFilterToggle, Tag } from 'flashpoint-launcher';
@@ -28,6 +29,7 @@ export function SearchBar() {
2829
const dispatch = useAppDispatch();
2930
const strings = useLocalization();
3031
const viewName = useViewName();
32+
const searchFilter = useAppSelector(state => state.search.views[viewName].searchFilter);
3133
const advancedFilter = useAppSelector(state => state.search.views[viewName].advancedFilter);
3234
const insidePlaylist = useAppSelector(state => state.search.views[viewName].selectedPlaylist !== undefined);
3335
const extOrder = useAppSelector(state => state.search.views[viewName].extOrder);
@@ -537,12 +539,18 @@ export function SearchBar() {
537539
<DynamicComponent key={name} name={name} props={searchComponentProps}/>
538540
);
539541
}) }
542+
<SimpleButton
543+
value="Download All Results"
544+
onClick={() => {
545+
window.Shared.back.send(BackIn.DOWNLOAD_SEARCH_RESULTS, searchFilter);
546+
}}/>
540547
</div>
541548
)}
542549
</div>
543550
);
544551
}
545552

553+
546554
type ThreeStateCheckboxProps = {
547555
value?: boolean;
548556
title: string;

‎src/renderer/components/pages/Downloads.tsx‎

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useAppDispatch, useAppSelector } from '@renderer/hooks/useAppSelector';
22
import { setStatus } from '@renderer/store/downloads/slice';
3-
import { DownloaderStatus } from 'flashpoint-launcher';
3+
import { DownloaderStatus, DownloadWorkerState } from 'flashpoint-launcher';
44
import { SimpleButton } from '../SimpleButton';
55

66
export function DownloadsPage() {
@@ -33,25 +33,38 @@ export function DownloadsPage() {
3333
onClick={onAddMissingContent}/> */}
3434
</div>
3535
</div>
36-
{downloaderState.workers.map((worker) => (
37-
<div key={worker.id} className='downloads-worker'>
38-
<div className='downloads-worker__header'>
39-
<span className='downloads-worker__id'>{`Worker ${worker.id}`}</span>
40-
<span className='downloads-worker__task'>{worker.taskText || 'Idle'}</span>
41-
</div>
36+
<div className='downloads-page__workers'>
37+
{downloaderState.workers.map((worker) => (
38+
<DownloadWorkerRow key={worker.id} worker={worker}/>
39+
))}
40+
</div>
41+
</div>
42+
);
43+
}
4244

43-
{/* Step progress bar */}
44-
<div className='downloads-worker__bar-container'>
45-
<div
46-
className='downloads-worker__bar-fill'
47-
style={{ width: `${Math.round(worker.stepProgress * 100)}%` }}
48-
/>
49-
</div>
50-
<div className='downloads-worker__percent'>
51-
{`${Math.round(worker.stepProgress * 100)}%`}
52-
</div>
53-
</div>
54-
))}
45+
type DownloadWorkerProps = {
46+
worker: DownloadWorkerState
47+
}
48+
49+
function DownloadWorkerRow({ worker }: DownloadWorkerProps) {
50+
return (
51+
<div className='downloads-worker'>
52+
<div className='downloads-worker__header'>
53+
<span className='downloads-worker__id'>{`Worker ${worker.id}`}</span>
54+
<span className='downloads-worker__task'>{worker.taskText || 'Idle'}</span>
55+
</div>
56+
57+
{/* Step progress bar */}
58+
<div className='downloads-worker__bar-container'>
59+
<div
60+
className='downloads-worker__bar-fill'
61+
style={{ width: `${Math.round(worker.stepProgress * 100)}%` }}
62+
/>
63+
</div>
64+
<div className='downloads-worker__percent'>
65+
{`${Math.round(worker.stepProgress * 100)}%`}
66+
</div>
5567
</div>
5668
);
5769
}
70+

‎src/shared/Util.ts‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,11 @@ export function createErrorProxy(title: string): any {
386386
});
387387
}
388388

389+
const KB = 1024;
390+
const MB = KB * 1024;
391+
const GB = MB * 1024;
392+
const TB = GB * 1024;
393+
389394
/**
390395
* Convert a size (in bytes) to a more human readable format.
391396
*
@@ -394,10 +399,11 @@ export function createErrorProxy(title: string): any {
394399
* @returns Size, but in a more human readable format.
395400
*/
396401
export function sizeToString(size: number, precision = 3): string {
397-
if (size < 1000) { return `${size}B`; }
398-
if (size < 1000000) { return `${(size / 1000).toPrecision(precision)}KB`; }
399-
if (size < 1000000000) { return `${(size / 1000000).toPrecision(precision)}MB`; }
400-
return `${(size / 1000000000).toPrecision(precision)}GB`;
402+
if (size < KB) { return `${size}B`; }
403+
if (size < MB) { return `${(size / KB).toPrecision(precision)}KB`; }
404+
if (size < GB) { return `${(size / MB).toPrecision(precision)}MB`; }
405+
if (size < TB) { return `${(size / GB).toPrecision(precision)}GB`; }
406+
return `${(size / TB).toPrecision(precision)}TB`;
401407
}
402408

403409
/**

‎src/shared/back/types.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export enum BackIn {
109109

110110
// Web?
111111
DOWNLOAD_PLAYLIST,
112+
DOWNLOAD_SEARCH_RESULTS,
112113

113114
// Tag funcs
114115
GET_OR_CREATE_TAG,
@@ -395,6 +396,7 @@ export type BackInTemplate = SocketTemplate<BackIn, {
395396

396397
// Web?
397398
[BackIn.DOWNLOAD_PLAYLIST]: (url: string) => Playlist;
399+
[BackIn.DOWNLOAD_SEARCH_RESULTS]: (filter: SearchQuery) => void;
398400

399401
// Tag funcs
400402
[BackIn.GET_OR_CREATE_TAG]: (tagName: string, tagCategory?: string) => Tag | null;

‎static/window/styles/core.css‎

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3797,54 +3797,6 @@ body {
37973797
padding: 12px;
37983798
}
37993799

3800-
.downloads-worker {
3801-
background: #1e1e1e;
3802-
border: 1px solid #333;
3803-
border-radius: 6px;
3804-
padding: 10px 14px;
3805-
}
3806-
3807-
.downloads-worker__header {
3808-
display: flex;
3809-
justify-content: space-between;
3810-
margin-bottom: 6px;
3811-
font-weight: bold;
3812-
}
3813-
3814-
.downloads-worker__task {
3815-
color: #aaa;
3816-
font-size: 0.85em;
3817-
font-weight: normal;
3818-
}
3819-
3820-
.downloads-worker__steps {
3821-
font-size: 0.8em;
3822-
color: #ccc;
3823-
margin-bottom: 6px;
3824-
}
3825-
3826-
.downloads-worker__bar-container {
3827-
height: 8px;
3828-
background: #333;
3829-
border-radius: 4px;
3830-
overflow: hidden;
3831-
}
3832-
3833-
.downloads-worker__bar-fill {
3834-
height: 100%;
3835-
background: #4caf50;
3836-
transition: width 0.2s ease;
3837-
}
3838-
3839-
.downloads-worker__percent {
3840-
font-size: 0.75em;
3841-
color: #aaa;
3842-
text-align: right;
3843-
margin-top: 2px;
3844-
}
3845-
3846-
3847-
38483800
/* --- Menu --- */
38493801
.context-menu {
38503802
z-index: 999999;

‎static/window/styles/fancy.css‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,3 +1625,54 @@ body {
16251625
overflow: auto;
16261626
white-space: pre-wrap;
16271627
}
1628+
1629+
1630+
/* ------ Downloads ------ */
1631+
.downloads-worker {
1632+
background-color: var(--layout__quaternary-background);
1633+
border-color: var(--layout__primary-outline-color);
1634+
border-style: solid;
1635+
border-width: 1px;
1636+
border-radius: 6px;
1637+
padding: 10px 14px;
1638+
box-shadow: 0px 2px 4px 0px #09090a;
1639+
}
1640+
1641+
.downloads-worker__header {
1642+
display: flex;
1643+
justify-content: space-between;
1644+
margin-bottom: 6px;
1645+
font-weight: bold;
1646+
}
1647+
1648+
.downloads-worker__task {
1649+
color: var(--layout__secondary-text-color);
1650+
font-size: 0.85em;
1651+
font-weight: normal;
1652+
}
1653+
1654+
.downloads-worker__steps {
1655+
font-size: 0.8em;
1656+
color: var(--layout__primary-text-color);
1657+
margin-bottom: 6px;
1658+
}
1659+
1660+
.downloads-worker__bar-container {
1661+
height: 8px;
1662+
background-color: var(--layout__slider-background);
1663+
border-radius: 4px;
1664+
overflow: hidden;
1665+
}
1666+
1667+
.downloads-worker__bar-fill {
1668+
height: 100%;
1669+
background-color: var(--layout__confirm-color);
1670+
transition: width 0.2s ease;
1671+
}
1672+
1673+
.downloads-worker__percent {
1674+
font-size: 0.75em;
1675+
color: var(--layout__secondary-text-color);
1676+
text-align: right;
1677+
margin-top: 2px;
1678+
}

0 commit comments

Comments
 (0)