diff --git a/frontend/src/Components/Form/CardigannCaptchaInput.js b/frontend/src/Components/Form/CardigannCaptchaInput.js
new file mode 100644
index 000000000..73aef732f
--- /dev/null
+++ b/frontend/src/Components/Form/CardigannCaptchaInput.js
@@ -0,0 +1,84 @@
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+import Icon from 'Components/Icon';
+import { icons } from 'Helpers/Props';
+import FormInputButton from './FormInputButton';
+import TextInput from './TextInput';
+import styles from './CaptchaInput.css';
+
+function CardigannCaptchaInput(props) {
+ const {
+ className,
+ name,
+ value,
+ hasError,
+ hasWarning,
+ type,
+ refreshing,
+ contentType,
+ imageData,
+ onChange,
+ onRefreshPress
+ } = props;
+
+ const img = `data:${contentType};base64,${imageData}`;
+
+ return (
+
+
+
+
+
+
+
+
+
+ {
+ type === 'image' &&
+
+

+
+ }
+
+ );
+}
+
+CardigannCaptchaInput.propTypes = {
+ className: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ hasError: PropTypes.bool,
+ hasWarning: PropTypes.bool,
+ type: PropTypes.string,
+ refreshing: PropTypes.bool.isRequired,
+ contentType: PropTypes.string.isRequired,
+ imageData: PropTypes.string,
+ onChange: PropTypes.func.isRequired,
+ onRefreshPress: PropTypes.func.isRequired,
+ onCaptchaChange: PropTypes.func.isRequired
+};
+
+CardigannCaptchaInput.defaultProps = {
+ className: styles.input,
+ value: ''
+};
+
+export default CardigannCaptchaInput;
diff --git a/frontend/src/Components/Form/CardigannCaptchaInputConnector.js b/frontend/src/Components/Form/CardigannCaptchaInputConnector.js
new file mode 100644
index 000000000..728755bd9
--- /dev/null
+++ b/frontend/src/Components/Form/CardigannCaptchaInputConnector.js
@@ -0,0 +1,77 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
+import { getCaptchaCookie, refreshCaptcha, resetCaptcha } from 'Store/Actions/captchaActions';
+import CardigannCaptchaInput from './CardigannCaptchaInput';
+
+function createMapStateToProps() {
+ return createSelector(
+ (state) => state.captcha,
+ (captcha) => {
+ return captcha;
+ }
+ );
+}
+
+const mapDispatchToProps = {
+ refreshCaptcha,
+ getCaptchaCookie,
+ resetCaptcha
+};
+
+class CardigannCaptchaInputConnector extends Component {
+
+ //
+ // Lifecycle
+
+ componentDidMount() {
+ this.onRefreshPress();
+ }
+
+ componentWillUnmount = () => {
+ this.props.resetCaptcha();
+ }
+
+ //
+ // Listeners
+
+ onRefreshPress = () => {
+ const {
+ provider,
+ providerData,
+ name,
+ onChange
+ } = this.props;
+
+ onChange({ name, value: '' });
+ this.props.resetCaptcha();
+ this.props.refreshCaptcha({ provider, providerData });
+
+ }
+
+ //
+ // Render
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+CardigannCaptchaInputConnector.propTypes = {
+ provider: PropTypes.string.isRequired,
+ providerData: PropTypes.object.isRequired,
+ name: PropTypes.string.isRequired,
+ token: PropTypes.string,
+ onChange: PropTypes.func.isRequired,
+ refreshCaptcha: PropTypes.func.isRequired,
+ getCaptchaCookie: PropTypes.func.isRequired,
+ resetCaptcha: PropTypes.func.isRequired
+};
+
+export default connect(createMapStateToProps, mapDispatchToProps)(CardigannCaptchaInputConnector);
diff --git a/frontend/src/Components/Form/FormInputGroup.js b/frontend/src/Components/Form/FormInputGroup.js
index 068e831f2..97775c2a7 100644
--- a/frontend/src/Components/Form/FormInputGroup.js
+++ b/frontend/src/Components/Form/FormInputGroup.js
@@ -6,6 +6,7 @@ import translate from 'Utilities/String/translate';
import AutoCompleteInput from './AutoCompleteInput';
import AvailabilitySelectInput from './AvailabilitySelectInput';
import CaptchaInputConnector from './CaptchaInputConnector';
+import CardigannCaptchaInputConnector from './CardigannCaptchaInputConnector';
import CheckInput from './CheckInput';
import DeviceInputConnector from './DeviceInputConnector';
import EnhancedSelectInput from './EnhancedSelectInput';
@@ -37,6 +38,9 @@ function getComponent(type) {
case inputTypes.CAPTCHA:
return CaptchaInputConnector;
+ case inputTypes.CARDIGANNCAPTCHA:
+ return CardigannCaptchaInputConnector;
+
case inputTypes.CHECK:
return CheckInput;
diff --git a/frontend/src/Components/Form/ProviderFieldFormGroup.js b/frontend/src/Components/Form/ProviderFieldFormGroup.js
index 3095bfb7f..3a9a2f5a5 100644
--- a/frontend/src/Components/Form/ProviderFieldFormGroup.js
+++ b/frontend/src/Components/Form/ProviderFieldFormGroup.js
@@ -10,6 +10,8 @@ function getType({ type, selectOptionsProviderAction }) {
switch (type) {
case 'captcha':
return inputTypes.CAPTCHA;
+ case 'cardigannCaptcha':
+ return inputTypes.CARDIGANNCAPTCHA;
case 'checkbox':
return inputTypes.CHECK;
case 'device':
diff --git a/frontend/src/Helpers/Props/inputTypes.js b/frontend/src/Helpers/Props/inputTypes.js
index c6b94a87f..ef4af8252 100644
--- a/frontend/src/Helpers/Props/inputTypes.js
+++ b/frontend/src/Helpers/Props/inputTypes.js
@@ -1,6 +1,7 @@
export const AUTO_COMPLETE = 'autoComplete';
export const AVAILABILITY_SELECT = 'availabilitySelect';
export const CAPTCHA = 'captcha';
+export const CARDIGANNCAPTCHA = 'cardigannCaptcha';
export const CHECK = 'check';
export const DEVICE = 'device';
export const INFO = 'info';
@@ -21,6 +22,7 @@ export const all = [
AUTO_COMPLETE,
AVAILABILITY_SELECT,
CAPTCHA,
+ CARDIGANNCAPTCHA,
CHECK,
DEVICE,
INFO,
diff --git a/frontend/src/Store/Actions/captchaActions.js b/frontend/src/Store/Actions/captchaActions.js
index c83d231b7..a1fa1df2b 100644
--- a/frontend/src/Store/Actions/captchaActions.js
+++ b/frontend/src/Store/Actions/captchaActions.js
@@ -20,7 +20,10 @@ export const defaultState = {
secretToken: null,
ray: null,
stoken: null,
- responseUrl: null
+ responseUrl: null,
+ type: null,
+ contentType: null,
+ imageData: null
};
//
diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Captcha.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Captcha.cs
new file mode 100644
index 000000000..bed95162d
--- /dev/null
+++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Captcha.cs
@@ -0,0 +1,9 @@
+namespace NzbDrone.Core.Indexers.Definitions.Cardigann
+{
+ public class Captcha
+ {
+ public string Type { get; set; } = "image";
+ public string ContentType { get; set; }
+ public byte[] ImageData { get; set; }
+ }
+}
diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs
index 7e421b8b3..f714fe866 100644
--- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs
+++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs
@@ -1,7 +1,10 @@
+using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using FluentValidation.Results;
using NLog;
+using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerVersions;
@@ -13,6 +16,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
public class Cardigann : HttpIndexerBase
{
private readonly IIndexerDefinitionUpdateService _definitionService;
+ private readonly ICached _generatorCache;
public override string Name => "Cardigann";
public override string BaseUrl => "";
@@ -23,21 +27,24 @@ namespace NzbDrone.Core.Indexers.Cardigann
public override IIndexerRequestGenerator GetRequestGenerator()
{
- return new CardigannRequestGenerator(_configService,
- _definitionService.GetDefinition(Settings.DefinitionFile),
- Settings,
- _logger)
- {
- HttpClient = _httpClient
- };
+ return _generatorCache.Get(Settings.DefinitionFile, () =>
+ new CardigannRequestGenerator(_configService,
+ _definitionService.GetDefinition(Settings.DefinitionFile),
+ _logger)
+ {
+ HttpClient = _httpClient,
+ Settings = Settings
+ });
}
public override IParseIndexerResponse GetParser()
{
return new CardigannParser(_configService,
- _definitionService.GetDefinition(Settings.DefinitionFile),
- Settings,
- _logger);
+ _definitionService.GetDefinition(Settings.DefinitionFile),
+ _logger)
+ {
+ Settings = Settings
+ };
}
public override IEnumerable DefaultDefinitions
@@ -55,10 +62,12 @@ namespace NzbDrone.Core.Indexers.Cardigann
IHttpClient httpClient,
IIndexerStatusService indexerStatusService,
IConfigService configService,
+ ICacheManager cacheManager,
Logger logger)
: base(httpClient, indexerStatusService, configService, logger)
{
_definitionService = definitionService;
+ _generatorCache = cacheManager.GetRollingCache(GetType(), "CardigannGeneratorCache", TimeSpan.FromMinutes(5));
}
private IndexerDefinition GetDefinition(CardigannMetaDefinition definition)
@@ -71,6 +80,16 @@ namespace NzbDrone.Core.Indexers.Cardigann
var settings = definition.Settings ?? defaultSettings;
+ if (definition.Login?.Captcha != null)
+ {
+ settings.Add(new SettingsField
+ {
+ Name = "cardigannCaptcha",
+ Type = "cardigannCaptcha",
+ Label = "CAPTCHA"
+ });
+ }
+
return new IndexerDefinition
{
Enable = true,
@@ -93,6 +112,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
SetCookieFunctions(generator);
+ generator.Settings = Settings;
+
return generator.CheckIfLoginIsNeeded(httpResponse);
}
@@ -102,6 +123,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
SetCookieFunctions(generator);
+ generator.Settings = Settings;
+
await generator.DoLogin();
}
@@ -113,5 +136,21 @@ namespace NzbDrone.Core.Indexers.Cardigann
return;
}
}
+
+ public override object RequestAction(string action, IDictionary query)
+ {
+ if (action == "checkCaptcha")
+ {
+ var generator = (CardigannRequestGenerator)GetRequestGenerator();
+
+ var result = generator.GetConfigurationForSetup(false).GetAwaiter().GetResult();
+ return new
+ {
+ captchaRequest = result
+ };
+ }
+
+ return null;
+ }
}
}
diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs
index 93902b9e9..e04a17aa3 100644
--- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs
+++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs
@@ -18,7 +18,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
public class CardigannBase
{
protected readonly CardigannDefinition _definition;
- protected readonly CardigannSettings _settings;
protected readonly Logger _logger;
protected readonly Encoding _encoding;
protected readonly IConfigService _configService;
@@ -48,14 +47,14 @@ namespace NzbDrone.Core.Indexers.Cardigann
protected static readonly Regex _LogicFunctionRegex = new Regex(
$@"\b({string.Join("|", _SupportedLogicFunctions.Select(Regex.Escape))})(?:\s+(\(?\.[^\)\s]+\)?|""[^""]+"")){{2,}}");
+ public CardigannSettings Settings { get; set; }
+
public CardigannBase(IConfigService configService,
CardigannDefinition definition,
- CardigannSettings settings,
Logger logger)
{
_configService = configService;
_definition = definition;
- _settings = settings;
_encoding = Encoding.GetEncoding(definition.Encoding);
_logger = logger;
@@ -224,7 +223,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
foreach (var setting in _definition.Settings)
{
var name = ".Config." + setting.Name;
- var value = _settings.ExtraFieldData.GetValueOrDefault(setting.Name, setting.Default);
+ var value = Settings.ExtraFieldData.GetValueOrDefault(setting.Name, setting.Default);
if (setting.Type != "password" && indexerLogging)
{
@@ -260,6 +259,9 @@ namespace NzbDrone.Core.Indexers.Cardigann
{
variables[name] = value;
}
+ else if (setting.Type == "cardigannCaptcha")
+ {
+ }
else
{
throw new NotSupportedException();
diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannMetaDef.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannMetaDef.cs
index 6fb1645be..18d6ac4e3 100644
--- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannMetaDef.cs
+++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannMetaDef.cs
@@ -15,5 +15,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
public List Legacylinks { get; set; }
public List Settings { get; set; }
public string Sha { get; set; }
+ public LoginBlock Login { get; set; }
}
}
diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs
index 9ed3d296d..d4e81902b 100644
--- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs
+++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs
@@ -20,9 +20,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
public CardigannParser(IConfigService configService,
CardigannDefinition definition,
- CardigannSettings settings,
Logger logger)
- : base(configService, definition, settings, logger)
+ : base(configService, definition, logger)
{
}
diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs
index b74433d88..9f791d74a 100644
--- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs
+++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs
@@ -25,9 +25,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
public CardigannRequestGenerator(IConfigService configService,
CardigannDefinition definition,
- CardigannSettings settings,
Logger logger)
- : base(configService, definition, settings, logger)
+ : base(configService, definition, logger)
{
}
@@ -181,7 +180,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
LogResponseContent = true,
Method = HttpMethod.POST,
AllowAutoRedirect = true,
- SuppressHttpError = true
+ SuppressHttpError = true,
};
foreach (var pair in pairs)
@@ -339,46 +338,22 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (login.Captcha != null)
{
var captcha = login.Captcha;
- if (captcha.Type == "image")
+ Settings.ExtraFieldData.TryGetValue("CAPTCHA", out var captchaText);
+ if (captchaText != null)
{
- _settings.ExtraFieldData.TryGetValue("CaptchaText", out var captchaText);
- if (captchaText != null)
+ var input = captcha.Input;
+ if (login.Selectors)
{
- var input = captcha.Input;
- if (login.Selectors)
+ var inputElement = landingResultDocument.QuerySelector(captcha.Input);
+ if (inputElement == null)
{
- 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));
- }
-
- input = inputElement.GetAttribute("name");
+ throw new CardigannConfigException(_definition, string.Format("Login failed: No captcha input found using {0}", captcha.Input));
}
- pairs[input] = (string)captchaText;
+ input = inputElement.GetAttribute("name");
}
- }
- if (captcha.Type == "text")
- {
- _settings.ExtraFieldData.TryGetValue("CaptchaAnswer", out var captchaAnswer);
- if (captchaAnswer != null)
- {
- 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));
- }
-
- input = inputElement.GetAttribute("name");
- }
-
- pairs[input] = (string)captchaAnswer;
- }
+ pairs[input] = (string)captchaText;
}
}
@@ -462,7 +437,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
else if (login.Method == "cookie")
{
CookiesUpdater(null, null);
- _settings.ExtraFieldData.TryGetValue("cookie", out var cookies);
+ Settings.ExtraFieldData.TryGetValue("cookie", out var cookies);
CookiesUpdater(CookieUtil.CookieHeaderToDictionary((string)cookies), DateTime.Now + TimeSpan.FromDays(30));
}
else if (login.Method == "get")
@@ -557,13 +532,13 @@ namespace NzbDrone.Core.Indexers.Cardigann
return true;
}
- public async Task GetConfigurationForSetup(bool automaticlogin)
+ public async Task GetConfigurationForSetup(bool automaticlogin)
{
var login = _definition.Login;
if (login == null || login.Method != "form")
{
- return;
+ return null;
}
var loginUrl = ResolvePath(login.Path);
@@ -588,7 +563,9 @@ namespace NzbDrone.Core.Indexers.Cardigann
requestBuilder.SetCookies(Cookies);
}
- landingResult = await HttpClient.ExecuteAsync(requestBuilder.Build());
+ var request = requestBuilder.Build();
+
+ landingResult = await HttpClient.ExecuteAsync(request);
Cookies = landingResult.GetCookies();
@@ -597,122 +574,60 @@ namespace NzbDrone.Core.Indexers.Cardigann
//{
// await FollowIfRedirect(landingResult, loginUrl.AbsoluteUri, overrideCookies: landingResult.Cookies, accumulateCookies: true);
//}
- var hasCaptcha = false;
var htmlParser = new HtmlParser();
landingResultDocument = htmlParser.ParseDocument(landingResult.Content);
+ Captcha captcha = null;
+
if (login.Captcha != null)
{
- var captcha = login.Captcha;
- if (captcha.Type == "image")
+ captcha = await GetCaptcha(login);
+ }
+
+ if (captcha != null && automaticlogin)
+ {
+ _logger.Error(string.Format("CardigannIndexer ({0}): Found captcha during automatic login, aborting", _definition.Id));
+ }
+
+ return captcha;
+ }
+
+ private async Task GetCaptcha(LoginBlock login)
+ {
+ var captcha = login.Captcha;
+
+ if (captcha.Type == "image")
+ {
+ var captchaElement = landingResultDocument.QuerySelector(captcha.Selector);
+ if (captchaElement != null)
{
- var captchaElement = landingResultDocument.QuerySelector(captcha.Selector);
- if (captchaElement != null)
- {
- hasCaptcha = true;
+ var loginUrl = ResolvePath(login.Path);
+ var captchaUrl = ResolvePath(captchaElement.GetAttribute("src"), loginUrl);
- //TODO Bubble this to UI when we get a captcha so that user can action it
- //Jackett does this by inserting image or question into the extrasettings which then show up in the add modal
- //
- //var captchaUrl = ResolvePath(captchaElement.GetAttribute("src"), loginUrl);
- //var captchaImageData = RequestWithCookiesAsync(captchaUrl.ToString(), landingResult.GetCookies, referer: loginUrl.AbsoluteUri);
- // var CaptchaImage = new ImageItem { Name = "Captcha Image" };
- //var CaptchaText = new StringItem { Name = "Captcha Text" };
- //CaptchaImage.Value = captchaImageData.ContentBytes;
- //configData.AddDynamic("CaptchaImage", CaptchaImage);
- //configData.AddDynamic("CaptchaText", CaptchaText);
- }
- else
- {
- _logger.Debug(string.Format("CardigannIndexer ({0}): No captcha image found", _definition.Id));
- }
- }
- else if (captcha.Type == "text")
- {
- var captchaElement = landingResultDocument.QuerySelector(captcha.Selector);
- if (captchaElement != null)
- {
- hasCaptcha = true;
+ var request = new HttpRequestBuilder(captchaUrl.ToString())
+ .SetCookies(landingResult.GetCookies())
+ .SetHeader("Referrer", loginUrl.AbsoluteUri)
+ .Build();
- //var captchaChallenge = new DisplayItem(captchaElement.TextContent) { Name = "Captcha Challenge" };
- //var captchaAnswer = new StringItem { Name = "Captcha Answer" };
+ var response = await HttpClient.ExecuteAsync(request);
- //configData.AddDynamic("CaptchaChallenge", captchaChallenge);
- //configData.AddDynamic("CaptchaAnswer", captchaAnswer);
- }
- else
+ return new Captcha
{
- _logger.Debug(string.Format("CardigannIndexer ({0}): No captcha image found", _definition.Id));
- }
+ ContentType = response.Headers.ContentType,
+ ImageData = response.ResponseData
+ };
}
else
{
- throw new NotImplementedException(string.Format("Captcha type \"{0}\" is not implemented", captcha.Type));
+ _logger.Debug(string.Format("CardigannIndexer ({0}): No captcha image found", _definition.Id));
}
}
-
- if (hasCaptcha && automaticlogin)
+ else
{
- _logger.Error(string.Format("CardigannIndexer ({0}): Found captcha during automatic login, aborting", _definition.Id));
- return;
+ throw new NotImplementedException(string.Format("Captcha type \"{0}\" is not implemented", captcha.Type));
}
- return;
- }
-
- protected async Task TestLogin()
- {
- var login = _definition.Login;
-
- if (login == null || login.Test == null)
- {
- return false;
- }
-
- // test if login was successful
- var loginTestUrl = ResolvePath(login.Test.Path).ToString();
-
- // var headers = ParseCustomHeaders(_definition.Search?.Headers, GetBaseTemplateVariables());
- var requestBuilder = new HttpRequestBuilder(loginTestUrl)
- {
- LogResponseContent = true,
- Method = HttpMethod.GET,
- SuppressHttpError = true
- };
-
- if (Cookies != null)
- {
- requestBuilder.SetCookies(Cookies);
- }
-
- var testResult = await HttpClient.ExecuteAsync(requestBuilder.Build());
-
- if (testResult.HasHttpRedirect)
- {
- var errormessage = "Login Failed, got redirected.";
- var domainHint = GetRedirectDomainHint(testResult);
- if (domainHint != null)
- {
- errormessage += " Try changing the indexer URL to " + domainHint + ".";
- }
-
- _logger.Debug(errormessage);
- return false;
- }
-
- if (login.Test.Selector != null)
- {
- var testResultParser = new HtmlParser();
- var testResultDocument = testResultParser.ParseDocument(testResult.Content);
- var selection = testResultDocument.QuerySelectorAll(login.Test.Selector);
- if (selection.Length == 0)
- {
- _logger.Debug(string.Format("Login failed: Selector \"{0}\" didn't match", login.Test.Selector));
- return false;
- }
- }
-
- return true;
+ return null;
}
protected string GetRedirectDomainHint(string requestUrl, string redirectUrl)
diff --git a/src/NzbDrone.Core/Indexers/IndexerFactory.cs b/src/NzbDrone.Core/Indexers/IndexerFactory.cs
index 5b1ab6d75..ecec57ff0 100644
--- a/src/NzbDrone.Core/Indexers/IndexerFactory.cs
+++ b/src/NzbDrone.Core/Indexers/IndexerFactory.cs
@@ -78,6 +78,17 @@ namespace NzbDrone.Core.Indexers
var settings = (CardigannSettings)definition.Settings;
var defFile = _definitionService.GetDefinition(settings.DefinitionFile);
definition.ExtraFields = defFile.Settings;
+
+ if (defFile.Login?.Captcha != null && !definition.ExtraFields.Any(x => x.Type == "cardigannCaptcha"))
+ {
+ definition.ExtraFields.Add(new SettingsField
+ {
+ Name = "cardigannCaptcha",
+ Type = "cardigannCaptcha",
+ Label = "CAPTCHA"
+ });
+ }
+
definition.BaseUrl = defFile.Links.First();
definition.Privacy = defFile.Type == "private" ? IndexerPrivacy.Private : IndexerPrivacy.Public;
definition.Capabilities = new IndexerCapabilities();
diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs
index ebd93c6ce..c18632a35 100644
--- a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs
+++ b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs
@@ -97,8 +97,15 @@ namespace Prowlarr.Api.V1.Indexers
{
if (!standardFields.Contains(field.Name))
{
- var cardigannSetting = cardigannDefinition.Settings.FirstOrDefault(x => x.Name == field.Name);
- settings.ExtraFieldData[field.Name] = MapValue(cardigannSetting, field.Value);
+ if (field.Name == "cardigannCaptcha")
+ {
+ settings.ExtraFieldData["CAPTCHA"] = field.Value?.ToString() ?? string.Empty;
+ }
+ else
+ {
+ var cardigannSetting = cardigannDefinition.Settings.FirstOrDefault(x => x.Name == field.Name);
+ settings.ExtraFieldData[field.Name] = MapValue(cardigannSetting, field.Value);
+ }
}
}
}
@@ -132,7 +139,7 @@ namespace Prowlarr.Api.V1.Indexers
}
else
{
- return value.ToString();
+ return value?.ToString() ?? string.Empty;
}
}
diff --git a/src/Prowlarr.Api.V1/ProviderControllerBase.cs b/src/Prowlarr.Api.V1/ProviderControllerBase.cs
index 69056edbf..16d187d09 100644
--- a/src/Prowlarr.Api.V1/ProviderControllerBase.cs
+++ b/src/Prowlarr.Api.V1/ProviderControllerBase.cs
@@ -174,7 +174,7 @@ namespace Prowlarr.Api.V1
var data = _providerFactory.RequestAction(providerDefinition, name, query);
- return Content(data.ToJson(), "application/json");
+ return Json(data);
}
protected virtual void Validate(TProviderDefinition definition, bool includeWarnings)