Compare commits

...

28 Commits

Author SHA1 Message Date
Qstick
7f28f64cbe Fix server settings on API docs 2021-12-28 18:47:44 -06:00
Qstick
9bad31af84 Eliminate PR Extension from pipeline 2021-12-28 18:26:51 -06:00
Qstick
01c7a05841 Only push to api-docs when changes 2021-12-28 17:52:15 -06:00
Qstick
9859b4a3d9 force it 2021-12-28 17:37:30 -06:00
Gabriel Sjöberg
177084fe8b Fixed: (Indexer) Update RARBG API query options
* Added app_id to captcha check to avoid 403 forbidden error
* Migrated app_id from hard coded to BuildInfo.AppName
2021-12-28 17:04:55 -06:00
Qstick
c57a91bc64 Skip build of doc only change, ignore PR errors for docs 2021-12-28 17:01:31 -06:00
Servarr
ca67a40c72 Automated API Docs update 2021-12-28 15:39:14 -06:00
Qstick
de7505bbe6 correctly push upstream 2021-12-28 15:27:54 -06:00
Qstick
97956ce951 Branch and push prior to PR 2021-12-28 15:10:56 -06:00
Qstick
8a38e124fd Speed up Checkout for Docs job 2021-12-28 14:45:28 -06:00
Qstick
38fcffe871 Identify user for git 2021-12-28 14:31:12 -06:00
Qstick
4c7b5a47d3 Autogenerated API docs 2021-12-28 13:43:45 -06:00
Qstick
34597e6ecb Boolean default should be a boolean
Fixes #729
2021-12-24 14:23:09 -06:00
bakerboy448
735be4f467 New: (TvVault) Mark as Obsolete per Site Bot Ban
Closes #573
2021-12-24 14:08:00 -06:00
bakerboy448
1c737d77fb Bump to 0.1.9 2021-12-24 14:07:42 -06:00
bakerboy448
55788ac04d Fixed: (Usenet) (DrunkenSlug) Update URL
https://api.drunkenslug.com gives a 301
2021-12-24 14:07:17 -06:00
bakerboy448
d108ab0339 Fix misleading Tags helptext [skip ci] 2021-12-24 14:06:42 -06:00
bakerboy448
5928eea83e Fixed: (PornoLab) Add new 2022 Categories
Based on Jackett f61a2b47400e68422ba6620a5ef2f5b4d0a929a3
2021-12-24 12:58:51 -06:00
bakerboy448
27898aa3b5 Fixed: (DanishBytes) Update Domain to .club
Based on Jackett f890ddd119a35eb4ba40b407aa65461c713b5e5d
2021-12-24 12:58:51 -06:00
bakerboy448
5e3322c538 New: OnApplicationUpdate Notifications
(based on Radarr Commits
9e175e28efcfc6ac3e414649b955a10fb0e951e7
4f5f9ff77ee4de05ba04cc677eb7baf4df726af5
4ebcbc28aa3e3268ecc37c5fc2b5565da8f13305
)
Fixes #723

Co-authored-by: Qstick <qstick@gmail.com>
2021-12-22 19:07:07 -06:00
bakerboy448
80c31e8660 fixup add rationale for Obsolete of C# Indexer Implementations 2021-12-18 09:34:51 -06:00
bakerboy448
46401ee187 Fixed: (BB) Remove '.' from Search String
based on jackett fbb1f15d7014b2d8c23c6ee94c2bcf37612066db
2021-12-18 09:27:26 -06:00
Davo1624
3610becc64 Fixed: (Orpheus) Drop Caps Support for Movie & TV Search
Drop TV and Movie search as no Movie/TV categories (#713)

e-learning results are how to play guitar videos, etc
comedy are comedy/standup recordings
2021-12-15 16:30:26 -06:00
Davo1624
06d9c157d8 Fixed: (Orpheus) Map Categories Comedy & E-Learning Videos to 'Other'
indexer does not actually have movies and tv
2021-12-15 16:27:50 -06:00
bakerboy448
d0d1f40128 Fixed: (Anilibria) Duplicate entries
Mark C# Anilibria as obsolete
2021-12-12 17:28:07 -06:00
bakerboy448
383d5464b7 New: (FlareSolverr Proxy) Configurable Request Timeout
Closes #696
2021-12-11 22:10:06 -06:00
Qstick
62d15536df Fixed: NullRef in SchemaBuilder when sending payload without optional Provider.Settings fields 2021-12-11 13:38:25 -06:00
ta264
147cdf2cce Fixed: Forms login persists across restarts in docker
(cherry picked from commit a219b4a1b869863b2ef47d4bdf33d308cb261ba3)

Fixes #409
2021-12-10 03:14:55 -06:00
67 changed files with 772 additions and 318 deletions

4
.gitignore vendored
View File

@@ -188,6 +188,10 @@ packages.config.md5sum
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# ignore node_modules symlink
node_modules
node_modules.nosync
# API doc generation
.config/

View File

@@ -7,7 +7,7 @@ variables:
outputFolder: './_output'
artifactsFolder: './_artifacts'
testsFolder: './_tests'
majorVersion: '0.1.8'
majorVersion: '0.1.9'
minorVersion: $[counter('minorVersion', 1)]
prowlarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
@@ -29,6 +29,7 @@ pr:
paths:
exclude:
- src/NzbDrone.Core/Localization/Core
- src/Prowlarr.API.*/openapi.json
stages:
- stage: Setup
@@ -823,6 +824,59 @@ stages:
FORCE_COLOR: 0
YARN_CACHE_FOLDER: $(yarnCacheFolder)
- job: Api_Docs
displayName: API Docs
dependsOn: Prepare
condition: |
and
(
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
)
pool:
vmImage: windows-2019
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: self
submodules: true
persistCredentials: true
fetchDepth: 1
- bash: ./docs.sh Windows
displayName: Create openapi.json
- bash: |
git config --global user.email "development@lidarr.audio"
git config --global user.name "Servarr"
git checkout -b api-docs
git add .
if git status | grep -q modified
then
git commit -am 'Automated API Docs update'
git push -f --set-upstream origin api-docs
curl -X POST -H 'Authorization: token $GITHUB_TOKEN' -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/prowlarr/prowlarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
else
echo "No changes since last run"
fi
displayName: Commit API Doc Change
continueOnError: true
env:
GITHUB_TOKEN: $(githubToken)
- task: CopyFiles@2
displayName: 'Copy openapi.json to: $(Build.ArtifactStagingDirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)'
Contents: |
**/*openapi.json
TargetFolder: '$(Build.ArtifactStagingDirectory)/api_docs'
- publish: $(Build.ArtifactStagingDirectory)/api_docs
artifact: 'APIDocs'
displayName: Publish API Docs Bundle
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
- job: Analyze_Backend
displayName: Backend
dependsOn: Prepare

38
docs.sh Normal file
View File

@@ -0,0 +1,38 @@
PLATFORM=$1
if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64"
elif [ "$PLATFORM" = "Linux" ]; then
WHERE="linux-x64"
elif [ "$PLATFORM" = "Mac" ]; then
WHERE="osx-x64"
else
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
exit 1
fi
outputFolder='_output'
testPackageFolder='_tests'
rm -rf $outputFolder
rm -rf $testPackageFolder
slnFile=src/Prowlarr.sln
platform=Posix
dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
dotnet tool install --version 6.2.3 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/prowlarr.console.dll" v1 &
sleep 10
kill %1
exit 0

View File

@@ -40,7 +40,7 @@ class Notification extends Component {
});
};
onDeleteNotificationModalClose= () => {
onDeleteNotificationModalClose = () => {
this.setState({ isDeleteNotificationModalOpen: false });
};
@@ -61,12 +61,14 @@ class Notification extends Component {
onRename,
onDelete,
onHealthIssue,
onApplicationUpdate,
supportsOnGrab,
supportsOnDownload,
supportsOnUpgrade,
supportsOnRename,
supportsOnDelete,
supportsOnHealthIssue
supportsOnHealthIssue,
supportsOnApplicationUpdate
} = this.props;
return (
@@ -82,53 +84,62 @@ class Notification extends Component {
{
supportsOnGrab && onGrab &&
<Label kind={kinds.SUCCESS}>
On Grab
{translate('OnGrab')}
</Label>
}
{
supportsOnDelete && onDelete &&
<Label kind={kinds.SUCCESS}>
On Delete
{translate('OnDelete')}
</Label>
}
{
supportsOnDownload && onDownload &&
<Label kind={kinds.SUCCESS}>
On Import
{translate('OnImport')}
</Label>
}
{
supportsOnUpgrade && onDownload && onUpgrade &&
<Label kind={kinds.SUCCESS}>
On Upgrade
{translate('OnUpgrade')}
</Label>
}
{
supportsOnRename && onRename &&
<Label kind={kinds.SUCCESS}>
On Rename
{translate('OnRename')}
</Label>
}
{
supportsOnHealthIssue && onHealthIssue &&
<Label kind={kinds.SUCCESS}>
On Health Issue
{translate('OnHealthIssue')}
</Label>
}
{
!onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete &&
supportsOnApplicationUpdate && onApplicationUpdate ?
<Label kind={kinds.SUCCESS}>
{translate('OnApplicationUpdate')}
</Label> :
null
}
{
!onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete && !onApplicationUpdate ?
<Label
kind={kinds.DISABLED}
outline={true}
>
Disabled
</Label>
{translate('Disabled')}
</Label> :
null
}
<EditNotificationModalConnector
@@ -161,12 +172,14 @@ Notification.propTypes = {
onRename: PropTypes.bool.isRequired,
onDelete: PropTypes.bool.isRequired,
onHealthIssue: PropTypes.bool.isRequired,
onApplicationUpdate: PropTypes.bool.isRequired,
supportsOnGrab: PropTypes.bool.isRequired,
supportsOnDownload: PropTypes.bool.isRequired,
supportsOnDelete: PropTypes.bool.isRequired,
supportsOnUpgrade: PropTypes.bool.isRequired,
supportsOnRename: PropTypes.bool.isRequired,
supportsOnHealthIssue: PropTypes.bool.isRequired,
supportsOnApplicationUpdate: PropTypes.bool.isRequired,
onConfirmDeleteNotification: PropTypes.func.isRequired
};

View File

@@ -16,8 +16,10 @@ function NotificationEventItems(props) {
const {
onHealthIssue,
onApplicationUpdate,
supportsOnHealthIssue,
includeHealthWarnings
includeHealthWarnings,
supportsOnApplicationUpdate
} = item;
return (
@@ -53,6 +55,17 @@ function NotificationEventItems(props) {
/>
</div>
}
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onApplicationUpdate"
helpText={translate('OnApplicationUpdateHelpText')}
isDisabled={!supportsOnApplicationUpdate.value}
{...onApplicationUpdate}
onChange={onInputChange}
/>
</div>
</div>
</div>
</FormGroup>

View File

@@ -106,6 +106,7 @@ export default {
selectedSchema.onDownload = selectedSchema.supportsOnDownload;
selectedSchema.onUpgrade = selectedSchema.supportsOnUpgrade;
selectedSchema.onRename = selectedSchema.supportsOnRename;
selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate;
return selectedSchema;
});

View File

@@ -258,6 +258,11 @@ namespace NzbDrone.Common.Extensions
return appFolderInfo.AppDataFolder;
}
public static string GetDataProtectionPath(this IAppFolderInfo appFolderInfo)
{
return Path.Combine(GetAppDataPath(appFolderInfo), "asp");
}
public static string GetLogFolder(this IAppFolderInfo appFolderInfo)
{
return Path.Combine(GetAppDataPath(appFolderInfo), "logs");

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Rarbg;
@@ -51,7 +52,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
torrentInfo.Title.Should().Be("Sense8.S01E01.WEBRip.x264-FGT");
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
torrentInfo.DownloadUrl.Should().Be("magnet:?xt=urn:btih:d8bde635f573acb390c7d7e7efc1556965fdc802&dn=Sense8.S01E01.WEBRip.x264-FGT&tr=http%3A%2F%2Ftracker.trackerfix.com%3A80%2Fannounce&tr=udp%3A%2F%2F9.rarbg.me%3A2710&tr=udp%3A%2F%2F9.rarbg.to%3A2710&tr=udp%3A%2F%2Fopen.demonii.com%3A1337%2Fannounce");
torrentInfo.InfoUrl.Should().Be("https://torrentapi.org/redirect_to_info.php?token=i5cx7b9agd&p=8_6_4_4_5_6__d8bde635f5&app_id=Prowlarr");
torrentInfo.InfoUrl.Should().Be($"https://torrentapi.org/redirect_to_info.php?token=i5cx7b9agd&p=8_6_4_4_5_6__d8bde635f5&app_id={BuildInfo.AppName}");
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2015-06-05 16:58:11 +0000").ToUniversalTime());
torrentInfo.Size.Should().Be(564198371);

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using FluentValidation.Results;
using NUnit.Framework;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.NotificationTests
{
[TestFixture]
public class NotificationBaseFixture : TestBase
{
private class TestSetting : IProviderConfig
{
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult();
}
}
private class TestNotificationWithApplicationUpdate : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
public override ValidationResult Test()
{
throw new NotImplementedException();
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
TestLogger.Info("OnApplicationUpdate was called");
}
}
private class TestNotificationWithAllEvents : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
public override ValidationResult Test()
{
throw new NotImplementedException();
}
public override void OnHealthIssue(NzbDrone.Core.HealthCheck.HealthCheck artist)
{
TestLogger.Info("OnHealthIssue was called");
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
TestLogger.Info("OnApplicationUpdate was called");
}
}
private class TestNotificationWithNoEvents : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
public override ValidationResult Test()
{
throw new NotImplementedException();
}
}
[Test]
public void should_support_all_if_implemented()
{
var notification = new TestNotificationWithAllEvents();
notification.SupportsOnHealthIssue.Should().BeTrue();
notification.SupportsOnApplicationUpdate.Should().BeTrue();
}
[Test]
public void should_support_none_if_none_are_implemented()
{
var notification = new TestNotificationWithNoEvents();
notification.SupportsOnHealthIssue.Should().BeFalse();
notification.SupportsOnApplicationUpdate.Should().BeFalse();
}
}
}

View File

@@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(014)]
public class add_on_update_to_notifications : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Notifications").AddColumn("OnApplicationUpdate").AsBoolean().WithDefaultValue(false);
}
}
}

View File

@@ -63,7 +63,8 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<NotificationDefinition>("Notifications").RegisterModel()
.Ignore(x => x.ImplementationName)
.Ignore(i => i.SupportsOnHealthIssue);
.Ignore(i => i.SupportsOnHealthIssue)
.Ignore(i => i.SupportsOnApplicationUpdate);
Mapper.Entity<IndexerProxyDefinition>("IndexerProxies").RegisterModel()
.Ignore(x => x.ImplementationName);

View File

@@ -113,7 +113,7 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
var url = request.Url.ToString();
var userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36";
var maxTimeout = 60000;
var maxTimeout = Settings.RequestTimeout * 1000;
if (request.Method == HttpMethod.GET)
{

View File

@@ -1,4 +1,5 @@
using FluentValidation;
using NLog.Config;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation;
@@ -9,6 +10,7 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
public FlareSolverrSettingsValidator()
{
RuleFor(c => c.Host).NotEmpty();
RuleFor(c => c.RequestTimeout).InclusiveBetween(1, 180);
}
}
@@ -19,11 +21,15 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
public FlareSolverrSettings()
{
Host = "http://localhost:8191/";
RequestTimeout = 60;
}
[FieldDefinition(0, Label = "Host")]
public string Host { get; set; }
[FieldDefinition(2, Label = "Request Timeout", Advanced = true, HelpText = "FlareSolverr maxTimeout Request Parameter", Unit = "seconds")]
public int RequestTimeout { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -8,7 +8,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class Aither : Unit3dBase
{
public override string Name => "Aither";

View File

@@ -17,6 +17,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete("Moved to YML for Cardigann v3")]
public class Anilibria : TorrentIndexerBase<AnilibriaSettings>
{
public override string Name => "Anilibria";

View File

@@ -8,7 +8,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class AnimeWorld : Unit3dBase
{
public override string Name => "AnimeWorld";

View File

@@ -178,7 +178,7 @@ namespace NzbDrone.Core.Indexers.Definitions
{ "searchtags", "" },
{ "tags_type", "0" },
{ "action", "basic" },
{ "searchstr", term }
{ "searchstr", term.Replace(".", " ") }
};
var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories);

View File

@@ -8,7 +8,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class Blutopia : Unit3dBase
{
public override string Name => "Blutopia";

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public class DanishBytes : TorrentIndexerBase<DanishBytesSettings>
{
public override string Name => "DanishBytes";
public override string[] IndexerUrls => new string[] { "https://danishbytes.org/" };
public override string[] IndexerUrls => new string[] { "https://danishbytes.club/" };
public override string Description => "DanishBytes is a Private Danish Tracker";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;

View File

@@ -9,7 +9,7 @@ using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class DesiTorrents : Unit3dBase
{
public override string Name => "DesiTorrents";

View File

@@ -20,7 +20,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class DigitalCore : TorrentIndexerBase<DigitalCoreSettings>
{
public override string Name => "DigitalCore";

View File

@@ -17,7 +17,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class InternetArchive : TorrentIndexerBase<InternetArchiveSettings>
{
public override string Name => "Internet Archive";

View File

@@ -15,7 +15,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class Milkie : TorrentIndexerBase<MilkieSettings>
{
public override string Name => "Milkie";

View File

@@ -88,7 +88,7 @@ namespace NzbDrone.Core.Indexers.Newznab
yield return GetDefinition("altHUB", GetSettings("https://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://api.drunkenslug.com"));
yield return GetDefinition("DrunkenSlug", GetSettings("https://drunkenslug.com"));
yield return GetDefinition("GingaDADDY", GetSettings("https://www.gingadaddy.com"));
yield return GetDefinition("Miatrix", GetSettings("https://www.miatrix.com"));
yield return GetDefinition("Newz-Complex", GetSettings("https://newz-complex.org/www"));

View File

@@ -23,14 +23,6 @@ namespace NzbDrone.Core.Indexers.Definitions
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year
@@ -45,8 +37,8 @@ namespace NzbDrone.Core.Indexers.Definitions
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC, "Applications");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Books, "E-Books");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.AudioAudiobook, "Audiobooks");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Movies, "E-Learning Videos");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TV, "Comedy");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other, "E-Learning Videos");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.Other, "Comedy");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Books, "Comics");
return caps;

View File

@@ -142,6 +142,7 @@ namespace NzbDrone.Core.Indexers.Definitions
caps.Categories.AddCategoryMapping(1836, NewznabStandardCategory.XXX, "Сайтрипы 2019 (HD Video) / SiteRip's 2019 (HD Video)");
caps.Categories.AddCategoryMapping(1842, NewznabStandardCategory.XXX, "Сайтрипы 2020 (HD Video) / SiteRip's 2020 (HD Video)");
caps.Categories.AddCategoryMapping(1846, NewznabStandardCategory.XXX, "Сайтрипы 2021 (HD Video) / SiteRip's 2021 (HD Video)");
caps.Categories.AddCategoryMapping(1857, NewznabStandardCategory.XXX, "Сайтрипы 2022 (HD Video) / SiteRip's 2022 (HD Video)");
caps.Categories.AddCategoryMapping(1451, NewznabStandardCategory.XXX, "Сайтрипы 1991-2010 / SiteRip's 1991-2010");
caps.Categories.AddCategoryMapping(1788, NewznabStandardCategory.XXX, "Сайтрипы 2011-2012 / SiteRip's 2011-2012");
@@ -154,6 +155,7 @@ namespace NzbDrone.Core.Indexers.Definitions
caps.Categories.AddCategoryMapping(1837, NewznabStandardCategory.XXX, "Сайтрипы 2019 / SiteRip's 2019");
caps.Categories.AddCategoryMapping(1843, NewznabStandardCategory.XXX, "Сайтрипы 2020 / SiteRip's 2020");
caps.Categories.AddCategoryMapping(1847, NewznabStandardCategory.XXX, "Сайтрипы 2021 / SiteRip's 2021");
caps.Categories.AddCategoryMapping(1856, NewznabStandardCategory.XXX, "Сайтрипы 2022 / SiteRip's 2022");
caps.Categories.AddCategoryMapping(1707, NewznabStandardCategory.XXX, "Сцены из фильмов / Movie Scenes");
caps.Categories.AddCategoryMapping(284, NewznabStandardCategory.XXX, "Порноролики Разное / Clips (various)");

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
@@ -103,7 +104,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
try
{
var request = new HttpRequestBuilder(Settings.BaseUrl.Trim('/'))
.Resource("/pubapi_v2.php?get_token=get_token")
.Resource($"/pubapi_v2.php?get_token=get_token&app_id={BuildInfo.AppName}")
.Accept(HttpAccept.Json)
.Build();

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Parser.Model;
@@ -62,7 +63,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
torrentInfo.Title = torrent.title;
torrentInfo.Size = torrent.size;
torrentInfo.DownloadUrl = torrent.download;
torrentInfo.InfoUrl = torrent.info_page + "&app_id=Prowlarr";
torrentInfo.InfoUrl = $"{torrent.info_page}&app_id={BuildInfo.AppName}";
torrentInfo.PublishDate = torrent.pubdate.ToUniversalTime();
torrentInfo.Seeders = torrent.seeders;
torrentInfo.Peers = torrent.leechers + torrent.seeders;

View File

@@ -2,6 +2,7 @@ using System;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
@@ -32,7 +33,7 @@ namespace NzbDrone.Core.Indexers.Rarbg
{
var requestBuilder = new HttpRequestBuilder(baseUrl.Trim('/'))
.WithRateLimit(3.0)
.Resource("/pubapi_v2.php?get_token=get_token&app_id=Prowlarr")
.Resource($"/pubapi_v2.php?get_token=get_token&app_id={BuildInfo.AppName}")
.Accept(HttpAccept.Json);
if (settings.CaptchaToken.IsNotNullOrWhiteSpace())

View File

@@ -8,7 +8,7 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class ShareIsland : Unit3dBase
{
public override string Name => "ShareIsland";

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class SuperBits : TorrentIndexerBase<SuperBitsSettings>
{
public override string Name => "SuperBits";

View File

@@ -22,6 +22,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete("Remove per Site Request Prowlarr Issue 573")]
public class TVVault : TorrentIndexerBase<TVVaultSettings>
{
public override string Name => "TVVault";

View File

@@ -16,7 +16,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class ThePirateBay : TorrentIndexerBase<ThePirateBaySettings>
{
public override string Name => "ThePirateBay";

View File

@@ -20,7 +20,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class TorrentLeech : TorrentIndexerBase<TorrentLeechSettings>
{
public override string Name => "TorrentLeech";

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class TorrentParadiseMl : TorrentIndexerBase<TorrentParadiseMlSettings>
{
public override string Name => "TorrentParadiseMl";

View File

@@ -18,7 +18,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
[Obsolete]
[Obsolete("Moved to YML for Cardigann v3")]
public class YTS : TorrentIndexerBase<YTSSettings>
{
public override string Name => "YTS";

View File

@@ -203,7 +203,7 @@
"IndexersSelectedInterp": "{0} Indexer(s) Selected",
"IndexerStatusCheckAllClientMessage": "All indexers are unavailable due to failures",
"IndexerStatusCheckSingleClientMessage": "Indexers unavailable due to failures: {0}",
"IndexerTagsHelpText": "Use tags to specify default clients, specify Indexer Proxies, or just to organize your indexers.",
"IndexerTagsHelpText": "Use tags to specify Indexer Proxies or just to organize your indexers.",
"IndexerVipCheckExpiredClientMessage": "Indexer VIP benefits have expired: {0}",
"IndexerVipCheckExpiringClientMessage": "Indexer VIP benefits expiring soon: {0}",
"Info": "Info",
@@ -262,6 +262,9 @@
"NoUpdatesAreAvailable": "No updates are available",
"OAuthPopupMessage": "Pop-ups are being blocked by your browser",
"Ok": "Ok",
"OnApplicationUpdate": "On Application Update",
"OnApplicationUpdateHelpText": "On Application Update",
"OnHealthIssue": "On Health Issue",
"OnHealthIssueHelpText": "On Health Issue",
"OpenBrowserOnStart": "Open browser on start",
"OpenThisModal": "Open This Modal",
@@ -446,4 +449,4 @@
"Wiki": "Wiki",
"YesCancel": "Yes, Cancel",
"Yesterday": "Yesterday"
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace NzbDrone.Core.Notifications
{
public class ApplicationUpdateMessage
{
public string Message { get; set; }
public Version PreviousVersion { get; set; }
public Version NewVersion { get; set; }
public override string ToString()
{
return NewVersion.ToString();
}
}
}

View File

@@ -20,6 +20,11 @@ namespace NzbDrone.Core.Notifications.Boxcar
_proxy.SendNotification(HEALTH_ISSUE_TITLE, message.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage message)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, message.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -44,6 +44,18 @@ namespace NzbDrone.Core.Notifications.CustomScript
ExecuteScript(environmentVariables);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var environmentVariables = new StringDictionary();
environmentVariables.Add("Prowlarr_EventType", "ApplicationUpdate");
environmentVariables.Add("Prowlarr_Update_Message", updateMessage.Message);
environmentVariables.Add("Prowlarr_Update_NewVersion", updateMessage.NewVersion.ToString());
environmentVariables.Add("Prowlarr_Update_PreviousVersion", updateMessage.PreviousVersion.ToString());
ExecuteScript(environmentVariables);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -42,6 +42,41 @@ namespace NzbDrone.Core.Notifications.Discord
_proxy.SendPayload(payload, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var attachments = new List<Embed>
{
new Embed
{
Author = new DiscordAuthor
{
Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author,
IconUrl = "https://raw.githubusercontent.com/Prowlarr/Prowlarr/develop/Logo/256.png"
},
Title = APPLICATION_UPDATE_TITLE,
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
Color = (int)DiscordColors.Standard,
Fields = new List<DiscordField>()
{
new DiscordField()
{
Name = "Previous Version",
Value = updateMessage.PreviousVersion.ToString()
},
new DiscordField()
{
Name = "New Version",
Value = updateMessage.NewVersion.ToString()
}
},
}
};
var payload = CreatePayload(null, attachments);
_proxy.SendPayload(payload, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -28,6 +28,13 @@ namespace NzbDrone.Core.Notifications.Email
SendEmail(Settings, HEALTH_ISSUE_TITLE_BRANDED, message.Message);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var body = $"{updateMessage.Message}";
SendEmail(Settings, APPLICATION_UPDATE_TITLE_BRANDED, body);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -24,6 +24,11 @@ namespace NzbDrone.Core.Notifications.Gotify
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -7,7 +7,9 @@ namespace NzbDrone.Core.Notifications
string Link { get; }
void OnHealthIssue(HealthCheck.HealthCheck healthCheck);
void OnApplicationUpdate(ApplicationUpdateMessage updateMessage);
void ProcessQueue();
bool SupportsOnHealthIssue { get; }
bool SupportsOnApplicationUpdate { get; }
}
}

View File

@@ -22,6 +22,11 @@ namespace NzbDrone.Core.Notifications.Join
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, message.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -29,6 +29,18 @@ namespace NzbDrone.Core.Notifications.Notifiarr
_proxy.SendNotification(variables, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var variables = new StringDictionary();
variables.Add("Prowlarr_EventType", "ApplicationUpdate");
variables.Add("Prowlarr_Update_Message", updateMessage.Message);
variables.Add("Prowlarr_Update_NewVersion", updateMessage.NewVersion.ToString());
variables.Add("Prowlarr_Update_PreviousVersion", updateMessage.PreviousVersion.ToString());
_proxy.SendNotification(variables, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -9,8 +9,10 @@ namespace NzbDrone.Core.Notifications
where TSettings : IProviderConfig, new()
{
protected const string HEALTH_ISSUE_TITLE = "Health Check Failure";
protected const string APPLICATION_UPDATE_TITLE = "Application Updated";
protected const string HEALTH_ISSUE_TITLE_BRANDED = "Prowlarr - " + HEALTH_ISSUE_TITLE;
protected const string APPLICATION_UPDATE_TITLE_BRANDED = "Prowlarr - " + APPLICATION_UPDATE_TITLE;
public abstract string Name { get; }
@@ -29,11 +31,16 @@ namespace NzbDrone.Core.Notifications
{
}
public virtual void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
}
public virtual void ProcessQueue()
{
}
public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue");
public bool SupportsOnApplicationUpdate => HasConcreteImplementation("OnApplicationUpdate");
protected TSettings Settings => (TSettings)Definition.Settings;

View File

@@ -5,9 +5,11 @@ namespace NzbDrone.Core.Notifications
public class NotificationDefinition : ProviderDefinition
{
public bool OnHealthIssue { get; set; }
public bool OnApplicationUpdate { get; set; }
public bool SupportsOnHealthIssue { get; set; }
public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnApplicationUpdate { get; set; }
public override bool Enable => OnHealthIssue;
public override bool Enable => OnHealthIssue || OnApplicationUpdate;
}
}

View File

@@ -10,6 +10,7 @@ namespace NzbDrone.Core.Notifications
public interface INotificationFactory : IProviderFactory<INotification, NotificationDefinition>
{
List<INotification> OnHealthIssueEnabled();
List<INotification> OnApplicationUpdateEnabled();
}
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
@@ -24,11 +25,17 @@ namespace NzbDrone.Core.Notifications
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthIssue).ToList();
}
public List<INotification> OnApplicationUpdateEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnApplicationUpdate).ToList();
}
public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition)
{
base.SetProviderCharacteristics(provider, definition);
definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue;
definition.SupportsOnApplicationUpdate = provider.SupportsOnApplicationUpdate;
}
}
}

View File

@@ -2,12 +2,14 @@ using System;
using NLog;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Update.History.Events;
namespace NzbDrone.Core.Notifications
{
public class NotificationService
: IHandle<HealthCheckFailedEvent>,
IHandleAsync<HealthCheckCompleteEvent>
IHandleAsync<HealthCheckCompleteEvent>,
IHandle<UpdateInstalledEvent>
{
private readonly INotificationFactory _notificationFactory;
private readonly Logger _logger;
@@ -56,6 +58,26 @@ namespace NzbDrone.Core.Notifications
ProcessQueue();
}
public void Handle(UpdateInstalledEvent message)
{
var updateMessage = new ApplicationUpdateMessage();
updateMessage.Message = $"Prowlarr updated from {message.PreviousVerison.ToString()} to {message.NewVersion.ToString()}";
updateMessage.PreviousVersion = message.PreviousVerison;
updateMessage.NewVersion = message.NewVersion;
foreach (var notification in _notificationFactory.OnApplicationUpdateEnabled())
{
try
{
notification.OnApplicationUpdate(updateMessage);
}
catch (Exception ex)
{
_logger.Warn(ex, "Unable to send OnApplicationUpdate notification to: " + notification.Definition.Name);
}
}
}
private void ProcessQueue()
{
foreach (var notification in _notificationFactory.GetAvailableProviders())

View File

@@ -21,6 +21,11 @@ namespace NzbDrone.Core.Notifications.Prowl
_prowlProxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_prowlProxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -24,6 +24,11 @@ namespace NzbDrone.Core.Notifications.PushBullet
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -21,6 +21,11 @@ namespace NzbDrone.Core.Notifications.Pushover
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -24,6 +24,11 @@ namespace NzbDrone.Core.Notifications.SendGrid
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -36,6 +36,23 @@ namespace NzbDrone.Core.Notifications.Slack
_proxy.SendPayload(payload, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var attachments = new List<Attachment>
{
new Attachment
{
Title = Environment.MachineName,
Text = updateMessage.Message,
Color = "good"
}
};
var payload = CreatePayload("Application Updated", attachments);
_proxy.SendPayload(payload, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -21,6 +21,11 @@ namespace NzbDrone.Core.Notifications.Telegram
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

View File

@@ -23,6 +23,11 @@ namespace NzbDrone.Core.Notifications.Twitter
_twitterService.SendNotification($"Health Issue: {healthCheck.Message}", Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_twitterService.SendNotification($"Application Updated: {updateMessage.Message}", Settings);
}
public override object RequestAction(string action, IDictionary<string, string> query)
{
if (action == "startOAuth")

View File

@@ -19,13 +19,26 @@ namespace NzbDrone.Core.Notifications.Webhook
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
var payload = new WebhookHealthPayload
{
EventType = WebhookEventType.Health,
Level = healthCheck.Type,
Message = healthCheck.Message,
Type = healthCheck.Source.Name,
WikiUrl = healthCheck.WikiUrl?.ToString()
};
{
EventType = WebhookEventType.Health,
Level = healthCheck.Type,
Message = healthCheck.Message,
Type = healthCheck.Source.Name,
WikiUrl = healthCheck.WikiUrl?.ToString()
};
_proxy.SendWebhook(payload, Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var payload = new WebhookApplicationUpdatePayload
{
EventType = WebhookEventType.ApplicationUpdate,
Message = updateMessage.Message,
PreviousVersion = updateMessage.PreviousVersion.ToString(),
NewVersion = updateMessage.NewVersion.ToString()
};
_proxy.SendWebhook(payload, Settings);
}

View File

@@ -0,0 +1,11 @@
using NzbDrone.Core.HealthCheck;
namespace NzbDrone.Core.Notifications.Webhook
{
public class WebhookApplicationUpdatePayload : WebhookPayload
{
public string Message { get; set; }
public string PreviousVersion { get; set; }
public string NewVersion { get; set; }
}
}

View File

@@ -12,6 +12,7 @@ namespace NzbDrone.Core.Notifications.Webhook
Grab,
Download,
Rename,
Health
Health,
ApplicationUpdate
}
}

View File

@@ -8,8 +8,10 @@ using System.Security.Cryptography.X509Certificates;
using System.Text;
using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.WindowsServices;
using NLog;
@@ -129,6 +131,7 @@ namespace NzbDrone.Host
})
.ConfigureWebHost(builder =>
{
builder.UseConfiguration(config);
builder.UseUrls(urls.ToArray());
builder.UseKestrel(options =>
{
@@ -195,6 +198,7 @@ namespace NzbDrone.Host
var appFolder = new AppFolderInfo(context);
return new ConfigurationBuilder()
.AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: false)
.AddInMemoryCollection(new List<KeyValuePair<string, string>> { new ("dataProtectionFolder", appFolder.GetDataProtectionPath()) })
.Build();
}

View File

@@ -7,6 +7,7 @@
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.3" />
<PackageReference Include="DryIoc.dll" Version="4.8.1" />
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="5.1.0" />
</ItemGroup>

View File

@@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using NLog.Extensions.Logging;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
@@ -45,6 +48,7 @@ namespace NzbDrone.Host
b.SetMinimumLevel(LogLevel.Trace);
b.AddFilter("Microsoft.AspNetCore", LogLevel.Warning);
b.AddFilter("Prowlarr.Http.Authentication", LogLevel.Information);
b.AddFilter("Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager", LogLevel.Error);
b.AddNLog();
});
@@ -87,6 +91,73 @@ namespace NzbDrone.Host
})
.AddControllersAsServices();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "1.0.0",
Title = "Prowlarr",
Description = "Prowlarr API docs",
License = new OpenApiLicense
{
Name = "GPL-3.0",
Url = new Uri("https://github.com/Prowlarr/Prowlarr/blob/develop/LICENSE")
}
});
var apiKeyHeader = new OpenApiSecurityScheme
{
Name = "X-Api-Key",
Type = SecuritySchemeType.ApiKey,
Scheme = "apiKey",
Description = "Apikey passed as header",
In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "X-Api-Key"
},
};
c.AddSecurityDefinition("X-Api-Key", apiKeyHeader);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{ apiKeyHeader, new string[] { } }
});
var apikeyQuery = new OpenApiSecurityScheme
{
Name = "apikey",
Type = SecuritySchemeType.ApiKey,
Scheme = "apiKey",
Description = "Apikey passed as header",
In = ParameterLocation.Query,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "apikey"
},
};
c.AddServer(new OpenApiServer
{
Url = "{protocol}://{hostpath}",
Variables = new Dictionary<string, OpenApiServerVariable>
{
{ "protocol", new OpenApiServerVariable { Default = "http", Enum = new List<string> { "http", "https" } } },
{ "hostpath", new OpenApiServerVariable { Default = "localhost:9696" } }
}
});
c.AddSecurityDefinition("apikey", apikeyQuery);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{ apikeyQuery, new string[] { } }
});
});
services
.AddSignalR()
.AddJsonProtocol(options =>
@@ -94,6 +165,9 @@ namespace NzbDrone.Host
options.PayloadSerializerOptions = STJson.GetSerializerSettings();
});
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Configuration["dataProtectionFolder"]));
services.AddSingleton<IAuthorizationPolicyProvider, UiAuthorizationPolicyProvider>();
services.AddAuthorization(options =>
{
@@ -175,6 +249,15 @@ namespace NzbDrone.Host
app.UseWebSockets();
// Enable middleware to serve generated Swagger as a JSON endpoint.
if (BuildInfo.IsDebug)
{
app.UseSwagger(c =>
{
c.RouteTemplate = "docs/{documentName}/openapi.json";
});
}
app.UseEndpoints(x =>
{
x.MapHub<MessageHub>("/signalr/messages").RequireAuthorization("SignalR");

View File

@@ -6,8 +6,10 @@ namespace Prowlarr.Api.V1.Notifications
{
public string Link { get; set; }
public bool OnHealthIssue { get; set; }
public bool OnApplicationUpdate { get; set; }
public bool SupportsOnHealthIssue { get; set; }
public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnApplicationUpdate { get; set; }
public string TestCommand { get; set; }
}
@@ -25,6 +27,8 @@ namespace Prowlarr.Api.V1.Notifications
resource.OnHealthIssue = definition.OnHealthIssue;
resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue;
resource.IncludeHealthWarnings = definition.IncludeHealthWarnings;
resource.OnApplicationUpdate = definition.OnApplicationUpdate;
resource.SupportsOnApplicationUpdate = definition.SupportsOnApplicationUpdate;
return resource;
}
@@ -41,6 +45,8 @@ namespace Prowlarr.Api.V1.Notifications
definition.OnHealthIssue = resource.OnHealthIssue;
definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue;
definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;
definition.OnApplicationUpdate = resource.OnApplicationUpdate;
definition.SupportsOnApplicationUpdate = resource.SupportsOnApplicationUpdate;
return definition;
}

View File

@@ -20,6 +20,7 @@ namespace Prowlarr.Api.V1.Profiles.App
}
[RestPostById]
[Produces("application/json")]
public ActionResult<AppProfileResource> Create(AppProfileResource resource)
{
var model = resource.ToModel();
@@ -28,12 +29,14 @@ namespace Prowlarr.Api.V1.Profiles.App
}
[RestDeleteById]
[Produces("application/json")]
public void DeleteProfile(int id)
{
_profileService.Delete(id);
}
[RestPutById]
[Produces("application/json")]
public ActionResult<AppProfileResource> Update(AppProfileResource resource)
{
var model = resource.ToModel();
@@ -43,12 +46,17 @@ namespace Prowlarr.Api.V1.Profiles.App
return Accepted(model.Id);
}
[Produces("application/json")]
[ProducesResponseType(typeof(AppProfileResource), 200)]
[ProducesResponseType(typeof(IDictionary<string, string>), 404)]
[ProducesResponseType(500)]
public override AppProfileResource GetResourceById(int id)
{
return _profileService.Get(id).ToResource();
}
[HttpGet]
[Produces("application/json")]
public List<AppProfileResource> GetAll()
{
return _profileService.All().ToResource();

View File

@@ -2,6 +2,11 @@
"openapi": "3.0.1",
"info": {
"title": "Prowlarr",
"description": "Prowlarr API docs",
"license": {
"name": "GPL-3.0",
"url": "https://github.com/Prowlarr/Prowlarr/blob/develop/LICENSE"
},
"version": "1.0.0"
},
"paths": {
@@ -356,20 +361,10 @@
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
}
}
}
@@ -383,14 +378,6 @@
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AppProfileResource"
}
}
},
"application/json": {
"schema": {
"type": "array",
@@ -398,14 +385,6 @@
"$ref": "#/components/schemas/AppProfileResource"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AppProfileResource"
}
}
}
}
}
@@ -471,20 +450,10 @@
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
}
}
}
@@ -509,22 +478,28 @@
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AppProfileResource"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"500": {
"description": "Server Error"
}
}
}
@@ -2341,131 +2316,6 @@
}
}
},
"/api/v1/config/indexer/{id}": {
"get": {
"tags": [
"IndexerConfig"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
}
}
}
}
},
"put": {
"tags": [
"IndexerConfig"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
}
}
}
}
}
},
"/api/v1/config/indexer": {
"get": {
"tags": [
"IndexerConfig"
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/IndexerConfigResource"
}
}
}
}
}
}
},
"/api/v1/indexer/categories": {
"get": {
"tags": [
@@ -2892,6 +2742,24 @@
"tags": [
"IndexerStats"
],
"parameters": [
{
"name": "startDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "endDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
}
],
"responses": {
"200": {
"description": "Success",
@@ -4176,6 +4044,14 @@
"format": "int32"
}
}
},
{
"name": "type",
"in": "query",
"schema": {
"type": "string",
"default": "search"
}
}
],
"responses": {
@@ -4211,6 +4087,63 @@
}
}
},
"/api/v1/search/bulk": {
"post": {
"tags": [
"Search"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchResource"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchResource"
}
}
},
"application/*+json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchResource"
}
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/SearchResource"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/SearchResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/SearchResource"
}
}
}
}
}
}
},
"/content/{path}": {
"get": {
"tags": [
@@ -5292,22 +5225,6 @@
"downloadClientWorkingFolders": {
"type": "string",
"nullable": true
},
"enableCompletedDownloadHandling": {
"type": "boolean"
},
"removeCompletedDownloads": {
"type": "boolean"
},
"checkForFinishedDownloadInterval": {
"type": "integer",
"format": "int32"
},
"autoRedownloadFailed": {
"type": "boolean"
},
"removeFailedDownloads": {
"type": "boolean"
}
},
"additionalProperties": false
@@ -5441,6 +5358,10 @@
"hidden": {
"type": "string",
"nullable": true
},
"placeholder": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
@@ -5719,34 +5640,28 @@
},
"scheme": {
"type": "string",
"nullable": true,
"readOnly": true
"nullable": true
},
"host": {
"type": "string",
"nullable": true,
"readOnly": true
"nullable": true
},
"port": {
"type": "integer",
"format": "int32",
"nullable": true,
"readOnly": true
"nullable": true
},
"path": {
"type": "string",
"nullable": true,
"readOnly": true
"nullable": true
},
"query": {
"type": "string",
"nullable": true,
"readOnly": true
"nullable": true
},
"fragment": {
"type": "string",
"nullable": true,
"readOnly": true
"nullable": true
}
},
"additionalProperties": false
@@ -5804,46 +5719,6 @@
},
"additionalProperties": false
},
"IndexerConfigResource": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"minimumAge": {
"type": "integer",
"format": "int32"
},
"maximumSize": {
"type": "integer",
"format": "int32"
},
"retention": {
"type": "integer",
"format": "int32"
},
"rssSyncInterval": {
"type": "integer",
"format": "int32"
},
"preferIndexerFlags": {
"type": "boolean"
},
"availabilityDelay": {
"type": "integer",
"format": "int32"
},
"allowHardcodedSubs": {
"type": "boolean"
},
"whitelistedHardcodedSubs": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"IndexerEditorResource": {
"type": "object",
"properties": {
@@ -6359,12 +6234,18 @@
"onHealthIssue": {
"type": "boolean"
},
"onApplicationUpdate": {
"type": "boolean"
},
"supportsOnHealthIssue": {
"type": "boolean"
},
"includeHealthWarnings": {
"type": "boolean"
},
"supportsOnApplicationUpdate": {
"type": "boolean"
},
"testCommand": {
"type": "string",
"nullable": true
@@ -6555,6 +6436,11 @@
"hint": {
"type": "string",
"nullable": true
},
"parentValue": {
"type": "integer",
"format": "int32",
"nullable": true
}
},
"additionalProperties": false
@@ -6661,33 +6547,27 @@
"properties": {
"ticks": {
"type": "integer",
"format": "int64",
"readOnly": true
"format": "int64"
},
"days": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"hours": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"milliseconds": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"minutes": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"seconds": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"totalDays": {
"type": "number",
@@ -6864,23 +6744,19 @@
"properties": {
"major": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"minor": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"build": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"revision": {
"type": "integer",
"format": "int32",
"readOnly": true
"format": "int32"
},
"majorRevision": {
"type": "integer",
@@ -6895,6 +6771,28 @@
},
"additionalProperties": false
}
},
"securitySchemes": {
"X-Api-Key": {
"type": "apiKey",
"description": "Apikey passed as header",
"name": "X-Api-Key",
"in": "header"
},
"apikey": {
"type": "apiKey",
"description": "Apikey passed as header",
"name": "apikey",
"in": "query"
}
}
}
},
"security": [
{
"X-Api-Key": [ ]
},
{
"apikey": [ ]
}
]
}

View File

@@ -47,7 +47,10 @@ namespace Prowlarr.Http.ClientSchema
var propertyType = mapping.PropertyType;
var field = fields.Find(f => f.Name == mapping.Field.Name);
mapping.SetterFunc(target, field.Value);
if (field != null)
{
mapping.SetterFunc(target, field.Value);
}
}
return target;