Compare commits

..

20 Commits

Author SHA1 Message Date
Qstick
580fc528e5 Fix Donation Links 2022-06-24 18:49:08 -05:00
Qstick
dfed229a1d Fix Tooltips in Dark Theme 2022-06-24 18:46:58 -05:00
bakerboy448
e76a255229 Fixed: (AnimeBytes) Cleanse Passkey from response
Fixes #1041
2022-06-24 09:54:36 -05:00
Qstick
a0b650e7a5 Fixed: (Cardigann) Use variables in keywordsfilters block
Fixes #1035
Fixes v5 TorrentLand
2022-06-23 22:22:30 -05:00
Qstick
7cf9fc6a4f New: (BeyondHD) Better status messages for failures
Closes #1028
2022-06-23 20:56:07 -05:00
Qstick
86f5768461 Fixed: VIP Healthcheck not triggered for expired indexers 2022-06-23 20:36:13 -05:00
ta264
479e29dde7 Use DryIoc for Automoqer, drop Unity dependency
(cherry picked from commit e3468daba04b52fbf41ce3004934a26b0220ec4f)
2022-06-22 10:57:36 +01:00
olly
761e15a476 New: Send description element in nab response 2022-06-21 09:16:07 -05:00
Davo1624
d3ffb7b490 (Filelist) Update help text for pass key (#1039) 2022-06-21 09:14:02 -05:00
Qstick
0db804b647 Fixed: (Exoticaz) Category parsing kills search/feed
Fixes #938
2022-06-20 21:39:20 -05:00
Qstick
4334e7eef1 New: (PassThePopcorn) Freeleech only option
Fixes #1014
2022-06-11 15:04:35 -05:00
Qstick
fbfb70a1bb Fixed: (Cardigann) Searching with nab Parent should also use Child categories
Fixes #1031
2022-06-11 14:22:09 -05:00
bakerboy448
59e990227d Fixed: Better Cleansing of Tracker Announce Keys
Fixed: Cleanse Notifiarr secret from URL in logs

(cherry picked from commit e6210aede6f7ead197e82572976bc0267d910d46)
(cherry picked from commit ec866082d44d299096112a6c7c232384b1f74505)
2022-06-11 13:42:32 -05:00
Servarr
f0abfae978 Automated API Docs update 2022-06-04 08:47:47 -05:00
Qstick
40e7f80be9 Update FE dev dependencies 2022-06-04 00:42:40 -05:00
ta264
3c913eac24 Ensure .Mono and .Windows projects have all dependencies in build output
Fixes development on linux
2022-05-31 05:35:16 +01:00
Qstick
95a2bd3d03 Fixed: (Gazelle) Parse grouptime as long or date
Closes #973
Fixes #773
Closes #1008
2022-05-19 21:58:35 -05:00
Qstick
d5abde98e1 Fixed: (ExoticaZ) Category Parsing
Fixes #938
2022-05-19 21:23:33 -05:00
Qstick
5d14d2c134 Fixed: Input options background color on mobile 2022-05-18 15:47:17 -04:00
gofaster
ed46c5c86d Fixed: Update AltHub API URL (#1010) 2022-05-18 12:36:16 -05:00
42 changed files with 7025 additions and 1347 deletions

View File

@@ -78,7 +78,7 @@
border: 1px solid var(--inputBorderColor);
border-radius: 4px;
background-color: var(--white);
background-color: var(--inputBackgroundColor);
}
.loading {

View File

@@ -74,7 +74,7 @@ class PageHeader extends Component {
<IconButton
className={styles.donate}
name={icons.HEART}
to="https://opencollective.com/prowlarr"
to="https://prowlarr.com/donate"
size={14}
/>
<IconButton

View File

@@ -7,7 +7,7 @@
position: relative;
&.default {
background-color: var(--white);
background-color: var(--popoverBodyBackgroundColor);
box-shadow: 0 5px 10px var(--popoverShadowColor);
}

View File

@@ -21,7 +21,6 @@ const requiresRestartKeys = [
'bindAddress',
'port',
'urlBase',
'instanceName',
'enableSsl',
'sslPort',
'sslCertPath',

View File

@@ -168,10 +168,11 @@ module.exports = {
//
// Popover
popoverTitleBackgroundColor: '#f7f7f7',
popoverTitleBorderColor: '#ebebeb',
popoverTitleBackgroundColor: '#424242',
popoverTitleBorderColor: '#2a2a2a',
popoverBodyBackgroundColor: '#2a2a2a',
popoverShadowColor: 'rgba(0, 0, 0, 0.2)',
popoverArrowBorderColor: '#fff',
popoverArrowBorderColor: '#2a2a2a',
popoverTitleBackgroundInverseColor: '#595959',
popoverTitleBorderInverseColor: '#707070',

View File

@@ -170,6 +170,7 @@ module.exports = {
popoverTitleBackgroundColor: '#f7f7f7',
popoverTitleBorderColor: '#ebebeb',
popoverBodyBackgroundColor: '#e9e9e9',
popoverShadowColor: 'rgba(0, 0, 0, 0.2)',
popoverArrowBorderColor: '#fff',

View File

@@ -13,7 +13,7 @@ class Donations extends Component {
return (
<FieldSet legend={translate('Donations')}>
<div className={styles.logoContainer} title="Radarr">
<Link to="https://opencollective.com/radarr">
<Link to="https://radarr.video/donate">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-radarr.png`}
@@ -21,7 +21,7 @@ class Donations extends Component {
</Link>
</div>
<div className={styles.logoContainer} title="Lidarr">
<Link to="https://opencollective.com/lidarr">
<Link to="https://lidarr.audio/donate">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-lidarr.png`}
@@ -29,7 +29,7 @@ class Donations extends Component {
</Link>
</div>
<div className={styles.logoContainer} title="Readarr">
<Link to="https://opencollective.com/readarr">
<Link to="https://readarr.com/donate">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-readarr.png`}
@@ -37,7 +37,7 @@ class Donations extends Component {
</Link>
</div>
<div className={styles.logoContainer} title="Prowlarr">
<Link to="https://opencollective.com/prowlarr">
<Link to="https://prowlarr.com/donate">
<img
className={styles.logo}
src={`${window.Prowlarr.urlBase}/Content/Images/Icons/logo-prowlarr.png`}

View File

@@ -78,38 +78,38 @@
"reselect": "4.0.0"
},
"devDependencies": {
"@babel/core": "7.17.8",
"@babel/eslint-parser": "7.17.0",
"@babel/plugin-proposal-class-properties": "7.16.7",
"@babel/plugin-proposal-decorators": "7.17.8",
"@babel/plugin-proposal-export-default-from": "7.16.7",
"@babel/plugin-proposal-export-namespace-from": "7.16.7",
"@babel/plugin-proposal-function-sent": "7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7",
"@babel/core": "7.18.2",
"@babel/eslint-parser": "7.18.2",
"@babel/plugin-proposal-class-properties": "7.17.12",
"@babel/plugin-proposal-decorators": "7.18.2",
"@babel/plugin-proposal-export-default-from": "7.17.12",
"@babel/plugin-proposal-export-namespace-from": "7.17.12",
"@babel/plugin-proposal-function-sent": "7.18.2",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.17.12",
"@babel/plugin-proposal-numeric-separator": "7.16.7",
"@babel/plugin-proposal-optional-chaining": "7.16.7",
"@babel/plugin-proposal-optional-chaining": "7.17.12",
"@babel/plugin-proposal-throw-expressions": "7.16.7",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/preset-env": "7.16.11",
"@babel/preset-react": "7.16.7",
"autoprefixer": "10.4.4",
"babel-loader": "8.2.4",
"@babel/preset-env": "7.18.2",
"@babel/preset-react": "7.17.12",
"autoprefixer": "10.4.7",
"babel-loader": "8.2.5",
"babel-plugin-inline-classnames": "2.0.1",
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
"core-js": "3.21.1",
"core-js": "3.22.8",
"css-loader": "6.7.1",
"eslint": "8.11.0",
"eslint": "8.17.0",
"eslint-plugin-filenames": "1.3.2",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-react": "7.29.4",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-react": "7.30.0",
"eslint-plugin-simple-import-sort": "7.0.0",
"esprint": "3.3.0",
"esprint": "3.6.0",
"file-loader": "6.2.0",
"filemanager-webpack-plugin": "6.1.7",
"html-webpack-plugin": "5.5.0",
"loader-utils": "^3.0.0",
"mini-css-extract-plugin": "2.6.0",
"postcss": "8.4.12",
"postcss": "8.4.14",
"postcss-color-function": "4.1.0",
"postcss-loader": "6.2.1",
"postcss-mixins": "9.0.2",
@@ -121,10 +121,10 @@
"run-sequence": "2.2.1",
"streamqueue": "1.1.2",
"style-loader": "3.3.1",
"stylelint": "14.6.0",
"stylelint": "14.8.5",
"stylelint-order": "5.0.0",
"url-loader": "4.1.1",
"webpack": "5.70.0",
"webpack": "5.73.0",
"webpack-cli": "4.9.2",
"webpack-livereload-plugin": "3.0.2"
}

View File

@@ -31,6 +31,9 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase(@"""download"":""https:\/\/avistaz.to\/rss\/download\/2b51db35e1910123321025a12b9933d2\/tb51db35e1910123321025a12b9933d2.torrent"",")]
[TestCase(@",""info_hash"":""2b51db35e1910123321025a12b9933d2"",")]
// animebytes response
[TestCase(@"""Link"":""https:\/\/animebytes.tv\/torrent\/994064\/download\/tb51db35e1910123321025a12b9933d2"",")]
// danish bytes response
[TestCase(@",""rsskey"":""2b51db35e1910123321025a12b9933d2"",")]
[TestCase(@",""passkey"":""2b51db35e1910123321025a12b9933d2"",")]
@@ -77,20 +80,24 @@ namespace NzbDrone.Common.Test.InstrumentationTests
// Download Station
[TestCase(@"webapi/entry.cgi?api=(removed)&version=2&method=login&account=01233210&passwd=mySecret&format=sid&session=DownloadStation")]
// Tracker Responses
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
// BroadcastheNet
[TestCase(@"method: ""getTorrents"", ""params"": [ ""mySecret"",")]
[TestCase(@"getTorrents(""mySecret"", [asdfasdf], 100, 0)")]
[TestCase(@"""DownloadURL"":""https:\/\/broadcasthe.net\/torrents.php?action=download&id=123&authkey=mySecret&torrent_pass=mySecret""")]
// Notifiarr
// Webhooks - Notifiarr
[TestCase(@"https://xxx.yyy/api/v1/notification/prowlarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
// RSS
[TestCase(@"<atom:link href = ""https://api.nzb.su/api?t=search&amp;extended=1&amp;cat=3030&apikey=mySecret&amp;q=Diggers"" rel=""self"" type=""application/rss+xml"" />")]
// Internal
[TestCase(@"[Info] MigrationController: *** Migrating Database=prowlarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
public void should_clean_message(string message)
{

View File

@@ -11,7 +11,7 @@ namespace NzbDrone.Common.Instrumentation
private static readonly Regex[] CleansingRules = new[]
{
// Url
new Regex(@"(?<=[?&: ;])(apikey|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=]+?)(?= |&|$|<)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?&: ;])(apikey|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=""]+?)(?=[ ""&=]|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"rss\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"rss\.torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
@@ -28,6 +28,9 @@ namespace NzbDrone.Common.Instrumentation
new Regex(@"""C:\\Users\\(?<secret>[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"""/home/(?<secret>[^/""]+?)(/|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
// NzbGet
new Regex(@"""Name""\s*:\s*""[^""]*(username|password)""\s*,\s*""Value""\s*:\s*""(?<secret>[^""]+?)""", RegexOptions.Compiled | RegexOptions.IgnoreCase),
@@ -52,6 +55,7 @@ namespace NzbDrone.Common.Instrumentation
// Indexer Responses
new Regex(@"(?:avistaz|exoticaz|cinemaz|privatehd)\.[a-z]{2,3}\\\/rss\\\/download\\\/(?<secret>[^&=]+?)\\\/(?<secret>[^&=]+?)\.torrent", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?:animebytes)\.[a-z]{2,3}\\\/torrent\\\/[0-9]+\\\/download\\\/(?<secret>[^&=]+?)[""]", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@",""info_hash"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@",""pass[- _]?key"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@",""rss[- _]?key"":""(?<secret>[^&=]+?)"",", RegexOptions.Compiled | RegexOptions.IgnoreCase),

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Definitions;
using NzbDrone.Core.Indexers.Definitions.Avistaz;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
{
[TestFixture]
public class ExoticazFixture : CoreTest<ExoticaZ>
{
[SetUp]
public void Setup()
{
Subject.Definition = new IndexerDefinition()
{
Name = "ExoticaZ",
Settings = new AvistazSettings() { Username = "someuser", Password = "somepass", Pid = "somepid" }
};
}
[Test]
public async Task should_parse_recent_feed_from_ExoticaZ()
{
var recentFeed = ReadAllText(@"Files/Indexers/Exoticaz/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;
releases.Should().HaveCount(100);
releases.First().Should().BeOfType<TorrentInfo>();
var torrentInfo = releases.First() as TorrentInfo;
torrentInfo.Title.Should().Be("[SSIS-419] My first experience is Yua Mikami. From the day I lost my virginity, I was devoted to sex.");
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
torrentInfo.DownloadUrl.Should().Be("https://exoticaz.to/rss/download/(removed)/(removed).torrent");
torrentInfo.InfoUrl.Should().Be("https://exoticaz.to/torrent/64040-ssis-419-my-first-experience-is-yua-mikami-from-the-day-i-lost-my-virginity-i-was-devoted-to-sex");
torrentInfo.CommentUrl.Should().BeNullOrEmpty();
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2022-06-11 11:04:50"));
torrentInfo.Size.Should().Be(7085405541);
torrentInfo.InfoHash.Should().Be("asdjfiasdf54asd7f4a2sdf544asdf");
torrentInfo.MagnetUrl.Should().Be(null);
torrentInfo.Peers.Should().Be(33);
torrentInfo.Seeders.Should().Be(33);
torrentInfo.Categories.First().Id.Should().Be(6040);
}
}
}

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
var torrents = (await Subject.Fetch(new MovieSearchCriteria())).Releases;
torrents.Should().HaveCount(293);
torrents.First().Should().BeOfType<PassThePopcornInfo>();
torrents.First().Should().BeOfType<TorrentInfo>();
var first = torrents.First() as TorrentInfo;

View File

@@ -23,11 +23,10 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check()
{
var enabled = _indexerFactory.Enabled(false);
var indexers = _indexerFactory.AllProviders(false);
var expiringProviders = new List<IIndexer>();
var expiredProviders = new List<IIndexer>();
foreach (var provider in enabled)
foreach (var provider in indexers)
{
var settingsType = provider.Definition.Settings.GetType();
var vipProp = settingsType.GetProperty("VipExpiration");
@@ -44,11 +43,6 @@ namespace NzbDrone.Core.HealthCheck.Checks
continue;
}
if (DateTime.Parse(expiration).Before(DateTime.Now))
{
expiredProviders.Add(provider);
}
if (DateTime.Parse(expiration).Between(DateTime.Now, DateTime.Now.AddDays(7)))
{
expiringProviders.Add(provider);
@@ -64,15 +58,6 @@ namespace NzbDrone.Core.HealthCheck.Checks
"#indexer-vip-expiring");
}
if (!expiredProviders.Empty())
{
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiredClientMessage"),
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
"#indexer-vip-expired");
}
return new HealthCheck(GetType());
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.HealthCheck.Checks
{
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
public class IndexerVIPExpiredCheck : HealthCheckBase
{
private readonly IIndexerFactory _indexerFactory;
public IndexerVIPExpiredCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
: base(localizationService)
{
_indexerFactory = indexerFactory;
}
public override HealthCheck Check()
{
var indexers = _indexerFactory.AllProviders(false);
var expiredProviders = new List<IIndexer>();
foreach (var provider in indexers)
{
var settingsType = provider.Definition.Settings.GetType();
var vipProp = settingsType.GetProperty("VipExpiration");
if (vipProp == null)
{
continue;
}
var expiration = (string)vipProp.GetValue(provider.Definition.Settings);
if (expiration.IsNullOrWhiteSpace())
{
continue;
}
if (DateTime.Parse(expiration).Before(DateTime.Now))
{
expiredProviders.Add(provider);
}
}
if (!expiredProviders.Empty())
{
return new HealthCheck(GetType(),
HealthCheckResult.Error,
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiredClientMessage"),
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
"#indexer-vip-expired");
}
return new HealthCheck(GetType());
}
}
}

View File

@@ -75,6 +75,7 @@ namespace NzbDrone.Core.IndexerSearch
let t = (r as TorrentInfo) ?? new TorrentInfo()
select new XElement("item",
new XElement("title", RemoveInvalidXMLChars(r.Title)),
new XElement("description", RemoveInvalidXMLChars(r.Description)),
new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory
new XElement("prowlarrindexer", new XAttribute("id", r.IndexerId), r.Indexer),
r.InfoUrl == null ? null : new XElement("comments", r.InfoUrl),

View File

@@ -8,6 +8,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
{
public string Url { get; set; }
public string Download { get; set; }
public Dictionary<string, string> Category { get; set; }
[JsonProperty(PropertyName = "movie_tv")]
public AvistazIdInfo MovieTvinfo { get; set; }

View File

@@ -201,6 +201,11 @@ namespace NzbDrone.Core.Indexers.Definitions
var jsonResponse = new HttpResponse<BeyondHDResponse>(indexerHttpResponse);
if (jsonResponse.Resource.StatusCode == 0)
{
throw new IndexerException(indexerResponse, $"Indexer Error: {jsonResponse.Resource.StatusMessage}");
}
foreach (var row in jsonResponse.Resource.Results)
{
var details = row.InfoUrl;
@@ -272,6 +277,11 @@ namespace NzbDrone.Core.Indexers.Definitions
public class BeyondHDResponse
{
[JsonProperty(PropertyName = "status_code")]
public int StatusCode { get; set; }
[JsonProperty(PropertyName = "status_message")]
public string StatusMessage { get; set; }
public List<BeyondHDTorrent> Results { get; set; }
}

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
protected virtual string SiteLink { get; private set; }
protected readonly List<CategoryMapping> _categoryMapping = new List<CategoryMapping>();
protected readonly IndexerCapabilitiesCategories _categories = new IndexerCapabilitiesCategories();
protected readonly List<string> _defaultCategories = new List<string>();
protected readonly string[] OptionalFields = new string[] { "imdb", "imdbid", "rageid", "tmdbid", "tvdbid", "poster", "banner", "description" };
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
continue;
}
AddCategoryMapping(category.Key, cat);
_categories.AddCategoryMapping(category.Key, cat);
}
}
@@ -95,7 +95,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
}
AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
_categories.AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
if (categorymapping.Default)
{
@@ -105,30 +105,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
}
public void AddCategoryMapping(string trackerCategory, IndexerCategory torznabCategory, string trackerCategoryDesc = null)
{
_categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, torznabCategory.Id));
if (trackerCategoryDesc == null)
{
return;
}
// create custom cats (1:1 categories) if trackerCategoryDesc is defined
// - if trackerCategory is "integer" we use that number to generate custom category id
// - if trackerCategory is "string" we compute a hash to generate fixed integer id for the custom category
// the hash is not perfect but it should work in most cases. we can't use sequential numbers because
// categories are updated frequently and the id must be fixed to work in 3rd party apps
if (!int.TryParse(trackerCategory, out var trackerCategoryInt))
{
var hashed = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(trackerCategory));
trackerCategoryInt = BitConverter.ToUInt16(hashed, 0); // id between 0 and 65535 < 100000
}
var customCat = new IndexerCategory(trackerCategoryInt + 100000, trackerCategoryDesc);
_categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, customCat.Id));
}
protected IElement QuerySelector(IElement element, string selector)
{
// AngleSharp doesn't support the :root pseudo selector, so we check for it manually
@@ -362,54 +338,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
return variables;
}
protected ICollection<IndexerCategory> MapTrackerCatToNewznab(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
return new List<IndexerCategory>();
}
var cats = _categoryMapping
.Where(m =>
!string.IsNullOrWhiteSpace(m.TrackerCategory) &&
string.Equals(m.TrackerCategory, input, StringComparison.InvariantCultureIgnoreCase))
.Select(c => NewznabStandardCategory.AllCats.FirstOrDefault(n => n.Id == c.NewzNabCategory) ?? new IndexerCategory { Id = c.NewzNabCategory })
.ToList();
return cats;
}
public List<string> MapTorznabCapsToTrackers(int[] searchCategories, bool mapChildrenCatsToParent = false)
{
if (searchCategories == null)
{
return new List<string>();
}
var results = new List<string>();
results.AddRange(_categoryMapping
.Where(c => searchCategories.Contains(c.NewzNabCategory))
.Select(mapping => mapping.TrackerCategory).Distinct().ToList());
return results;
}
public ICollection<IndexerCategory> MapTrackerCatDescToNewznab(string trackerCategoryDesc)
{
if (string.IsNullOrWhiteSpace(trackerCategoryDesc))
{
return new List<IndexerCategory>();
}
var cats = _categoryMapping
.Where(m =>
!string.IsNullOrWhiteSpace(m.TrackerCategoryDesc) &&
string.Equals(m.TrackerCategoryDesc, trackerCategoryDesc, StringComparison.InvariantCultureIgnoreCase))
.Select(c => NewznabStandardCategory.AllCats.FirstOrDefault(n => n.Id == c.NewzNabCategory) ?? new IndexerCategory { Id = c.NewzNabCategory })
.ToList();
return cats;
}
protected delegate string TemplateTextModifier(string str);
protected string ApplyGoTemplateText(string template, Dictionary<string, object> variables = null, TemplateTextModifier modifier = null)

View File

@@ -451,7 +451,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
value = release.Description;
break;
case "category":
var cats = MapTrackerCatToNewznab(value);
var cats = _categories.MapTrackerCatToNewznab(value);
if (cats.Any())
{
if (release.Categories == null || fieldModifiers.Contains("noappend"))
@@ -467,7 +467,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
value = release.Categories.ToString();
break;
case "categorydesc":
var catsDesc = MapTrackerCatDescToNewznab(value);
var catsDesc = _categories.MapTrackerCatDescToNewznab(value);
if (catsDesc.Any())
{
if (release.Categories == null || fieldModifiers.Contains("noappend"))

View File

@@ -975,7 +975,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
{
var search = _definition.Search;
var mappedCategories = MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]);
var mappedCategories = _categories.MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]);
if (mappedCategories.Count == 0)
{
mappedCategories = _defaultCategories;
@@ -1000,7 +1000,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
variables[".Query.Keywords"] = string.Join(" ", keywordTokens);
variables[".Keywords"] = ApplyFilters((string)variables[".Query.Keywords"], search.Keywordsfilters);
variables[".Keywords"] = ApplyFilters((string)variables[".Query.Keywords"], search.Keywordsfilters, variables);
// TODO: prepare queries first and then send them parallel
var searchPaths = search.Paths;

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Avistaz;
using NzbDrone.Core.Messaging.Events;
@@ -30,15 +30,14 @@ namespace NzbDrone.Core.Indexers.Definitions
};
}
public override IParseIndexerResponse GetParser()
{
return new ExoticaZParser(Capabilities.Categories);
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
}
};
var caps = new IndexerCapabilities();
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.XXXx264, "Video Clip");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.XXXPack, "Video Pack");
@@ -52,4 +51,21 @@ namespace NzbDrone.Core.Indexers.Definitions
return caps;
}
}
public class ExoticaZParser : AvistazParser
{
private readonly IndexerCapabilitiesCategories _categories;
public ExoticaZParser(IndexerCapabilitiesCategories categories)
{
_categories = categories;
}
protected override List<IndexerCategory> ParseCategories(AvistazRelease row)
{
var cat = row.Category;
return cat.SelectMany(c => _categories.MapTrackerCatToNewznab(c.Key)).ToList();
}
}
}

View File

@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Indexers.FileList
[FieldDefinition(2, Label = "Username", HelpText = "Site Username", Privacy = PrivacyLevel.UserName)]
public string Username { get; set; }
[FieldDefinition(3, Label = "Passkey", HelpText = "Site Passkey", Privacy = PrivacyLevel.Password, Type = FieldType.Password)]
[FieldDefinition(3, Label = "Passkey", HelpText = "Site Passkey (This is the alphanumeric string in the tracker url shown in your download client)", Privacy = PrivacyLevel.Password, Type = FieldType.Password)]
public string Passkey { get; set; }
public override NzbDroneValidationResult Validate()

View File

@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
public int TotalSeeders { get; set; }
public int TotalSnatched { get; set; }
public long MaxSize { get; set; }
public long GroupTime { get; set; }
public string GroupTime { get; set; }
public List<GazelleTorrent> Torrents { get; set; }
public bool IsFreeLeech { get; set; }
public bool IsNeutralLeech { get; set; }

View File

@@ -5,6 +5,7 @@ using System.Net;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Gazelle
@@ -120,7 +121,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
Files = result.FileCount,
Grabs = result.Snatches,
PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime,
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime),
PosterUrl = posterUrl,
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1

View File

@@ -90,7 +90,7 @@ namespace NzbDrone.Core.Indexers.Newznab
get
{
yield return GetDefinition("abNZB", GetSettings("https://abnzb.com"));
yield return GetDefinition("altHUB", GetSettings("https://althub.co.za"));
yield return GetDefinition("altHUB", GetSettings("https://api.althub.co.za"));
yield return GetDefinition("AnimeTosho (Usenet)", GetSettings("https://feed.animetosho.org"));
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
yield return GetDefinition("DrunkenSlug", GetSettings("https://drunkenslug.com"));

View File

@@ -1,11 +0,0 @@
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.PassThePopcorn
{
public class PassThePopcornInfo : TorrentInfo
{
public bool? Golden { get; set; }
public bool? Scene { get; set; }
public bool? Approved { get; set; }
}
}

View File

@@ -94,7 +94,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
// Only add approved torrents
try
{
torrentInfos.Add(new PassThePopcornInfo()
torrentInfos.Add(new TorrentInfo()
{
Guid = string.Format("PassThePopcorn-{0}", id),
Title = title,
@@ -104,9 +104,6 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.UploadTime.ToUniversalTime(),
Golden = torrent.GoldenPopcorn,
Scene = torrent.Scene,
Approved = torrent.Checked,
ImdbId = result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0,
IndexerFlags = flags,
MinimumRatio = 1,

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Indexers.PassThePopcorn
{
@@ -37,9 +39,21 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{
var queryParams = new NameValueCollection
{
{ "action", "advanced" },
{ "json", "noredirect" },
{ "searchstr", searchParameters }
};
if (Settings.FreeleechOnly)
{
queryParams.Add("freetorrent", "1");
}
var request =
new IndexerRequest(
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?action=advanced&json=noredirect&searchstr={searchParameters}",
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?{queryParams.GetQueryString()}",
HttpAccept.Json);
request.HttpRequest.Headers["ApiUser"] = Settings.APIUser;

View File

@@ -22,12 +22,15 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
{
}
[FieldDefinition(2, Label = "APIUser", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", Privacy = PrivacyLevel.UserName)]
[FieldDefinition(2, Label = "API User", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", Privacy = PrivacyLevel.UserName)]
public string APIUser { get; set; }
[FieldDefinition(3, Label = "API Key", HelpText = "Site API Key", Privacy = PrivacyLevel.ApiKey)]
public string APIKey { get; set; }
[FieldDefinition(4, Label = "Freeleech Only", HelpText = "Return only freeleech torrents", Type = FieldType.Checkbox)]
public bool FreeleechOnly { get; set; }
public override NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -16,6 +16,7 @@ using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Indexers.Settings;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
@@ -276,7 +277,7 @@ namespace NzbDrone.Core.Indexers.Definitions
InfoUrl = infoUrl,
Seeders = int.Parse(result.Seeders),
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime,
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
Freeleech = result.IsFreeLeech || result.IsPersonalFreeLeech,
Files = result.FileCount,
Grabs = result.Snatches,

View File

@@ -17,6 +17,7 @@ using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
@@ -182,7 +183,7 @@ namespace NzbDrone.Core.Indexers.Definitions
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
Files = result.FileCount,
Grabs = result.Snatches,
PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime,
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
};
var category = result.Category;

View File

@@ -62,10 +62,10 @@ namespace NzbDrone.Core.Validation
return ruleBuilder.WithState(v => NzbDroneValidationState.Warning);
}
public static IRuleBuilderOptions<T, string> StartsOrEndsWithProwlarr<T>(this IRuleBuilder<T, string> ruleBuilder)
public static IRuleBuilderOptions<T, string> ContainsProwlarr<T>(this IRuleBuilder<T, string> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
return ruleBuilder.SetValidator(new RegularExpressionValidator("^Prowlarr|Prowlarr$")).WithMessage("Must start or end with Prowlarr");
return ruleBuilder.SetValidator(new RegularExpressionValidator("prowlarr", RegexOptions.IgnoreCase)).WithMessage("Must contain Prowlarr");
}
}
}

View File

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr18" />

View File

@@ -3,17 +3,11 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using DryIoc;
using Moq;
using Moq.Language.Flow;
using NzbDrone.Common.Composition;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Test.Common.AutoMoq.Unity;
using Unity;
[assembly: InternalsVisibleTo("AutoMoq.Tests")]
namespace NzbDrone.Test.Common.AutoMoq
{
@@ -21,32 +15,18 @@ namespace NzbDrone.Test.Common.AutoMoq
public class AutoMoqer
{
public readonly MockBehavior DefaultBehavior = MockBehavior.Default;
public Type ResolveType;
private IUnityContainer _container;
private IContainer _container;
private IDictionary<Type, object> _registeredMocks;
public AutoMoqer()
{
SetupAutoMoqer(new UnityContainer());
}
public AutoMoqer(MockBehavior defaultBehavior)
{
DefaultBehavior = defaultBehavior;
SetupAutoMoqer(new UnityContainer());
}
public AutoMoqer(IUnityContainer container)
{
SetupAutoMoqer(container);
SetupAutoMoqer(CreateTestContainer(new Container()));
}
public virtual T Resolve<T>()
{
ResolveType = typeof(T);
var result = _container.Resolve<T>();
SetConstant(result);
ResolveType = null;
return result;
}
@@ -59,7 +39,6 @@ namespace NzbDrone.Test.Common.AutoMoq
public virtual Mock<T> GetMock<T>(MockBehavior behavior)
where T : class
{
ResolveType = null;
var type = GetTheMockType<T>();
if (GetMockHasNotBeenCalledForThisType(type))
{
@@ -78,87 +57,81 @@ namespace NzbDrone.Test.Common.AutoMoq
public virtual void SetMock(Type type, Mock mock)
{
if (_registeredMocks.ContainsKey(type) == false)
if (GetMockHasNotBeenCalledForThisType(type))
{
_registeredMocks.Add(type, mock);
}
if (mock != null)
{
_container.RegisterInstance(type, mock.Object);
_container.RegisterInstance(type, mock.Object, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
}
}
public virtual void SetConstant<T>(T instance)
{
_container.RegisterInstance(instance);
_container.RegisterInstance(instance, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
SetMock(instance.GetType(), null);
}
public ISetup<T> Setup<T>(Expression<Action<T>> expression)
where T : class
private IContainer CreateTestContainer(IContainer container)
{
return GetMock<T>().Setup(expression);
var c = container.CreateChild(IfAlreadyRegistered.Replace,
container.Rules
.WithDynamicRegistration((serviceType, serviceKey) =>
{
// ignore services with non-default key
if (serviceKey != null)
{
return null;
}
if (serviceType == typeof(object))
{
return null;
}
if (serviceType.IsGenericType && serviceType.IsOpenGeneric())
{
return null;
}
// get the Mock object for the abstract class or interface
if (serviceType.IsInterface || serviceType.IsAbstract)
{
var mockType = typeof(Mock<>).MakeGenericType(serviceType);
var mockFactory = new DelegateFactory(r =>
{
var mock = (Mock)r.Resolve(mockType);
SetMock(serviceType, mock);
return mock.Object;
}, Reuse.Singleton);
return new[] { new DynamicRegistration(mockFactory, IfAlreadyRegistered.Keep) };
}
// concrete types
var concreteTypeFactory = serviceType.ToFactory(Reuse.Singleton, FactoryMethod.ConstructorWithResolvableArgumentsIncludingNonPublic);
return new[] { new DynamicRegistration(concreteTypeFactory) };
},
DynamicRegistrationFlags.Service | DynamicRegistrationFlags.AsFallback));
c.Register(typeof(Mock<>), Reuse.Singleton, FactoryMethod.DefaultConstructor());
return c;
}
public ISetup<T, TResult> Setup<T, TResult>(Expression<Func<T, TResult>> expression)
where T : class
{
return GetMock<T>().Setup(expression);
}
public void Verify<T>(Expression<Action<T>> expression)
where T : class
{
GetMock<T>().Verify(expression);
}
public void Verify<T>(Expression<Action<T>> expression, string failMessage)
where T : class
{
GetMock<T>().Verify(expression, failMessage);
}
public void Verify<T>(Expression<Action<T>> expression, Times times)
where T : class
{
GetMock<T>().Verify(expression, times);
}
public void Verify<T>(Expression<Action<T>> expression, Times times, string failMessage)
where T : class
{
GetMock<T>().Verify(expression, times, failMessage);
}
public void VerifyAllMocks()
{
foreach (var registeredMock in _registeredMocks)
{
var mock = registeredMock.Value as Mock;
if (mock != null)
{
mock.VerifyAll();
}
}
}
private void SetupAutoMoqer(IUnityContainer container)
private void SetupAutoMoqer(IContainer container)
{
_container = container;
container.RegisterInstance(this);
RegisterPlatformLibrary(container);
_registeredMocks = new Dictionary<Type, object>();
AddTheAutoMockingContainerExtensionToTheContainer(container);
AssemblyLoader.RegisterSQLiteResolver();
}
private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container)
{
container.AddNewExtension<AutoMockingContainerExtension>();
return;
LoadPlatformLibrary();
AssemblyLoader.RegisterSQLiteResolver();
}
private Mock<T> TheRegisteredMockForThisType<T>(Type type)
@@ -177,7 +150,7 @@ namespace NzbDrone.Test.Common.AutoMoq
private bool GetMockHasNotBeenCalledForThisType(Type type)
{
return _registeredMocks.ContainsKey(type) == false;
return !_registeredMocks.ContainsKey(type);
}
private static Type GetTheMockType<T>()
@@ -186,7 +159,7 @@ namespace NzbDrone.Test.Common.AutoMoq
return typeof(T);
}
private void RegisterPlatformLibrary(IUnityContainer container)
private void LoadPlatformLibrary()
{
var assemblyName = "Prowlarr.Windows";

View File

@@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Moq;
using Unity;
using Unity.Builder;
using Unity.Strategies;
namespace NzbDrone.Test.Common.AutoMoq.Unity
{
public class AutoMockingBuilderStrategy : BuilderStrategy
{
private readonly IUnityContainer _container;
private readonly MockRepository _mockFactory;
private readonly IEnumerable<Type> _registeredTypes;
public AutoMockingBuilderStrategy(IEnumerable<Type> registeredTypes, IUnityContainer container)
{
var autoMoqer = container.Resolve<AutoMoqer>();
_mockFactory = new MockRepository(autoMoqer.DefaultBehavior);
_registeredTypes = registeredTypes;
_container = container;
}
public override void PreBuildUp(ref BuilderContext context)
{
var autoMoqer = _container.Resolve<AutoMoqer>();
var type = GetTheTypeFromTheBuilderContext(context);
if (AMockObjectShouldBeCreatedForThisType(type))
{
var mock = CreateAMockObject(type);
context.Existing = mock.Object;
autoMoqer.SetMock(type, mock);
}
}
private bool AMockObjectShouldBeCreatedForThisType(Type type)
{
var mocker = _container.Resolve<AutoMoqer>();
return TypeIsNotRegistered(type) && (mocker.ResolveType == null || mocker.ResolveType != type);
}
private static Type GetTheTypeFromTheBuilderContext(BuilderContext context)
{
// return (context.OriginalBuildKey).Type;
return context.Type;
}
private bool TypeIsNotRegistered(Type type)
{
return _registeredTypes.Any(x => x.Equals(type)) == false;
}
private Mock CreateAMockObject(Type type)
{
var createMethod = GenerateAnInterfaceMockCreationMethod(type);
return InvokeTheMockCreationMethod(createMethod);
}
private Mock InvokeTheMockCreationMethod(MethodInfo createMethod)
{
return (Mock)createMethod.Invoke(_mockFactory, new object[] { new List<object>().ToArray() });
}
private MethodInfo GenerateAnInterfaceMockCreationMethod(Type type)
{
var createMethodWithNoParameters = _mockFactory.GetType().GetMethod("Create", EmptyArgumentList());
return createMethodWithNoParameters.MakeGenericMethod(new[] { type });
}
private static Type[] EmptyArgumentList()
{
return new[] { typeof(object[]) };
}
}
}

View File

@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using Unity.Builder;
using Unity.Extension;
namespace NzbDrone.Test.Common.AutoMoq.Unity
{
public class AutoMockingContainerExtension : UnityContainerExtension
{
private readonly IList<Type> _registeredTypes = new List<Type>();
protected override void Initialize()
{
SetEventsOnContainerToTrackAllRegisteredTypes();
SetBuildingStrategyForBuildingUnregisteredTypes();
}
private void SetEventsOnContainerToTrackAllRegisteredTypes()
{
Context.Registering += (sender, e) => RegisterType(e.TypeFrom);
Context.RegisteringInstance += (sender, e) => RegisterType(e.RegisteredType);
}
private void RegisterType(Type typeToRegister)
{
_registeredTypes.Add(typeToRegister);
}
private void SetBuildingStrategyForBuildingUnregisteredTypes()
{
var strategy = new AutoMockingBuilderStrategy(_registeredTypes, Container);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
}
}

View File

@@ -10,7 +10,6 @@
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="RestSharp" Version="106.15.0" />
<PackageReference Include="RestSharp.Serializers.SystemTextJson" Version="106.15.0" />
<PackageReference Include="Unity" Version="5.11.10" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" />

View File

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="4.7.14" />
@@ -9,4 +10,4 @@
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\Prowlarr.Common.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@@ -40,7 +40,7 @@ namespace Prowlarr.Api.V1.Config
SharedValidator.RuleFor(c => c.Port).ValidPort();
SharedValidator.RuleFor(c => c.UrlBase).ValidUrlBase();
SharedValidator.RuleFor(c => c.InstanceName).StartsOrEndsWithProwlarr();
SharedValidator.RuleFor(c => c.InstanceName).ContainsProwlarr().When(c => c.InstanceName.IsNotNullOrWhiteSpace());
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationMethod != AuthenticationType.None);
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationMethod != AuthenticationType.None);

View File

@@ -4210,44 +4210,6 @@
}
},
"/api/v1/config/ui/{id}": {
"get": {
"tags": [
"UiConfig"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/UiConfigResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/UiConfigResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/UiConfigResource"
}
}
}
}
}
},
"put": {
"tags": [
"UiConfig"
@@ -4293,6 +4255,44 @@
}
}
}
},
"get": {
"tags": [
"UiConfig"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/UiConfigResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/UiConfigResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/UiConfigResource"
}
}
}
}
}
}
},
"/api/v1/config/ui": {
@@ -6316,6 +6316,10 @@
"uiLanguage": {
"type": "integer",
"format": "int32"
},
"theme": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false

1929
yarn.lock

File diff suppressed because it is too large Load Diff