mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-16 21:35:04 -04:00
Compare commits
18 Commits
v1.6.3.360
...
v1.7.0.362
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f4f6a5726 | ||
|
|
d9ace9a862 | ||
|
|
95691c7476 | ||
|
|
90f2020e59 | ||
|
|
6afa1dc8ba | ||
|
|
e8139f2a5b | ||
|
|
45328db2c7 | ||
|
|
e55d6b827a | ||
|
|
34cd68fa07 | ||
|
|
aed3f9f887 | ||
|
|
6880e67507 | ||
|
|
e0e1b1494e | ||
|
|
20df31919d | ||
|
|
8785fe02e8 | ||
|
|
b2b877a8c3 | ||
|
|
0de302ad48 | ||
|
|
06391489cf | ||
|
|
8fcceb0702 |
2
.github/label-actions.yml
vendored
2
.github/label-actions.yml
vendored
@@ -7,6 +7,7 @@
|
||||
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
|
||||
or [Subreddit](https://reddit.com/r/prowlarr)
|
||||
close: true
|
||||
close-reason: 'not planned'
|
||||
|
||||
'Type: Indexer Request':
|
||||
comment: >
|
||||
@@ -14,6 +15,7 @@
|
||||
for bug reports and feature requests. However, this issue appears
|
||||
to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/)
|
||||
close: true
|
||||
close-reason: 'not planned'
|
||||
|
||||
'Status: Logs Needed':
|
||||
comment: >
|
||||
|
||||
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.6.3'
|
||||
majorVersion: '1.7.0'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
|
||||
@@ -76,7 +76,7 @@ function HistoryDetails(props) {
|
||||
if (eventType === 'releaseGrabbed') {
|
||||
const {
|
||||
source,
|
||||
title,
|
||||
grabTitle,
|
||||
url
|
||||
} = data;
|
||||
|
||||
@@ -101,8 +101,8 @@ function HistoryDetails(props) {
|
||||
{
|
||||
!!data &&
|
||||
<DescriptionListItem
|
||||
title={translate('Title')}
|
||||
data={title ? title : '-'}
|
||||
title={translate('GrabTitle')}
|
||||
data={grabTitle ? grabTitle : '-'}
|
||||
/>
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.parameters {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
.parametersContent {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
2
frontend/src/History/HistoryRow.css.d.ts
vendored
2
frontend/src/History/HistoryRow.css.d.ts
vendored
@@ -6,7 +6,7 @@ interface CssExports {
|
||||
'details': string;
|
||||
'elapsedTime': string;
|
||||
'indexer': string;
|
||||
'parameters': string;
|
||||
'parametersContent': string;
|
||||
'query': string;
|
||||
'releaseGroup': string;
|
||||
'source': string;
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Label from 'Components/Label';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import CapabilitiesLabel from 'Indexer/Index/Table/CapabilitiesLabel';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
||||
import * as historyDataTypes from './historyDataTypes';
|
||||
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
||||
import HistoryRowParameter from './HistoryRowParameter';
|
||||
import styles from './HistoryRow.css';
|
||||
|
||||
const historyParameters = [
|
||||
{ key: historyDataTypes.IMDB_ID, title: 'IMDb' },
|
||||
{ key: historyDataTypes.TMDB_ID, title: 'TMDb' },
|
||||
{ key: historyDataTypes.TVDB_ID, title: 'TVDb' },
|
||||
{ key: historyDataTypes.TRAKT_ID, title: 'Trakt' },
|
||||
{ key: historyDataTypes.R_ID, title: 'TvRage' },
|
||||
{ key: historyDataTypes.TVMAZE_ID, title: 'TvMaze' },
|
||||
{ key: historyDataTypes.SEASON, title: translate('Season') },
|
||||
{ key: historyDataTypes.EPISODE, title: translate('Episode') },
|
||||
{ key: historyDataTypes.ARTIST, title: translate('Artist') },
|
||||
{ key: historyDataTypes.ALBUM, title: translate('Album') },
|
||||
{ key: historyDataTypes.LABEL, title: translate('Label') },
|
||||
{ key: historyDataTypes.TRACK, title: translate('Track') },
|
||||
{ key: historyDataTypes.YEAR, title: translate('Year') },
|
||||
{ key: historyDataTypes.GENRE, title: translate('Genre') },
|
||||
{ key: historyDataTypes.AUTHOR, title: translate('Author') },
|
||||
{ key: historyDataTypes.TITLE, title: translate('Title') },
|
||||
{ key: historyDataTypes.PUBLISHER, title: translate('Publisher') }
|
||||
];
|
||||
|
||||
class HistoryRow extends Component {
|
||||
|
||||
//
|
||||
@@ -44,15 +66,52 @@ class HistoryRow extends Component {
|
||||
data
|
||||
} = this.props;
|
||||
|
||||
const { query, queryType } = data;
|
||||
|
||||
let searchQuery = query;
|
||||
let categories = [];
|
||||
|
||||
if (data.categories) {
|
||||
categories = data.categories.split(',').map((item) => {
|
||||
return parseInt(item);
|
||||
});
|
||||
categories = data.categories.split(',').map((item) => parseInt(item));
|
||||
}
|
||||
|
||||
this.props.onSearchPress(data.query, indexer.id, categories);
|
||||
const searchParams = [
|
||||
historyDataTypes.IMDB_ID,
|
||||
historyDataTypes.TMDB_ID,
|
||||
historyDataTypes.TVDB_ID,
|
||||
historyDataTypes.TRAKT_ID,
|
||||
historyDataTypes.R_ID,
|
||||
historyDataTypes.TVMAZE_ID,
|
||||
historyDataTypes.SEASON,
|
||||
historyDataTypes.EPISODE,
|
||||
historyDataTypes.ARTIST,
|
||||
historyDataTypes.ALBUM,
|
||||
historyDataTypes.LABEL,
|
||||
historyDataTypes.TRACK,
|
||||
historyDataTypes.YEAR,
|
||||
historyDataTypes.GENRE,
|
||||
historyDataTypes.AUTHOR,
|
||||
historyDataTypes.TITLE,
|
||||
historyDataTypes.PUBLISHER
|
||||
]
|
||||
.reduce((acc, key) => {
|
||||
if (key in data && data[key].length > 0) {
|
||||
const value = data[key];
|
||||
|
||||
acc.push({ key, value });
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [])
|
||||
.map((item) => `{${item.key}:${item.value}}`)
|
||||
.join('')
|
||||
;
|
||||
|
||||
if (searchParams.length > 0) {
|
||||
searchQuery += `${searchParams}`;
|
||||
}
|
||||
|
||||
this.props.onSearchPress(searchQuery, indexer.id, categories, queryType);
|
||||
};
|
||||
|
||||
onDetailsPress = () => {
|
||||
@@ -84,6 +143,8 @@ class HistoryRow extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parameters = historyParameters.filter((parameter) => parameter.key in data && data[parameter.key]);
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
{
|
||||
@@ -133,162 +194,19 @@ class HistoryRow extends Component {
|
||||
|
||||
if (name === 'parameters') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.parameters}
|
||||
>
|
||||
{
|
||||
data.imdbId ?
|
||||
<HistoryRowParameter
|
||||
title='IMDb'
|
||||
value={data.imdbId}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.tmdbId ?
|
||||
<HistoryRowParameter
|
||||
title='TMDb'
|
||||
value={data.tmdbId}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.tvdbId ?
|
||||
<HistoryRowParameter
|
||||
title='TVDb'
|
||||
value={data.tvdbId}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.traktId ?
|
||||
<HistoryRowParameter
|
||||
title='Trakt'
|
||||
value={data.traktId}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.rId ?
|
||||
<HistoryRowParameter
|
||||
title='TvRage'
|
||||
value={data.rId}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.tvMazeId ?
|
||||
<HistoryRowParameter
|
||||
title='TvMaze'
|
||||
value={data.tvMazeId}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.season ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Season')}
|
||||
value={data.season}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.episode ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Episode')}
|
||||
value={data.episode}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.artist ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Artist')}
|
||||
value={data.artist}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.album ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Album')}
|
||||
value={data.album}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.label ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Label')}
|
||||
value={data.label}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.track ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Track')}
|
||||
value={data.track}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.year ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Year')}
|
||||
value={data.year}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.genre ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Genre')}
|
||||
value={data.genre}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.author ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Author')}
|
||||
value={data.author}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.bookTitle ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Book')}
|
||||
value={data.bookTitle}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
data.publisher ?
|
||||
<HistoryRowParameter
|
||||
title={translate('Publisher')}
|
||||
value={data.publisher}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
<TableRowCell key={name}>
|
||||
<div className={styles.parametersContent}>
|
||||
{parameters.map((parameter) => {
|
||||
return (
|
||||
<HistoryRowParameter
|
||||
key={parameter.key}
|
||||
title={parameter.title}
|
||||
value={data[parameter.key]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
@@ -300,8 +218,25 @@ class HistoryRow extends Component {
|
||||
className={styles.indexer}
|
||||
>
|
||||
{
|
||||
data.title ?
|
||||
data.title :
|
||||
data.grabTitle ?
|
||||
data.grabTitle :
|
||||
null
|
||||
}
|
||||
</TableRowCell>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'queryType') {
|
||||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.query}
|
||||
>
|
||||
{
|
||||
data.queryType ?
|
||||
<Label kind={kinds.INFO}>
|
||||
{data.queryType}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
</TableRowCell>
|
||||
|
||||
@@ -48,8 +48,8 @@ class HistoryRowConnector extends Component {
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onSearchPress = (term, indexerId, categories) => {
|
||||
this.props.setSearchDefault({ searchQuery: term, searchIndexerIds: [indexerId], searchCategories: categories });
|
||||
onSearchPress = (term, indexerId, categories, type) => {
|
||||
this.props.setSearchDefault({ searchQuery: term, searchIndexerIds: [indexerId], searchCategories: categories, searchType: type });
|
||||
this.props.push(`${window.Prowlarr.urlBase}/search`);
|
||||
};
|
||||
|
||||
|
||||
17
frontend/src/History/historyDataTypes.js
Normal file
17
frontend/src/History/historyDataTypes.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export const IMDB_ID = 'imdbId';
|
||||
export const TMDB_ID = 'tmdbId';
|
||||
export const TVDB_ID = 'tvdbId';
|
||||
export const TRAKT_ID = 'traktId';
|
||||
export const R_ID = 'rId';
|
||||
export const TVMAZE_ID = 'tvMazeId';
|
||||
export const SEASON = 'season';
|
||||
export const EPISODE = 'episode';
|
||||
export const ARTIST = 'artist';
|
||||
export const ALBUM = 'album';
|
||||
export const LABEL = 'label';
|
||||
export const TRACK = 'track';
|
||||
export const YEAR = 'year';
|
||||
export const GENRE = 'genre';
|
||||
export const AUTHOR = 'author';
|
||||
export const TITLE = 'title';
|
||||
export const PUBLISHER = 'publisher';
|
||||
@@ -58,6 +58,12 @@ export const defaultState = {
|
||||
isSortable: false,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
name: 'queryType',
|
||||
label: translate('QueryType'),
|
||||
isSortable: false,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
name: 'categories',
|
||||
label: translate('Categories'),
|
||||
|
||||
@@ -121,6 +121,11 @@ namespace NzbDrone.Common.Serializer
|
||||
return JsonConvert.SerializeObject(obj, SerializerSettings);
|
||||
}
|
||||
|
||||
public static string ToJson(this object obj, Formatting formatting)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj, formatting, SerializerSettings);
|
||||
}
|
||||
|
||||
public static void Serialize<TModel>(TModel model, TextWriter outputStream)
|
||||
{
|
||||
var jsonTextWriter = new JsonTextWriter(outputStream);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class history_fix_data_titlesFixture : MigrationTest<history_fix_data_titles>
|
||||
{
|
||||
[Test]
|
||||
public void should_update_data_for_book_search()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("History").Row(new
|
||||
{
|
||||
IndexerId = 1,
|
||||
Date = DateTime.UtcNow,
|
||||
Data = new
|
||||
{
|
||||
Author = "Fake Author",
|
||||
BookTitle = "Fake Book Title",
|
||||
Publisher = "",
|
||||
Year = "",
|
||||
Genre = "",
|
||||
Query = "",
|
||||
QueryType = "book",
|
||||
Source = "Prowlarr",
|
||||
Host = "localhost"
|
||||
}.ToJson(),
|
||||
EventType = 2,
|
||||
Successful = true
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<HistoryDefinition34>("SELECT * FROM \"History\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Data.Should().NotContainKey("bookTitle");
|
||||
items.First().Data.Should().ContainKey("title");
|
||||
items.First().Data.GetValueOrDefault("title").Should().Be("Fake Book Title");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_update_data_for_release_grabbed()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("History").Row(new
|
||||
{
|
||||
IndexerId = 1,
|
||||
Date = DateTime.UtcNow,
|
||||
Data = new
|
||||
{
|
||||
GrabMethod = "Proxy",
|
||||
Title = "Fake Release Title",
|
||||
Source = "Prowlarr",
|
||||
Host = "localhost"
|
||||
}.ToJson(),
|
||||
EventType = 1,
|
||||
Successful = true
|
||||
});
|
||||
});
|
||||
|
||||
var items = db.Query<HistoryDefinition34>("SELECT * FROM \"History\"");
|
||||
|
||||
items.Should().HaveCount(1);
|
||||
|
||||
items.First().Data.Should().NotContainKey("title");
|
||||
items.First().Data.Should().ContainKey("grabTitle");
|
||||
items.First().Data.GetValueOrDefault("grabTitle").Should().Be("Fake Release Title");
|
||||
}
|
||||
}
|
||||
|
||||
public class HistoryDefinition34
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int IndexerId { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public Dictionary<string, string> Data { get; set; }
|
||||
public int EventType { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
public bool Successful { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -32,10 +31,10 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{
|
||||
failures.AddIfNotNull(_lazyLibrarianV1Proxy.TestConnection(Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to LazyLibrarian"));
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to LazyLibrarian. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
[FieldDefinition(1, Label = "LazyLibrarian Server", HelpText = "URL used to connect to LazyLibrarian server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:5299")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by LazyLibrarian in Settings/Web Interface")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by LazyLibrarian in Settings/Web Interface")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{
|
||||
@@ -139,11 +139,11 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
return new ValidationFailure("ApiKey", status.Error.Message);
|
||||
}
|
||||
|
||||
var indexers = GetIndexers(settings);
|
||||
GetIndexers(settings);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (LazyLibrarianException ex)
|
||||
@@ -153,8 +153,8 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
return new ValidationFailure("", $"Unable to send test message. {ex.Message}");
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -164,7 +164,9 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var requestBuilder = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.AddQueryParam("cmd", command)
|
||||
.AddQueryParam("apikey", settings.ApiKey);
|
||||
|
||||
@@ -191,9 +193,12 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
|
||||
private int CalculatePriority(int indexerPriority) => ProwlarrHighestPriority - indexerPriority + 1;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
@@ -48,9 +51,36 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
failures.AddIfNotNull(_lidarrV1Proxy.TestConnection(BuildLidarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ApiKey", "API Key is invalid"));
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Lidarr cannot connect to Prowlarr"));
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "Lidarr returned redirect and is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Lidarr URL is invalid, Prowlarr cannot connect to Lidarr - are you missing a URL base?"));
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Lidarr. {ex.Message}"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to parse JSON response from application. {ex.Message}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Lidarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
[FieldDefinition(1, Label = "Lidarr Server", HelpText = "URL used to connect to Lidarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8686")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Lidarr in Settings/General")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Lidarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -85,13 +85,25 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
try
|
||||
{
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
catch (HttpException ex) when (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Debug("Retrying to add indexer forcefully");
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public LidarrIndexer UpdateIndexer(LidarrIndexer indexer, LidarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
@@ -103,47 +115,16 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
var applicationVersion = _httpClient.Post(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
var applicationVersion = _httpClient.Post<LidarrIndexer>(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Lidarr version");
|
||||
}
|
||||
|
||||
if (new Version(applicationVersion) < MinimumApplicationVersion)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Lidarr version should be at least {MinimumApplicationVersion.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Lidarr version");
|
||||
}
|
||||
catch (HttpException ex)
|
||||
|
||||
if (new Version(applicationVersion) < MinimumApplicationVersion)
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Lidarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.SeeOther)
|
||||
{
|
||||
_logger.Error(ex, "Lidarr returned redirect and is invalid");
|
||||
return new ValidationFailure("BaseUrl", "Lidarr url is invalid, Prowlarr cannot connect to Lidarr - are you missing a url base?");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
return new ValidationFailure(string.Empty, $"Lidarr version should be at least {MinimumApplicationVersion.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -179,8 +160,10 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
break;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
@@ -192,15 +175,15 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(LidarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var request = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var request = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.SetHeader("X-Api-Key", settings.ApiKey)
|
||||
.Build();
|
||||
|
||||
@@ -217,9 +200,12 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -32,10 +31,10 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
{
|
||||
failures.AddIfNotNull(_mylarV3Proxy.TestConnection(Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Mylar"));
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Mylar. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
[FieldDefinition(1, Label = "Mylar Server", HelpText = "URL used to connect to Mylar server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8090")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Mylar in Settings/Web Interface")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Mylar in Settings/Web Interface")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Applications.Mylar
|
||||
{
|
||||
@@ -135,11 +135,11 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
return new ValidationFailure("ApiKey", status.Error.Message);
|
||||
}
|
||||
|
||||
var indexers = GetIndexers(settings);
|
||||
GetIndexers(settings);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (MylarException ex)
|
||||
@@ -149,8 +149,8 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
return new ValidationFailure("", $"Unable to send test message. {ex.Message}");
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -160,7 +160,9 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var requestBuilder = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.AddQueryParam("cmd", command)
|
||||
.AddQueryParam("apikey", settings.ApiKey);
|
||||
|
||||
@@ -187,9 +189,12 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
@@ -48,9 +51,36 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
failures.AddIfNotNull(_radarrV3Proxy.TestConnection(BuildRadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ApiKey", "API Key is invalid"));
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Radarr cannot connect to Prowlarr"));
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "Radarr returned redirect and is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Radarr URL is invalid, Prowlarr cannot connect to Radarr - are you missing a URL base?"));
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Radarr. {ex.Message}"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to parse JSON response from application. {ex.Message}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Radarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
[FieldDefinition(1, Label = "Radarr Server", HelpText = "URL used to connect to Radarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:7878")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Radarr in Settings/General")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Radarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -86,13 +86,25 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
try
|
||||
{
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
catch (HttpException ex) when (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Debug("Retrying to add indexer forcefully");
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public RadarrIndexer UpdateIndexer(RadarrIndexer indexer, RadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
@@ -104,59 +116,28 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
var applicationVersion = _httpClient.Post(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
var applicationVersion = _httpClient.Post<RadarrIndexer>(request).Headers.GetSingleValue("X-Application-Version");
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Radarr version");
|
||||
}
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Radarr version");
|
||||
}
|
||||
var version = new Version(applicationVersion);
|
||||
|
||||
var version = new Version(applicationVersion);
|
||||
|
||||
if (version.Major == 3)
|
||||
if (version.Major == 3)
|
||||
{
|
||||
if (version < MinimumApplicationV3Version)
|
||||
{
|
||||
if (version < MinimumApplicationV3Version)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Radarr version should be at least {MinimumApplicationV3Version.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (version < MinimumApplicationV4Version)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Radarr version should be at least {MinimumApplicationV4Version.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
return new ValidationFailure(string.Empty, $"Radarr version should be at least {MinimumApplicationV3Version.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
}
|
||||
catch (HttpException ex)
|
||||
else
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
if (version < MinimumApplicationV4Version)
|
||||
{
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
return new ValidationFailure(string.Empty, $"Radarr version should be at least {MinimumApplicationV4Version.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Radarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.SeeOther)
|
||||
{
|
||||
_logger.Error(ex, "Radarr returned redirect and is invalid");
|
||||
return new ValidationFailure("BaseUrl", "Radarr url is invalid, Prowlarr cannot connect to Radarr - are you missing a url base?");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -192,8 +173,10 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
break;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
@@ -205,15 +188,15 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(RadarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var request = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var request = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.SetHeader("X-Api-Key", settings.ApiKey)
|
||||
.Build();
|
||||
|
||||
@@ -230,9 +213,12 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
@@ -49,10 +51,37 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
failures.AddIfNotNull(_readarrV1Proxy.TestConnection(BuildReadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Readarr"));
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ApiKey", "API Key is invalid"));
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Readarr cannot connect to Prowlarr"));
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "Readarr returned redirect and is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Readarr URL is invalid, Prowlarr cannot connect to Readarr - are you missing a URL base?"));
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Readarr. {ex.Message}"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to parse JSON response from application. {ex.Message}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Readarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
[FieldDefinition(1, Label = "Readarr Server", HelpText = "URL used to connect to Readarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8787")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Readarr in Settings/General")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Readarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -82,13 +82,25 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
try
|
||||
{
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
catch (HttpException ex) when (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Debug("Retrying to add indexer forcefully");
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadarrIndexer UpdateIndexer(ReadarrIndexer indexer, ReadarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
@@ -100,38 +112,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
{
|
||||
Execute<ReadarrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Readarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.SeeOther)
|
||||
{
|
||||
_logger.Error(ex, "Readarr returned redirect and is invalid");
|
||||
return new ValidationFailure("BaseUrl", "Readarr url is invalid, Prowlarr cannot connect to Readarr - are you missing a url base?");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
}
|
||||
_httpClient.Post(request);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -166,8 +147,10 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
break;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
@@ -179,15 +162,15 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(ReadarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var request = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var request = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.SetHeader("X-Api-Key", settings.ApiKey)
|
||||
.Build();
|
||||
|
||||
@@ -204,9 +187,12 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
@@ -48,9 +51,40 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
failures.AddIfNotNull(_sonarrV3Proxy.TestConnection(BuildSonarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ApiKey", "API Key is invalid"));
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Sonarr cannot connect to Prowlarr"));
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "Sonarr returned redirect and is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Sonarr URL is invalid, Prowlarr cannot connect to Sonarr - are you missing a URL base?"));
|
||||
break;
|
||||
case HttpStatusCode.NotFound:
|
||||
_logger.Error(ex, "Sonarr not found");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Sonarr URL is invalid, Prowlarr cannot connect to Sonarr. Is Sonarr running and accessible? Sonarr v2 is not supported."));
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Sonarr. {ex.Message}"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to parse JSON response from application. {ex.Message}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Sonarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
[FieldDefinition(1, Label = "Sonarr Server", HelpText = "URL used to connect to Sonarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8989")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Sonarr in Settings/General")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Sonarr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -85,13 +85,25 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
try
|
||||
{
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
catch (HttpException ex) when (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Debug("Retrying to add indexer forcefully");
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public SonarrIndexer UpdateIndexer(SonarrIndexer indexer, SonarrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
@@ -103,53 +115,16 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
var applicationVersion = _httpClient.Post(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
var applicationVersion = _httpClient.Post<SonarrIndexer>(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Sonarr version");
|
||||
}
|
||||
|
||||
if (new Version(applicationVersion) < MinimumApplicationVersion)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Sonarr version should be at least {MinimumApplicationVersion.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Sonarr version");
|
||||
}
|
||||
catch (HttpException ex)
|
||||
|
||||
if (new Version(applicationVersion) < MinimumApplicationVersion)
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Sonarr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.SeeOther)
|
||||
{
|
||||
_logger.Error(ex, "Sonarr returned redirect and is invalid");
|
||||
return new ValidationFailure("BaseUrl", "Sonarr url is invalid, Prowlarr cannot connect to Sonarr - are you missing a url base?");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
_logger.Error(ex, "Sonarr not found");
|
||||
return new ValidationFailure("BaseUrl", "Sonarr url is invalid, Prowlarr cannot connect to Sonarr. Is Sonarr running and accessible? Sonarr v2 is not supported.");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
return new ValidationFailure(string.Empty, $"Sonarr version should be at least {MinimumApplicationVersion.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -185,8 +160,10 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
break;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
@@ -198,15 +175,15 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(SonarrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var request = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var request = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.SetHeader("X-Api-Key", settings.ApiKey)
|
||||
.Build();
|
||||
|
||||
@@ -223,9 +200,12 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
@@ -49,10 +51,37 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
failures.AddIfNotNull(_whisparrV3Proxy.TestConnection(BuildWhisparrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Whisparr"));
|
||||
switch (ex.Response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ApiKey", "API Key is invalid"));
|
||||
break;
|
||||
case HttpStatusCode.BadRequest:
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("ProwlarrUrl", "Prowlarr URL is invalid, Whisparr cannot connect to Prowlarr"));
|
||||
break;
|
||||
case HttpStatusCode.SeeOther:
|
||||
_logger.Error(ex, "Whisparr returned redirect and is invalid");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Whisparr URL is invalid, Prowlarr cannot connect to Whisparr - are you missing a URL base?"));
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Whisparr. {ex.Message}"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to parse JSON response from application");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to parse JSON response from application. {ex.Message}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to complete application test");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Whisparr. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
[FieldDefinition(1, Label = "Whisparr Server", HelpText = "URL used to connect to Whisparr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:6969")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Whisparr in Settings/General")]
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Whisparr in Settings/General")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
|
||||
|
||||
@@ -82,13 +82,23 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return Execute<WhisparrIndexer>(request);
|
||||
try
|
||||
{
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
catch (HttpException ex) when (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public WhisparrIndexer UpdateIndexer(WhisparrIndexer indexer, WhisparrSettings settings)
|
||||
{
|
||||
var request = BuildRequest(settings, $"{AppIndexerApiRoute}/{indexer.Id}", HttpMethod.Put);
|
||||
|
||||
request.Url = request.Url.AddQueryParam("forceSave", "true");
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
return ExecuteIndexerRequest(request);
|
||||
@@ -100,38 +110,7 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
|
||||
request.SetContent(indexer.ToJson());
|
||||
|
||||
try
|
||||
{
|
||||
Execute<WhisparrIndexer>(request);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
_logger.Error(ex, "Prowlarr URL is invalid");
|
||||
return new ValidationFailure("ProwlarrUrl", "Prowlarr url is invalid, Whisparr cannot connect to Prowlarr");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.SeeOther)
|
||||
{
|
||||
_logger.Error(ex, "Whisparr returned redirect and is invalid");
|
||||
return new ValidationFailure("BaseUrl", "Whisparr url is invalid, Prowlarr cannot connect to Whisparr - are you missing a url base?");
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("BaseUrl", "Unable to complete application test");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
return new ValidationFailure("", "Unable to send test message");
|
||||
}
|
||||
_httpClient.Post(request);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -166,8 +145,10 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
break;
|
||||
default:
|
||||
_logger.Error(ex, "Unexpected response status code: {0}", ex.Response.StatusCode);
|
||||
throw;
|
||||
break;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
@@ -179,15 +160,15 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
_logger.Error(ex, "Unable to add or update indexer");
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest BuildRequest(WhisparrSettings settings, string resource, HttpMethod method)
|
||||
{
|
||||
var baseUrl = settings.BaseUrl.TrimEnd('/');
|
||||
|
||||
var request = new HttpRequestBuilder(baseUrl).Resource(resource)
|
||||
var request = new HttpRequestBuilder(baseUrl)
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json)
|
||||
.SetHeader("X-Api-Key", settings.ApiKey)
|
||||
.Build();
|
||||
|
||||
@@ -204,9 +185,12 @@ namespace NzbDrone.Core.Applications.Whisparr
|
||||
{
|
||||
var response = _httpClient.Execute(request);
|
||||
|
||||
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
|
||||
if ((int)response.StatusCode >= 300)
|
||||
{
|
||||
throw new HttpException(response);
|
||||
}
|
||||
|
||||
return results;
|
||||
return Json.Deserialize<TResource>(response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(034)]
|
||||
public class history_fix_data_titles : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(MigrateHistoryDataTitle);
|
||||
}
|
||||
|
||||
private void MigrateHistoryDataTitle(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updatedHistory = new List<object>();
|
||||
|
||||
using (var selectCommand = conn.CreateCommand())
|
||||
{
|
||||
selectCommand.Transaction = tran;
|
||||
selectCommand.CommandText = "SELECT \"Id\", \"Data\", \"EventType\" FROM \"History\" WHERE \"EventType\" != 3";
|
||||
|
||||
using var reader = selectCommand.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var data = reader.GetString(1);
|
||||
var eventType = reader.GetInt32(2);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(data))
|
||||
{
|
||||
var jsonObject = Json.Deserialize<JObject>(data);
|
||||
|
||||
if (eventType == 1 && jsonObject.ContainsKey("title"))
|
||||
{
|
||||
jsonObject.Add("grabTitle", jsonObject.Value<string>("title"));
|
||||
jsonObject.Remove("title");
|
||||
}
|
||||
|
||||
if (eventType != 1 && jsonObject.ContainsKey("bookTitle"))
|
||||
{
|
||||
jsonObject.Add("title", jsonObject.Value<string>("bookTitle"));
|
||||
jsonObject.Remove("bookTitle");
|
||||
}
|
||||
|
||||
data = jsonObject.ToJson();
|
||||
|
||||
if (!jsonObject.ContainsKey("grabTitle") && !jsonObject.ContainsKey("title"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
updatedHistory.Add(new
|
||||
{
|
||||
Id = id,
|
||||
Data = data
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateHistorySql = "UPDATE \"History\" SET \"Data\" = @Data WHERE \"Id\" = @Id";
|
||||
conn.Execute(updateHistorySql, updatedHistory, transaction: tran);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,52 +128,55 @@ namespace NzbDrone.Core.History
|
||||
Successful = response?.StatusCode == HttpStatusCode.OK || (response is { Request: { SuppressHttpError: true, SuppressHttpErrorStatusCodes: not null } } && response.Request.SuppressHttpErrorStatusCodes.Contains(response.StatusCode))
|
||||
};
|
||||
|
||||
if (message.Query is MovieSearchCriteria)
|
||||
if (message.Query is MovieSearchCriteria movieSearchCriteria)
|
||||
{
|
||||
history.Data.Add("ImdbId", ((MovieSearchCriteria)message.Query).FullImdbId ?? string.Empty);
|
||||
history.Data.Add("TmdbId", ((MovieSearchCriteria)message.Query).TmdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TraktId", ((MovieSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Year", ((MovieSearchCriteria)message.Query).Year?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Genre", ((MovieSearchCriteria)message.Query).Genre ?? string.Empty);
|
||||
history.Data.Add("ImdbId", movieSearchCriteria.FullImdbId);
|
||||
history.Data.Add("TmdbId", movieSearchCriteria.TmdbId?.ToString());
|
||||
history.Data.Add("TraktId", movieSearchCriteria.TraktId?.ToString());
|
||||
history.Data.Add("Year", movieSearchCriteria.Year?.ToString());
|
||||
history.Data.Add("Genre", movieSearchCriteria.Genre);
|
||||
}
|
||||
|
||||
if (message.Query is TvSearchCriteria)
|
||||
if (message.Query is TvSearchCriteria tvSearchCriteria)
|
||||
{
|
||||
history.Data.Add("ImdbId", ((TvSearchCriteria)message.Query).FullImdbId ?? string.Empty);
|
||||
history.Data.Add("TvdbId", ((TvSearchCriteria)message.Query).TvdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TmdbId", ((TvSearchCriteria)message.Query).TmdbId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TraktId", ((TvSearchCriteria)message.Query).TraktId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("RId", ((TvSearchCriteria)message.Query).RId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("TvMazeId", ((TvSearchCriteria)message.Query).TvMazeId?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Season", ((TvSearchCriteria)message.Query).Season?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Episode", ((TvSearchCriteria)message.Query).Episode ?? string.Empty);
|
||||
history.Data.Add("Year", ((TvSearchCriteria)message.Query).Year?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Genre", ((TvSearchCriteria)message.Query).Genre ?? string.Empty);
|
||||
history.Data.Add("ImdbId", tvSearchCriteria.FullImdbId);
|
||||
history.Data.Add("TvdbId", tvSearchCriteria.TvdbId?.ToString());
|
||||
history.Data.Add("TmdbId", tvSearchCriteria.TmdbId?.ToString());
|
||||
history.Data.Add("TraktId", tvSearchCriteria.TraktId?.ToString());
|
||||
history.Data.Add("RId", tvSearchCriteria.RId?.ToString());
|
||||
history.Data.Add("TvMazeId", tvSearchCriteria.TvMazeId?.ToString());
|
||||
history.Data.Add("Season", tvSearchCriteria.Season?.ToString());
|
||||
history.Data.Add("Episode", tvSearchCriteria.Episode);
|
||||
history.Data.Add("Year", tvSearchCriteria.Year?.ToString());
|
||||
history.Data.Add("Genre", tvSearchCriteria.Genre);
|
||||
}
|
||||
|
||||
if (message.Query is MusicSearchCriteria)
|
||||
if (message.Query is MusicSearchCriteria musicSearchCriteria)
|
||||
{
|
||||
history.Data.Add("Artist", ((MusicSearchCriteria)message.Query).Artist ?? string.Empty);
|
||||
history.Data.Add("Album", ((MusicSearchCriteria)message.Query).Album ?? string.Empty);
|
||||
history.Data.Add("Track", ((MusicSearchCriteria)message.Query).Track ?? string.Empty);
|
||||
history.Data.Add("Label", ((MusicSearchCriteria)message.Query).Label ?? string.Empty);
|
||||
history.Data.Add("Year", ((MusicSearchCriteria)message.Query).Year?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Genre", ((MusicSearchCriteria)message.Query).Genre ?? string.Empty);
|
||||
history.Data.Add("Artist", musicSearchCriteria.Artist);
|
||||
history.Data.Add("Album", musicSearchCriteria.Album);
|
||||
history.Data.Add("Track", musicSearchCriteria.Track);
|
||||
history.Data.Add("Label", musicSearchCriteria.Label);
|
||||
history.Data.Add("Year", musicSearchCriteria.Year?.ToString());
|
||||
history.Data.Add("Genre", musicSearchCriteria.Genre);
|
||||
}
|
||||
|
||||
if (message.Query is BookSearchCriteria)
|
||||
if (message.Query is BookSearchCriteria bookSearchCriteria)
|
||||
{
|
||||
history.Data.Add("Author", ((BookSearchCriteria)message.Query).Author ?? string.Empty);
|
||||
history.Data.Add("BookTitle", ((BookSearchCriteria)message.Query).Title ?? string.Empty);
|
||||
history.Data.Add("Publisher", ((BookSearchCriteria)message.Query).Publisher ?? string.Empty);
|
||||
history.Data.Add("Year", ((BookSearchCriteria)message.Query).Year?.ToString() ?? string.Empty);
|
||||
history.Data.Add("Genre", ((BookSearchCriteria)message.Query).Genre ?? string.Empty);
|
||||
history.Data.Add("Author", bookSearchCriteria.Author);
|
||||
history.Data.Add("Title", bookSearchCriteria.Title);
|
||||
history.Data.Add("Publisher", bookSearchCriteria.Publisher);
|
||||
history.Data.Add("Year", bookSearchCriteria.Year?.ToString());
|
||||
history.Data.Add("Genre", bookSearchCriteria.Genre);
|
||||
}
|
||||
|
||||
// Clean empty data
|
||||
history.Data = history.Data.Where(d => d.Value != null).ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
history.Data.Add("ElapsedTime", message.QueryResult.Cached ? "0" : message.QueryResult.Response?.ElapsedTime.ToString() ?? string.Empty);
|
||||
history.Data.Add("Query", message.Query.SearchTerm ?? string.Empty);
|
||||
history.Data.Add("QueryType", message.Query.SearchType ?? string.Empty);
|
||||
history.Data.Add("Categories", string.Join(",", message.Query.Categories) ?? string.Empty);
|
||||
history.Data.Add("Categories", string.Join(",", message.Query.Categories ?? Array.Empty<int>()));
|
||||
history.Data.Add("Source", message.Query.Source ?? string.Empty);
|
||||
history.Data.Add("Host", message.Query.Host ?? string.Empty);
|
||||
history.Data.Add("QueryResults", message.QueryResult.Releases?.Count.ToString() ?? string.Empty);
|
||||
@@ -196,7 +199,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);
|
||||
history.Data.Add("GrabTitle", message.Title);
|
||||
history.Data.Add("Url", message.Url ?? string.Empty);
|
||||
|
||||
_historyRepository.Insert(history);
|
||||
|
||||
@@ -52,12 +52,14 @@ namespace NzbDrone.Core.IndexerStats
|
||||
};
|
||||
|
||||
var sortedEvents = indexer.OrderBy(v => v.Date)
|
||||
.ThenBy(v => v.Id)
|
||||
.ToArray();
|
||||
var temp = 0;
|
||||
.ThenBy(v => v.Id)
|
||||
.ToArray();
|
||||
|
||||
var elapsedTimeEvents = sortedEvents.Where(h => int.TryParse(h.Data.GetValueOrDefault("elapsedTime"), out temp))
|
||||
.Select(h => temp);
|
||||
var temp = 0;
|
||||
var elapsedTimeEvents = sortedEvents
|
||||
.Where(h => int.TryParse(h.Data.GetValueOrDefault("elapsedTime"), out temp) && h.Data.GetValueOrDefault("cached") != "1")
|
||||
.Select(h => temp)
|
||||
.ToArray();
|
||||
|
||||
indexerStats.AverageResponseTime = elapsedTimeEvents.Any() ? (int)elapsedTimeEvents.Average() : 0;
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
public class BeyondHD : TorrentIndexerBase<BeyondHDSettings>
|
||||
{
|
||||
public override string Name => "BeyondHD";
|
||||
|
||||
public override string[] IndexerUrls => new string[] { "https://beyond-hd.me/" };
|
||||
public override string[] IndexerUrls => new[] { "https://beyond-hd.me/" };
|
||||
public override string Description => "BeyondHD (BHD) is a Private Torrent Tracker for HD MOVIES / TV";
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
@@ -38,12 +37,12 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new BeyondHDRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
return new BeyondHDRequestGenerator(Settings, Capabilities);
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new BeyondHDParser(Settings, Capabilities.Categories);
|
||||
return new BeyondHDParser(Capabilities.Categories);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
@@ -51,13 +50,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
}
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies");
|
||||
@@ -69,15 +68,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class BeyondHDRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public BeyondHDSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
private readonly BeyondHDSettings _settings;
|
||||
private readonly IndexerCapabilities _capabilities;
|
||||
|
||||
public BeyondHDRequestGenerator(BeyondHDSettings settings, IndexerCapabilities capabilities)
|
||||
{
|
||||
_settings = settings;
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null, int tmdbId = 0)
|
||||
{
|
||||
var body = new Dictionary<string, object>
|
||||
{
|
||||
{ "action", "search" },
|
||||
{ "rsskey", Settings.RssKey }
|
||||
{ "rsskey", _settings.RssKey }
|
||||
};
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
@@ -95,31 +100,34 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
body.Add("search", term);
|
||||
}
|
||||
|
||||
var cats = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
var cats = _capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
|
||||
if (cats.Count > 0)
|
||||
{
|
||||
body.Add("categories", string.Join(",", cats));
|
||||
}
|
||||
|
||||
var searchUrl = Settings.BaseUrl + "api/torrents/" + Settings.ApiKey;
|
||||
var searchUrl = $"{_settings.BaseUrl}api/torrents/{_settings.ApiKey}";
|
||||
|
||||
var request = new HttpRequest(searchUrl, HttpAccept.Json);
|
||||
|
||||
request.Headers.ContentType = "application/json";
|
||||
request.Method = HttpMethod.Post;
|
||||
var request = new HttpRequest(searchUrl, HttpAccept.Json)
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
ContentType = "application/json"
|
||||
},
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
request.SetContent(body.ToJson());
|
||||
request.ContentSummary = body.ToJson(Formatting.None);
|
||||
|
||||
var indexerRequest = new IndexerRequest(request);
|
||||
|
||||
yield return indexerRequest;
|
||||
yield return new IndexerRequest(request);
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId, searchCriteria.TmdbId.GetValueOrDefault()));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories, searchCriteria.FullImdbId, searchCriteria.TmdbId.GetValueOrDefault()));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -128,7 +136,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -137,7 +145,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria.SanitizedTvSearchString, searchCriteria.Categories, searchCriteria.FullImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -146,7 +154,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -155,7 +163,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
@@ -166,19 +174,17 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class BeyondHDParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly BeyondHDSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public BeyondHDParser(BeyondHDSettings settings, IndexerCapabilitiesCategories categories)
|
||||
public BeyondHDParser(IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<TorrentInfo>();
|
||||
var indexerHttpResponse = indexerResponse.HttpResponse;
|
||||
|
||||
if (indexerHttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerHttpResponse.StatusCode} code from indexer request");
|
||||
@@ -201,6 +207,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
throw new IndexerException(indexerResponse, $"Indexer Error: {jsonResponse.Resource.StatusMessage}");
|
||||
}
|
||||
|
||||
var releaseInfos = new List<ReleaseInfo>();
|
||||
|
||||
foreach (var row in jsonResponse.Resource.Results)
|
||||
{
|
||||
var details = row.InfoUrl;
|
||||
@@ -231,11 +239,13 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
MinimumSeedTime = 172800, // 120 hours
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
releaseInfos.Add(release);
|
||||
}
|
||||
|
||||
// order by date
|
||||
return torrentInfos.OrderByDescending(o => o.PublishDate).ToArray();
|
||||
return releaseInfos
|
||||
.OrderByDescending(o => o.PublishDate)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
@@ -400,16 +400,18 @@ namespace NzbDrone.Core.Indexers.Definitions.Cardigann
|
||||
if (login.Captcha != null)
|
||||
{
|
||||
var captcha = login.Captcha;
|
||||
Settings.ExtraFieldData.TryGetValue("CAPTCHA", out var captchaText);
|
||||
if (captchaText != null)
|
||||
|
||||
if (Settings.ExtraFieldData.TryGetValue("CAPTCHA", out var captchaText) && ((string)captchaText).IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var input = captcha.Input;
|
||||
|
||||
if (login.Selectors)
|
||||
{
|
||||
var inputElement = landingResultDocument.QuerySelector(captcha.Input);
|
||||
|
||||
if (inputElement == null)
|
||||
{
|
||||
throw new CardigannConfigException(_definition, string.Format("Login failed: No captcha input found using {0}", captcha.Input));
|
||||
throw new CardigannConfigException(_definition, $"Login failed: No captcha input found using {captcha.Input}");
|
||||
}
|
||||
|
||||
input = inputElement.GetAttribute("name");
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
@@ -64,6 +65,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
query.Medium = Settings.Mediums.ToArray();
|
||||
|
||||
request.SetContent(query.ToJson());
|
||||
request.ContentSummary = query.ToJson(Formatting.None);
|
||||
|
||||
yield return new IndexerRequest(request);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
[Obsolete("Moved to YML for Cardigann")]
|
||||
public class PornoLab : TorrentIndexerBase<PornoLabSettings>
|
||||
{
|
||||
public override string Name => "PornoLab";
|
||||
|
||||
@@ -338,6 +338,7 @@
|
||||
"Query": "Query",
|
||||
"QueryOptions": "Query Options",
|
||||
"QueryResults": "Query Results",
|
||||
"QueryType": "Query Type",
|
||||
"Queue": "Queue",
|
||||
"Queued": "Queued",
|
||||
"RSS": "RSS",
|
||||
|
||||
Reference in New Issue
Block a user