mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-09 15:00:14 -04:00
Compare commits
5 Commits
sonarr-pul
...
sonarr-pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d6023f2f9 | ||
|
|
14d74f2eca | ||
|
|
c3cbbb7627 | ||
|
|
ad3a58c422 | ||
|
|
347b154882 |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -5,9 +5,9 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
- label: I have searched the existing open and closed issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -42,12 +42,14 @@ body:
|
||||
- **Docker Install**: Yes
|
||||
- **Using Reverse Proxy**: No
|
||||
- **Browser**: Firefox 90 (If UI related)
|
||||
- **Database**: Sqlite 3.36.0
|
||||
value: |
|
||||
- OS:
|
||||
- Readarr:
|
||||
- Docker Install:
|
||||
- Using Reverse Proxy:
|
||||
- Browser:
|
||||
- Database:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -7,7 +7,6 @@ using NzbDrone.Core.Test.Framework;
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class QualityParserFixture : CoreTest
|
||||
{
|
||||
public static object[] SelfQualityParserCases =
|
||||
|
||||
@@ -30,9 +30,12 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||
|
||||
if (jsonResponse.Resource.error_code.HasValue)
|
||||
{
|
||||
if (jsonResponse.Resource.error_code == 20 || jsonResponse.Resource.error_code == 8)
|
||||
if (jsonResponse.Resource.error_code == 5 || jsonResponse.Resource.error_code == 8
|
||||
|| jsonResponse.Resource.error_code == 9 || jsonResponse.Resource.error_code == 10
|
||||
|| jsonResponse.Resource.error_code == 13 || jsonResponse.Resource.error_code == 14
|
||||
|| jsonResponse.Resource.error_code == 20)
|
||||
{
|
||||
// No results found
|
||||
// No results, rate limit, tvdbid not found/invalid, or imdbid not found/invalid
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
74
src/NzbDrone.Core/Notifications/Kavita/Kavita.cs
Normal file
74
src/NzbDrone.Core/Notifications/Kavita/Kavita.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public class Kavita : NotificationBase<KavitaSettings>
|
||||
{
|
||||
private readonly IKavitaService _kavitaService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public Kavita(IKavitaService kavitaService, Logger logger)
|
||||
{
|
||||
_kavitaService = kavitaService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override string Link => "https://www.kavitareader.com/";
|
||||
|
||||
public override void OnReleaseImport(BookDownloadMessage message)
|
||||
{
|
||||
var allPaths = message.BookFiles.Select(v => v.Path).Distinct();
|
||||
var path = Directory.GetParent(allPaths.First())?.FullName;
|
||||
Notify(Settings, BOOK_DOWNLOADED_TITLE_BRANDED, path);
|
||||
}
|
||||
|
||||
public override void OnBookDelete(BookDeleteMessage deleteMessage)
|
||||
{
|
||||
var allPaths = deleteMessage.Book.BookFiles.Value.Select(v => v.Path).Distinct();
|
||||
var path = Directory.GetParent(allPaths.First())?.FullName;
|
||||
Notify(Settings, BOOK_FILE_DELETED_TITLE_BRANDED, path);
|
||||
}
|
||||
|
||||
public override void OnBookFileDelete(BookFileDeleteMessage message)
|
||||
{
|
||||
Notify(Settings, BOOK_FILE_DELETED_TITLE_BRANDED, Directory.GetParent(message.BookFile.Path)?.FullName);
|
||||
}
|
||||
|
||||
public override void OnBookRetag(BookRetagMessage message)
|
||||
{
|
||||
Notify(Settings, BOOK_RETAGGED_TITLE_BRANDED, Directory.GetParent(message.BookFile.Path)?.FullName);
|
||||
}
|
||||
|
||||
public override string Name => "Kavita";
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_kavitaService.Test(Settings, "Success! Kavita has been successfully configured!"));
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
private void Notify(KavitaSettings settings, string header, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Settings.Notify)
|
||||
{
|
||||
_kavitaService.Notify(Settings, $"{header} - {message}");
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
var logMessage = $"Unable to connect to Subsonic Host: {Settings.Host}:{Settings.Port}";
|
||||
_logger.Debug(ex, logMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public class KavitaAuthenticationException : KavitaException
|
||||
{
|
||||
public KavitaAuthenticationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public KavitaAuthenticationException(string message, params object[] args)
|
||||
: base(message, args)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public class KavitaAuthenticationResult
|
||||
{
|
||||
[JsonPropertyName("token")]
|
||||
public string Token { get; set; }
|
||||
[JsonPropertyName("apiKey")]
|
||||
public string ApiKey { get; init; }
|
||||
}
|
||||
16
src/NzbDrone.Core/Notifications/Kavita/KavitaException.cs
Normal file
16
src/NzbDrone.Core/Notifications/Kavita/KavitaException.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public class KavitaException : NzbDroneException
|
||||
{
|
||||
public KavitaException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public KavitaException(string message, params object[] args)
|
||||
: base(message, args)
|
||||
{
|
||||
}
|
||||
}
|
||||
56
src/NzbDrone.Core/Notifications/Kavita/KavitaService.cs
Normal file
56
src/NzbDrone.Core/Notifications/Kavita/KavitaService.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public interface IKavitaService
|
||||
{
|
||||
void Notify(KavitaSettings settings, string message);
|
||||
ValidationFailure Test(KavitaSettings settings, string message);
|
||||
}
|
||||
|
||||
public class KavitaService : IKavitaService
|
||||
{
|
||||
private readonly IKavitaServiceProxy _proxy;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public KavitaService(IKavitaServiceProxy proxy,
|
||||
Logger logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Notify(KavitaSettings settings, string folderPath)
|
||||
{
|
||||
_proxy.Notify(settings, folderPath);
|
||||
}
|
||||
|
||||
private string GetToken(KavitaSettings settings)
|
||||
{
|
||||
return _proxy.GetToken(settings);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(KavitaSettings settings, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Debug("Determining Authentication of Host: {0}", _proxy.GetBaseUrl(settings));
|
||||
var token = GetToken(settings);
|
||||
_logger.Debug("Token is: {0}", token);
|
||||
}
|
||||
catch (KavitaAuthenticationException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to connect to Kavita Server");
|
||||
return new ValidationFailure("ApiKey", "Incorrect ApiKey");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to connect to Kavita Server");
|
||||
return new ValidationFailure("Host", "Unable to connect to Kavita Server");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
87
src/NzbDrone.Core/Notifications/Kavita/KavitaServiceProxy.cs
Normal file
87
src/NzbDrone.Core/Notifications/Kavita/KavitaServiceProxy.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public interface IKavitaServiceProxy
|
||||
{
|
||||
string GetBaseUrl(KavitaSettings settings, string relativePath = null);
|
||||
void Notify(KavitaSettings settings, string message);
|
||||
string GetToken(KavitaSettings settings);
|
||||
}
|
||||
|
||||
public class KavitaServiceProxy : IKavitaServiceProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public KavitaServiceProxy(IHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GetBaseUrl(KavitaSettings settings, string relativePath = null)
|
||||
{
|
||||
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, string.Empty);
|
||||
baseUrl = HttpUri.CombinePath(baseUrl, relativePath);
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void Notify(KavitaSettings settings, string folderPath)
|
||||
{
|
||||
var request = GetKavitaServerRequest("library/scan-folder", HttpMethod.Post, settings);
|
||||
request.Headers.ContentType = "application/json";
|
||||
var postRequest = request.Build();
|
||||
postRequest.SetContent(new
|
||||
{
|
||||
ApiKey = settings.ApiKey,
|
||||
FolderPath = folderPath.Replace("/", "//")
|
||||
}.ToJson());
|
||||
|
||||
var response = _httpClient.Post(postRequest);
|
||||
_logger.Trace("Update response: {0}", string.IsNullOrEmpty(response.Content) ? "Success" : response.Content);
|
||||
}
|
||||
|
||||
public string GetToken(KavitaSettings settings)
|
||||
{
|
||||
var request = GetKavitaServerRequest("plugin/authenticate", HttpMethod.Post, settings);
|
||||
request.AddQueryParam("apiKey", settings.ApiKey)
|
||||
.AddQueryParam("pluginName", BuildInfo.AppName);
|
||||
var response = _httpClient.Execute(request.Build());
|
||||
|
||||
_logger.Trace("Authenticate response: {0}", response.Content);
|
||||
|
||||
var authResult = JsonSerializer.Deserialize<KavitaAuthenticationResult>(response.Content);
|
||||
|
||||
if (authResult == null)
|
||||
{
|
||||
throw new KavitaException("Could not authenticate with Kavita");
|
||||
}
|
||||
|
||||
return authResult.Token;
|
||||
}
|
||||
|
||||
private HttpRequestBuilder GetKavitaServerRequest(string resource, HttpMethod method, KavitaSettings settings)
|
||||
{
|
||||
var client = new HttpRequestBuilder(GetBaseUrl(settings, "api"));
|
||||
|
||||
client.Resource(resource);
|
||||
|
||||
if (settings.ApiKey.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
client.Headers["x-kavita-apikey"] = settings.ApiKey;
|
||||
client.Headers["x-kavita-plugin"] = BuildInfo.AppName;
|
||||
}
|
||||
|
||||
client.Method = method;
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
46
src/NzbDrone.Core/Notifications/Kavita/KavitaSettings.cs
Normal file
46
src/NzbDrone.Core/Notifications/Kavita/KavitaSettings.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Kavita;
|
||||
|
||||
public class KavitaSettingsValidator : AbstractValidator<KavitaSettings>
|
||||
{
|
||||
public KavitaSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Host).ValidHost();
|
||||
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
|
||||
RuleFor(c => c.ApiKey).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class KavitaSettings : IProviderConfig
|
||||
{
|
||||
private static readonly KavitaSettingsValidator Validator = new KavitaSettingsValidator();
|
||||
|
||||
public KavitaSettings()
|
||||
{
|
||||
Port = 4040;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Host")]
|
||||
public string Host { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpLink = "https://wiki.kavitareader.com/en/guides/settings/opds")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Connect to Kavita over HTTPS instead of HTTP")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Update Library", Type = FieldType.Checkbox)]
|
||||
public bool Notify { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Parser
|
||||
private static readonly Regex RepackRegex = new Regex(@"\b(?<repack>repack|rerip)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex VersionRegex = new Regex(@"\dv(?<version>\d)\b|\[v(?<version>\d)\]",
|
||||
private static readonly Regex VersionRegex = new Regex(@"\d[-._ ]?v(?<version>\d)[-._ ]|\[v(?<version>\d)\]",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Readarr.Http.Authentication
|
||||
options.AccessDeniedPath = "/login?loginFailed=true";
|
||||
options.LoginPath = "/login";
|
||||
options.ExpireTimeSpan = TimeSpan.FromDays(7);
|
||||
options.SlidingExpiration = true;
|
||||
})
|
||||
.AddApiKey("API", options =>
|
||||
{
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using Readarr.Http.Extensions;
|
||||
@@ -18,14 +15,17 @@ namespace Readarr.Http.Authentication
|
||||
|
||||
public class AuthenticationService : IAuthenticationService
|
||||
{
|
||||
private const string AnonymousUser = "Anonymous";
|
||||
private static readonly Logger _authLogger = LogManager.GetLogger("Auth");
|
||||
private readonly IUserService _userService;
|
||||
|
||||
private static string API_KEY;
|
||||
private static AuthenticationType AUTH_METHOD;
|
||||
|
||||
public AuthenticationService(IConfigFileProvider configFileProvider, IUserService userService)
|
||||
{
|
||||
_userService = userService;
|
||||
API_KEY = configFileProvider.ApiKey;
|
||||
AUTH_METHOD = configFileProvider.AuthenticationMethod;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user