mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-17 21:44:48 -04:00
Compare commits
39 Commits
v0.1.7.120
...
v0.1.9.131
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f28f64cbe | ||
|
|
9bad31af84 | ||
|
|
01c7a05841 | ||
|
|
9859b4a3d9 | ||
|
|
177084fe8b | ||
|
|
c57a91bc64 | ||
|
|
ca67a40c72 | ||
|
|
de7505bbe6 | ||
|
|
97956ce951 | ||
|
|
8a38e124fd | ||
|
|
38fcffe871 | ||
|
|
4c7b5a47d3 | ||
|
|
34597e6ecb | ||
|
|
735be4f467 | ||
|
|
1c737d77fb | ||
|
|
55788ac04d | ||
|
|
d108ab0339 | ||
|
|
5928eea83e | ||
|
|
27898aa3b5 | ||
|
|
5e3322c538 | ||
|
|
80c31e8660 | ||
|
|
46401ee187 | ||
|
|
3610becc64 | ||
|
|
06d9c157d8 | ||
|
|
d0d1f40128 | ||
|
|
383d5464b7 | ||
|
|
62d15536df | ||
|
|
147cdf2cce | ||
|
|
dd27d69e97 | ||
|
|
32fd0911a2 | ||
|
|
0e6ec58a83 | ||
|
|
69f5963f6f | ||
|
|
6ca708f523 | ||
|
|
9e7af8369e | ||
|
|
b05d8c930d | ||
|
|
6b886b938c | ||
|
|
4a7bf39723 | ||
|
|
7fcd320e23 | ||
|
|
88677ce236 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -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/
|
||||
|
||||
@@ -7,7 +7,7 @@ variables:
|
||||
outputFolder: './_output'
|
||||
artifactsFolder: './_artifacts'
|
||||
testsFolder: './_tests'
|
||||
majorVersion: '0.1.7'
|
||||
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
38
docs.sh
Normal 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
|
||||
@@ -68,6 +68,7 @@ function ProviderFieldFormGroup(props) {
|
||||
label,
|
||||
helpText,
|
||||
helpLink,
|
||||
placeholder,
|
||||
value,
|
||||
type,
|
||||
advanced,
|
||||
@@ -100,6 +101,7 @@ function ProviderFieldFormGroup(props) {
|
||||
label={label}
|
||||
helpText={helpText}
|
||||
helpLink={helpLink}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
values={getSelectValues(selectOptions)}
|
||||
errors={errors}
|
||||
@@ -125,6 +127,7 @@ ProviderFieldFormGroup.propTypes = {
|
||||
label: PropTypes.string,
|
||||
helpText: PropTypes.string,
|
||||
helpLink: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.any,
|
||||
type: PropTypes.string.isRequired,
|
||||
advanced: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -106,6 +106,7 @@ export default {
|
||||
selectedSchema.onDownload = selectedSchema.supportsOnDownload;
|
||||
selectedSchema.onUpgrade = selectedSchema.supportsOnUpgrade;
|
||||
selectedSchema.onRename = selectedSchema.supportsOnRename;
|
||||
selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate;
|
||||
|
||||
return selectedSchema;
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DateTimeRoutinesFixture : CoreTest
|
||||
{
|
||||
public static IEnumerable DateTimeTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new TestCaseData(@"Member since: 10-Feb-2008").Returns(new DateTime(2008, 2, 10, 0, 0, 0));
|
||||
yield return new TestCaseData(@"Last Update: 18:16 11 Feb '08 ").Returns(new DateTime(2008, 2, 11, 18, 16, 0));
|
||||
yield return new TestCaseData(@"date Tue, Feb 10, 2008 at 11:06 AM").Returns(new DateTime(2008, 2, 10, 11, 06, 0));
|
||||
yield return new TestCaseData(@"see at 12/31/2007 14:16:32").Returns(new DateTime(2007, 12, 31, 14, 16, 32));
|
||||
yield return new TestCaseData(@"sack finish 14:16:32 November 15 2008, 1-144 app").Returns(new DateTime(2008, 11, 15, 14, 16, 32));
|
||||
yield return new TestCaseData(@"Genesis Message - Wed 04 Feb 08 - 19:40").Returns(new DateTime(2008, 2, 4, 19, 40, 0));
|
||||
yield return new TestCaseData(@"The day 07/31/07 14:16:32 is ").Returns(new DateTime(2007, 7, 31, 14, 16, 32));
|
||||
yield return new TestCaseData(@"Shipping is on us until December 24, 2008 within the U.S. ").Returns(new DateTime(2008, 12, 24, 0, 0, 0));
|
||||
yield return new TestCaseData(@" 2008 within the U.S. at 14:16:32").Returns(new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 14, 16, 32));
|
||||
yield return new TestCaseData(@"5th November, 1994, 8:15:30 pm").Returns(new DateTime(1994, 11, 5, 20, 15, 30));
|
||||
yield return new TestCaseData(@"7 boxes January 31 , 14:16:32.").Returns(new DateTime(DateTime.Now.Year, 1, 31, 14, 16, 32));
|
||||
yield return new TestCaseData(@"the blue sky of Sept 30th 2008 14:16:32").Returns(new DateTime(2008, 9, 30, 14, 16, 32));
|
||||
yield return new TestCaseData(@" e.g. 1997-07-16T19:20:30+01:00").Returns(new DateTime(1997, 7, 16, 19, 20, 30));
|
||||
yield return new TestCaseData(@"Apr 1st, 2008 14:16:32 tufa 6767").Returns(new DateTime(2008, 4, 1, 14, 16, 32));
|
||||
yield return new TestCaseData(@"wait for 07/31/07 14:16:32").Returns(new DateTime(2007, 7, 31, 14, 16, 32));
|
||||
yield return new TestCaseData(@"later 12.31.08 and before 1.01.09").Returns(new DateTime(2008, 12, 31, 0, 0, 0));
|
||||
yield return new TestCaseData(@"Expires: Sept 30th 2008 14:16:32").Returns(new DateTime(2008, 9, 30, 14, 16, 32));
|
||||
yield return new TestCaseData(@"Offer expires Apr 1st, 2007, 14:16:32").Returns(new DateTime(2007, 4, 1, 14, 16, 32));
|
||||
yield return new TestCaseData(@"Expires 14:16:32 January 31.").Returns(new DateTime(DateTime.Now.Year, 1, 31, 14, 16, 32));
|
||||
yield return new TestCaseData(@"Expires 14:16:32 January 31-st.").Returns(new DateTime(DateTime.Now.Year, 1, 31, 14, 16, 32));
|
||||
yield return new TestCaseData(@"Expires 23rd January 2010.").Returns(new DateTime(2010, 1, 23, 0, 0, 0));
|
||||
yield return new TestCaseData(@"Expires January 22nd, 2010.").Returns(new DateTime(2010, 1, 22, 0, 0, 0));
|
||||
yield return new TestCaseData(@"Expires DEC 22, 2010.").Returns(new DateTime(2010, 12, 22, 0, 0, 0));
|
||||
yield return new TestCaseData(@"Version: 1.0.0.692 6/1/2010 2:28:04 AM ").Returns(new DateTime(2010, 6, 1, 2, 28, 4));
|
||||
yield return new TestCaseData(@"Version: 1.0.0.692 04/21/11 12:30am ").Returns(new DateTime(2011, 4, 21, 00, 30, 00));
|
||||
yield return new TestCaseData(@"Version: 1.0.0.692 04/21/11 12:30pm ").Returns(new DateTime(2011, 4, 21, 12, 30, 00));
|
||||
yield return new TestCaseData(@"Version: Thu Aug 06 22:32:15 MDT 2009 ").Returns(new DateTime(2009, 8, 6, 22, 32, 15));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource("DateTimeTestCases")]
|
||||
public DateTime should_parse_date(string date)
|
||||
{
|
||||
DateTimeRoutines.TryParseDateOrTime(date, DateTimeRoutines.DateTimeFormat.USDate, out var parsedDateTime);
|
||||
|
||||
return parsedDateTime.DateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Annotations
|
||||
public string Section { get; set; }
|
||||
public HiddenType Hidden { get; set; }
|
||||
public PrivacyLevel Privacy { get; set; }
|
||||
public string Placeholder { get; set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
|
||||
|
||||
@@ -23,8 +23,6 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
|
||||
public LazyLibrarianSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:5299";
|
||||
SyncCategories = new[]
|
||||
{
|
||||
NewznabStandardCategory.AudioAudiobook.Id,
|
||||
@@ -38,10 +36,10 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
|
||||
};
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as LazyLibrarian sees it, including http(s)://, port, and urlbase if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as LazyLibrarian sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "LazyLibrarian Server", HelpText = "URL used to connect to LazyLibrarian server, including http(s)://, port, and urlbase if required")]
|
||||
[FieldDefinition(1, Label = "LazyLibrarian Server", HelpText = "URL used to connect to LazyLibrarian server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:5299")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by LazyLibrarian in Settings/Web Interface")]
|
||||
|
||||
@@ -22,15 +22,13 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public LidarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8686";
|
||||
SyncCategories = new[] { 3000, 3010, 3030, 3040, 3050, 3060 };
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Lidarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Lidarr sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Lidarr Server", HelpText = "URL used to connect to Lidarr server, including http(s)://, port, and urlbase if required")]
|
||||
[FieldDefinition(1, Label = "Lidarr Server", HelpText = "URL used to connect to Lidarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8686")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Lidarr in Settings/General")]
|
||||
|
||||
@@ -23,15 +23,13 @@ namespace NzbDrone.Core.Applications.Mylar
|
||||
|
||||
public MylarSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8090";
|
||||
SyncCategories = new[] { NewznabStandardCategory.BooksComics.Id };
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Mylar sees it, including http(s)://, port, and urlbase if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Mylar sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Mylar Server", HelpText = "URL used to connect to Mylar server, including http(s)://, port, and urlbase if required")]
|
||||
[FieldDefinition(1, Label = "Mylar Server", HelpText = "URL used to connect to Mylar server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8090")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Mylar in Settings/Web Interface")]
|
||||
|
||||
@@ -23,15 +23,13 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public RadarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:7878";
|
||||
SyncCategories = new[] { 2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080 };
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Radarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Radarr sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Radarr Server", HelpText = "URL used to connect to Radarr server, including http(s)://, port, and urlbase if required")]
|
||||
[FieldDefinition(1, Label = "Radarr Server", HelpText = "URL used to connect to Radarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:7878")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Radarr in Settings/General")]
|
||||
|
||||
@@ -23,15 +23,13 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
|
||||
public ReadarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8787";
|
||||
SyncCategories = new[] { 3030, 7000, 7010, 7020, 7030, 7040, 7050, 7060 };
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Readarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Readarr sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Readarr Server", HelpText = "URL used to connect to Readarr server, including http(s)://, port, and urlbase if required")]
|
||||
[FieldDefinition(1, Label = "Readarr Server", HelpText = "URL used to connect to Readarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8787")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Readarr in Settings/General")]
|
||||
|
||||
@@ -22,16 +22,14 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public SonarrSettings()
|
||||
{
|
||||
ProwlarrUrl = "http://localhost:9696";
|
||||
BaseUrl = "http://localhost:8989";
|
||||
SyncCategories = new[] { 5000, 5010, 5020, 5030, 5040, 5045, 5050 };
|
||||
AnimeSyncCategories = new[] { 5070 };
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Sonarr sees it, including http(s)://, port, and urlbase if needed")]
|
||||
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Sonarr sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
|
||||
public string ProwlarrUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Sonarr Server", HelpText = "URL used to connect to Sonarr server, including http(s)://, port, and urlbase if required")]
|
||||
[FieldDefinition(1, Label = "Sonarr Server", HelpText = "URL used to connect to Sonarr server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:8989")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Sonarr in Settings/General")]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -633,7 +633,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
var date = DateTimeUtil.ParseDateTimeGoLang(data, layout);
|
||||
data = date.ToString(DateTimeUtil.Rfc1123ZPattern);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
catch (InvalidDateException ex)
|
||||
{
|
||||
_logger.Debug(ex.Message);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
};
|
||||
|
||||
// manually url encode parenthesis to prevent "hacking" detection
|
||||
searchUrl += queryCollection.GetQueryString().Replace("(", "%28").Replace(")", "%29");
|
||||
searchUrl += queryCollection.GetQueryString().Replace("(", "%28").Replace(")", "%29").Replace(".", " ");
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Rss);
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)");
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
16
src/NzbDrone.Core/Notifications/ApplicationUpdateMessage.cs
Normal file
16
src/NzbDrone.Core/Notifications/ApplicationUpdateMessage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -32,15 +32,13 @@ namespace NzbDrone.Core.Notifications.Email
|
||||
|
||||
public EmailSettings()
|
||||
{
|
||||
Server = "smtp.gmail.com";
|
||||
Port = 587;
|
||||
|
||||
To = Array.Empty<string>();
|
||||
CC = Array.Empty<string>();
|
||||
Bcc = Array.Empty<string>();
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Server", HelpText = "Hostname or IP of Email server")]
|
||||
[FieldDefinition(0, Label = "Server", HelpText = "Hostname or IP of Email server", Placeholder = "smtp.gmail.com")]
|
||||
public string Server { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Port")]
|
||||
@@ -55,16 +53,16 @@ namespace NzbDrone.Core.Notifications.Email
|
||||
[FieldDefinition(4, Label = "Password", HelpText = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "From Address")]
|
||||
[FieldDefinition(5, Label = "From Address", Placeholder = "example@email.com")]
|
||||
public string From { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Recipient Address(es)", HelpText = "Comma separated list of email recipients")]
|
||||
[FieldDefinition(6, Label = "Recipient Address(es)", HelpText = "Comma separated list of email recipients", Placeholder = "example@email.com,example1@email.com")]
|
||||
public IEnumerable<string> To { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "CC Address(es)", HelpText = "Comma separated list of email cc recipients", Advanced = true)]
|
||||
[FieldDefinition(7, Label = "CC Address(es)", HelpText = "Comma separated list of email cc recipients", Placeholder = "example@email.com,example1@email.com", Advanced = true)]
|
||||
public IEnumerable<string> CC { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "BCC Address(es)", HelpText = "Comma separated list of email bcc recipients", Advanced = true)]
|
||||
[FieldDefinition(8, Label = "BCC Address(es)", HelpText = "Comma separated list of email bcc recipients", Placeholder = "example@email.com,example1@email.com", Advanced = true)]
|
||||
public IEnumerable<string> Bcc { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace NzbDrone.Core.Notifications.PushBullet
|
||||
[FieldDefinition(0, Label = "Access Token", Privacy = PrivacyLevel.ApiKey, HelpLink = "https://www.pushbullet.com/#settings/account")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Device IDs", HelpText = "List of device IDs (leave blank to send to all devices)", Type = FieldType.Device)]
|
||||
[FieldDefinition(1, Label = "Device IDs", HelpText = "List of device IDs (leave blank to send to all devices)", Type = FieldType.Device, Placeholder = "123456789,987654321")]
|
||||
public IEnumerable<string> DeviceIds { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Channel Tags", HelpText = "List of Channel Tags to send notifications to", Type = FieldType.Tag)]
|
||||
[FieldDefinition(2, Label = "Channel Tags", HelpText = "List of Channel Tags to send notifications to", Type = FieldType.Tag, Placeholder = "Channel1234,Channel4321")]
|
||||
public IEnumerable<string> ChannelTags { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sender ID", HelpText = "The device ID to send notifications from, use device_iden in the device's URL on pushbullet.com (leave blank to send from yourself)")]
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
@@ -23,7 +24,7 @@ namespace NzbDrone.Core.Notifications.Pushover
|
||||
public PushoverSettings()
|
||||
{
|
||||
Priority = 0;
|
||||
Devices = System.Array.Empty<string>();
|
||||
Devices = Array.Empty<string>();
|
||||
}
|
||||
|
||||
//TODO: Get Pushover to change our app name (or create a new app) when we have a new logo
|
||||
@@ -33,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Pushover
|
||||
[FieldDefinition(1, Label = "User Key", Privacy = PrivacyLevel.UserName, HelpLink = "https://pushover.net/")]
|
||||
public string UserKey { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Devices", HelpText = "List of device names (leave blank to send to all devices)", Type = FieldType.Tag)]
|
||||
[FieldDefinition(2, Label = "Devices", HelpText = "List of device names (leave blank to send to all devices)", Type = FieldType.Tag, Placeholder = "device1")]
|
||||
public IEnumerable<string> Devices { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(PushoverPriority))]
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Notifications.SendGrid
|
||||
[FieldDefinition(2, Label = "From Address")]
|
||||
public string From { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Recipient Address(es)", Type = FieldType.Tag)]
|
||||
[FieldDefinition(3, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")]
|
||||
public IEnumerable<string> Recipients { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ namespace NzbDrone.Core.Notifications.Webhook
|
||||
Grab,
|
||||
Download,
|
||||
Rename,
|
||||
Health
|
||||
Health,
|
||||
ApplicationUpdate
|
||||
}
|
||||
}
|
||||
|
||||
466
src/NzbDrone.Core/Parser/DateTimeRoutines.cs
Normal file
466
src/NzbDrone.Core/Parser/DateTimeRoutines.cs
Normal file
@@ -0,0 +1,466 @@
|
||||
//********************************************************************************************
|
||||
//Author: Sergey Stoyan, CliverSoft.com
|
||||
// http://cliversoft.com
|
||||
// stoyan@cliversoft.com
|
||||
// sergey.stoyan@gmail.com
|
||||
// 27 February 2007
|
||||
//********************************************************************************************
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Parser
|
||||
{
|
||||
public static class DateTimeRoutines
|
||||
{
|
||||
public class ParsedDateTime
|
||||
{
|
||||
public readonly int IndexOfDate = -1;
|
||||
public readonly int LengthOfDate = -1;
|
||||
public readonly int IndexOfTime = -1;
|
||||
public readonly int LengthOfTime = -1;
|
||||
public readonly DateTime DateTime;
|
||||
public readonly bool IsDateFound;
|
||||
public readonly bool IsTimeFound;
|
||||
public readonly TimeSpan UtcOffset;
|
||||
public readonly bool IsUtcOffsetFound;
|
||||
public DateTime UtcDateTime;
|
||||
|
||||
internal ParsedDateTime(int index_of_date, int length_of_date, int index_of_time, int length_of_time, DateTime date_time)
|
||||
{
|
||||
IndexOfDate = index_of_date;
|
||||
LengthOfDate = length_of_date;
|
||||
IndexOfTime = index_of_time;
|
||||
LengthOfTime = length_of_time;
|
||||
DateTime = date_time;
|
||||
IsDateFound = index_of_date > -1;
|
||||
IsTimeFound = index_of_time > -1;
|
||||
UtcOffset = new TimeSpan(25, 0, 0);
|
||||
IsUtcOffsetFound = false;
|
||||
UtcDateTime = new DateTime(1, 1, 1);
|
||||
}
|
||||
|
||||
internal ParsedDateTime(int index_of_date, int length_of_date, int index_of_time, int length_of_time, DateTime date_time, TimeSpan utc_offset)
|
||||
{
|
||||
IndexOfDate = index_of_date;
|
||||
LengthOfDate = length_of_date;
|
||||
IndexOfTime = index_of_time;
|
||||
LengthOfTime = length_of_time;
|
||||
DateTime = date_time;
|
||||
IsDateFound = index_of_date > -1;
|
||||
IsTimeFound = index_of_time > -1;
|
||||
UtcOffset = utc_offset;
|
||||
IsUtcOffsetFound = Math.Abs(utc_offset.TotalHours) < 12;
|
||||
if (!IsUtcOffsetFound)
|
||||
{
|
||||
UtcDateTime = new DateTime(1, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index_of_date < 0)
|
||||
{
|
||||
//to avoid negative date exception when date is undefined
|
||||
var ts = date_time.TimeOfDay + utc_offset;
|
||||
if (ts < new TimeSpan(0))
|
||||
{
|
||||
UtcDateTime = new DateTime(1, 1, 2) + ts;
|
||||
}
|
||||
else
|
||||
{
|
||||
UtcDateTime = new DateTime(1, 1, 1) + ts;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UtcDateTime = date_time + utc_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime DefaultDate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DefaultDateIsNow)
|
||||
{
|
||||
return DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _DefaultDate;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
_DefaultDate = value;
|
||||
DefaultDateIsNow = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static DateTime _DefaultDate = DateTime.Now;
|
||||
|
||||
public static bool DefaultDateIsNow = true;
|
||||
|
||||
public enum DateTimeFormat
|
||||
{
|
||||
USDate,
|
||||
UKDate,
|
||||
}
|
||||
|
||||
public static bool TryParseDateOrTime(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_date_time)
|
||||
{
|
||||
parsed_date_time = null;
|
||||
|
||||
ParsedDateTime parsed_date;
|
||||
ParsedDateTime parsed_time;
|
||||
if (!TryParseDate(str, default_format, out parsed_date))
|
||||
{
|
||||
if (!TryParseTime(str, default_format, out parsed_time, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var date_time = new DateTime(DefaultDate.Year, DefaultDate.Month, DefaultDate.Day, parsed_time.DateTime.Hour, parsed_time.DateTime.Minute, parsed_time.DateTime.Second);
|
||||
parsed_date_time = new ParsedDateTime(-1, -1, parsed_time.IndexOfTime, parsed_time.LengthOfTime, date_time, parsed_time.UtcOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TryParseTime(str, default_format, out parsed_time, parsed_date))
|
||||
{
|
||||
var date_time = new DateTime(parsed_date.DateTime.Year, parsed_date.DateTime.Month, parsed_date.DateTime.Day, 0, 0, 0);
|
||||
parsed_date_time = new ParsedDateTime(parsed_date.IndexOfDate, parsed_date.LengthOfDate, -1, -1, date_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
var date_time = new DateTime(parsed_date.DateTime.Year, parsed_date.DateTime.Month, parsed_date.DateTime.Day, parsed_time.DateTime.Hour, parsed_time.DateTime.Minute, parsed_time.DateTime.Second);
|
||||
parsed_date_time = new ParsedDateTime(parsed_date.IndexOfDate, parsed_date.LengthOfDate, parsed_time.IndexOfTime, parsed_time.LengthOfTime, date_time, parsed_time.UtcOffset);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseTime(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_time, ParsedDateTime parsed_date)
|
||||
{
|
||||
parsed_time = null;
|
||||
|
||||
string time_zone_r;
|
||||
if (default_format == DateTimeFormat.USDate)
|
||||
{
|
||||
time_zone_r = @"(?:\s*(?'time_zone'UTC|GMT|CST|EST))?";
|
||||
}
|
||||
else
|
||||
{
|
||||
time_zone_r = @"(?:\s*(?'time_zone'UTC|GMT))?";
|
||||
}
|
||||
|
||||
Match m;
|
||||
if (parsed_date != null && parsed_date.IndexOfDate > -1)
|
||||
{
|
||||
//look around the found date
|
||||
//look for <date> hh:mm:ss <UTC offset>
|
||||
m = Regex.Match(str.Substring(parsed_date.IndexOfDate + parsed_date.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for <date> [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
||||
m = Regex.Match(str.Substring(parsed_date.IndexOfDate + parsed_date.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for [h]h:mm:ss [PM/AM] [UTC/GMT] <date>
|
||||
m = Regex.Match(str.Substring(0, parsed_date.IndexOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for [h]h:mm:ss [PM/AM] [UTC/GMT] within <date>
|
||||
m = Regex.Match(str.Substring(parsed_date.IndexOfDate, parsed_date.LengthOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//look anywhere within string
|
||||
//look for hh:mm:ss <UTC offset>
|
||||
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})?(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
||||
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + time_zone_r + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//try
|
||||
//{
|
||||
var hour = int.Parse(m.Groups["hour"].Value);
|
||||
if (hour < 0 || hour > 23)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var minute = int.Parse(m.Groups["minute"].Value);
|
||||
if (minute < 0 || minute > 59)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var second = 0;
|
||||
if (!string.IsNullOrEmpty(m.Groups["second"].Value))
|
||||
{
|
||||
second = int.Parse(m.Groups["second"].Value);
|
||||
if (second < 0 || second > 59)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Compare(m.Groups["ampm"].Value, "PM", true) == 0 && hour < 12)
|
||||
{
|
||||
hour += 12;
|
||||
}
|
||||
else if (string.Compare(m.Groups["ampm"].Value, "AM", true) == 0 && hour == 12)
|
||||
{
|
||||
hour -= 12;
|
||||
}
|
||||
|
||||
var date_time = new DateTime(1, 1, 1, hour, minute, second);
|
||||
|
||||
if (m.Groups["offset_hh"].Success)
|
||||
{
|
||||
var offset_hh = int.Parse(m.Groups["offset_hh"].Value);
|
||||
var offset_mm = 0;
|
||||
if (m.Groups["offset_mm"].Success)
|
||||
{
|
||||
offset_mm = int.Parse(m.Groups["offset_mm"].Value);
|
||||
}
|
||||
|
||||
var utc_offset = new TimeSpan(offset_hh, offset_mm, 0);
|
||||
if (m.Groups["offset_sign"].Value == "-")
|
||||
{
|
||||
utc_offset = -utc_offset;
|
||||
}
|
||||
|
||||
parsed_time = new ParsedDateTime(-1, -1, m.Index, m.Length, date_time, utc_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m.Groups["time_zone"].Success)
|
||||
{
|
||||
TimeSpan utc_offset;
|
||||
switch (m.Groups["time_zone"].Value)
|
||||
{
|
||||
case "UTC":
|
||||
case "GMT":
|
||||
utc_offset = new TimeSpan(0, 0, 0);
|
||||
break;
|
||||
case "CST":
|
||||
utc_offset = new TimeSpan(-6, 0, 0);
|
||||
break;
|
||||
case "EST":
|
||||
utc_offset = new TimeSpan(-5, 0, 0);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Time zone: " + m.Groups["time_zone"].Value + " is not defined.");
|
||||
}
|
||||
|
||||
parsed_time = new ParsedDateTime(-1, -1, m.Index, m.Length, date_time, utc_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
parsed_time = new ParsedDateTime(-1, -1, m.Index, m.Length, date_time);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseDate(this string str, DateTimeFormat default_format, out ParsedDateTime parsed_date)
|
||||
{
|
||||
parsed_date = null;
|
||||
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//look for dd/mm/yy
|
||||
var m = Regex.Match(str, @"(?<=^|[^\d])(?'day'\d{1,2})\s*(?'separator'[\\/\.])+\s*(?'month'\d{1,2})\s*\'separator'+\s*(?'year'\d{2}|\d{4})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
DateTime date;
|
||||
if ((default_format ^ DateTimeFormat.USDate) == DateTimeFormat.USDate)
|
||||
{
|
||||
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["day"].Value), int.Parse(m.Groups["month"].Value), out date))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out date))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
parsed_date = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
||||
return true;
|
||||
}
|
||||
|
||||
//look for [yy]yy-mm-dd
|
||||
m = Regex.Match(str, @"(?<=^|[^\d])(?'year'\d{2}|\d{4})\s*(?'separator'[\-])\s*(?'month'\d{1,2})\s*\'separator'+\s*(?'day'\d{1,2})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
DateTime date;
|
||||
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out date))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_date = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
||||
return true;
|
||||
}
|
||||
|
||||
//look for month dd yyyy
|
||||
m = Regex.Match(str, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?\s*,?\s*(?'year'\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for dd month [yy]yy
|
||||
m = Regex.Match(str, @"(?:^|[^\d\w:])(?'day'\d{1,2})(?:-?st\s+|-?th\s+|-?rd\s+|-?nd\s+|-|\s+)(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*(?:\s*,?\s*|-)'?(?'year'\d{2}|\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for yyyy month dd
|
||||
m = Regex.Match(str, @"(?:^|[^\d\w])(?'year'\d{4})\s+(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for month dd hh:mm:ss MDT|UTC yyyy
|
||||
m = Regex.Match(str, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})\s+\d{2}\:\d{2}\:\d{2}\s+(?:MDT|UTC)\s+(?'year'\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
if (!m.Success)
|
||||
{
|
||||
//look for month dd [yyyy]
|
||||
m = Regex.Match(str, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?(?:\s*,?\s*(?'year'\d{4}))?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
if (m.Success)
|
||||
{
|
||||
var month = -1;
|
||||
var index_of_date = m.Index;
|
||||
var length_of_date = m.Length;
|
||||
|
||||
switch (m.Groups["month"].Value)
|
||||
{
|
||||
case "Jan":
|
||||
case "JAN":
|
||||
month = 1;
|
||||
break;
|
||||
case "Feb":
|
||||
case "FEB":
|
||||
month = 2;
|
||||
break;
|
||||
case "Mar":
|
||||
case "MAR":
|
||||
month = 3;
|
||||
break;
|
||||
case "Apr":
|
||||
case "APR":
|
||||
month = 4;
|
||||
break;
|
||||
case "May":
|
||||
case "MAY":
|
||||
month = 5;
|
||||
break;
|
||||
case "Jun":
|
||||
case "JUN":
|
||||
month = 6;
|
||||
break;
|
||||
case "Jul":
|
||||
month = 7;
|
||||
break;
|
||||
case "Aug":
|
||||
case "AUG":
|
||||
month = 8;
|
||||
break;
|
||||
case "Sep":
|
||||
case "SEP":
|
||||
month = 9;
|
||||
break;
|
||||
case "Oct":
|
||||
case "OCT":
|
||||
month = 10;
|
||||
break;
|
||||
case "Nov":
|
||||
case "NOV":
|
||||
month = 11;
|
||||
break;
|
||||
case "Dec":
|
||||
case "DEC":
|
||||
month = 12;
|
||||
break;
|
||||
}
|
||||
|
||||
int year;
|
||||
if (!string.IsNullOrEmpty(m.Groups["year"].Value))
|
||||
{
|
||||
year = int.Parse(m.Groups["year"].Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
year = DefaultDate.Year;
|
||||
}
|
||||
|
||||
DateTime date;
|
||||
if (!ConvertToDate(year, month, int.Parse(m.Groups["day"].Value), out date))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_date = new ParsedDateTime(index_of_date, length_of_date, -1, -1, date);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ConvertToDate(int year, int month, int day, out DateTime date)
|
||||
{
|
||||
if (year >= 100)
|
||||
{
|
||||
if (year < 1000)
|
||||
{
|
||||
date = new DateTime(1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (year > 30)
|
||||
{
|
||||
year += 1900;
|
||||
}
|
||||
else
|
||||
{
|
||||
year += 2000;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
date = new DateTime(year, month, day);
|
||||
}
|
||||
catch
|
||||
{
|
||||
date = new DateTime(1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace NzbDrone.Core.Parser
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("TimeAgo parsing failed, unknown unit: " + unit);
|
||||
throw new InvalidDateException("TimeAgo parsing failed, unknown unit: " + unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,19 +102,17 @@ namespace NzbDrone.Core.Parser
|
||||
// http://www.codeproject.com/Articles/33298/C-Date-Time-Parser
|
||||
public static DateTime FromFuzzyTime(string str, string format = null)
|
||||
{
|
||||
//var dtFormat = format == "UK" ?
|
||||
// DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UkDate :
|
||||
// DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UsaDate;
|
||||
var dtFormat = format == "UK" ?
|
||||
DateTimeRoutines.DateTimeFormat.UKDate :
|
||||
DateTimeRoutines.DateTimeFormat.USDate;
|
||||
|
||||
//if (DateTimeRoutines.DateTimeRoutines.TryParseDateOrTime(
|
||||
// str, dtFormat, out DateTimeRoutines.DateTimeRoutines.ParsedDateTime dt))
|
||||
// return dt.DateTime;
|
||||
if (DateTime.TryParse(str, out var dateTimeParsed))
|
||||
if (DateTimeRoutines.TryParseDateOrTime(
|
||||
str, dtFormat, out DateTimeRoutines.ParsedDateTime dt))
|
||||
{
|
||||
return dateTimeParsed;
|
||||
return dt.DateTime;
|
||||
}
|
||||
|
||||
throw new Exception($"FromFuzzyTime parsing failed for string {str}");
|
||||
throw new InvalidDateException($"FromFuzzyTime parsing failed for string {str}");
|
||||
}
|
||||
|
||||
public static DateTime FromUnknown(string str, string format = null)
|
||||
@@ -214,16 +212,13 @@ namespace NzbDrone.Core.Parser
|
||||
return dt;
|
||||
}
|
||||
|
||||
try
|
||||
// try parsing the str as an unix timestamp
|
||||
if (long.TryParse(str, out var unixTimeStamp))
|
||||
{
|
||||
// try parsing the str as an unix timestamp
|
||||
var unixTimeStamp = long.Parse(str);
|
||||
return UnixTimestampToDateTime(unixTimeStamp);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// it wasn't a timestamp, continue....
|
||||
}
|
||||
|
||||
// it wasn't a timestamp, continue....
|
||||
|
||||
// add missing year
|
||||
match = _MissingYearRegexp.Match(str);
|
||||
@@ -247,7 +242,7 @@ namespace NzbDrone.Core.Parser
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"DateTime parsing failed for \"{str}\": {ex}");
|
||||
throw new InvalidDateException($"DateTime parsing failed for \"{str}\": {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +312,7 @@ namespace NzbDrone.Core.Parser
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
throw new FormatException($"Error while parsing DateTime \"{date}\", using layout \"{layout}\" ({pattern}): {ex.Message}");
|
||||
throw new InvalidDateException($"Error while parsing DateTime \"{date}\", using layout \"{layout}\" ({pattern}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =>
|
||||
{
|
||||
@@ -171,8 +174,6 @@ namespace NzbDrone.Host
|
||||
return ApplicationModes.UninstallService;
|
||||
}
|
||||
|
||||
Logger.Debug("Getting windows service status");
|
||||
|
||||
// IsWindowsService can throw sometimes, so wrap it
|
||||
var isWindowsService = false;
|
||||
try
|
||||
@@ -197,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr17" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr18" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Common.Test\Prowlarr.Common.Test.csproj" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr17" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr18" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,6 +19,7 @@ namespace Prowlarr.Http.ClientSchema
|
||||
public string SelectOptionsProviderAction { get; set; }
|
||||
public string Section { get; set; }
|
||||
public string Hidden { get; set; }
|
||||
public string Placeholder { get; set; }
|
||||
|
||||
public Field Clone()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -101,7 +104,8 @@ namespace Prowlarr.Http.ClientSchema
|
||||
Order = fieldAttribute.Order,
|
||||
Advanced = fieldAttribute.Advanced,
|
||||
Type = fieldAttribute.Type.ToString().FirstCharToLower(),
|
||||
Section = fieldAttribute.Section
|
||||
Section = fieldAttribute.Section,
|
||||
Placeholder = fieldAttribute.Placeholder
|
||||
};
|
||||
|
||||
if (fieldAttribute.Type == FieldType.Select || fieldAttribute.Type == FieldType.TagSelect)
|
||||
|
||||
Reference in New Issue
Block a user