diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 2684ccc3e..000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-#### Ombi build Version:
-
-V 3.0.XX
-
-#### Update Branch:
-
-Open Beta
-
-#### Media Sever:
-
-Plex/Emby
-
-#### Media Server Version:
-
-
-
-#### Operating System:
-
-(Place text here)
-
-
-#### Ombi Applicable Logs (from `/logs/` directory or the Admin page):
-
-```
-
-(Logs go here. Don't remove the ' tags for showing your logs correctly. Please make sure you remove any personal information from the logs)
-
-```
-
-#### Problem Description:
-
-(Place text here)
-
-#### Reproduction Steps:
-
-Please include any steps to reproduce the issue, this the request that is causing the problem etc.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..2236cc395
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,37 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Logs (Logs directory where Ombi is located)**
+If applicable, a snippet of the logs that seems relevant to the bug if present.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+
+**Ombi Version (please complete the following information):**
+ - Version [e.g. 3.0.1158]
+- Media Server [e.g. Plex]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e7ea6f21..5f2948060 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,105 @@
# Changelog
-## (unreleased)
+## v3.0.4256 (2019-02-18)
### **New Features**
+- Update discord link to follow the scheme of other links. [Tom McClellan]
+
+- Update issue templates. [Jamie]
+
+- Update README.md. [Jamie]
+
+- Update CHANGELOG.md. [Jamie]
+
+- Added the functionality to remove a user from getting notifications to their mobile device #2780. [tidusjar]
+
+- Added a demo mode, this will only show movies and shows that are in the public domain. Dam that stupid fruit company. [tidusjar]
+
+- Added Actor Searching for Movies! [TidusJar]
+
+- Added the ability to change where the View on Emby link goes to #2730. [TidusJar]
+
+- Added the request queue to the notifications UI so you can turn it off per notification agent #2747. [TidusJar]
+
- Added new classes to the posters #2732. [TidusJar]
+### **Fixes**
+
+- Fix: src/Ombi/package.json to reduce vulnerabilities. [snyk-bot]
+
+- Fixed #2801 this is when a season is not correctly monitored in sonarr when approved by an admin. [tidusjar]
+
+- Small improvements to try and mitigate #2750. [tidusjar]
+
+- Removed some areas where we clear out the cache. This should help with DB locking #2750. [tidusjar]
+
+- Fixed #2810. [tidusjar]
+
+- Cannot create an issue comment with the API #2811. [tidusjar]
+
+- Set the local domain if the Application URL is set for the HELO or EHLO commands. #2636. [tidusjar]
+
+- New translations en.json (Spanish) [Jamie]
+
+- Delete ISSUE_TEMPLATE.md. [Jamie]
+
+- More minor grammatical edits. [Andrew Metzger]
+
+- Minor grammatical edits. [Andrew Metzger]
+
+- Fixed #2802 the issue where "Issues" were not being deleted correctly. [tidusjar]
+
+- Fixed #2797. [tidusjar]
+
+- New translations en.json (Dutch) [Jamie]
+
+- New translations en.json (Spanish) [Jamie]
+
+- New translations en.json (Portuguese, Brazilian) [Jamie]
+
+- Fixed #2786. [tidusjar]
+
+- Fixed #2756. [tidusjar]
+
+- Ignore the UserName header as part of the Api is the value is an empty string. [tidusjar]
+
+- Fixed #2759. [tidusjar]
+
+- Did #2756. [TidusJar]
+
+- Fixed the exception that sometimes makes ombi fallover. [TidusJar]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- Log the error to the ui to figure out what's going on with #2755. [tidusjar]
+
+- Small fix when denying a request with a reason, we wasn't updating the ui. [TidusJar]
+
+- Make sure we can only set the ApiAlias when using the API Key. [tidusjar]
+
+- #2363 Added the ability to pass any username into the API using the ApiAlias header. [tidusjar]
+
+- Removed the Add user to Plex from Ombi. [tidusjar]
+
## v3.0.4119 (2019-01-09)
diff --git a/README.md b/README.md
index 6347a8a5f..0af908f65 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ We integrate with the following applications:
Supported notifications:
* SMTP Notifications (Email)
* Discord
+* Gotify
* Slack
* Pushbullet
* Pushover
@@ -117,13 +118,12 @@ Please feel free to submit a pull request!
# Donation
If you feel like donating you can donate with the below buttons!
-[](https://patreon.com/tidusjar/Ombi)
-[](https://paypal.me/PlexRequestsNet)
+
+[](https://patreon.com/tidusjar/Ombi)
+[](https://paypal.me/PlexRequestsNet)
### A massive thanks to everyone for all their help!
-## Stats
-[](https://waffle.io/tidusjar/PlexRequests.Net/metrics/throughput)
### Sponsors ###
- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
diff --git a/appveyor.yml b/appveyor.yml
index 3c60a0006..0d5591971 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,13 +1,17 @@
-version: 3.0.{build}
+version: 4.0.{build}
configuration: Release
os: Visual Studio 2017
+
environment:
- nodejs_version: "9.8.0"
+ nodejs_version: "11.5.0"
typescript_version: "3.0.1"
github_auth_token:
secure: H/7uCrjmWHGJxgN3l9fbhhdVjvvWI8VVF4ZzQqeXuJwAf+PgSNBdxv4SS+rMQ+RH
- sonarrcloudtoken:
- secure: WGkIog4wuMSx1q5vmSOgIBXMtI/leMpLbZhi9MJnJdBBuDfcv12zwXg3LQwY0WbE
+
+
+
+# Do not build on tags (GitHub and BitBucket)
+skip_tags: true
install:
# Get the latest stable version of Node.js or io.js
@@ -16,35 +20,45 @@ install:
- cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\3.0;%path%
- cmd: tsc -v
build_script:
- # - dotnet tool install --global dotnet-sonarscanner
- #- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { dotnet sonarscanner begin /k:"tidusjar_Ombi" /d:sonar.organization="tidusjar-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="$env.sonarrcloud_token" /d:sonar.analysis.mode="preview" /d:sonar.github.pullRequest="$env:APPVEYOR_PULL_REQUEST_NUMBER" /d:sonar.github.repository="https://github.com/tidusjar/ombi" /d:sonar.github.oauth="$env.github_auth_token" }
- # - ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { dotnet sonarscanner begin /k:"tidusjar_Ombi" /d:sonar.organization="tidusjar-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="$env.SONARRCLOUDTOKEN" }
- - ps: ./build.ps1 --settings_skipverification=true
- # - dotnet sonarscanner end /d:sonar.login="%sonarrcloudtoken%"
+ - ps: |
+ $deployBranches =
+ "feature/v4",
+ "develop",
+ "master";
-test: off
+ If(($env:APPVEYOR_REPO_BRANCH -in $deployBranches -Or $env:APPVEYOR_REPO_COMMIT_MESSAGE -Match '!deploy') -And $env:APPVEYOR_REPO_COMMIT_MESSAGE -NotMatch '!build') {
+ Write-Output "This is a deployment build"
+ $env:Deploy = 'true'
+ ./build.ps1
+ }
+ Else
+ {
+ $env:Deploy = 'false'
+ Write-Output "This is a not a deployment build"
+ ./build.ps1 --target=build
+ }
+skip_commits:
+ files:
+ - '**/*.md'
+
after_build:
-- cmd: >-
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows.zip"
-
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\osx.tar.gz"
-
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux.tar.gz"
+- ps: |
+ $deployBranches =
+ "feature/v4",
+ "develop",
+ "master";
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm.tar.gz"
-
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows-32bit.zip"
-
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm64.tar.gz"
-
-
+ If(($env:APPVEYOR_REPO_BRANCH -in $deployBranches -Or $env:APPVEYOR_REPO_COMMIT_MESSAGE -Match '!deploy') -And $env:APPVEYOR_REPO_COMMIT_MESSAGE -NotMatch '!build')
+ {
+ Write-Output "Deploying!"
+ Get-ChildItem -Recurse .\*.zip | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
+ Get-ChildItem -Recurse .\*.gz | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
+ }
+ Else
+ {
+ Write-Output "No Deployment"
+ }
#cache:
#- '%USERPROFILE%\.nuget\packages'
diff --git a/build.cake b/build.cake
index 080ef167e..fb72fb839 100644
--- a/build.cake
+++ b/build.cake
@@ -1,10 +1,10 @@
-#tool "nuget:?package=GitVersion.CommandLine"
-#addin "Cake.Gulp"
-#addin "SharpZipLib"
-#addin nuget:?package=Cake.Compression&version=0.1.4
-#addin "Cake.Incubator"
-#addin "Cake.Yarn"
+#tool "nuget:?package=GitVersion.CommandLine&version=4.0.0"
+#addin nuget:?package=SharpZipLib&version=1.1.0
+#addin nuget:?package=Cake.Compression&version=0.2.2
+#addin "Cake.Incubator&version=3.1.0"
+#addin nuget:?package=Cake.Yarn&version=0.4.5
+#addin "Cake.Powershell"
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
@@ -82,9 +82,9 @@ Task("SetVersionInfo")
versionInfo = GitVersion(settings);
- Information("GitResults -> {0}", versionInfo.Dump());
+// Information("GitResults -> {0}", versionInfo.Dump());
- Information(@"Build:{0}",AppVeyor.Environment.Build.Dump());
+//Information(@"Build:{0}",AppVeyor.Environment.Build.Dump());
var buildVersion = string.Empty;
if(string.IsNullOrEmpty(AppVeyor.Environment.Build.Version))
@@ -135,7 +135,7 @@ Task("Gulp Publish")
Task("TSLint")
.Does(() =>
{
- // Yarn.FromPath(uiProjectDir).RunScript("lint");
+ //Yarn.FromPath(uiProjectDir).RunScript("lint");
});
Task("PrePublish")
@@ -156,6 +156,7 @@ Task("Package")
});
Task("Publish")
+ .IsDependentOn("Run-Unit-Tests")
.IsDependentOn("PrePublish")
.IsDependentOn("Publish-Windows")
.IsDependentOn("Publish-Windows-32bit")
@@ -250,9 +251,43 @@ Task("Publish-Linux-ARM-64Bit")
Task("Run-Unit-Tests")
.Does(() =>
-{
- DotNetCoreBuild(csProj, buildSettings);
+{
+ var settings = new DotNetCoreTestSettings
+ {
+ ArgumentCustomization = args => args.Append("--logger \"trx;LogFileName=Test.trx\""),
+ Configuration = "Release"
+ };
+ var projectFiles = GetFiles("./**/*Tests.csproj");
+ foreach(var file in projectFiles)
+ {
+ DotNetCoreTest(file.FullPath, settings);
+ }
+
+ var script = @"
+ $wc = New-Object 'System.Net.WebClient'
+ foreach ($name in Resolve-Path .\src\**\TestResults\Test*.trx)
+ {
+ $wc.UploadFile(""https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)\"", $name)
+ }
+";
+ // Upload the results
+ StartPowershellScript(script);
});
+
+Task("Run-Server-Build")
+ .Does(() =>
+ {
+ var settings = new DotNetCoreBuildSettings
+ {
+ Framework = frameworkVer,
+ Configuration = "Release",
+ OutputDirectory = Directory(buildDir)
+ };
+ DotNetCoreBuild(csProj, settings);
+ });
+
+Task("Run-UI-Build")
+ .IsDependentOn("PrePublish");
//////////////////////////////////////////////////////////////////////
// TASK TARGETS
//////////////////////////////////////////////////////////////////////
@@ -260,6 +295,12 @@ Task("Run-Unit-Tests")
Task("Default")
.IsDependentOn("Publish");
+Task("Build")
+ .IsDependentOn("SetVersionInfo")
+ .IsDependentOn("Run-Unit-Tests")
+ .IsDependentOn("Run-Server-Build");
+ // .IsDependentOn("Run-UI-Build");
+
//////////////////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////////////////
diff --git a/build.ps1 b/build.ps1
index 3a8ef5c4c..e61281292 100644
--- a/build.ps1
+++ b/build.ps1
@@ -21,40 +21,49 @@ The build script target to run.
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
-.PARAMETER Experimental
-Tells Cake to use the latest Roslyn release.
-.PARAMETER WhatIf
-Performs a dry run of the build script.
-No tasks will be executed.
-.PARAMETER Mono
-Tells Cake to use the Mono scripting engine.
+.PARAMETER ShowDescription
+Shows description about tasks.
+.PARAMETER DryRun
+Performs a dry run.
.PARAMETER SkipToolPackageRestore
Skips restoring of packages.
.PARAMETER ScriptArgs
Remaining arguments are added here.
.LINK
-http://cakebuild.net
+https://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
- [string]$Target = "Default",
- [ValidateSet("Release", "Debug")]
- [string]$Configuration = "Release",
+ [string]$Target,
+ [string]$Configuration,
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
- [string]$Verbosity = "Verbose",
- [switch]$Experimental,
- [Alias("DryRun","Noop")]
- [switch]$WhatIf,
- [switch]$Mono,
+ [string]$Verbosity,
+ [switch]$ShowDescription,
+ [Alias("WhatIf", "Noop")]
+ [switch]$DryRun,
[switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
+# Attempt to set highest encryption available for SecurityProtocol.
+# PowerShell will not set this by default (until maybe .NET 4.6.x). This
+# will typically produce a message for PowerShell v2 (just an info
+# message though)
+try {
+ # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48)
+ # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't
+ # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is
+ # installed (.NET 4.5 is an in-place upgrade).
+ [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48
+ } catch {
+ Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3'
+ }
+
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
function MD5HashFile([string] $filePath)
{
@@ -80,6 +89,15 @@ function MD5HashFile([string] $filePath)
}
}
+function GetProxyEnabledWebClient
+{
+ $wc = New-Object System.Net.WebClient
+ $proxy = [System.Net.WebRequest]::GetSystemWebProxy()
+ $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
+ $wc.Proxy = $proxy
+ return $wc
+}
+
Write-Host "Preparing to run build script..."
if(!$PSScriptRoot){
@@ -87,31 +105,15 @@ if(!$PSScriptRoot){
}
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
+$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
+$MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
-
-# Should we use mono?
-$UseMono = "";
-if($Mono.IsPresent) {
- Write-Verbose -Message "Using the Mono based scripting engine."
- $UseMono = "-mono"
-}
-
-# Should we use the new Roslyn?
-$UseExperimental = "";
-if($Experimental.IsPresent -and !($Mono.IsPresent)) {
- Write-Verbose -Message "Using experimental version of Roslyn."
- $UseExperimental = "-experimental"
-}
-
-# Is this a dry run?
-$UseDryRun = "";
-if($WhatIf.IsPresent) {
- $UseDryRun = "-dryrun"
-}
+$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
+$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
# Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
@@ -122,7 +124,10 @@ if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
# Make sure that packages.config exist.
if (!(Test-Path $PACKAGES_CONFIG)) {
Write-Verbose -Message "Downloading packages.config..."
- try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
+ try {
+ $wc = GetProxyEnabledWebClient
+ $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG)
+ } catch {
Throw "Could not download packages.config."
}
}
@@ -142,7 +147,8 @@ if (!(Test-Path $NUGET_EXE)) {
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..."
try {
- (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
+ $wc = GetProxyEnabledWebClient
+ $wc.DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
@@ -161,20 +167,56 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
Write-Verbose -Message "Missing or changed package.config hash..."
- Remove-Item * -Recurse -Exclude packages.config,nuget.exe
+ Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
+ Remove-Item -Recurse
}
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
if ($LASTEXITCODE -ne 0) {
- Throw "An error occured while restoring NuGet tools."
+ Throw "An error occurred while restoring NuGet tools."
}
else
{
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
}
Write-Verbose -Message ($NuGetOutput | out-string)
+
+ Pop-Location
+}
+
+# Restore addins from NuGet
+if (Test-Path $ADDINS_PACKAGES_CONFIG) {
+ Push-Location
+ Set-Location $ADDINS_DIR
+
+ Write-Verbose -Message "Restoring addins from NuGet..."
+ $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
+
+ if ($LASTEXITCODE -ne 0) {
+ Throw "An error occurred while restoring NuGet addins."
+ }
+
+ Write-Verbose -Message ($NuGetOutput | out-string)
+
+ Pop-Location
+}
+
+# Restore modules from NuGet
+if (Test-Path $MODULES_PACKAGES_CONFIG) {
+ Push-Location
+ Set-Location $MODULES_DIR
+
+ Write-Verbose -Message "Restoring modules from NuGet..."
+ $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
+
+ if ($LASTEXITCODE -ne 0) {
+ Throw "An error occurred while restoring NuGet modules."
+ }
+
+ Write-Verbose -Message ($NuGetOutput | out-string)
+
Pop-Location
}
@@ -183,7 +225,18 @@ if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
+
+
+# Build Cake arguments
+$cakeArguments = @("$Script");
+if ($Target) { $cakeArguments += "-target=$Target" }
+if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
+if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
+if ($ShowDescription) { $cakeArguments += "-showdescription" }
+if ($DryRun) { $cakeArguments += "-dryrun" }
+$cakeArguments += $ScriptArgs
+
# Start Cake
Write-Host "Running build script..."
-Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
+&$CAKE_EXE $cakeArguments
exit $LASTEXITCODE
\ No newline at end of file
diff --git a/src/Ombi.Api.Gotify/GotifyApi.cs b/src/Ombi.Api.Gotify/GotifyApi.cs
new file mode 100644
index 000000000..8cd79a689
--- /dev/null
+++ b/src/Ombi.Api.Gotify/GotifyApi.cs
@@ -0,0 +1,36 @@
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace Ombi.Api.Gotify
+{
+ public class GotifyApi : IGotifyApi
+ {
+ public GotifyApi(IApi api)
+ {
+ _api = api;
+ }
+
+ private readonly IApi _api;
+
+ public async Task PushAsync(string baseUrl, string accessToken, string subject, string body, sbyte priority)
+ {
+ var request = new Request("/message", baseUrl, HttpMethod.Post);
+ request.AddQueryString("token", accessToken);
+
+ request.AddHeader("Access-Token", accessToken);
+ request.ApplicationJsonContentType();
+
+
+ var jsonBody = new
+ {
+ message = body,
+ title = subject,
+ priority = priority
+ };
+
+ request.AddJsonBody(jsonBody);
+
+ await _api.Request(request);
+ }
+ }
+}
diff --git a/src/Ombi.Api.Gotify/IGotifyApi.cs b/src/Ombi.Api.Gotify/IGotifyApi.cs
new file mode 100644
index 000000000..e6a6b4060
--- /dev/null
+++ b/src/Ombi.Api.Gotify/IGotifyApi.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace Ombi.Api.Gotify
+{
+ public interface IGotifyApi
+ {
+ Task PushAsync(string endpoint, string accessToken, string subject, string body, sbyte priority);
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj b/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj
new file mode 100644
index 000000000..ce5475fae
--- /dev/null
+++ b/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj
@@ -0,0 +1,15 @@
+
+
+
+ netstandard2.0
+ 3.0.0.0
+ 3.0.0.0
+
+
+
+
+
+
+
+
+
diff --git a/src/Ombi.Api.Trakt/ITraktApi.cs b/src/Ombi.Api.Trakt/ITraktApi.cs
index 17c4203cb..087784e01 100644
--- a/src/Ombi.Api.Trakt/ITraktApi.cs
+++ b/src/Ombi.Api.Trakt/ITraktApi.cs
@@ -12,5 +12,6 @@ namespace Ombi.Api.Trakt
Task> GetMostWatchesShows(TraktTimePeriod period = null, int? page = default(int?), int? limitPerPage = default(int?));
Task> GetPopularShows(int? page = default(int?), int? limitPerPage = default(int?));
Task> GetTrendingShows(int? page = default(int?), int? limitPerPage = default(int?));
+ Task GetTvExtendedInfo(string imdbId);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Api.Trakt/TraktApi.cs b/src/Ombi.Api.Trakt/TraktApi.cs
index 23773eb87..2e09ec071 100644
--- a/src/Ombi.Api.Trakt/TraktApi.cs
+++ b/src/Ombi.Api.Trakt/TraktApi.cs
@@ -23,7 +23,7 @@ namespace Ombi.Api.Trakt
public async Task> GetPopularShows(int? page = null, int? limitPerPage = null)
{
- var popular = await Client.Shows.GetPopularShowsAsync(new TraktExtendedInfo { Full = true, Images = true}, null, page ?? 1, limitPerPage ?? 10);
+ var popular = await Client.Shows.GetPopularShowsAsync(new TraktExtendedInfo { Full = true, Images = true }, null, page ?? 1, limitPerPage ?? 10);
return popular.Value;
}
@@ -44,6 +44,11 @@ namespace Ombi.Api.Trakt
var anticipatedShows = await Client.Shows.GetMostWatchedShowsAsync(period ?? TraktTimePeriod.Monthly, new TraktExtendedInfo { Full = true, Images = true }, null, page ?? 1, limitPerPage ?? 10);
return anticipatedShows.Value;
}
+
+ public async Task GetTvExtendedInfo(string imdbId)
+ {
+ return await Client.Shows.GetShowAsync(imdbId, new TraktExtendedInfo { Full = true });
+ }
}
}
diff --git a/src/Ombi.Api.TvMaze/ITvMazeApi.cs b/src/Ombi.Api.TvMaze/ITvMazeApi.cs
index 9a8688568..819051d83 100644
--- a/src/Ombi.Api.TvMaze/ITvMazeApi.cs
+++ b/src/Ombi.Api.TvMaze/ITvMazeApi.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.TvMaze.Models;
+using Ombi.Api.TvMaze.Models.V2;
namespace Ombi.Api.TvMaze
{
@@ -11,5 +12,6 @@ namespace Ombi.Api.TvMaze
Task> Search(string searchTerm);
Task ShowLookup(int showId);
Task ShowLookupByTheTvDbId(int theTvDbId);
+ Task GetTvFullInformation(int id);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Api.TvMaze/Models/V2/FullSearch.cs b/src/Ombi.Api.TvMaze/Models/V2/FullSearch.cs
new file mode 100644
index 000000000..77b9c4a28
--- /dev/null
+++ b/src/Ombi.Api.TvMaze/Models/V2/FullSearch.cs
@@ -0,0 +1,144 @@
+using System;
+
+namespace Ombi.Api.TvMaze.Models.V2
+{
+ public class FullSearch
+ {
+ public int id { get; set; }
+ public string url { get; set; }
+ public string name { get; set; }
+ public string type { get; set; }
+ public string language { get; set; }
+ public string[] genres { get; set; }
+ public string status { get; set; }
+ public int runtime { get; set; }
+ public string premiered { get; set; }
+ public string officialSite { get; set; }
+ public Schedule schedule { get; set; }
+ public Rating rating { get; set; }
+ public int weight { get; set; }
+ public Network network { get; set; }
+ public object webChannel { get; set; }
+ public Externals externals { get; set; }
+ public Image image { get; set; }
+ public string summary { get; set; }
+ public int updated { get; set; }
+ public _Links _links { get; set; }
+ public _Embedded _embedded { get; set; }
+ }
+
+ public class Schedule
+ {
+ public string time { get; set; }
+ public string[] days { get; set; }
+ }
+
+ public class Rating
+ {
+ public float average { get; set; }
+ }
+
+ public class Network
+ {
+ public int id { get; set; }
+ public string name { get; set; }
+ public Country country { get; set; }
+ }
+
+ public class Country
+ {
+ public string name { get; set; }
+ public string code { get; set; }
+ public string timezone { get; set; }
+ }
+
+ public class Externals
+ {
+ public int tvrage { get; set; }
+ public int thetvdb { get; set; }
+ public string imdb { get; set; }
+ }
+
+ public class Image
+ {
+ public string medium { get; set; }
+ public string original { get; set; }
+ }
+
+ public class _Links
+ {
+ public Self self { get; set; }
+ public Previousepisode previousepisode { get; set; }
+ }
+
+ public class Self
+ {
+ public string href { get; set; }
+ }
+
+ public class Previousepisode
+ {
+ public string href { get; set; }
+ }
+
+ public class _Embedded
+ {
+ public Cast[] cast { get; set; }
+ public Crew[] crew { get; set; }
+ public Episode[] episodes { get; set; }
+ }
+
+ public class Cast
+ {
+ public Person person { get; set; }
+ public Character character { get; set; }
+ public bool self { get; set; }
+ public bool voice { get; set; }
+ }
+
+ public class Person
+ {
+ public int id { get; set; }
+ public string url { get; set; }
+ public string name { get; set; }
+ public Country country { get; set; }
+ public string birthday { get; set; }
+ public object deathday { get; set; }
+ public string gender { get; set; }
+ public Image image { get; set; }
+ public _Links _links { get; set; }
+ }
+
+
+ public class Character
+ {
+ public int id { get; set; }
+ public string url { get; set; }
+ public string name { get; set; }
+ public Image image { get; set; }
+ public _Links _links { get; set; }
+ }
+
+ public class Crew
+ {
+ public string type { get; set; }
+ public Person person { get; set; }
+ }
+
+ public class Episode
+ {
+ public int id { get; set; }
+ public string url { get; set; }
+ public string name { get; set; }
+ public int season { get; set; }
+ public int number { get; set; }
+ public string airdate { get; set; }
+ public string airtime { get; set; }
+ public DateTime airstamp { get; set; }
+ public int runtime { get; set; }
+ public Image image { get; set; }
+ public string summary { get; set; }
+ public _Links _links { get; set; }
+ }
+
+}
diff --git a/src/Ombi.Api.TvMaze/TvMazeApi.cs b/src/Ombi.Api.TvMaze/TvMazeApi.cs
index 9aa547483..5d761da47 100644
--- a/src/Ombi.Api.TvMaze/TvMazeApi.cs
+++ b/src/Ombi.Api.TvMaze/TvMazeApi.cs
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.TvMaze.Models;
+using Ombi.Api.TvMaze.Models.V2;
using Ombi.Helpers;
namespace Ombi.Api.TvMaze
@@ -15,7 +15,6 @@ namespace Ombi.Api.TvMaze
{
Api = api;
Logger = logger;
- //Mapper = mapper;
}
private string Uri = "http://api.tvmaze.com";
private IApi Api { get; }
@@ -75,5 +74,17 @@ namespace Ombi.Api.TvMaze
return await Api.Request>(request);
}
+ public async Task GetTvFullInformation(int id)
+ {
+ var request = new Request($"shows/{id}", Uri, HttpMethod.Get);
+
+ request.AddQueryString("embed[]", "cast");
+ request.AddQueryString("embed[]", "crew");
+ request.AddQueryString("embed[]", "episodes");
+
+ request.AddContentHeader("Content-Type", "application/json");
+
+ return await Api.Request(request);
+ }
}
}
diff --git a/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs b/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs
new file mode 100644
index 000000000..bd6e8d5a2
--- /dev/null
+++ b/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs
@@ -0,0 +1,106 @@
+using Microsoft.AspNetCore.Identity;
+using Moq;
+using NUnit.Framework;
+using Ombi.Api.Plex;
+using Ombi.Api.Plex.Models;
+using Ombi.Core.Authentication;
+using Ombi.Core.Settings;
+using Ombi.Settings.Settings.Models;
+using Ombi.Store.Entities;
+using Ombi.Test.Common;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Ombi.Core.Tests.Authentication
+{
+ [TestFixture]
+ public class OmbiUserManagerTests
+ {
+
+ [SetUp]
+ public void Setup()
+ {
+
+ UserStore = new Mock>();
+ PlexApi = new Mock();
+ AuthenticationSettings = new Mock>();
+
+ AuthenticationSettings.Setup(x => x.GetSettingsAsync())
+ .ReturnsAsync(new AuthenticationSettings());
+ _um = new OmbiUserManager(UserStore.Object, null, null, null, null, null, null, null, null,
+ PlexApi.Object, null, null, AuthenticationSettings.Object);
+ }
+
+ public OmbiUserManager _um { get; set; }
+ private Mock> UserStore { get; set; }
+ private Mock PlexApi { get; set; }
+ private Mock> AuthenticationSettings { get; set; }
+
+ [Test]
+ public async Task CheckPassword_PlexUser_EmailLogin_ValidPassword()
+ {
+ var user = new OmbiUser
+ {
+ UserType = UserType.PlexUser,
+ EmailLogin = true,
+ Email = "MyEmail@email.com"
+ };
+ PlexApi.Setup(x => x.SignIn(It.IsAny()))
+ .ReturnsAsync(new PlexAuthentication
+ {
+ user = new User
+ {
+ authentication_token = "abc"
+ }
+ });
+ var result = await _um.CheckPasswordAsync(user, "pass");
+
+ Assert.That(result, Is.True);
+ PlexApi.Verify(x => x.SignIn(It.Is(c => c.login == "MyEmail@email.com")), Times.Once);
+ }
+
+ [Test]
+ public async Task CheckPassword_PlexUser_UserNameLogin_ValidPassword()
+ {
+ var user = new OmbiUser
+ {
+ UserType = UserType.PlexUser,
+ EmailLogin = false,
+ Email = "MyEmail@email.com",
+ UserName = "heyhey"
+ };
+ PlexApi.Setup(x => x.SignIn(It.IsAny()))
+ .ReturnsAsync(new PlexAuthentication
+ {
+ user = new User
+ {
+ authentication_token = "abc"
+ }
+ });
+ var result = await _um.CheckPasswordAsync(user, "pass");
+
+ Assert.That(result, Is.True);
+ PlexApi.Verify(x => x.SignIn(It.Is(c => c.login == "heyhey")), Times.Once);
+ }
+
+ [Test]
+ public async Task CheckPassword_PlexUser_UserNameLogin_InvalidPassword()
+ {
+ var user = new OmbiUser
+ {
+ UserType = UserType.PlexUser,
+ EmailLogin = false,
+ Email = "MyEmail@email.com",
+ UserName = "heyhey"
+ };
+ PlexApi.Setup(x => x.SignIn(It.IsAny()))
+ .ReturnsAsync(new PlexAuthentication());
+ var result = await _um.CheckPasswordAsync(user, "pass");
+
+ Assert.That(result, Is.False);
+ PlexApi.Verify(x => x.SignIn(It.Is(c => c.login == "heyhey")), Times.Once);
+ }
+ }
+}
diff --git a/src/Ombi.Core.Tests/Engine/CalendarEngineTests.cs b/src/Ombi.Core.Tests/Engine/CalendarEngineTests.cs
new file mode 100644
index 000000000..b922a21c0
--- /dev/null
+++ b/src/Ombi.Core.Tests/Engine/CalendarEngineTests.cs
@@ -0,0 +1,195 @@
+
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using Moq;
+using NUnit.Framework;
+using Ombi.Core.Authentication;
+using Ombi.Core.Engine.V2;
+using Ombi.Store.Entities.Requests;
+using Ombi.Store.Repository.Requests;
+
+namespace Ombi.Core.Tests.Engine
+{
+ [TestFixture]
+ public class CalendarEngineTests
+ {
+ public Mock MovieRepo { get; set; }
+ public Mock TvRepo { get; set; }
+ public CalendarEngine CalendarEngine { get; set; }
+
+ [SetUp]
+ public void Setup()
+ {
+ MovieRepo = new Mock();
+ TvRepo = new Mock();
+ var principle = new Mock();
+ var identity = new Mock();
+ identity.Setup(x => x.Name).Returns("UnitTest");
+ principle.Setup(x => x.Identity).Returns(identity.Object);
+ CalendarEngine = new CalendarEngine(principle.Object, null, null, MovieRepo.Object, TvRepo.Object);
+ }
+
+ [Test]
+ public async Task Calendar_Movies_OnlyGet_PreviousAndFuture_90_Days()
+ {
+ var movies = new List
+ {
+ new MovieRequests
+ {
+ Title="Invalid",
+ ReleaseDate = new DateTime(2018,10,01)
+ },
+ new MovieRequests
+ {
+ Title="Invalid",
+ ReleaseDate = DateTime.Now.AddDays(91)
+ },
+
+ new MovieRequests
+ {
+ Title="Valid",
+ ReleaseDate = DateTime.Now
+ }
+ };
+ MovieRepo.Setup(x => x.GetAll()).Returns(movies.AsQueryable());
+ var data = await CalendarEngine.GetCalendarData();
+
+ Assert.That(data.Count, Is.EqualTo(1));
+ Assert.That(data[0].Title, Is.EqualTo("Valid"));
+ }
+
+ [Test]
+ public async Task Calendar_Episodes_OnlyGet_PreviousAndFuture_90_Days()
+ {
+ var tv = new List
+ {
+ new ChildRequests
+ {
+ SeasonRequests = new List
+ {
+ new SeasonRequests
+ {
+ Episodes = new List
+ {
+ new EpisodeRequests
+ {
+ Title = "Invalid",
+ AirDate = new DateTime(2018,01,01)
+ },
+ new EpisodeRequests
+ {
+ Title = "Invalid",
+ AirDate = DateTime.Now.AddDays(91)
+ },
+ new EpisodeRequests
+ {
+ Title = "Valid",
+ AirDate = DateTime.Now
+ },
+ }
+ }
+ }
+ },
+ };
+ TvRepo.Setup(x => x.GetChild()).Returns(tv.AsQueryable());
+ var data = await CalendarEngine.GetCalendarData();
+
+ Assert.That(data.Count, Is.EqualTo(1));
+ Assert.That(data[0].Title, Is.EqualTo("Valid"));
+ }
+
+
+ [TestCaseSource(nameof(StatusTvColorData))]
+ public async Task Calendar_Tv_StatusColor(AvailabilityTestModel model)
+ {
+ var tv = new List
+ {
+ new ChildRequests
+ {
+ SeasonRequests = new List
+ {
+ new SeasonRequests
+ {
+ Episodes = new List
+ {
+ new EpisodeRequests
+ {
+ Title = "Valid",
+ AirDate = DateTime.Now,
+ Approved = model.Approved,
+ Available = model.Available
+ },
+ }
+ }
+ }
+ },
+ };
+ TvRepo.Setup(x => x.GetChild()).Returns(tv.AsQueryable());
+ var data = await CalendarEngine.GetCalendarData();
+
+ return data[0].BackgroundColor;
+ }
+
+ [TestCaseSource(nameof(StatusColorData))]
+ public async Task Calendar_Movie_StatusColor(AvailabilityTestModel model)
+ {
+ var movies = new List
+ {
+ new MovieRequests
+ {
+ Title="Valid",
+ ReleaseDate = DateTime.Now,
+ Denied = model.Denied,
+ Approved = model.Approved,
+ Available = model.Available
+ },
+ };
+ MovieRepo.Setup(x => x.GetAll()).Returns(movies.AsQueryable());
+ var data = await CalendarEngine.GetCalendarData();
+
+ return data[0].BackgroundColor;
+ }
+
+ public static IEnumerable StatusColorData
+ {
+ get
+ {
+ yield return new TestCaseData(new AvailabilityTestModel
+ {
+ Approved = true,
+ Denied = true
+ }).Returns("red").SetName("Calendar_DeniedRequest");
+ foreach (var testCaseData in StatusTvColorData)
+ {
+ yield return testCaseData;
+ }
+ }
+ }
+
+ public static IEnumerable StatusTvColorData
+ {
+ get
+ {
+ yield return new TestCaseData(new AvailabilityTestModel
+ {
+ Available = true,
+ Approved = true
+ }).Returns("#469c83").SetName("Calendar_AvailableRequest");
+ yield return new TestCaseData(new AvailabilityTestModel
+ {
+ Approved = true
+ }).Returns("blue").SetName("Calendar_ApprovedRequest");
+ }
+ }
+ }
+
+ public class AvailabilityTestModel
+ {
+ public bool Available { get; set; }
+ public bool Denied { get; set; }
+ public bool Approved { get; set; }
+ }
+}
diff --git a/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs b/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
index ad4c33131..41c645815 100644
--- a/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
+++ b/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
@@ -3,16 +3,19 @@ using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using AutoFixture;
+using MockQueryable.Moq;
using Moq;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
+using Ombi.Core.Models;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
+using Ombi.Test.Common;
namespace Ombi.Core.Tests.Engine
{
@@ -30,12 +33,17 @@ namespace Ombi.Core.Tests.Engine
MovieRequestEngine = new Mock();
MovieRequestEngine = new Mock();
User = new Mock();
- UserManager = new Mock();
- UserManager.Setup(x => x.Users)
- .Returns(new EnumerableQuery(new List {new OmbiUser {Id = "abc"}}));
+ User.Setup(x => x.Identity.Name).Returns("abc");
+ UserManager = MockHelper.MockUserManager(new List { new OmbiUser { Id = "abc", UserName = "abc" } });
Rule = new Mock();
Engine = new VoteEngine(VoteRepository.Object, User.Object, UserManager.Object, Rule.Object, VoteSettings.Object, MusicRequestEngine.Object,
TvRequestEngine.Object, MovieRequestEngine.Object);
+
+ F.Behaviors.OfType().ToList()
+ .ForEach(b => F.Behaviors.Remove(b));
+ F.Behaviors.Add(new OmitOnRecursionBehavior());
+
+
}
public Fixture F { get; set; }
@@ -49,25 +57,160 @@ namespace Ombi.Core.Tests.Engine
public Mock TvRequestEngine { get; set; }
public Mock MovieRequestEngine { get; set; }
- [Test]
- [Ignore("Need to mock the user manager")]
- public async Task New_Upvote()
+ [TestCaseSource(nameof(VoteData))]
+ public async Task Vote(VoteType type, RequestType request)
{
- VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings());
+ VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings
+ {
+ Enabled = true,
+ MovieVoteMax = 10
+ });
+ var votes = F.CreateMany().ToList();
+
+ VoteRepository.Setup(x => x.GetAll()).Returns(new EnumerableQuery(votes)
+ .AsQueryable()
+ .BuildMock().Object);
+ var result = new VoteEngineResult();
+ if (type == VoteType.Downvote)
+ {
+ result = await Engine.DownVote(1, request);
+ }
+ else
+ {
+ result = await Engine.UpVote(1, request);
+ }
+
+ Assert.That(result.Result, Is.True);
+ VoteRepository.Verify(x => x.Add(It.Is(c => c.UserId == "abc" && c.VoteType == type)), Times.Once);
+ VoteRepository.Verify(x => x.Delete(It.IsAny()), Times.Never);
+ MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
+ }
+ public static IEnumerable VoteData
+ {
+
+ get
+ {
+ yield return new TestCaseData(VoteType.Upvote, RequestType.Movie).SetName("Movie_Upvote");
+ yield return new TestCaseData(VoteType.Downvote, RequestType.Movie).SetName("Movie_Downvote");
+ yield return new TestCaseData(VoteType.Upvote, RequestType.TvShow).SetName("Tv_Upvote");
+ yield return new TestCaseData(VoteType.Downvote, RequestType.TvShow).SetName("Tv_Downvote");
+ }
+ }
+
+
+ [TestCaseSource(nameof(AttemptedTwiceData))]
+ public async Task Attempted_Twice(VoteType type, RequestType request)
+ {
+ VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings
+ {
+ Enabled = true,
+ MovieVoteMax = 10
+ });
var votes = F.CreateMany().ToList();
votes.Add(new Votes
{
RequestId = 1,
RequestType = RequestType.Movie,
- UserId = "abc"
+ UserId = "abc",
+ VoteType = type
});
- VoteRepository.Setup(x => x.GetAll()).Returns(new EnumerableQuery(votes));
- var result = await Engine.UpVote(1, RequestType.Movie);
+ VoteRepository.Setup(x => x.GetAll()).Returns(new EnumerableQuery(votes)
+ .AsQueryable()
+ .BuildMock().Object);
+ var result = new VoteEngineResult();
+ if (type == VoteType.Downvote)
+ {
+ result = await Engine.DownVote(1, request);
+ }
+ else
+ {
+ result = await Engine.UpVote(1, request);
+ }
+
+ Assert.That(result.Result, Is.False);
+ VoteRepository.Verify(x => x.Delete(It.IsAny()), Times.Never);
+ MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
+ }
+ public static IEnumerable AttemptedTwiceData
+ {
+
+ get
+ {
+ yield return new TestCaseData(VoteType.Upvote, RequestType.Movie).SetName("Upvote_Attemped_Twice_Movie");
+ yield return new TestCaseData(VoteType.Downvote, RequestType.Movie).SetName("Downvote_Attempted_Twice_Movie");
+ yield return new TestCaseData(VoteType.Upvote, RequestType.TvShow).SetName("Upvote_Attemped_Twice_Tv");
+ yield return new TestCaseData(VoteType.Downvote, RequestType.TvShow).SetName("Downvote_Attempted_Twice_Tv");
+ }
+ }
+
+ [TestCaseSource(nameof(VoteConvertData))]
+ public async Task Downvote_Converted_To_Upvote(VoteType type, RequestType request)
+ {
+ VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings
+ {
+ Enabled = true,
+ MovieVoteMax = 10
+ });
+ var votes = F.CreateMany().ToList();
+ votes.Add(new Votes
+ {
+ RequestId = 1,
+ RequestType = request,
+ UserId = "abc",
+ VoteType = type == VoteType.Upvote ? VoteType.Downvote : VoteType.Upvote
+ });
+ VoteRepository.Setup(x => x.GetAll()).Returns(new EnumerableQuery(votes)
+ .AsQueryable()
+ .BuildMock().Object);
+
+ var result = new VoteEngineResult();
+ if (type == VoteType.Downvote)
+ {
+ result = await Engine.DownVote(1, request);
+ }
+ else
+ {
+ result = await Engine.UpVote(1, request);
+ }
+ Assert.That(result.Result, Is.True);
+ VoteRepository.Verify(x => x.Delete(It.IsAny()), Times.Once);
+ VoteRepository.Verify(x => x.Add(It.Is(v => v.VoteType == type)), Times.Once);
+ MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
+ }
+ public static IEnumerable VoteConvertData
+ {
+
+ get
+ {
+ yield return new TestCaseData(VoteType.Upvote, RequestType.Movie).SetName("Downvote_Converted_To_UpVote_Movie");
+ yield return new TestCaseData(VoteType.Downvote, RequestType.Movie).SetName("UpVote_Converted_To_DownVote_Movie");
+ yield return new TestCaseData(VoteType.Upvote, RequestType.TvShow).SetName("Downvote_Converted_To_UpVote_TvShow");
+ yield return new TestCaseData(VoteType.Downvote, RequestType.TvShow).SetName("UpVote_Converted_To_DownVote_TvShow");
+ }
+ }
+
+
+ [TestCaseSource(nameof(VotingDisabledData))]
+ public async Task Voting_Disabled(RequestType type)
+ {
+ VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings
+ {
+ Enabled = false,
+ MovieVoteMax = 10
+ });
+
+ var result = await Engine.UpVote(1, type);
Assert.That(result.Result, Is.True);
- VoteRepository.Verify(x => x.Add(It.Is(c => c.UserId == "abc" && c.VoteType == VoteType.Upvote)), Times.Once);
- VoteRepository.Verify(x => x.Delete(It.IsAny()), Times.Once);
- MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
+ VoteRepository.Verify(x => x.Add(It.IsAny()), Times.Never);
+ }
+ public static IEnumerable VotingDisabledData
+ {
+ get
+ {
+ yield return new TestCaseData(RequestType.Movie).SetName("Voting_Disabled_Movie");
+ yield return new TestCaseData(RequestType.TvShow).SetName("Voting_Disabled_TV");
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj
index 23fc6db78..ed43d4991 100644
--- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj
+++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj
@@ -7,14 +7,16 @@
-
+
-
-
+
+
+
+
diff --git a/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs b/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs
index 7ff8283da..b4d41cb4b 100644
--- a/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs
@@ -4,29 +4,43 @@ using Moq;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Store.Entities.Requests;
using NUnit.Framework;
+using Ombi.Core.Authentication;
using Ombi.Helpers;
+using Ombi.Test.Common;
+using System.Collections.Generic;
+using Ombi.Store.Entities;
+using System;
namespace Ombi.Core.Tests.Rule.Request
{
[TestFixture]
public class AutoApproveRuleTests
{
+ private List _users = new List
+ {
+ new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="abc" }
+ };
+
[SetUp]
public void Setup()
{
PrincipalMock = new Mock();
- Rule = new AutoApproveRule(PrincipalMock.Object);
+ PrincipalMock.Setup(x => x.Identity.Name).Returns("abc");
+
+ UserManager = MockHelper.MockUserManager(_users);
+ Rule = new AutoApproveRule(PrincipalMock.Object, UserManager.Object);
}
private AutoApproveRule Rule { get; set; }
private Mock PrincipalMock { get; set; }
+ private Mock UserManager { get; set; }
[Test]
public async Task Should_ReturnSuccess_WhenAdminAndRequestMovie()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.Admin)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.Admin)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
@@ -37,7 +51,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenAdminAndRequestTV()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.Admin)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.Admin)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
@@ -48,7 +62,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenAutoApproveMovieAndRequestMovie()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.AutoApproveMovie)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
@@ -56,10 +70,21 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(request.Approved);
}
+ [Test]
+ public async Task Should_ReturnFail_WhenAutoApproveMovie_And_RequestTV()
+ {
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true);
+ var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ }
+
[Test]
public async Task Should_ReturnSuccess_WhenAutoApproveTVAndRequestTV()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.AutoApproveTv)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.AutoApproveTv)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
@@ -67,9 +92,21 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(request.Approved);
}
+ [Test]
+ public async Task Should_ReturnFail_WhenAutoApproveTV_And_RequestMovie()
+ {
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.AutoApproveTv)).ReturnsAsync(true);
+ var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ }
+
[Test]
public async Task Should_ReturnFail_WhenNoClaimsAndRequestMovie()
{
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
@@ -80,6 +117,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnFail_WhenNoClaimsAndRequestTV()
{
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
diff --git a/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs b/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs
index c9db5875a..a1630af92 100644
--- a/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs
@@ -1,31 +1,46 @@
+using System;
+using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
+using Ombi.Core.Authentication;
using Ombi.Core.Rule.Rules;
+using Ombi.Core.Rule.Rules.Request;
using Ombi.Helpers;
+using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
+using Ombi.Test.Common;
namespace Ombi.Core.Tests.Rule.Request
{
public class CanRequestRuleTests
{
+ private List _users = new List
+ {
+ new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="abc" }
+ };
+
[SetUp]
public void Setup()
{
PrincipalMock = new Mock();
- Rule = new CanRequestRule(PrincipalMock.Object);
+ PrincipalMock.Setup(x => x.Identity.Name).Returns("abc");
+
+ UserManager = MockHelper.MockUserManager(_users);
+ Rule = new CanRequestRule(PrincipalMock.Object, UserManager.Object);
}
private CanRequestRule Rule { get; set; }
private Mock PrincipalMock { get; set; }
+ private Mock UserManager { get; set; }
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithMovieRole()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.RequestMovie)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
@@ -35,7 +50,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnFail_WhenRequestingMovieWithoutMovieRole()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.RequestMovie)).Returns(false);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.RequestMovie)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
@@ -46,7 +61,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithAdminRole()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.Admin)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.Admin)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
@@ -56,7 +71,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenRequestingTVWithAdminRole()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.Admin)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.Admin)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
@@ -66,7 +81,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenRequestingTVWithTVRole()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.RequestTv)).Returns(true);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.RequestTv)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
@@ -76,7 +91,7 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnFail_WhenRequestingTVWithoutTVRole()
{
- PrincipalMock.Setup(x => x.IsInRole(OmbiRoles.RequestTv)).Returns(false);
+ UserManager.Setup(x => x.IsInRoleAsync(It.IsAny(), OmbiRoles.RequestTv)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
diff --git a/src/Ombi.Core.Tests/Rule/Request/ExistingMovieRequestRuleTests.cs b/src/Ombi.Core.Tests/Rule/Request/ExistingMovieRequestRuleTests.cs
new file mode 100644
index 000000000..7ff69c9f2
--- /dev/null
+++ b/src/Ombi.Core.Tests/Rule/Request/ExistingMovieRequestRuleTests.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using MockQueryable.Moq;
+using Moq;
+using NUnit.Framework;
+using Ombi.Core.Authentication;
+using Ombi.Core.Rule.Rules;
+using Ombi.Core.Rule.Rules.Request;
+using Ombi.Helpers;
+using Ombi.Store.Entities;
+using Ombi.Store.Entities.Requests;
+using Ombi.Store.Repository.Requests;
+using Ombi.Test.Common;
+
+namespace Ombi.Core.Tests.Rule.Request
+{
+ public class ExistingMovieRequestRuleTests
+ {
+
+ [SetUp]
+ public void Setup()
+ {
+ ContextMock = new Mock();
+ Rule = new ExistingMovieRequestRule(ContextMock.Object);
+ }
+
+
+ private ExistingMovieRequestRule Rule { get; set; }
+ private Mock ContextMock { get; set; }
+
+ [Test]
+ public async Task ExistingRequestRule_Movie_Has_Been_Requested_With_TheMovieDBId()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new MovieRequests
+ {
+ TheMovieDbId = 1,
+ RequestType = RequestType.Movie
+ }
+ }.AsQueryable().BuildMock().Object);
+ var o = new MovieRequests
+ {
+ TheMovieDbId = 1,
+ };
+ var result = await Rule.Execute(o);
+
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Message, Is.Not.Empty);
+ }
+
+ [Test]
+ public async Task ExistingRequestRule_Movie_Has_Been_Requested_With_ImdbId()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new MovieRequests
+ {
+ TheMovieDbId = 11111,
+ ImdbId = 1.ToString(),
+ RequestType = RequestType.Movie
+ }
+ }.AsQueryable().BuildMock().Object);
+ var o = new MovieRequests
+ {
+ ImdbId = 1.ToString(),
+ };
+ var result = await Rule.Execute(o);
+
+ Assert.That(result.Success, Is.False);
+ Assert.That(result.Message, Is.Not.Empty);
+ }
+
+ [Test]
+ public async Task ExistingRequestRule_Movie_HasNot_Been_Requested()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new MovieRequests
+ {
+ TheMovieDbId = 2,
+ ImdbId = "2",
+ RequestType = RequestType.Movie
+ }
+ }.AsQueryable().BuildMock().Object);
+ var o = new MovieRequests
+ {
+ TheMovieDbId = 1,
+ ImdbId = "1"
+ };
+ var result = await Rule.Execute(o);
+
+ Assert.That(result.Success, Is.True);
+ Assert.That(result.Message, Is.Null.Or.Empty);
+ }
+ }
+}
diff --git a/src/Ombi.Core.Tests/Rule/Search/AvailabilityRuleHelperTests.cs b/src/Ombi.Core.Tests/Rule/Search/AvailabilityRuleHelperTests.cs
new file mode 100644
index 000000000..7580ed9c8
--- /dev/null
+++ b/src/Ombi.Core.Tests/Rule/Search/AvailabilityRuleHelperTests.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using Ombi.Core.Models.Search;
+using Ombi.Core.Rule.Rules.Search;
+using Ombi.Store.Repository.Requests;
+
+namespace Ombi.Core.Tests.Rule.Search
+{
+ public class AvailabilityRuleHelperTests
+ {
+
+
+ [Test]
+ public void Is_Available_When_All_We_Have_All_Aired_Episodes()
+ {
+ var episodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-1), // Yesterday
+ Available = true
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(1), // Tomorrow!
+ Available = false
+ }
+ };
+
+ var model = new SearchTvShowViewModel
+ {
+ SeasonRequests = new List { new SeasonRequests { Episodes = episodes } }
+ };
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(model);
+ Assert.That(model.FullyAvailable, Is.True);
+ }
+
+ [Test]
+ public void Is_Available_When_All_We_Have_All_Aired_Episodes_With_Unknown_Dates()
+ {
+ var episodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-1), // Yesterday
+ Available = true
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue, // Unknown date!
+ Available = false
+ }
+ };
+
+ var model = new SearchTvShowViewModel
+ {
+ SeasonRequests = new List { new SeasonRequests { Episodes = episodes } }
+ };
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(model);
+ Assert.That(model.FullyAvailable, Is.True);
+ }
+
+ [Test]
+ public void Is_PartlyAvailable_When_All_We_Have_Some_Aired_Episodes()
+ {
+ var episodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-1), // Yesterday
+ Available = true
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-14), // Yesterday
+ Available = false
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue, // Unknown date!
+ Available = false
+ }
+ };
+
+ var model = new SearchTvShowViewModel
+ {
+ SeasonRequests = new List { new SeasonRequests { Episodes = episodes } }
+ };
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(model);
+ Assert.That(model.FullyAvailable, Is.False);
+ Assert.That(model.PartlyAvailable, Is.True);
+ }
+
+ [Test]
+ public void Is_SeasonAvailable_When_All_We_Have_All_Aired_Episodes_In_A_Season()
+ {
+ var episodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-1), // Yesterday
+ Available = true
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-14), // Yesterday
+ Available = false
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue, // Unknown date!
+ Available = false
+ }
+ };
+
+ var availableEpisodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-1), // Yesterday
+ Available = true
+ },
+ };
+
+ var model = new SearchTvShowViewModel
+ {
+ SeasonRequests = new List
+ {
+ new SeasonRequests { Episodes = episodes },
+ new SeasonRequests { Episodes = availableEpisodes },
+ }
+ };
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(model);
+ Assert.That(model.FullyAvailable, Is.False);
+ Assert.That(model.PartlyAvailable, Is.True);
+ Assert.That(model.SeasonRequests[1].SeasonAvailable, Is.True);
+ }
+
+ [Test]
+ public void Is_NotAvailable_When_All_We_Have_No_Aired_Episodes()
+ {
+ var episodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-1), // Yesterday
+ Available = false
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.Now.AddDays(-14),
+ Available = false
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue, // Unknown date!
+ Available = false
+ }
+ };
+
+ var model = new SearchTvShowViewModel
+ {
+ SeasonRequests = new List { new SeasonRequests { Episodes = episodes } }
+ };
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(model);
+ Assert.That(model.FullyAvailable, Is.False);
+ Assert.That(model.PartlyAvailable, Is.False);
+ }
+ [Test]
+ public void Is_NotAvailable_When_All_Episodes_Are_Unknown()
+ {
+ var episodes = new List
+ {
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue,
+ Available = false
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue,
+ Available = false
+ },
+ new EpisodeRequests
+ {
+ AirDate = DateTime.MinValue, // Unknown date!
+ Available = false
+ }
+ };
+
+ var model = new SearchTvShowViewModel
+ {
+ SeasonRequests = new List { new SeasonRequests { Episodes = episodes } }
+ };
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(model);
+ Assert.That(model.FullyAvailable, Is.False);
+ Assert.That(model.PartlyAvailable, Is.False);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
index 99ff5b6bd..8aac99464 100644
--- a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
@@ -4,6 +4,8 @@ using Moq;
using NUnit.Framework;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Rules.Search;
+using Ombi.Core.Settings;
+using Ombi.Core.Settings.Models.External;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
@@ -16,15 +18,18 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup()
{
ContextMock = new Mock();
- Rule = new EmbyAvailabilityRule(ContextMock.Object);
+ SettingsMock = new Mock>();
+ Rule = new EmbyAvailabilityRule(ContextMock.Object, SettingsMock.Object);
}
private EmbyAvailabilityRule Rule { get; set; }
private Mock ContextMock { get; set; }
+ private Mock> SettingsMock { get; set; }
[Test]
public async Task Movie_ShouldBe_Available_WhenFoundInEmby()
{
+ SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings());
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new EmbyContent
{
ProviderId = "123"
@@ -39,6 +44,64 @@ namespace Ombi.Core.Tests.Rule.Search
Assert.True(search.Available);
}
+ [Test]
+ public async Task Movie_Has_Custom_Url_When_Specified_In_Settings()
+ {
+ SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings
+ {
+ Enable = true,
+ Servers = new List
+ {
+ new EmbyServers
+ {
+ ServerHostname = "http://test.com/"
+ }
+ }
+ });
+ ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new EmbyContent
+ {
+ ProviderId = "123",
+ EmbyId = 1.ToString()
+ });
+ var search = new SearchMovieViewModel()
+ {
+ TheMovieDbId = "123",
+ };
+ var result = await Rule.Execute(search);
+
+ Assert.True(result.Success);
+ Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/#!/itemdetails.html?id=1"));
+ }
+
+ [Test]
+ public async Task Movie_Uses_Default_Url_When()
+ {
+ SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings
+ {
+ Enable = true,
+ Servers = new List
+ {
+ new EmbyServers
+ {
+ ServerHostname = string.Empty
+ }
+ }
+ });
+ ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new EmbyContent
+ {
+ ProviderId = "123",
+ EmbyId = 1.ToString()
+ });
+ var search = new SearchMovieViewModel()
+ {
+ TheMovieDbId = "123",
+ };
+ var result = await Rule.Execute(search);
+
+ Assert.True(result.Success);
+ Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/#!/itemdetails.html?id=1"));
+ }
+
[Test]
public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInEmby()
{
diff --git a/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs
index e32c8e996..ea9208cf7 100644
--- a/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs
@@ -11,7 +11,7 @@ using Ombi.Store.Repository.Requests;
namespace Ombi.Core.Tests.Rule.Search
{
- public class ExistignRequestRuleTests
+ public class ExistingRequestRuleTests
{
[SetUp]
public void Setup()
@@ -39,18 +39,16 @@ namespace Ombi.Core.Tests.Rule.Search
RequestType = RequestType.Movie
};
- MovieMock.Setup(x => x.GetRequest(123)).Returns(list);
+ MovieMock.Setup(x => x.GetRequestAsync(123)).ReturnsAsync(list);
var search = new SearchMovieViewModel
{
Id = 123,
-
-
};
var result = await Rule.Execute(search);
- Assert.True(result.Success);
- Assert.True(search.Approved);
- Assert.True(search.Requested);
+ Assert.That(result.Success, Is.True);
+ Assert.That(search.Approved, Is.True);
+ Assert.That(search.Requested, Is.True);
}
[Test]
@@ -62,7 +60,7 @@ namespace Ombi.Core.Tests.Rule.Search
Approved = true
};
- MovieMock.Setup(x => x.GetRequest(123)).Returns(list);
+ MovieMock.Setup(x => x.GetRequestAsync(123)).ReturnsAsync(list);
var search = new SearchMovieViewModel
{
Id = 999,
diff --git a/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs
new file mode 100644
index 000000000..6b04a57b7
--- /dev/null
+++ b/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs
@@ -0,0 +1,126 @@
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using Moq;
+using NUnit.Framework;
+using Ombi.Core.Models.Search;
+using Ombi.Core.Rule.Rules.Search;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository;
+
+namespace Ombi.Core.Tests.Rule.Search
+{
+ public class LidarrAlbumCacheRuleTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ ContextMock = new Mock>();
+ Rule = new LidarrAlbumCacheRule(ContextMock.Object);
+
+ }
+
+ private LidarrAlbumCacheRule Rule { get; set; }
+ private Mock> ContextMock { get; set; }
+
+ [Test]
+ public async Task Should_Not_Be_Monitored_Or_Available()
+ {
+ var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ Assert.False(request.Monitored);
+ }
+
+ [Test]
+ public async Task Should_Be_Monitored_But_Not_Available()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new LidarrAlbumCache
+ {
+ ForeignAlbumId = "abc",
+ PercentOfTracks = 0
+ }
+ }.AsQueryable());
+ var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ Assert.True(request.Monitored);
+ Assert.That(request.PartiallyAvailable, Is.EqualTo(false));
+ Assert.That(request.Available, Is.EqualTo(false));
+ Assert.That(request.FullyAvailable, Is.EqualTo(false));
+ }
+
+ [Test]
+ public async Task Should_Be_Monitored_And_Partly_Available()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new LidarrAlbumCache
+ {
+ ForeignAlbumId = "abc",
+ PercentOfTracks = 1
+ }
+ }.AsQueryable());
+ var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ Assert.True(request.Monitored);
+ Assert.That(request.PartiallyAvailable, Is.EqualTo(true));
+ Assert.That(request.Available, Is.EqualTo(false));
+ Assert.That(request.FullyAvailable, Is.EqualTo(false));
+ }
+
+ [Test]
+ public async Task Should_Be_Monitored_And_Fully_Available()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new LidarrAlbumCache
+ {
+ ForeignAlbumId = "abc",
+ PercentOfTracks = 100
+ }
+ }.AsQueryable());
+ var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ Assert.True(request.Monitored);
+ Assert.That(request.PartiallyAvailable, Is.EqualTo(false));
+ Assert.That(request.FullyAvailable, Is.EqualTo(true));
+ }
+
+ [Test]
+ public async Task Should_Be_Monitored_And_Fully_Available_Casing()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new LidarrAlbumCache
+ {
+ ForeignAlbumId = "abc",
+ PercentOfTracks = 100
+ }
+ }.AsQueryable());
+ var request = new SearchAlbumViewModel { ForeignAlbumId = "ABC" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Approved);
+ Assert.True(request.Monitored);
+ Assert.That(request.PartiallyAvailable, Is.EqualTo(false));
+ Assert.That(request.FullyAvailable, Is.EqualTo(true));
+ }
+ }
+}
diff --git a/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs
new file mode 100644
index 000000000..7a2da1e25
--- /dev/null
+++ b/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs
@@ -0,0 +1,74 @@
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using Moq;
+using NUnit.Framework;
+using Ombi.Core.Models.Search;
+using Ombi.Core.Rule.Rules.Search;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository;
+
+namespace Ombi.Core.Tests.Rule.Search
+{
+ public class LidarrArtistCacheRuleTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ ContextMock = new Mock>();
+ Rule = new LidarrArtistCacheRule(ContextMock.Object);
+ }
+
+ private LidarrArtistCacheRule Rule { get; set; }
+ private Mock> ContextMock { get; set; }
+
+ [Test]
+ public async Task Should_Not_Be_Monitored()
+ {
+ var request = new SearchArtistViewModel { ForignArtistId = "abc" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.False(request.Monitored);
+ }
+
+ [Test]
+ public async Task Should_Be_Monitored()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new LidarrArtistCache
+ {
+ ForeignArtistId = "abc",
+ }
+ }.AsQueryable());
+ var request = new SearchArtistViewModel { ForignArtistId = "abc" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.True(request.Monitored);
+ }
+
+
+ [Test]
+ public async Task Should_Be_Monitored_Casing()
+ {
+ ContextMock.Setup(x => x.GetAll()).Returns(new List
+ {
+ new LidarrArtistCache
+ {
+ ForeignArtistId = "abc",
+ }
+ }.AsQueryable());
+ var request = new SearchArtistViewModel { ForignArtistId = "ABC" };
+ var result = await Rule.Execute(request);
+
+ Assert.True(result.Success);
+ Assert.True(request.Monitored);
+ }
+
+ }
+}
diff --git a/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs
deleted file mode 100644
index 55177a6ac..000000000
--- a/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Threading.Tasks;
-using Moq;
-using NUnit.Framework;
-using Ombi.Core.Models.Search;
-using Ombi.Core.Rule.Rules.Search;
-using Ombi.Store.Entities;
-using Ombi.Store.Repository;
-
-namespace Ombi.Core.Tests.Rule.Search
-{
- public class PlexAvailabilityRuleTests
- {
- [SetUp]
- public void Setup()
- {
- ContextMock = new Mock();
- Rule = new PlexAvailabilityRule(ContextMock.Object);
- }
-
- private PlexAvailabilityRule Rule { get; set; }
- private Mock ContextMock { get; set; }
-
- [Test]
- public async Task ShouldBe_Available_WhenFoundInPlex()
- {
- ContextMock.Setup(x => x.Get(It.IsAny())).ReturnsAsync(new PlexServerContent
- {
- Url = "TestUrl",
- ImdbId = "132"
- });
-
- var search = new SearchMovieViewModel
- {
- ImdbId = "123",
- };
- var result = await Rule.Execute(search);
-
- Assert.True(result.Success);
- Assert.AreEqual("TestUrl", search.PlexUrl);
- Assert.True(search.Available);
- }
-
- [Test]
- public async Task ShouldBe_NotAvailable_WhenNotFoundInPlex()
- {
- ContextMock.Setup(x => x.Get(It.IsAny())).Returns(Task.FromResult(default(PlexServerContent)));
- var search = new SearchMovieViewModel();
- var result = await Rule.Execute(search);
-
- Assert.True(result.Success);
- Assert.Null(search.PlexUrl);
- Assert.False(search.Available);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Ombi.Core.Tests/StringHelperTests.cs b/src/Ombi.Core.Tests/StringHelperTests.cs
index c1b95fcd7..dcd05ae4c 100644
--- a/src/Ombi.Core.Tests/StringHelperTests.cs
+++ b/src/Ombi.Core.Tests/StringHelperTests.cs
@@ -18,7 +18,7 @@ namespace Ombi.Core.Tests
{
get
{
- yield return new TestCaseData("this!is^a*string",new []{'!','^','*'}).Returns("thisisastring").SetName("Basic Strip Multipe Chars");
+ yield return new TestCaseData("this!is^a*string",new []{'!','^','*'}).Returns("thisisastring").SetName("Basic Strip Multiple Chars");
yield return new TestCaseData("What is this madness'",new []{'\'','^','*'}).Returns("What is this madness").SetName("Basic Strip Multipe Chars");
}
}
diff --git a/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs b/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs
new file mode 100644
index 000000000..86582fb4d
--- /dev/null
+++ b/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+using AutoMapper;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Ombi.Api.TheMovieDb;
+using Ombi.Api.TheMovieDb.Models;
+using Ombi.Config;
+using Ombi.Core.Authentication;
+using Ombi.Core.Models.Requests;
+using Ombi.Core.Models.Search;
+using Ombi.Core.Rule.Interfaces;
+using Ombi.Core.Settings;
+using Ombi.Helpers;
+using Ombi.Settings.Settings.Models;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository;
+
+namespace Ombi.Core.Engine.Demo
+{
+ public class DemoMovieSearchEngine : MovieSearchEngine, IDemoMovieSearchEngine
+ {
+ public DemoMovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
+ ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s,
+ IRepository sub, IOptions lists)
+ : base(identity, service, movApi, mapper, logger, r, um, mem, s, sub)
+ {
+ _demoLists = lists.Value;
+ }
+
+ private readonly DemoLists _demoLists;
+
+ public async Task> Search(string search)
+ {
+ var result = await MovieApi.SearchMovie(search, null, "en");
+
+ for (var i = 0; i < result.Count; i++)
+ {
+ if (!_demoLists.Movies.Contains(result[i].Id))
+ {
+ result.RemoveAt(i);
+ }
+ }
+ if(result.Count > 0)
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
+ return null;
+ }
+
+ public async Task> NowPlayingMovies()
+ {
+ var rand = new Random();
+ var responses = new List();
+ for (int i = 0; i < 10; i++)
+ {
+ var item = rand.Next(_demoLists.Movies.Length);
+ var movie = _demoLists.Movies[item];
+ if (responses.Any(x => x.Id == movie))
+ {
+ i--;
+ continue;
+ }
+ var movieResult = await MovieApi.GetMovieInformationWithExtraInfo(movie);
+ var viewMovie = Mapper.Map(movieResult);
+
+ responses.Add(await ProcessSingleMovie(viewMovie));
+ }
+
+ return responses;
+ }
+
+ public async Task> PopularMovies()
+ {
+ return await NowPlayingMovies();
+ }
+
+
+ public async Task> TopRatedMovies()
+ {
+ return await NowPlayingMovies();
+ }
+
+ public async Task> UpcomingMovies()
+ {
+
+ return await NowPlayingMovies();
+ }
+ }
+
+ public interface IDemoMovieSearchEngine
+ {
+ Task> NowPlayingMovies();
+
+ Task> PopularMovies();
+
+ Task> Search(string search);
+
+ Task> TopRatedMovies();
+
+ Task> UpcomingMovies();
+
+ }
+}
diff --git a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs
new file mode 100644
index 000000000..edf9c430d
--- /dev/null
+++ b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs
@@ -0,0 +1,96 @@
+using AutoMapper;
+using Microsoft.Extensions.Options;
+using Ombi.Api.Trakt;
+using Ombi.Api.TvMaze;
+using Ombi.Config;
+using Ombi.Core.Authentication;
+using Ombi.Core.Models.Requests;
+using Ombi.Core.Models.Search;
+using Ombi.Core.Rule.Interfaces;
+using Ombi.Core.Settings;
+using Ombi.Core.Settings.Models.External;
+using Ombi.Helpers;
+using Ombi.Settings.Settings.Models;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Threading.Tasks;
+
+namespace Ombi.Core.Engine.Demo
+{
+ public class DemoTvSearchEngine : TvSearchEngine, IDemoTvSearchEngine
+ {
+
+ public DemoTvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
+ ISettingsService plexSettings, ISettingsService embySettings, IPlexContentRepository repo,
+ IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
+ ISettingsService s, IRepository sub, IOptions lists)
+ : base(identity, service, tvMaze, mapper, plexSettings, embySettings, repo, embyRepo, trakt, r, um, memCache, s, sub)
+ {
+ _demoLists = lists.Value;
+ }
+
+ private readonly DemoLists _demoLists;
+
+ public async Task> Search(string search)
+ {
+ var searchResult = await TvMazeApi.Search(search);
+
+ for (var i = 0; i < searchResult.Count; i++)
+ {
+ if (!_demoLists.TvShows.Contains(searchResult[i].show?.externals?.thetvdb ?? 0))
+ {
+ searchResult.RemoveAt(i);
+ }
+ }
+
+ if (searchResult != null)
+ {
+ var retVal = new List();
+ foreach (var tvMazeSearch in searchResult)
+ {
+ if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
+ {
+ continue;
+ }
+ retVal.Add(ProcessResult(tvMazeSearch));
+ }
+ return retVal;
+ }
+ return null;
+ }
+
+ public async Task> NowPlayingMovies()
+ {
+ var rand = new Random();
+ var responses = new List();
+ for (int i = 0; i < 10; i++)
+ {
+ var item = rand.Next(_demoLists.TvShows.Length);
+ var tv = _demoLists.TvShows[item];
+ if (responses.Any(x => x.Id == tv))
+ {
+ i--;
+ continue;
+ }
+
+ var movieResult = await TvMazeApi.ShowLookup(tv);
+ responses.Add(ProcessResult(movieResult));
+ }
+
+ return responses;
+ }
+
+
+
+ }
+
+ public interface IDemoTvSearchEngine
+ {
+ Task> Search(string search);
+ Task> NowPlayingMovies();
+ }
+}
diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs
index 8e34c4f19..20b5db148 100644
--- a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs
+++ b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs
@@ -1,12 +1,24 @@
-using Ombi.Core.Models.Search;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Threading.Tasks;
+using Ombi.Core.Models.Search;
using Ombi.Core.Models.Search.V2;
-namespace Ombi.Core
+namespace Ombi.Core.Engine.Interfaces
{
public interface IMovieEngineV2
{
Task GetFullMovieInformation(int theMovieDbId, string langCode = null);
+ Task> SimilarMovies(int theMovieDbId, string langCode);
+ Task> PopularMovies();
+ Task> TopRatedMovies();
+ Task> UpcomingMovies();
+ Task> NowPlayingMovies();
+ Task> NowPlayingMovies(int currentPosition, int amountToLoad);
+ Task GetCollection(int collectionId, string langCode = null);
+ Task GetTvDbId(int theMovieDbId);
+ Task> PopularMovies(int currentlyLoaded, int toLoad);
+ Task> TopRatedMovies(int currentlyLoaded, int toLoad);
+ Task> UpcomingMovies(int currentlyLoaded, int toLoad);
+ int ResultLimit { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs
index d741dc8bc..bc1fa68d1 100644
--- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Core.Models.Requests;
+using Ombi.Core.Models.UI;
using Ombi.Store.Entities.Requests;
namespace Ombi.Core.Engine.Interfaces
@@ -13,10 +14,11 @@ namespace Ombi.Core.Engine.Interfaces
Task RemoveMovieRequest(int requestId);
Task RemoveAllMovieRequests();
-
+ Task GetRequest(int requestId);
Task UpdateMovieRequest(MovieRequests request);
Task ApproveMovie(MovieRequests request);
Task ApproveMovieById(int requestId);
Task DenyMovieById(int modelId, string denyReason);
+ Task> GetRequests(int count, int position, string sortProperty, string sortOrder);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
index 63de72bd1..ded696c0f 100644
--- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
@@ -23,5 +23,6 @@ namespace Ombi.Core.Engine.Interfaces
Task> GetRequestsLite();
Task UpdateQualityProfile(int requestId, int profileId);
Task UpdateRootPath(int requestId, int rootPath);
+ Task> GetRequests(int count, int position, string sortProperty, string sortOrder);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs
index 1ca119862..501197fb9 100644
--- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs
@@ -9,9 +9,13 @@ namespace Ombi.Core.Engine.Interfaces
Task> Search(string searchTerm);
Task GetShowInformation(int tvdbid);
Task> Popular();
+ Task> Popular(int currentlyLoaded, int amountToLoad);
Task> Anticipated();
+ Task> Anticipated(int currentlyLoaded, int amountToLoad);
Task> MostWatches();
Task> Trending();
+ Task> MostWatches(int currentlyLoaded, int amountToLoad);
+ Task> Trending(int currentlyLoaded, int amountToLoad);
int ResultLimit { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs
new file mode 100644
index 000000000..0a18a32bf
--- /dev/null
+++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs
@@ -0,0 +1,10 @@
+using System.Threading.Tasks;
+using Ombi.Core.Models.Search.V2;
+
+namespace Ombi.Core
+{
+ public interface ITVSearchEngineV2
+ {
+ Task GetShowInformation(int tvdbid);
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs
index 456ba267a..231c1d062 100644
--- a/src/Ombi.Core/Engine/MovieRequestEngine.cs
+++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs
@@ -4,6 +4,7 @@ using Ombi.Helpers;
using Ombi.Store.Entities;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
@@ -200,6 +201,54 @@ namespace Ombi.Core.Engine
};
}
+ public async Task> GetRequests(int count, int position, string sortProperty, string sortOrder)
+ {
+ var shouldHide = await HideFromOtherUsers();
+ IQueryable allRequests;
+ if (shouldHide.Hide)
+ {
+ allRequests =
+ MovieRepository.GetWithUser(shouldHide
+ .UserId);
+ }
+ else
+ {
+ allRequests =
+ MovieRepository
+ .GetWithUser();
+ }
+
+ var prop = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(sortProperty, true);
+
+ if (sortProperty.Contains('.'))
+ {
+ // This is a navigation property currently not supported
+ prop = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find("RequestedDate", true);
+ //var properties = sortProperty.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries);
+ //var firstProp = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(properties[0], true);
+ //var propType = firstProp.PropertyType;
+ //var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true);
+ }
+
+ allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
+ ? allRequests.OrderBy(x => prop.GetValue(x))
+ : allRequests.OrderByDescending(x => prop.GetValue(x));
+ var total = await allRequests.CountAsync();
+ var requests = await allRequests.Skip(position).Take(count)
+ .ToListAsync();
+ requests.ForEach(async x =>
+ {
+ x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
+ await CheckForSubscription(shouldHide, x);
+ });
+ return new RequestsViewModel
+ {
+ Collection = requests,
+ Total = total
+ };
+
+ }
+
private IQueryable OrderMovies(IQueryable allRequests, OrderType type)
{
switch (type)
@@ -259,6 +308,15 @@ namespace Ombi.Core.Engine
return allRequests;
}
+ public async Task GetRequest(int requestId)
+ {
+ var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
+ request.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath);
+ await CheckForSubscription(new HideResult(), request);
+
+ return request;
+ }
+
private async Task CheckForSubscription(HideResult shouldHide, MovieRequests x)
{
if (shouldHide.UserId == x.RequestedUserId)
@@ -493,7 +551,7 @@ namespace Ombi.Core.Engine
RequestType = RequestType.Movie,
});
- return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!", RequestId = model.Id};
+ return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!", RequestId = model.Id };
}
public async Task GetRemainingRequests(OmbiUser user)
@@ -533,7 +591,7 @@ namespace Ombi.Core.Engine
return new RequestQuotaCountModel()
{
- HasLimit = true,
+ HasLimit = true,
Limit = limit,
Remaining = count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs
index 692776f72..4737c3992 100644
--- a/src/Ombi.Core/Engine/MovieSearchEngine.cs
+++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs
@@ -31,10 +31,11 @@ namespace Ombi.Core.Engine
Logger = logger;
}
- private IMovieDbApi MovieApi { get; }
- private IMapper Mapper { get; }
+ protected IMovieDbApi MovieApi { get; }
+ protected IMapper Mapper { get; }
private ILogger Logger { get; }
+ protected const int MovieLimit = 10;
///
/// Lookups the imdb information.
@@ -185,7 +186,7 @@ namespace Ombi.Core.Engine
return null;
}
- private async Task> TransformMovieResultsToResponse(
+ protected async Task> TransformMovieResultsToResponse(
IEnumerable movies)
{
var viewMovies = new List();
@@ -196,7 +197,7 @@ namespace Ombi.Core.Engine
return viewMovies;
}
- private async Task ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
+ protected async Task ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
{
if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty())
{
@@ -214,7 +215,7 @@ namespace Ombi.Core.Engine
// This requires the rules to be run first to populate the RequestId property
await CheckForSubscription(viewMovie);
-
+
return viewMovie;
}
@@ -228,7 +229,7 @@ namespace Ombi.Core.Engine
}
var request = await RequestService.MovieRequestService.GetAll()
.AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id);
- if (request)
+ if (request || viewModel.Available)
{
viewModel.ShowSubscribe = false;
}
diff --git a/src/Ombi.Core/Engine/MusicRequestEngine.cs b/src/Ombi.Core/Engine/MusicRequestEngine.cs
index 8457de515..8094e79c4 100644
--- a/src/Ombi.Core/Engine/MusicRequestEngine.cs
+++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs
@@ -69,6 +69,12 @@ namespace Ombi.Core.Engine
};
}
+ if(album?.artist == null)
+ {
+ // Lookup the artist
+ //album.artist = await _lidarrApi.ArtistLookup(album.artist, s.ApiKey, s.FullUri);
+ }
+
var userDetails = await GetUser();
var requestModel = new AlbumRequest
@@ -83,7 +89,7 @@ namespace Ombi.Core.Engine
Title = album.title,
Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url,
- ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty,
+ ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty, // This needs to be populated to send to Lidarr for new requests
RequestedByAlias = model.RequestedByAlias
};
if (requestModel.Cover.IsNullOrEmpty())
diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs
index ddcc22d7b..b82335c80 100644
--- a/src/Ombi.Core/Engine/TvRequestEngine.cs
+++ b/src/Ombi.Core/Engine/TvRequestEngine.cs
@@ -7,6 +7,7 @@ using Ombi.Core.Models.Search;
using Ombi.Helpers;
using Ombi.Store.Entities;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
@@ -31,14 +32,13 @@ namespace Ombi.Core.Engine
{
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
- ITvSender sender, IAuditRepository audit, IRepository rl, ISettingsService settings, ICacheService cache,
+ ITvSender sender, IRepository rl, ISettingsService settings, ICacheService cache,
IRepository sub) : base(user, requestService, rule, manager, cache, settings, sub)
{
TvApi = tvApi;
MovieDbApi = movApi;
NotificationHelper = helper;
TvSender = sender;
- Audit = audit;
_requestLog = rl;
}
@@ -46,7 +46,6 @@ namespace Ombi.Core.Engine
private ITvMazeApi TvApi { get; }
private IMovieDbApi MovieDbApi { get; }
private ITvSender TvSender { get; }
- private IAuditRepository Audit { get; }
private readonly IRepository _requestLog;
public async Task RequestTvShow(TvRequestViewModel tv)
@@ -84,8 +83,6 @@ namespace Ombi.Core.Engine
}
}
- await Audit.Record(AuditType.Added, AuditArea.TvRequest, $"Added Request {tvBuilder.ChildRequest.Title}", Username);
-
var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.TvDbId);
if (existingRequest != null)
{
@@ -160,7 +157,7 @@ namespace Ombi.Core.Engine
.ThenInclude(x => x.Episodes)
.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault())
.Skip(position).Take(count).ToListAsync();
-
+
}
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
@@ -225,6 +222,59 @@ namespace Ombi.Core.Engine
}
+ public async Task> GetRequests(int count, int position, string sortProperty, string sortOrder)
+ {
+ var shouldHide = await HideFromOtherUsers();
+ List allRequests;
+ if (shouldHide.Hide)
+ {
+ allRequests = await TvRepository.GetChild(shouldHide.UserId).ToListAsync();
+
+ // Filter out children
+
+ FilterChildren(allRequests, shouldHide);
+ }
+ else
+ {
+ allRequests = await TvRepository.GetChild().ToListAsync();
+
+ }
+
+ if (allRequests == null)
+ {
+ return new RequestsViewModel();
+ }
+
+ var total = allRequests.Count;
+
+
+ var prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find(sortProperty, true);
+
+ if (sortProperty.Contains('.'))
+ {
+ // This is a navigation property currently not supported
+ prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find("Title", true);
+ //var properties = sortProperty.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries);
+ //var firstProp = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(properties[0], true);
+ //var propType = firstProp.PropertyType;
+ //var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true);
+ }
+ allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
+ ? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
+ : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
+ allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
+
+ // Make sure we do not show duplicate child requests
+ allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
+
+ return new RequestsViewModel
+ {
+ Collection = allRequests,
+ Total = total,
+ };
+ }
+
+
public async Task> GetRequestsLite()
{
var shouldHide = await HideFromOtherUsers();
@@ -282,17 +332,22 @@ namespace Ombi.Core.Engine
private static void FilterChildren(TvRequests t, HideResult shouldHide)
{
// Filter out children
+ FilterChildren(t.ChildRequests, shouldHide);
+ }
- for (var j = 0; j < t.ChildRequests.Count; j++)
+ private static void FilterChildren(List t, HideResult shouldHide)
+ {
+ // Filter out children
+
+ for (var j = 0; j < t.Count; j++)
{
- var child = t.ChildRequests[j];
+ var child = t[j];
if (child.RequestedUserId != shouldHide.UserId)
{
- t.ChildRequests.RemoveAt(j);
+ t.RemoveAt(j);
j--;
}
}
-
}
public async Task> GetAllChldren(int tvId)
@@ -351,7 +406,6 @@ namespace Ombi.Core.Engine
public async Task UpdateTvRequest(TvRequests request)
{
- await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username);
var allRequests = TvRepository.Get();
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id);
@@ -385,6 +439,7 @@ namespace Ombi.Core.Engine
foreach (var ep in s.Episodes)
{
ep.Approved = true;
+ ep.Requested = true;
}
}
@@ -393,7 +448,6 @@ namespace Ombi.Core.Engine
if (request.Approved)
{
NotificationHelper.Notify(request, NotificationType.RequestApproved);
- await Audit.Record(AuditType.Approved, AuditArea.TvRequest, $"Approved Request {request.Title}", Username);
// Autosend
await TvSender.Send(request);
}
@@ -425,9 +479,7 @@ namespace Ombi.Core.Engine
public async Task UpdateChildRequest(ChildRequests request)
{
- await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username);
-
- await TvRepository.UpdateChild(request);
+ await TvRepository.UpdateChild(request);
return request;
}
@@ -445,16 +497,14 @@ namespace Ombi.Core.Engine
// Delete the parent
TvRepository.Db.TvRequests.Remove(parent);
}
- await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username);
-
+
await TvRepository.Db.SaveChangesAsync();
}
public async Task RemoveTvRequest(int requestId)
{
var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId);
- await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username);
- await TvRepository.Delete(request);
+ await TvRepository.Delete(request);
}
public async Task UserHasRequest(string userId)
@@ -569,7 +619,7 @@ namespace Ombi.Core.Engine
return await AfterRequest(model.ChildRequests.FirstOrDefault());
}
- private static List SortEpisodes(List items)
+ private static List SortEpisodes(List items)
{
foreach (var value in items)
{
@@ -606,7 +656,7 @@ namespace Ombi.Core.Engine
var result = await TvSender.Send(model);
if (result.Success)
{
- return new RequestEngineResult { Result = true, RequestId = model.Id};
+ return new RequestEngineResult { Result = true, RequestId = model.Id };
}
return new RequestEngineResult
{
@@ -659,10 +709,10 @@ namespace Ombi.Core.Engine
DateTime oldestRequestedAt = await log.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
-
+
return new RequestQuotaCountModel()
{
- HasLimit = true,
+ HasLimit = true,
Limit = limit,
Remaining = count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs
index d34a40deb..93e451f1d 100644
--- a/src/Ombi.Core/Engine/TvSearchEngine.cs
+++ b/src/Ombi.Core/Engine/TvSearchEngine.cs
@@ -21,6 +21,8 @@ using Ombi.Core.Authentication;
using Ombi.Helpers;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
+using TraktApiSharp.Objects.Get.Shows;
+using TraktApiSharp.Objects.Get.Shows.Common;
namespace Ombi.Core.Engine
{
@@ -40,8 +42,8 @@ namespace Ombi.Core.Engine
EmbyContentRepo = embyRepo;
}
- private ITvMazeApi TvMazeApi { get; }
- private IMapper Mapper { get; }
+ protected ITvMazeApi TvMazeApi { get; }
+ protected IMapper Mapper { get; }
private ISettingsService PlexSettings { get; }
private ISettingsService EmbySettings { get; }
private IPlexContentRepository PlexContentRepo { get; }
@@ -99,7 +101,7 @@ namespace Ombi.Core.Engine
{
Url = e.url,
Title = e.name,
- AirDate = DateTime.Parse(e.airstamp ?? DateTime.MinValue.ToString()),
+ AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
EpisodeNumber = e.number,
});
@@ -112,7 +114,7 @@ namespace Ombi.Core.Engine
{
Url = e.url,
Title = e.name,
- AirDate = DateTime.Parse(e.airstamp ?? DateTime.MinValue.ToString()),
+ AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
EpisodeNumber = e.number,
});
}
@@ -127,6 +129,19 @@ namespace Ombi.Core.Engine
return processed;
}
+ public async Task> Popular(int currentlyLoaded, int amountToLoad)
+ {
+ var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
+ var results = new List();
+ foreach (var pagesToLoad in pages)
+ {
+ var apiResult = await TraktApi.GetPopularShows(pagesToLoad.Page, ResultLimit);
+ results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
+ }
+ var processed = ProcessResults(results);
+ return processed;
+ }
+
public async Task> Anticipated()
{
@@ -135,6 +150,19 @@ namespace Ombi.Core.Engine
return processed;
}
+ public async Task> Anticipated(int currentlyLoaded, int amountToLoad)
+ {
+ var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
+ var results = new List();
+ foreach (var pagesToLoad in pages)
+ {
+ var apiResult = await TraktApi.GetAnticipatedShows(pagesToLoad.Page, ResultLimit);
+ results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
+ }
+ var processed = ProcessResults(results);
+ return processed;
+ }
+
public async Task> MostWatches()
{
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(null, ResultLimit), DateTime.Now.AddHours(12));
@@ -149,7 +177,33 @@ namespace Ombi.Core.Engine
return processed;
}
- private IEnumerable ProcessResults(IEnumerable items)
+ public async Task> MostWatches(int currentlyLoaded, int amountToLoad)
+ {
+ var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
+ var results = new List();
+ foreach (var pagesToLoad in pages)
+ {
+ var apiResult = await TraktApi.GetMostWatchesShows(null, pagesToLoad.Page, ResultLimit);
+ results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
+ }
+ var processed = ProcessResults(results);
+ return processed;
+ }
+
+ public async Task> Trending(int currentlyLoaded, int amountToLoad)
+ {
+ var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
+ var results = new List();
+ foreach (var pagesToLoad in pages)
+ {
+ var apiResult = await TraktApi.GetTrendingShows(pagesToLoad.Page, ResultLimit);
+ results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
+ }
+ var processed = ProcessResults(results);
+ return processed;
+ }
+
+ protected IEnumerable ProcessResults(IEnumerable items)
{
var retVal = new List();
foreach (var tvMazeSearch in items)
@@ -159,7 +213,7 @@ namespace Ombi.Core.Engine
return retVal;
}
- private SearchTvShowViewModel ProcessResult(T tvMazeSearch)
+ protected SearchTvShowViewModel ProcessResult