mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Creating download retry button for recurring downloads.
This commit is contained in:
parent
6713b9e4c9
commit
9a3796bcd0
5 changed files with 95 additions and 8 deletions
|
|
@ -486,6 +486,10 @@ export async function killDownload(download_id) {
|
|||
return response;
|
||||
}
|
||||
|
||||
export async function restartDownload(download_id) {
|
||||
return await apiPost(`${API_URI}/download/${download_id}/restart`);
|
||||
}
|
||||
|
||||
export async function killDownloads() {
|
||||
let response = await apiPost(`${API_URI}/download/kill`);
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {clearCompletedDownloads, clearFailedDownloads, deleteDownload, killDownload, postDownload} from "../../api";
|
||||
import {clearCompletedDownloads, clearFailedDownloads, deleteDownload, killDownload, restartDownload} from "../../api";
|
||||
import {Link} from "react-router-dom";
|
||||
import {
|
||||
APIButton,
|
||||
|
|
@ -24,6 +24,7 @@ import {
|
|||
} from "semantic-ui-react";
|
||||
import {Button, Header, Modal, ModalActions, ModalContent, ModalHeader, Placeholder, Segment, Table} from "../Theme";
|
||||
import {useDownloads} from "../../hooks/customHooks";
|
||||
import {toast} from "react-semantic-toasts-2";
|
||||
|
||||
function ClearCompleteDownloads({callback}) {
|
||||
async function localClearDownloads() {
|
||||
|
|
@ -67,7 +68,7 @@ function ClearFailedDownloads({callback}) {
|
|||
</APIButton>
|
||||
}
|
||||
|
||||
class DownloadRow extends React.Component {
|
||||
class RecurringDownloadRow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
|
@ -86,6 +87,28 @@ class DownloadRow extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleRetry = async () => {
|
||||
const {id} = this.props;
|
||||
try {
|
||||
const response = await restartDownload(id);
|
||||
if (response.status !== 204) {
|
||||
throw Error('Unable to restart download');
|
||||
}
|
||||
} catch (e) {
|
||||
toast({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
description: 'Unable to start download',
|
||||
time: 5000,
|
||||
})
|
||||
throw e;
|
||||
} finally {
|
||||
if (this.props.fetchDownloads) {
|
||||
this.props.fetchDownloads();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let {url, frequency, last_successful_download, status, location, next_download, error} = this.props;
|
||||
const {errorModalOpen} = this.state;
|
||||
|
|
@ -121,6 +144,17 @@ class DownloadRow extends React.Component {
|
|||
/>
|
||||
</>;
|
||||
|
||||
const retryButton = <>
|
||||
<APIButton
|
||||
color='green'
|
||||
icon='redo'
|
||||
confirmContent='Are you sure you want to retry this download?'
|
||||
confirmButton='Retry'
|
||||
onClick={this.handleRetry}
|
||||
obeyWROLMode={true}
|
||||
/>
|
||||
</>;
|
||||
|
||||
// Show "now" if we have passed the next_download.
|
||||
let next = 'now';
|
||||
if (next_download) {
|
||||
|
|
@ -142,12 +176,13 @@ class DownloadRow extends React.Component {
|
|||
<TableCell>
|
||||
{error && errorModal}
|
||||
{deleteButton}
|
||||
{retryButton}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
}
|
||||
}
|
||||
|
||||
class StoppableRow extends React.Component {
|
||||
class OnceDownloadRow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
|
@ -163,8 +198,17 @@ class StoppableRow extends React.Component {
|
|||
};
|
||||
|
||||
handleStart = async () => {
|
||||
let downloader = this.props.downloader;
|
||||
await postDownload(`${this.props.url}`, downloader);
|
||||
try {
|
||||
await restartDownload(this.props.id);
|
||||
} catch (e) {
|
||||
toast({
|
||||
type: 'error',
|
||||
title: 'Error',
|
||||
description: 'Unable to start download',
|
||||
time: 5000,
|
||||
})
|
||||
throw e;
|
||||
}
|
||||
await this.props.fetchDownloads();
|
||||
};
|
||||
|
||||
|
|
@ -203,11 +247,12 @@ class StoppableRow extends React.Component {
|
|||
/>
|
||||
<APIButton
|
||||
color='green'
|
||||
icon='redo'
|
||||
confirmContent='Are you sure you want to restart this download?'
|
||||
confirmButton='Start'
|
||||
onClick={this.handleStart}
|
||||
obeyWROLMode={true}
|
||||
>Retry</APIButton>
|
||||
/>
|
||||
</TableCell>
|
||||
);
|
||||
} else if (status === 'complete' && location) {
|
||||
|
|
@ -256,7 +301,7 @@ export function OnceDownloadsTable({downloads, fetchDownloads}) {
|
|||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{downloads.map(i => <StoppableRow fetchDownloads={fetchDownloads} key={i.id} {...i}/>)}
|
||||
{downloads.map(i => <OnceDownloadRow fetchDownloads={fetchDownloads} key={i.id} {...i}/>)}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
|
|
@ -292,7 +337,7 @@ export function RecurringDownloadsTable({downloads, fetchDownloads}) {
|
|||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{downloads.map(i => <DownloadRow key={i.id} fetchDownloads={fetchDownloads} {...i}/>)}
|
||||
{downloads.map(i => <RecurringDownloadRow key={i.id} fetchDownloads={fetchDownloads} {...i}/>)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
} else if (downloads) {
|
||||
|
|
|
|||
|
|
@ -766,6 +766,17 @@ class DownloadManager:
|
|||
return True
|
||||
return False
|
||||
|
||||
@optional_session
|
||||
def restart_download(self, download_id: int, session: Session = None) -> Download:
|
||||
"""Renews a download and resets its download attempts."""
|
||||
download = self.get_download(session, id_=download_id)
|
||||
if not download:
|
||||
raise InvalidDownload(f'No download with id {download_id}')
|
||||
download.renew(reset_attempts=True)
|
||||
session.commit()
|
||||
|
||||
return download
|
||||
|
||||
def kill_download(self, download_id: int):
|
||||
"""Fail a Download. If it is pending, kill the Downloader so the download stops."""
|
||||
with get_db_session(commit=True) as session:
|
||||
|
|
|
|||
|
|
@ -251,6 +251,13 @@ async def post_download(_: Request, body: schema.DownloadRequest):
|
|||
return response.empty()
|
||||
|
||||
|
||||
@api_bp.post('/download/<download_id:int>/restart')
|
||||
@openapi.description('Restart a download.')
|
||||
async def restart_download(_: Request, download_id: int):
|
||||
download_manager.restart_download(download_id)
|
||||
return response.empty()
|
||||
|
||||
|
||||
@api_bp.get('/download')
|
||||
@openapi.description('Get all Downloads that need to be processed.')
|
||||
async def get_downloads(_: Request):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import asyncio
|
||||
import json
|
||||
from http import HTTPStatus
|
||||
from itertools import zip_longest
|
||||
|
|
@ -374,6 +375,25 @@ def test_get_downloaders(test_client):
|
|||
'No downloaders returned'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_restart_download(test_session, test_async_client, test_download_manager, test_downloader):
|
||||
"""A Download can be restarted."""
|
||||
# Create a download, fail it, it should be restarted.
|
||||
download = test_download_manager.create_download('https://example.com', test_downloader.name)
|
||||
download.fail()
|
||||
test_session.commit()
|
||||
assert test_session.query(Download).one().status == 'failed'
|
||||
|
||||
# Download is now "new" again.
|
||||
request, response = await test_async_client.post(f'/api/download/{download.id}/restart')
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
assert test_session.query(Download).one().status == 'new'
|
||||
|
||||
# Wait for the background download to fail. It should be deferred.
|
||||
await asyncio.sleep(0.5)
|
||||
assert test_session.query(Download).one().status == 'deferred'
|
||||
|
||||
|
||||
def test_get_global_statistics(test_session, test_client):
|
||||
request, response = test_client.get('/api/statistics')
|
||||
assert response.json['global_statistics']['db_size'] > 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue