New: Improve history details for release grabs

This commit is contained in:
Bogdan
2024-07-20 18:44:59 +03:00
parent c7dfde0ce9
commit 96f49da79e
10 changed files with 194 additions and 20 deletions
+129 -3
View File
@@ -3,6 +3,7 @@ import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList'; import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem'; import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import formatDateTime from 'Utilities/Date/formatDateTime';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './HistoryDetails.css'; import styles from './HistoryDetails.css';
@@ -10,7 +11,10 @@ function HistoryDetails(props) {
const { const {
indexer, indexer,
eventType, eventType,
data date,
data,
shortDateFormat,
timeFormat
} = props; } = props;
if (eventType === 'indexerQuery' || eventType === 'indexerRss') { if (eventType === 'indexerQuery' || eventType === 'indexerRss') {
@@ -22,7 +26,9 @@ function HistoryDetails(props) {
offset, offset,
source, source,
host, host,
url url,
elapsedTime,
cached
} = data; } = data;
return ( return (
@@ -104,6 +110,24 @@ function HistoryDetails(props) {
/> : /> :
null null
} }
{
elapsedTime ?
<DescriptionListItem
title={translate('ElapsedTime')}
data={`${elapsedTime}ms${cached === '1' ? ' (cached)' : null}`}
/> :
null
}
{
date ?
<DescriptionListItem
title={translate('Date')}
data={formatDateTime(date, shortDateFormat, timeFormat, { includeSeconds: true })}
/> :
null
}
</DescriptionList> </DescriptionList>
); );
} }
@@ -111,10 +135,19 @@ function HistoryDetails(props) {
if (eventType === 'releaseGrabbed') { if (eventType === 'releaseGrabbed') {
const { const {
source, source,
host,
grabTitle, grabTitle,
url url,
publishedDate,
infoUrl,
downloadClient,
downloadClientName,
elapsedTime,
grabMethod
} = data; } = data;
const downloadClientNameInfo = downloadClientName ?? downloadClient;
return ( return (
<DescriptionList> <DescriptionList>
{ {
@@ -135,6 +168,15 @@ function HistoryDetails(props) {
null null
} }
{
data ?
<DescriptionListItem
title={translate('Host')}
data={host}
/> :
null
}
{ {
data ? data ?
<DescriptionListItem <DescriptionListItem
@@ -144,6 +186,33 @@ function HistoryDetails(props) {
null null
} }
{
infoUrl ?
<DescriptionListItem
title={translate('InfoUrl')}
data={<Link to={infoUrl}>{infoUrl}</Link>}
/> :
null
}
{
publishedDate ?
<DescriptionListItem
title={translate('PublishedDate')}
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
/> :
null
}
{
downloadClientNameInfo ?
<DescriptionListItem
title={translate('DownloadClient')}
data={downloadClientNameInfo}
/> :
null
}
{ {
data ? data ?
<DescriptionListItem <DescriptionListItem
@@ -152,11 +221,40 @@ function HistoryDetails(props) {
/> : /> :
null null
} }
{
elapsedTime ?
<DescriptionListItem
title={translate('ElapsedTime')}
data={`${elapsedTime}ms`}
/> :
null
}
{
grabMethod ?
<DescriptionListItem
title={translate('Redirected')}
data={grabMethod.toLowerCase() === 'redirect' ? translate('Yes') : translate('No')}
/> :
null
}
{
date ?
<DescriptionListItem
title={translate('Date')}
data={formatDateTime(date, shortDateFormat, timeFormat, { includeSeconds: true })}
/> :
null
}
</DescriptionList> </DescriptionList>
); );
} }
if (eventType === 'indexerAuth') { if (eventType === 'indexerAuth') {
const { elapsedTime } = data;
return ( return (
<DescriptionList <DescriptionList
descriptionClassName={styles.description} descriptionClassName={styles.description}
@@ -170,6 +268,24 @@ function HistoryDetails(props) {
/> : /> :
null null
} }
{
elapsedTime ?
<DescriptionListItem
title={translate('ElapsedTime')}
data={`${elapsedTime}ms`}
/> :
null
}
{
date ?
<DescriptionListItem
title={translate('Date')}
data={formatDateTime(date, shortDateFormat, timeFormat, { includeSeconds: true })}
/> :
null
}
</DescriptionList> </DescriptionList>
); );
} }
@@ -181,6 +297,15 @@ function HistoryDetails(props) {
title={translate('Name')} title={translate('Name')}
data={data.query} data={data.query}
/> />
{
date ?
<DescriptionListItem
title={translate('Date')}
data={formatDateTime(date, shortDateFormat, timeFormat, { includeSeconds: true })}
/> :
null
}
</DescriptionList> </DescriptionList>
); );
} }
@@ -188,6 +313,7 @@ function HistoryDetails(props) {
HistoryDetails.propTypes = { HistoryDetails.propTypes = {
indexer: PropTypes.object.isRequired, indexer: PropTypes.object.isRequired,
eventType: PropTypes.string.isRequired, eventType: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
shortDateFormat: PropTypes.string.isRequired, shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired timeFormat: PropTypes.string.isRequired
@@ -29,6 +29,7 @@ function HistoryDetailsModal(props) {
isOpen, isOpen,
eventType, eventType,
indexer, indexer,
date,
data, data,
shortDateFormat, shortDateFormat,
timeFormat, timeFormat,
@@ -49,6 +50,7 @@ function HistoryDetailsModal(props) {
<HistoryDetails <HistoryDetails
eventType={eventType} eventType={eventType}
indexer={indexer} indexer={indexer}
date={date}
data={data} data={data}
shortDateFormat={shortDateFormat} shortDateFormat={shortDateFormat}
timeFormat={timeFormat} timeFormat={timeFormat}
@@ -71,6 +73,7 @@ HistoryDetailsModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
eventType: PropTypes.string.isRequired, eventType: PropTypes.string.isRequired,
indexer: PropTypes.object.isRequired, indexer: PropTypes.object.isRequired,
date: PropTypes.string.isRequired,
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
shortDateFormat: PropTypes.string.isRequired, shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired, timeFormat: PropTypes.string.isRequired,
+14 -10
View File
@@ -20,21 +20,22 @@ function getIconName(eventType) {
} }
} }
function getIconKind(successful) { function getIconKind(successful, redirect) {
switch (successful) { if (redirect) {
case false: return kinds.INFO;
return kinds.DANGER; } else if (!successful) {
default: return kinds.DANGER;
return kinds.DEFAULT;
} }
return kinds.DEFAULT;
} }
function getTooltip(eventType, data, indexer) { function getTooltip(eventType, data, indexer, redirect) {
switch (eventType) { switch (eventType) {
case 'indexerQuery': case 'indexerQuery':
return `Query "${data.query}" sent to ${indexer.name}`; return `Query "${data.query}" sent to ${indexer.name}`;
case 'releaseGrabbed': case 'releaseGrabbed':
return `Release grabbed from ${indexer.name}`; return redirect ? `Release grabbed via redirect from ${indexer.name}` : `Release grabbed from ${indexer.name}`;
case 'indexerAuth': case 'indexerAuth':
return `Auth attempted for ${indexer.name}`; return `Auth attempted for ${indexer.name}`;
case 'indexerRss': case 'indexerRss':
@@ -45,9 +46,12 @@ function getTooltip(eventType, data, indexer) {
} }
function HistoryEventTypeCell({ eventType, successful, data, indexer }) { function HistoryEventTypeCell({ eventType, successful, data, indexer }) {
const { grabMethod } = data;
const redirect = grabMethod && grabMethod.toLowerCase() === 'redirect';
const iconName = getIconName(eventType); const iconName = getIconName(eventType);
const iconKind = getIconKind(successful); const iconKind = getIconKind(successful, redirect);
const tooltip = getTooltip(eventType, data, indexer); const tooltip = getTooltip(eventType, data, indexer, redirect);
return ( return (
<TableRowCell <TableRowCell
+3 -1
View File
@@ -370,8 +370,9 @@ class HistoryRow extends Component {
return ( return (
<RelativeDateCell <RelativeDateCell
key={name} key={name}
date={date}
className={styles.date} className={styles.date}
date={date}
includeSeconds={true}
/> />
); );
} }
@@ -408,6 +409,7 @@ class HistoryRow extends Component {
<HistoryDetailsModal <HistoryDetailsModal
isOpen={this.state.isDetailsModalOpen} isOpen={this.state.isDetailsModalOpen}
eventType={eventType} eventType={eventType}
date={date}
data={data} data={data}
indexer={indexer} indexer={indexer}
isMarkingAsFailed={isMarkingAsFailed} isMarkingAsFailed={isMarkingAsFailed}
@@ -74,7 +74,7 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) {
</div> </div>
</TableRowCell> </TableRowCell>
<RelativeDateCell date={date} /> <RelativeDateCell date={date} includeSeconds={true} />
<TableRowCell className={styles.source}> <TableRowCell className={styles.source}>
{data.source ? data.source : null} {data.source ? data.source : null}
@@ -91,6 +91,7 @@ function IndexerHistoryRow(props: IndexerHistoryRowProps) {
<HistoryDetailsModal <HistoryDetailsModal
isOpen={isDetailsModalOpen} isOpen={isDetailsModalOpen}
eventType={eventType} eventType={eventType}
date={date}
data={data} data={data}
indexer={indexer} indexer={indexer}
shortDateFormat={shortDateFormat} shortDateFormat={shortDateFormat}
@@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@@ -73,6 +74,9 @@ namespace NzbDrone.Core.Download
GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api
}; };
var sw = new Stopwatch();
sw.Start();
string downloadClientId; string downloadClientId;
try try
{ {
@@ -111,6 +115,11 @@ namespace NzbDrone.Core.Download
throw; throw;
} }
finally
{
sw.Stop();
grabEvent.ElapsedTime = sw.ElapsedMilliseconds;
}
_logger.ProgressInfo("Report sent to {0}. {1}", downloadClient.Definition.Name, downloadTitle); _logger.ProgressInfo("Report sent to {0}. {1}", downloadClient.Definition.Name, downloadTitle);
@@ -144,6 +153,9 @@ namespace NzbDrone.Core.Download
GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api GrabTrigger = source == "Prowlarr" ? GrabTrigger.Manual : GrabTrigger.Api
}; };
var sw = new Stopwatch();
sw.Start();
byte[] downloadedBytes; byte[] downloadedBytes;
try try
@@ -172,6 +184,11 @@ namespace NzbDrone.Core.Download
_eventAggregator.PublishEvent(grabEvent); _eventAggregator.PublishEvent(grabEvent);
throw; throw;
} }
finally
{
sw.Stop();
grabEvent.ElapsedTime = sw.ElapsedMilliseconds;
}
_logger.Trace("Downloaded {0} bytes from {1}", downloadedBytes.Length, link); _logger.Trace("Downloaded {0} bytes from {1}", downloadedBytes.Length, link);
_eventAggregator.PublishEvent(grabEvent); _eventAggregator.PublishEvent(grabEvent);
+19 -2
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using NLog; using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
@@ -203,8 +204,24 @@ namespace NzbDrone.Core.History
history.Data.Add("Host", message.Host ?? string.Empty); history.Data.Add("Host", message.Host ?? string.Empty);
history.Data.Add("GrabMethod", message.Redirect ? "Redirect" : "Proxy"); history.Data.Add("GrabMethod", message.Redirect ? "Redirect" : "Proxy");
history.Data.Add("GrabTitle", message.Title); history.Data.Add("GrabTitle", message.Title);
history.Data.Add("Categories", string.Join(",", message.Release.Categories.Select(x => x.Id) ?? Array.Empty<int>()));
history.Data.Add("Url", message.Url ?? string.Empty); history.Data.Add("Url", message.Url ?? string.Empty);
history.Data.Add("ElapsedTime", message.ElapsedTime.ToString());
if (message.Release.InfoUrl.IsNotNullOrWhiteSpace())
{
history.Data.Add("InfoUrl", message.Release.InfoUrl);
}
if (message.DownloadClient.IsNotNullOrWhiteSpace() || message.DownloadClientName.IsNotNullOrWhiteSpace())
{
history.Data.Add("DownloadClient", message.DownloadClient ?? string.Empty);
history.Data.Add("DownloadClientName", message.DownloadClientName ?? string.Empty);
}
if (message.Release.PublishDate != DateTime.MinValue)
{
history.Data.Add("PublishedDate", message.Release.PublishDate.ToString("s") + "Z");
}
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
@@ -219,7 +236,7 @@ namespace NzbDrone.Core.History
Successful = message.Successful Successful = message.Successful
}; };
history.Data.Add("ElapsedTime", message.Time.ToString()); history.Data.Add("ElapsedTime", message.ElapsedTime.ToString());
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
@@ -6,13 +6,13 @@ namespace NzbDrone.Core.Indexers.Events
{ {
public int IndexerId { get; set; } public int IndexerId { get; set; }
public bool Successful { get; set; } public bool Successful { get; set; }
public long Time { get; set; } public long ElapsedTime { get; set; }
public IndexerAuthEvent(int indexerId, bool successful, long time) public IndexerAuthEvent(int indexerId, bool successful, long elapsedTime)
{ {
IndexerId = indexerId; IndexerId = indexerId;
Successful = successful; Successful = successful;
Time = time; ElapsedTime = elapsedTime;
} }
} }
} }
@@ -18,6 +18,7 @@ namespace NzbDrone.Core.Indexers.Events
public string DownloadId { get; set; } public string DownloadId { get; set; }
public IIndexer Indexer { get; set; } public IIndexer Indexer { get; set; }
public GrabTrigger GrabTrigger { get; set; } public GrabTrigger GrabTrigger { get; set; }
public long ElapsedTime { get; set; }
public IndexerDownloadEvent(ReleaseInfo release, bool successful, string source, string host, string title, string url) public IndexerDownloadEvent(ReleaseInfo release, bool successful, string source, string host, string title, string url)
{ {
@@ -424,6 +424,7 @@
"IndexerVipExpiringHealthCheckMessage": "Indexer VIP benefits expiring soon: {indexerNames}", "IndexerVipExpiringHealthCheckMessage": "Indexer VIP benefits expiring soon: {indexerNames}",
"Indexers": "Indexers", "Indexers": "Indexers",
"Info": "Info", "Info": "Info",
"InfoUrl": "Info URL",
"InitialFailure": "Initial Failure", "InitialFailure": "Initial Failure",
"InstanceName": "Instance Name", "InstanceName": "Instance Name",
"InstanceNameHelpText": "Instance name in tab and for Syslog app name", "InstanceNameHelpText": "Instance name in tab and for Syslog app name",
@@ -555,6 +556,7 @@
"ProxyValidationBadRequest": "Failed to test proxy. Status code: {statusCode}", "ProxyValidationBadRequest": "Failed to test proxy. Status code: {statusCode}",
"ProxyValidationUnableToConnect": "Unable to connect to proxy: {exceptionMessage}. Check the log surrounding this error for details", "ProxyValidationUnableToConnect": "Unable to connect to proxy: {exceptionMessage}. Check the log surrounding this error for details",
"Public": "Public", "Public": "Public",
"PublishedDate": "Published Date",
"Publisher": "Publisher", "Publisher": "Publisher",
"Query": "Query", "Query": "Query",
"QueryOptions": "Query Options", "QueryOptions": "Query Options",
@@ -568,6 +570,7 @@
"Reddit": "Reddit", "Reddit": "Reddit",
"Redirect": "Redirect", "Redirect": "Redirect",
"RedirectHelpText": "Redirect incoming download request for indexer and pass the grab directly instead of proxying the request via {appName}", "RedirectHelpText": "Redirect incoming download request for indexer and pass the grab directly instead of proxying the request via {appName}",
"Redirected": "Redirected",
"Refresh": "Refresh", "Refresh": "Refresh",
"RefreshMovie": "Refresh movie", "RefreshMovie": "Refresh movie",
"ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid {appName} release branch, you will not receive updates", "ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid {appName} release branch, you will not receive updates",