mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-04-24 05:47:22 -04:00
New: Host Stats
This commit is contained in:
parent
2ea05285a1
commit
6c8f037813
15 changed files with 132 additions and 28 deletions
|
@ -1,6 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import BarChart from 'Components/Chart/BarChart';
|
||||
import DoughnutChart from 'Components/Chart/DoughnutChart';
|
||||
import StackedBarChart from 'Components/Chart/StackedBarChart';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
|
@ -87,6 +88,36 @@ function getUserAgentQueryData(indexerStats) {
|
|||
return data;
|
||||
}
|
||||
|
||||
function getHostGrabsData(indexerStats) {
|
||||
const data = indexerStats.map((indexer) => {
|
||||
return {
|
||||
label: indexer.host ? indexer.host : 'Other',
|
||||
value: indexer.numberOfGrabs
|
||||
};
|
||||
});
|
||||
|
||||
data.sort((a, b) => {
|
||||
return b.value - a.value;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function getHostQueryData(indexerStats) {
|
||||
const data = indexerStats.map((indexer) => {
|
||||
return {
|
||||
label: indexer.host ? indexer.host : 'Other',
|
||||
value: indexer.numberOfQueries
|
||||
};
|
||||
});
|
||||
|
||||
data.sort((a, b) => {
|
||||
return b.value - a.value;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function Stats(props) {
|
||||
const {
|
||||
item,
|
||||
|
@ -148,6 +179,20 @@ function Stats(props) {
|
|||
horizontal={true}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.halfWidthChart}>
|
||||
<DoughnutChart
|
||||
data={getHostQueryData(item.hosts)}
|
||||
title='Total Host Queries'
|
||||
horizontal={true}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.halfWidthChart}>
|
||||
<DoughnutChart
|
||||
data={getHostGrabsData(item.hosts)}
|
||||
title='Total Host Grabs'
|
||||
horizontal={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</PageContentBody>
|
||||
|
|
|
@ -17,9 +17,9 @@ namespace NzbDrone.Core.Download
|
|||
{
|
||||
public interface IDownloadService
|
||||
{
|
||||
void SendReportToClient(ReleaseInfo release, bool redirect);
|
||||
Task<byte[]> DownloadReport(string link, int indexerId, string source, string title);
|
||||
void RecordRedirect(string link, int indexerId, string source, string title);
|
||||
void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
|
||||
Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title);
|
||||
void RecordRedirect(string link, int indexerId, string source, string host, string title);
|
||||
}
|
||||
|
||||
public class DownloadService : IDownloadService
|
||||
|
@ -49,7 +49,7 @@ namespace NzbDrone.Core.Download
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public void SendReportToClient(ReleaseInfo release, bool redirect)
|
||||
public void SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
|
||||
{
|
||||
var downloadTitle = release.Title;
|
||||
var downloadClient = _downloadClientProvider.GetDownloadClient(release.DownloadProtocol);
|
||||
|
@ -79,13 +79,13 @@ namespace NzbDrone.Core.Download
|
|||
catch (ReleaseUnavailableException)
|
||||
{
|
||||
_logger.Trace("Release {0} no longer available on indexer.", release);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, release.Source, release.Title, redirect));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, source, host, release.Title, redirect));
|
||||
throw;
|
||||
}
|
||||
catch (DownloadClientRejectedReleaseException)
|
||||
{
|
||||
_logger.Trace("Release {0} rejected by download client, possible duplicate.", release);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, release.Source, release.Title, redirect));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, source, host, release.Title, redirect));
|
||||
throw;
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
|
@ -100,17 +100,17 @@ namespace NzbDrone.Core.Download
|
|||
_indexerStatusService.RecordFailure(release.IndexerId);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, release.Source, release.Title, redirect));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, false, source, host, release.Title, redirect));
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
_logger.ProgressInfo("Report sent to {0}. {1}", downloadClient.Definition.Name, downloadTitle);
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, true, release.Source, release.Title, redirect));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(release.IndexerId, true, source, host, release.Title, redirect));
|
||||
}
|
||||
|
||||
public async Task<byte[]> DownloadReport(string link, int indexerId, string source, string title)
|
||||
public async Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title)
|
||||
{
|
||||
var url = new Uri(link);
|
||||
|
||||
|
@ -133,7 +133,7 @@ namespace NzbDrone.Core.Download
|
|||
catch (ReleaseUnavailableException)
|
||||
{
|
||||
_logger.Trace("Release {0} no longer available on indexer.", link);
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, title));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, host, title));
|
||||
throw;
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
|
@ -148,17 +148,17 @@ namespace NzbDrone.Core.Download
|
|||
_indexerStatusService.RecordFailure(indexerId);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, title));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, host, title));
|
||||
throw;
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, title));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, host, title));
|
||||
return downloadedBytes;
|
||||
}
|
||||
|
||||
public void RecordRedirect(string link, int indexerId, string source, string title)
|
||||
public void RecordRedirect(string link, int indexerId, string source, string host, string title)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, true, source, title, true));
|
||||
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, true, source, host, title, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,6 +152,7 @@ namespace NzbDrone.Core.History
|
|||
history.Data.Add("QueryType", message.Query.SearchType ?? string.Empty);
|
||||
history.Data.Add("Categories", string.Join(",", message.Query.Categories) ?? string.Empty);
|
||||
history.Data.Add("Source", message.Query.Source ?? string.Empty);
|
||||
history.Data.Add("Host", message.Query.Host ?? string.Empty);
|
||||
history.Data.Add("QueryResults", message.Results.HasValue ? message.Results.ToString() : null);
|
||||
|
||||
_historyRepository.Insert(history);
|
||||
|
@ -168,6 +169,7 @@ namespace NzbDrone.Core.History
|
|||
};
|
||||
|
||||
history.Data.Add("Source", message.Source ?? string.Empty);
|
||||
history.Data.Add("Host", message.Host ?? string.Empty);
|
||||
history.Data.Add("GrabMethod", message.Redirect ? "Redirect" : "Proxy");
|
||||
history.Data.Add("Title", message.Title);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
|
|||
public int? Limit { get; set; }
|
||||
public int? Offset { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Host { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace NzbDrone.Core.IndexerSearch
|
|||
public string title { get; set; }
|
||||
public string configured { get; set; }
|
||||
public string source { get; set; }
|
||||
public string host { get; set; }
|
||||
public string server { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ namespace NzbDrone.Core.IndexerSearch
|
|||
spec.Limit = query.limit;
|
||||
spec.Offset = query.offset;
|
||||
spec.Source = query.source;
|
||||
spec.Host = query.host;
|
||||
|
||||
spec.IndexerIds = indexerIds;
|
||||
|
||||
|
|
|
@ -11,6 +11,10 @@ namespace NzbDrone.Core.IndexerStats
|
|||
public int NumberOfGrabs { get; set; }
|
||||
public int NumberOfRssQueries { get; set; }
|
||||
public int NumberOfAuthQueries { get; set; }
|
||||
public int NumberOfFailedQueries { get; set; }
|
||||
public int NumberOfFailedGrabs { get; set; }
|
||||
public int NumberOfFailedRssQueries { get; set; }
|
||||
public int NumberOfFailedAuthQueries { get; set; }
|
||||
}
|
||||
|
||||
public class UserAgentStatistics : ResultSet
|
||||
|
@ -18,7 +22,6 @@ namespace NzbDrone.Core.IndexerStats
|
|||
public string UserAgent { get; set; }
|
||||
public int NumberOfQueries { get; set; }
|
||||
public int NumberOfGrabs { get; set; }
|
||||
public int NumberOfRssQueries { get; set; }
|
||||
}
|
||||
|
||||
public class HostStatistics : ResultSet
|
||||
|
@ -26,6 +29,5 @@ namespace NzbDrone.Core.IndexerStats
|
|||
public string Host { get; set; }
|
||||
public int NumberOfQueries { get; set; }
|
||||
public int NumberOfGrabs { get; set; }
|
||||
public int NumberOfRssQueries { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace NzbDrone.Core.IndexerStats
|
|||
{
|
||||
List<IndexerStatistics> IndexerStatistics();
|
||||
List<UserAgentStatistics> UserAgentStatistics();
|
||||
List<HostStatistics> HostStatistics();
|
||||
}
|
||||
|
||||
public class IndexerStatisticsRepository : IIndexerStatisticsRepository
|
||||
|
@ -36,6 +37,12 @@ namespace NzbDrone.Core.IndexerStats
|
|||
return UserAgentQuery(UserAgentBuilder());
|
||||
}
|
||||
|
||||
public List<HostStatistics> HostStatistics()
|
||||
{
|
||||
var time = DateTime.UtcNow;
|
||||
return HostQuery(HostBuilder());
|
||||
}
|
||||
|
||||
private List<IndexerStatistics> Query(SqlBuilder builder)
|
||||
{
|
||||
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
|
||||
|
@ -56,22 +63,41 @@ namespace NzbDrone.Core.IndexerStats
|
|||
}
|
||||
}
|
||||
|
||||
private List<HostStatistics> HostQuery(SqlBuilder builder)
|
||||
{
|
||||
var sql = builder.AddTemplate(_selectTemplate).LogQuery();
|
||||
|
||||
using (var conn = _database.OpenConnection())
|
||||
{
|
||||
return conn.Query<HostStatistics>(sql.RawSql, sql.Parameters).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private SqlBuilder IndexerBuilder() => new SqlBuilder()
|
||||
.Select(@"Indexers.Id AS IndexerId,
|
||||
Indexers.Name AS IndexerName,
|
||||
SUM(CASE WHEN EventType == 2 then 1 else 0 end) AS NumberOfQueries,
|
||||
SUM(CASE WHEN EventType == 2 AND Successful == 0 then 1 else 0 end) AS NumberOfFailedQueries,
|
||||
SUM(CASE WHEN EventType == 3 then 1 else 0 end) AS NumberOfRssQueries,
|
||||
SUM(CASE WHEN EventType == 3 AND Successful == 0 then 1 else 0 end) AS NumberOfFailedRssQueries,
|
||||
SUM(CASE WHEN EventType == 4 then 1 else 0 end) AS NumberOfAuthQueries,
|
||||
SUM(CASE WHEN EventType == 4 AND Successful == 0 then 1 else 0 end) AS NumberOfFailedAuthQueries,
|
||||
SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs,
|
||||
SUM(CASE WHEN EventType == 1 AND Successful == 0 then 1 else 0 end) AS NumberOfFailedGrabs,
|
||||
AVG(json_extract(History.Data,'$.elapsedTime')) AS AverageResponseTime")
|
||||
.Join<History.History, IndexerDefinition>((t, r) => t.IndexerId == r.Id)
|
||||
.GroupBy<IndexerDefinition>(x => x.Id);
|
||||
|
||||
private SqlBuilder UserAgentBuilder() => new SqlBuilder()
|
||||
.Select(@"json_extract(History.Data,'$.source') AS UserAgent,
|
||||
SUM(CASE WHEN EventType == 2 then 1 else 0 end) AS NumberOfQueries,
|
||||
SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs,
|
||||
SUM(CASE WHEN EventType == 3 then 1 else 0 end) AS NumberOfRssQueries")
|
||||
SUM(CASE WHEN EventType == 2 OR EventType == 3 then 1 else 0 end) AS NumberOfQueries,
|
||||
SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs")
|
||||
.GroupBy("UserAgent");
|
||||
|
||||
private SqlBuilder HostBuilder() => new SqlBuilder()
|
||||
.Select(@"json_extract(History.Data,'$.host') AS Host,
|
||||
SUM(CASE WHEN EventType == 2 OR EventType == 3 then 1 else 0 end) AS NumberOfQueries,
|
||||
SUM(CASE WHEN EventType == 1 then 1 else 0 end) AS NumberOfGrabs")
|
||||
.GroupBy("Host");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace NzbDrone.Core.IndexerStats
|
|||
{
|
||||
List<IndexerStatistics> IndexerStatistics();
|
||||
List<UserAgentStatistics> UserAgentStatistics();
|
||||
List<HostStatistics> HostStatistics();
|
||||
}
|
||||
|
||||
public class IndexerStatisticsService : IIndexerStatisticsService
|
||||
|
@ -20,16 +21,23 @@ namespace NzbDrone.Core.IndexerStats
|
|||
|
||||
public List<IndexerStatistics> IndexerStatistics()
|
||||
{
|
||||
var seasonStatistics = _indexerStatisticsRepository.IndexerStatistics();
|
||||
var indexerStatistics = _indexerStatisticsRepository.IndexerStatistics();
|
||||
|
||||
return seasonStatistics.ToList();
|
||||
return indexerStatistics.ToList();
|
||||
}
|
||||
|
||||
public List<UserAgentStatistics> UserAgentStatistics()
|
||||
{
|
||||
var seasonStatistics = _indexerStatisticsRepository.UserAgentStatistics();
|
||||
var userAgentStatistics = _indexerStatisticsRepository.UserAgentStatistics();
|
||||
|
||||
return seasonStatistics.ToList();
|
||||
return userAgentStatistics.ToList();
|
||||
}
|
||||
|
||||
public List<HostStatistics> HostStatistics()
|
||||
{
|
||||
var hostStatistics = _indexerStatisticsRepository.HostStatistics();
|
||||
|
||||
return hostStatistics.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,16 @@ namespace NzbDrone.Core.Indexers.Events
|
|||
public int IndexerId { get; set; }
|
||||
public bool Successful { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Host { get; set; }
|
||||
public string Title { get; set; }
|
||||
public bool Redirect { get; set; }
|
||||
|
||||
public IndexerDownloadEvent(int indexerId, bool successful, string source, string title, bool redirect = false)
|
||||
public IndexerDownloadEvent(int indexerId, bool successful, string source, string host, string title, bool redirect = false)
|
||||
{
|
||||
IndexerId = indexerId;
|
||||
Successful = successful;
|
||||
Source = source;
|
||||
Host = host;
|
||||
Title = title;
|
||||
Redirect = redirect;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace Prowlarr.Api.V1.Indexers
|
|||
{
|
||||
Indexers = _indexerStatisticsService.IndexerStatistics(),
|
||||
UserAgents = _indexerStatisticsService.UserAgentStatistics(),
|
||||
Hosts = _indexerStatisticsService.HostStatistics()
|
||||
};
|
||||
|
||||
return indexerResource;
|
||||
|
|
|
@ -8,5 +8,6 @@ namespace Prowlarr.Api.V1.Indexers
|
|||
{
|
||||
public List<IndexerStatistics> Indexers { get; set; }
|
||||
public List<UserAgentStatistics> UserAgents { get; set; }
|
||||
public List<HostStatistics> Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using Prowlarr.Http.Extensions;
|
||||
using Prowlarr.Http.REST;
|
||||
|
@ -44,6 +44,7 @@ namespace NzbDrone.Api.V1.Indexers
|
|||
var requestType = request.t;
|
||||
request.source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
request.server = Request.GetServerUrl();
|
||||
request.host = Request.GetHostName();
|
||||
|
||||
if (requestType.IsNullOrWhiteSpace())
|
||||
{
|
||||
|
@ -107,18 +108,19 @@ namespace NzbDrone.Api.V1.Indexers
|
|||
}
|
||||
|
||||
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
var host = Request.GetHostName();
|
||||
|
||||
var unprotectedlLink = "https://superbits.org/api/v1/torrents/download/797354";
|
||||
|
||||
// If Indexer is set to download via Redirect then just redirect to the link
|
||||
if (indexer.SupportsRedirect && indexerDef.Redirect)
|
||||
{
|
||||
_downloadService.RecordRedirect(unprotectedlLink, id, source, file);
|
||||
_downloadService.RecordRedirect(unprotectedlLink, id, source, host, file);
|
||||
return RedirectPermanent(unprotectedlLink);
|
||||
}
|
||||
|
||||
var downloadBytes = Array.Empty<byte>();
|
||||
downloadBytes = await _downloadService.DownloadReport(unprotectedlLink, id, source, file);
|
||||
downloadBytes = await _downloadService.DownloadReport(unprotectedlLink, id, source, host, file);
|
||||
|
||||
// handle magnet URLs
|
||||
if (downloadBytes.Length >= 7
|
||||
|
|
|
@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
@ -58,10 +59,12 @@ namespace Prowlarr.Api.V1.Search
|
|||
var releaseInfo = _remoteReleaseCache.Find(GetCacheKey(release));
|
||||
|
||||
var indexerDef = _indexerFactory.Get(release.IndexerId);
|
||||
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
|
||||
var host = Request.GetHostName();
|
||||
|
||||
try
|
||||
{
|
||||
_downloadService.SendReportToClient(releaseInfo, indexerDef.Redirect);
|
||||
_downloadService.SendReportToClient(releaseInfo, source, host, indexerDef.Redirect);
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
{
|
||||
|
|
|
@ -161,6 +161,15 @@ namespace Prowlarr.Http.Extensions
|
|||
return remoteAddress;
|
||||
}
|
||||
|
||||
public static string GetHostName(this HttpRequest request)
|
||||
{
|
||||
string ip = request.GetRemoteIP();
|
||||
IPAddress myIP = IPAddress.Parse(ip);
|
||||
IPHostEntry getIPHost = Dns.GetHostEntry(myIP);
|
||||
List<string> compName = getIPHost.HostName.ToString().Split('.').ToList();
|
||||
return compName.First();
|
||||
}
|
||||
|
||||
public static string GetServerUrl(this HttpRequest request)
|
||||
{
|
||||
var scheme = request.Scheme;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue