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 655689bec..5f2948060 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,479 @@
# 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)
+
+### **New Features**
+
+- Update CHANGELOG.md. [Jamie]
+
+- Added a page where the admin can write/style/basically do whatever they want with e.g. FAQ for the users #2715 This needs to be enabled in the Customization Settings and then it's all configured on the page. [TidusJar]
+
+- Updated the AspnetCore.App package to remove the CVE-2019-0564 vulnerability. [TidusJar]
+
+- Added a global language flag that now applies to the search by default. [tidusjar]
+
+- Updated the frontend packages (Using Angular 7 now) [TidusJar]
+
+- Added capture of anonymous analytical data. [tidusjar]
+
+- Added {AvailableDate} as a Notification Variable, this is the date the request was marked as available. See here: https://github.com/tidusjar/Ombi/wiki/Notification-Template-Variables. [tidusjar]
+
+- Added the ability to search movies via the movie db with a different language! [tidusjar]
+
+- Added the ability to specify a year when searching for movies. [tidusjar]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update HtmlTemplateGenerator.cs. [d1slact0r]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update HtmlTemplateGenerator.cs. [d1slact0r]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update NewsletterTemplate.html. [d1slact0r]
+
+- Update HtmlTemplateGenerator.cs. [d1slact0r]
+
+- Updated boostrap #2694. [Jamie]
+
+- Added the ability to deny a request with a reason. [TidusJar]
+
+- Update EmbyEpisodeSync.cs. [Jamie]
+
+- Updated to .net core 2.2 and included a linux-arm64 build. [TidusJar]
+
+### **Fixes**
+
+- There is now a new Job in ombi that will clear out the Plex/Emby data and recache. This will prevent the issues going forward that we have when Ombi and the Media server fall out of sync with deletions/updates #2641 #2362 #1566. [TidusJar]
+
+- Potentially fix #2726. [TidusJar]
+
+- New translations en.json (Spanish) [Jamie]
+
+- New translations en.json (Spanish) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- Fixed #2725 and #2721. [TidusJar]
+
+- Made the newsletter use the default lanuage code set in the Ombi settings for movie information. [TidusJar]
+
+- Save the language code against the request so we can use it later e.g. Sending to the DVR apps. [tidusjar]
+
+- Fixed #2716. [tidusjar]
+
+- Make the newsletter BCC the users rather than creating a million newsletters (Hopefully will stop SMTP providers from marking as spam). This does mean that the custom user customization in the newsletter will no longer work. [TidusJar]
+
+- If we don't know the Plex agent, then see if it's a ImdbId, if it's not check the string for any episode and season hints #2695. [tidusjar]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Spanish) [Jamie]
+
+- New translations en.json (Portuguese, Brazilian) [Jamie]
+
+- New translations en.json (Polish) [Jamie]
+
+- New translations en.json (Norwegian) [Jamie]
+
+- New translations en.json (Italian) [Jamie]
+
+- New translations en.json (German) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- New translations en.json (Danish) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- Made the search results the language specified in the search refinement. [tidusjar]
+
+- Fixed #2704. [tidusjar]
+
+- Now it is fixed :) [d1slact0r]
+
+- Android please be nice now. [d1slact0r]
+
+- Fixed title bit better. [d1slact0r]
+
+- Fixed titles. [d1slact0r]
+
+- This should fix the build for sure (stupid quotes) [d1slact0r]
+
+- Fixes build. [d1slact0r]
+
+- Rewritten the whole newsletter template. [d1slact0r]
+
+- Fixed #2697. [tidusjar]
+
+- Add linux-arm runtime identifier. [aptalca]
+
+- Add back arm packages. [aptalca]
+
+- Add arm32 package. [aptalca]
+
+- Fixed #2691. [tidusjar]
+
+- Fixed linting. [TidusJar]
+
+- Fixed the Plex OAuth when going through the wizard. [TidusJar]
+
+- Fixed #2678. [TidusJar]
+
+- Deny reason for movie requests. [TidusJar]
+
+- Set the landing and login pages background refresh to 15 seconds rather than 10 and 7. [TidusJar]
+
+- Fixed a bug with us thinking future dated emby episodes are not available, Consoldated the emby and plex search rules (since they have the same logic) [TidusJar]
+
+- Fixed build. [TidusJar]
+
+
+## v3.0.4036 (2018-12-11)
+
+### **New Features**
+
+- Changelog. [Jamie]
+
+- Added Sonarr v3 #2359. [TidusJar]
+
+### **Fixes**
+
+- !changelog. [Jamie]
+
+- Fixed a missing translation. [Jamie]
+
+- Fixed a potential security vulnerability. [Jamie]
+
+- Sorted out some of the settings pages, trying to make it consistent. [Jamie]
+
+- #2669 Fixed missing translations. [TidusJar]
+
+- Maps alias email variable for welcome emails. [Victor Usoltsev]
+
+- Increased the logo size on the landing page to match the container below it. [Jamie]
+
+- Think the request queue is done! [Jamie]
+
+- Finished off the job. [TidusJar]
+
+
+## v3.0.3988 (2018-11-23)
+
+### **New Features**
+
+- Updated the emby api since we no longer need the extra parameters to send to emby to log in a local user #2546. [Jamie]
+
+- Added the ability to get the ombi user via a Plex Token #2591. [Jamie]
+
+- Update CHANGELOG.md. [Jamie]
+
+### **Fixes**
+
+- !changelog. [Jamie]
+
+- Made the subscribe/unsubscribe button more obvious on the UI #2309. [Jamie]
+
+- Fixed #2603. [Jamie]
+
+- Fixed the issue with the user overrides #2646. [Jamie]
+
+- Fixed the issue where we could sometimes allow the request of a whole series when the user shouldn't be able to. [Jamie]
+
+- Fixed the issue where we were marking episodes as available with the Emby connection when they have not yet aired #2417 #2623. [TidusJar]
+
+- Fixed the issue where we were marking the whole season as wanted in Sonarr rather than the individual episode #2629. [TidusJar]
+
+- Fixed #2623. [Jamie]
+
+- Fixed #2633. [TidusJar]
+
+- Fixed #2639. [Jamie]
+
+- Show the TV show as available when we have all the episodes but future episodes have not aired. #2585. [Jamie]
+
+
+## v3.0.3945 (2018-10-25)
+
+### **New Features**
+
+- Update Readme for Lidarr. [Qstick]
+
+- Update CHANGELOG.md. [Jamie]
+
+### **Fixes**
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- Fixed the issue with mobile notifications. [Jamie]
+
+- Fixed #2514. [Jamie]
+
+
+## v3.0.3923 (2018-10-19)
+
+### **New Features**
+
+- Update CHANGELOG.md. [Jamie]
+
+### **Fixes**
+
+- Fixed #2601. [Jamie]
+
+
+## v3.0.3919 (2018-10-17)
+
+### **New Features**
+
+- Added automation tests for the voting feature. [TidusJar]
+
+- Update LidarrAvailabilityChecker.cs. [Jamie]
+
+- Update CHANGELOG.md. [Jamie]
+
+- Changes language selector to always show native language name. [Victor Usoltsev]
+
+- Updated test dependancies. [TidusJar]
+
+- Added in the external repo so we can rip out external stuff. [TidusJar]
+
+- Added the ability to purge/remove issues. [TidusJar]
+
+### **Fixes**
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Spanish) [Jamie]
+
+- New translations en.json (Portuguese, Brazilian) [Jamie]
+
+- New translations en.json (Polish) [Jamie]
+
+- New translations en.json (Norwegian) [Jamie]
+
+- New translations en.json (Italian) [Jamie]
+
+- New translations en.json (German) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- New translations en.json (Danish) [Jamie]
+
+- When a users requests content and the voting is enabled, the user who requested is an automatic +1 vote. [TidusJar]
+
+- Revert, no idea how this happened. [TidusJar]
+
+- Fixed the build. Thanks Matt! [TidusJar]
+
+- Fixes untickable mass email checkboxes in Safari. [Victor Usoltsev]
+
+- [ImgBot] optimizes images. [ImgBotApp]
+
+- Revert "Feature/purge issues" [Jamie]
+
+- Fixed the issue where user preferences was not being inported into some notifications. [TidusJar]
+
+- New role to enable users to remove their own requests. [Anojh]
+
+- Users can now remove their own requests. [Anojh]
+
+- New translations en.json (Danish) [Jamie]
+
+- Fixed lidarr newsletter bug. [Jamie]
+
+- Potentially fix the user profiles issue. [Jamie]
+
+- Hides Radarr options on movie requests page if only 1 option available. [Victor Usoltsev]
+
+- Hides Sonarr options on tv requests page if only 1 option available. [Victor Usoltsev]
+
+- Fixed the issue where we could not delete users #2558. [TidusJar]
+
+- New translations en.json (German) [Jamie]
+
+- New translations en.json (Swedish) [Jamie]
+
+- New translations en.json (Spanish) [Jamie]
+
+- New translations en.json (Portuguese, Brazilian) [Jamie]
+
+- New translations en.json (Polish) [Jamie]
+
+- New translations en.json (Norwegian) [Jamie]
+
+- New translations en.json (Italian) [Jamie]
+
+- New translations en.json (German) [Jamie]
+
+- New translations en.json (French) [Jamie]
+
+- New translations en.json (Dutch) [Jamie]
+
+- New translations en.json (Danish) [Jamie]
+
+- Subscribe the user to the request when they vote on it. [TidusJar]
+
+- Fixed #2555. [Jamie]
+
+- Fixed #2549. [Jamie]
+
+- Removed the pinID from the OAuth url #2548. [Jamie]
+
+- Put the issue purge limit on the issues page. [Jamie]
+
+- Date and times are now in the local users date time. [TidusJar]
+
+- Fixed the migration. [TidusJar]
+
+- ExternalContext migrations. [TidusJar]
+
+- The settings have now been split out of the main db. [TidusJar]
+
+- Search for the Lidarr Album when it's a new artist. [TidusJar]
+
+- The album in Lidarr does not need to be marked as monitored for us to pick up it's available. Fixes #2536. [Jamie]
+
+- Truncate the request title. [Jamie]
+
+- Fixed #2535. [Jamie]
+
+
+## v3.0.3795 (2018-09-23)
+
+### **New Features**
+
+- Update CHANGELOG.md. [Jamie]
### **Fixes**
diff --git a/README.md b/README.md
index cfc0ad23c..4f854cb56 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,15 @@ ____
[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi)
[![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet)
+___
+
+[![Twitter](https://img.shields.io/twitter/follow/tidusjar.svg?style=social)](https://twitter.com/intent/follow?screen_name=tidusjar)
+
+Follow me developing Ombi!
+
+[![Twitch](https://img.shields.io/badge/Twitch-Watch-blue.svg?style=flat-square&logo=twitch)](https://www.twitch.tv/tidusjar)
+
+
___
@@ -32,7 +41,7 @@ ___
# Features
Here are some of the features Ombi V3 has:
* Now working without crashes on Linux.
-* Lets users request Movies and TV Shows (whether it being the entire series, an entire season, or even single episodes.)
+* Lets users request Movies, Music, and TV Shows (whether it being the entire series, an entire season, or even single episodes.)
* Easily manage your requests
* User management system (supports plex.tv, Emby and local accounts)
* A landing page that will give you the availability of your Plex/Emby server and also add custom notification text to inform your users of downtime.
@@ -50,6 +59,7 @@ We integrate with the following applications:
* Emby
* Sonarr
* Radarr
+* Lidarr
* DogNzb
* Couch Potato
@@ -58,6 +68,7 @@ We integrate with the following applications:
Supported notifications:
* SMTP Notifications (Email)
* Discord
+* Gotify
* Slack
* Pushbullet
* Pushover
@@ -87,6 +98,7 @@ We are planning to bring back these features in V3 but for now you can find a li
| DogNzb | Yes | No |
| Issues | Yes | Yes |
| Headphones | No | Yes |
+| Lidarr | Yes | No |
# Feature Requests
Feature requests are handled on FeatHub.
@@ -115,13 +127,12 @@ Please feel free to submit a pull request!
# Donation
If you feel like donating you can donate with the below buttons!
-[![Patreon](https://www.ombi.io/img/patreondonate.svg)](https://patreon.com/tidusjar/Ombi)
-[![Paypal](https://www.ombi.io/img/paypaldonate.svg)](https://paypal.me/PlexRequestsNet)
+
+[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi)
+[![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet)
### A massive thanks to everyone for all their help!
-## Stats
-[![Throughput Graph](https://graphs.waffle.io/tidusjar/PlexRequests.Net/throughput.svg)](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 862993a21..3c60a0006 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,34 +3,46 @@ configuration: Release
os: Visual Studio 2017
environment:
nodejs_version: "9.8.0"
+ typescript_version: "3.0.1"
+ github_auth_token:
+ secure: H/7uCrjmWHGJxgN3l9fbhhdVjvvWI8VVF4ZzQqeXuJwAf+PgSNBdxv4SS+rMQ+RH
+ sonarrcloudtoken:
+ secure: WGkIog4wuMSx1q5vmSOgIBXMtI/leMpLbZhi9MJnJdBBuDfcv12zwXg3LQwY0WbE
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
+
+ - 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%"
test: off
-
+
after_build:
- cmd: >-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip"
+ appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows.zip"
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz"
+ appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\osx.tar.gz"
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz"
+ appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux.tar.gz"
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz"
-
- appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip"
+ 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.0\linux-arm64.tar.gz"
+ appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm64.tar.gz"
diff --git a/music-placeholder.psd b/assets/music-placeholder.psd
similarity index 100%
rename from music-placeholder.psd
rename to assets/music-placeholder.psd
diff --git a/build.cake b/build.cake
index d706e7b6b..b2ae8842c 100644
--- a/build.cake
+++ b/build.cake
@@ -3,7 +3,7 @@
#addin "Cake.Gulp"
#addin "SharpZipLib"
#addin nuget:?package=Cake.Compression&version=0.1.4
-#addin "Cake.Incubator"
+#addin "Cake.Incubator&version=3.1.0"
#addin "Cake.Yarn"
//////////////////////////////////////////////////////////////////////
@@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
var solutionFile = "Ombi.sln"; // Solution file if needed
GitVersion versionInfo = null;
-var frameworkVer = "netcoreapp2.1";
+var frameworkVer = "netcoreapp2.2";
var buildSettings = new DotNetCoreBuildSettings
{
@@ -81,9 +81,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))
@@ -151,7 +151,7 @@ Task("Package")
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
- //GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
+ GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
});
Task("Publish")
@@ -227,7 +227,7 @@ Task("Publish-Linux-ARM")
CopyFile(
buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml",
buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml");
-
+
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 10c7546e4..000000000
--- a/package-lock.json
+++ /dev/null
@@ -1,291 +0,0 @@
-{
- "requires": true,
- "lockfileVersion": 1,
- "dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
- },
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
- },
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "requires": {
- "sprintf-js": "1.0.3"
- }
- },
- "babel-code-frame": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
- "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
- "requires": {
- "chalk": "1.1.3",
- "esutils": "2.0.2",
- "js-tokens": "3.0.2"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- }
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "builtin-modules": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
- },
- "chalk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
- "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
- "requires": {
- "ansi-styles": "3.2.1",
- "escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "requires": {
- "color-convert": "1.9.1"
- }
- },
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
- "requires": {
- "has-flag": "3.0.0"
- }
- }
- }
- },
- "color-convert": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
- "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
- },
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
- },
- "diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
- },
- "esprima": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
- "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
- },
- "esutils": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
- },
- "glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
- "requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.3",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
- }
- },
- "has-ansi": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- },
- "js-tokens": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
- "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
- },
- "js-yaml": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
- "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
- "requires": {
- "argparse": "1.0.10",
- "esprima": "4.0.0"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "requires": {
- "brace-expansion": "1.1.11"
- }
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "requires": {
- "wrappy": "1.0.2"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
- },
- "path-parse": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
- "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
- },
- "resolve": {
- "version": "1.7.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
- "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
- "requires": {
- "path-parse": "1.0.5"
- }
- },
- "semver": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
- },
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
- },
- "tslib": {
- "version": "1.9.2",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
- "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw=="
- },
- "tslint": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz",
- "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=",
- "requires": {
- "babel-code-frame": "6.26.0",
- "builtin-modules": "1.1.1",
- "chalk": "2.4.1",
- "commander": "2.15.1",
- "diff": "3.5.0",
- "glob": "7.1.2",
- "js-yaml": "3.12.0",
- "minimatch": "3.0.4",
- "resolve": "1.7.1",
- "semver": "5.5.0",
- "tslib": "1.9.2",
- "tsutils": "2.27.1"
- }
- },
- "tsutils": {
- "version": "2.27.1",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz",
- "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
- "requires": {
- "tslib": "1.9.2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- }
- }
-}
diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs
index fcb989094..43a2badb6 100644
--- a/src/Ombi.Api.Emby/EmbyApi.cs
+++ b/src/Ombi.Api.Emby/EmbyApi.cs
@@ -53,8 +53,6 @@ namespace Ombi.Api.Emby
{
username,
pw = password,
- password = password.GetSha1Hash().ToLower(),
- passwordMd5 = password.CalcuateMd5Hash()
};
request.AddJsonBody(body);
@@ -98,7 +96,7 @@ namespace Ombi.Api.Emby
request.AddQueryString("Fields", "ProviderIds,Overview");
- request.AddQueryString("VirtualItem", "False");
+ request.AddQueryString("IsVirtualItem", "False");
return await Api.Request>(request);
}
@@ -149,7 +147,7 @@ namespace Ombi.Api.Emby
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
- request.AddQueryString("VirtualItem", "False");
+ request.AddQueryString("IsVirtualItem", "False");
AddHeaders(request, apiKey);
@@ -167,7 +165,7 @@ namespace Ombi.Api.Emby
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
- request.AddQueryString("VirtualItem", "False");
+ request.AddQueryString("IsVirtualItem", "False");
AddHeaders(request, apiKey);
diff --git a/src/Ombi.Api.Github/GithubApi.cs b/src/Ombi.Api.Github/GithubApi.cs
index f7b3fbcbf..6865b33fe 100644
--- a/src/Ombi.Api.Github/GithubApi.cs
+++ b/src/Ombi.Api.Github/GithubApi.cs
@@ -24,16 +24,5 @@ namespace Ombi.Api.Github
request.AddHeader("User-Agent", "Ombi");
return await _api.Request>(request);
}
-
- public async Task GetThemesRawContent(string url)
- {
- var sections = url.Split('/');
- var lastPart = sections.Last();
- url = url.Replace(lastPart, string.Empty);
- var request = new Request(lastPart, url, HttpMethod.Get);
- request.AddHeader("Accept", "application/vnd.github.v3+json");
- request.AddHeader("User-Agent", "Ombi");
- return await _api.RequestContent(request);
- }
}
}
diff --git a/src/Ombi.Api.Github/IGithubApi.cs b/src/Ombi.Api.Github/IGithubApi.cs
index 307158b72..1cca37f02 100644
--- a/src/Ombi.Api.Github/IGithubApi.cs
+++ b/src/Ombi.Api.Github/IGithubApi.cs
@@ -7,6 +7,5 @@ namespace Ombi.Api.Github
public interface IGithubApi
{
Task> GetCakeThemes();
- Task GetThemesRawContent(string url);
}
}
\ 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.Lidarr/ILidarrApi.cs b/src/Ombi.Api.Lidarr/ILidarrApi.cs
index 4a23c6200..0eac960d0 100644
--- a/src/Ombi.Api.Lidarr/ILidarrApi.cs
+++ b/src/Ombi.Api.Lidarr/ILidarrApi.cs
@@ -23,5 +23,6 @@ namespace Ombi.Api.Lidarr
Task> GetLanguageProfile(string apiKey, string baseUrl);
Task Status(string apiKey, string baseUrl);
Task AlbumSearch(int[] albumIds, string apiKey, string baseUrl);
+ Task AlbumInformation(string albumId, string apiKey, string baseUrl);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Api.Lidarr/LidarrApi.cs b/src/Ombi.Api.Lidarr/LidarrApi.cs
index a6a283703..cb8db759e 100644
--- a/src/Ombi.Api.Lidarr/LidarrApi.cs
+++ b/src/Ombi.Api.Lidarr/LidarrApi.cs
@@ -105,6 +105,32 @@ namespace Ombi.Api.Lidarr
return Api.Request>(request);
}
+ public async Task AlbumInformation(string albumId, string apiKey, string baseUrl)
+ {
+ var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
+ request.AddQueryString("foreignAlbumId", albumId);
+ AddHeaders(request, apiKey);
+ var albums = await Api.Request>(request);
+ return albums.Where(x => x.foreignAlbumId.Equals(albumId, StringComparison.InvariantCultureIgnoreCase))
+ .FirstOrDefault();
+ }
+
+
+ ///
+ /// THIS ONLY SUPPORTS ALBUMS THAT THE ARTIST IS IN LIDARR
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Task> GetTracksForAlbum(int albumId, string apiKey, string baseUrl)
+ {
+ var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
+ request.AddQueryString("albumId", albumId.ToString());
+ AddHeaders(request, apiKey);
+ return Api.Request>(request);
+ }
+
public Task AddArtist(ArtistAdd artist, string apiKey, string baseUrl)
{
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post);
diff --git a/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs b/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
index b2394eb5f..1e909cf4c 100644
--- a/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
+++ b/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
@@ -1,10 +1,15 @@
using System;
+using System.Collections.Generic;
namespace Ombi.Api.Lidarr.Models
{
public class AlbumLookup
{
public string title { get; set; }
+ public string status { get; set; }
+ public string artistType { get; set; }
+ public string disambiguation { get; set; }
+ public List links { get; set; }
public int artistId { get; set; }
public string foreignAlbumId { get; set; }
public bool monitored { get; set; }
diff --git a/src/Ombi.Api.Lidarr/Models/LidarrLinks.cs b/src/Ombi.Api.Lidarr/Models/LidarrLinks.cs
new file mode 100644
index 000000000..e8bdb8975
--- /dev/null
+++ b/src/Ombi.Api.Lidarr/Models/LidarrLinks.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Ombi.Api.Lidarr.Models
+{
+ public class LidarrLinks
+ {
+ public string url { get; set; }
+ public string name { get; set; }
+ }
+}
diff --git a/src/Ombi.Api.Lidarr/Models/LidarrRatings.cs b/src/Ombi.Api.Lidarr/Models/LidarrRatings.cs
new file mode 100644
index 000000000..7728d58bf
--- /dev/null
+++ b/src/Ombi.Api.Lidarr/Models/LidarrRatings.cs
@@ -0,0 +1,8 @@
+namespace Ombi.Api.Lidarr.Models
+{
+ public class LidarrRatings
+ {
+ public int votes { get; set; }
+ public decimal value { get; set; }
+ }
+}
diff --git a/src/Ombi.Api.Lidarr/Models/LidarrTrack.cs b/src/Ombi.Api.Lidarr/Models/LidarrTrack.cs
new file mode 100644
index 000000000..367e8cc7c
--- /dev/null
+++ b/src/Ombi.Api.Lidarr/Models/LidarrTrack.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Ombi.Api.Lidarr.Models
+{
+ public class LidarrTrack
+ {
+ public int artistId { get; set; }
+ public int trackFileId { get; set; }
+ public int albumId { get; set; }
+ public bool _explicit { get; set; }
+ public int absoluteTrackNumber { get; set; }
+ public string trackNumber { get; set; }
+ public string title { get; set; }
+ public int duration { get; set; }
+ public int mediumNumber { get; set; }
+ public bool hasFile { get; set; }
+ public bool monitored { get; set; }
+ public int id { get; set; }
+ }
+}
diff --git a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj
index 83318be7b..98292f463 100644
--- a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj
+++ b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Ombi.Api.Notifications/OneSignalApi.cs b/src/Ombi.Api.Notifications/OneSignalApi.cs
index 8d5fc04a9..ee5c7e44a 100644
--- a/src/Ombi.Api.Notifications/OneSignalApi.cs
+++ b/src/Ombi.Api.Notifications/OneSignalApi.cs
@@ -26,7 +26,7 @@ namespace Ombi.Api.Notifications
{
return null;
}
- var id = await _appConfig.Get(ConfigurationTypes.Notification);
+ var id = await _appConfig.GetAsync(ConfigurationTypes.Notification);
var request = new Request(string.Empty, ApiUrl, HttpMethod.Post);
var body = new OneSignalNotificationBody
diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj
index 0c615f301..9f25a7946 100644
--- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj
+++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj
index 8cbddd874..bd53c9808 100644
--- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj
+++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/src/Ombi.Api.Sonarr/ISonarrV3Api.cs b/src/Ombi.Api.Sonarr/ISonarrV3Api.cs
new file mode 100644
index 000000000..1d3ea3468
--- /dev/null
+++ b/src/Ombi.Api.Sonarr/ISonarrV3Api.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Ombi.Api.Sonarr.Models;
+using System.Net.Http;
+using Ombi.Api.Sonarr.Models.V3;
+
+namespace Ombi.Api.Sonarr
+{
+ public interface ISonarrV3Api : ISonarrApi
+ {
+ Task> LanguageProfiles(string apiKey, string baseUrl);
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Api.Sonarr/Models/NewSeries.cs b/src/Ombi.Api.Sonarr/Models/NewSeries.cs
index ef18baddb..d6f721b0b 100644
--- a/src/Ombi.Api.Sonarr/Models/NewSeries.cs
+++ b/src/Ombi.Api.Sonarr/Models/NewSeries.cs
@@ -27,6 +27,9 @@ namespace Ombi.Api.Sonarr.Models
public int id { get; set; }
public List images { get; set; }
+ // V3 Property
+ public int languageProfileId { get; set; }
+
///
/// This is for us
///
diff --git a/src/Ombi.Api.Sonarr/Models/SonarrSeries.cs b/src/Ombi.Api.Sonarr/Models/SonarrSeries.cs
index 568592734..3ade006d5 100644
--- a/src/Ombi.Api.Sonarr/Models/SonarrSeries.cs
+++ b/src/Ombi.Api.Sonarr/Models/SonarrSeries.cs
@@ -44,6 +44,7 @@ namespace Ombi.Api.Sonarr.Models
public DateTime added { get; set; }
public Ratings ratings { get; set; }
public int qualityProfileId { get; set; }
+ public int languageProfileId { get; set; }
public int id { get; set; }
public DateTime nextAiring { get; set; }
}
diff --git a/src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs b/src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs
new file mode 100644
index 000000000..aa3b199bd
--- /dev/null
+++ b/src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs
@@ -0,0 +1,30 @@
+namespace Ombi.Api.Sonarr.Models.V3
+{
+ public class LanguageProfiles
+ {
+ public string name { get; set; }
+ public bool upgradeAllowed { get; set; }
+ public Cutoff cutoff { get; set; }
+ public Languages[] languages { get; set; }
+ public int id { get; set; }
+ }
+
+ public class Cutoff
+ {
+ public int id { get; set; }
+ public string name { get; set; }
+ }
+
+ public class Languages
+ {
+ public Language languages { get; set; }
+ public bool allowed { get; set; }
+ }
+
+ public class Language
+ {
+ public int id { get; set; }
+ public string name { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/src/Ombi.Api.Sonarr/SonarrApi.cs b/src/Ombi.Api.Sonarr/SonarrApi.cs
index 7fd74d2a3..0b0df4c15 100644
--- a/src/Ombi.Api.Sonarr/SonarrApi.cs
+++ b/src/Ombi.Api.Sonarr/SonarrApi.cs
@@ -16,18 +16,19 @@ namespace Ombi.Api.Sonarr
Api = api;
}
- private IApi Api { get; }
+ protected IApi Api { get; }
+ protected virtual string ApiBaseUrl => "/api/";
public async Task> GetProfiles(string apiKey, string baseUrl)
{
- var request = new Request("/api/profile", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}profile", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
return await Api.Request>(request);
}
public async Task> GetRootFolders(string apiKey, string baseUrl)
{
- var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}rootfolder", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
return await Api.Request>(request);
}
@@ -40,7 +41,7 @@ namespace Ombi.Api.Sonarr
///
public async Task> GetSeries(string apiKey, string baseUrl)
{
- var request = new Request("/api/series", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}series", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
var results = await Api.Request>(request);
@@ -63,7 +64,7 @@ namespace Ombi.Api.Sonarr
///
public async Task GetSeriesById(int id, string apiKey, string baseUrl)
{
- var request = new Request($"/api/series/{id}", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}series/{id}", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
var result = await Api.Request(request);
if (result?.seasons?.Length > 0)
@@ -82,7 +83,7 @@ namespace Ombi.Api.Sonarr
///
public async Task UpdateSeries(SonarrSeries updated, string apiKey, string baseUrl)
{
- var request = new Request("/api/series/", baseUrl, HttpMethod.Put);
+ var request = new Request($"{ApiBaseUrl}series/", baseUrl, HttpMethod.Put);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(updated);
return await Api.Request(request);
@@ -94,7 +95,7 @@ namespace Ombi.Api.Sonarr
{
return new NewSeries { ErrorMessages = new List { seriesToAdd.Validate() } };
}
- var request = new Request("/api/series/", baseUrl, HttpMethod.Post);
+ var request = new Request($"{ApiBaseUrl}series/", baseUrl, HttpMethod.Post);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(seriesToAdd);
@@ -120,7 +121,7 @@ namespace Ombi.Api.Sonarr
///
public async Task> GetEpisodes(int seriesId, string apiKey, string baseUrl)
{
- var request = new Request($"/api/Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
return await Api.Request>(request);
}
@@ -134,14 +135,14 @@ namespace Ombi.Api.Sonarr
///
public async Task GetEpisodeById(int episodeId, string apiKey, string baseUrl)
{
- var request = new Request($"/api/Episode/{episodeId}", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}Episode/{episodeId}", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
return await Api.Request(request);
}
public async Task UpdateEpisode(Episode episodeToUpdate, string apiKey, string baseUrl)
{
- var request = new Request($"/api/Episode/", baseUrl, HttpMethod.Put);
+ var request = new Request($"{ApiBaseUrl}Episode/", baseUrl, HttpMethod.Put);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(episodeToUpdate);
return await Api.Request(request);
@@ -189,7 +190,7 @@ namespace Ombi.Api.Sonarr
private async Task Command(string apiKey, string baseUrl, object body)
{
- var request = new Request($"/api/Command/", baseUrl, HttpMethod.Post);
+ var request = new Request($"{ApiBaseUrl}Command/", baseUrl, HttpMethod.Post);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(body);
return await Api.Request(request);
@@ -197,7 +198,7 @@ namespace Ombi.Api.Sonarr
public async Task SystemStatus(string apiKey, string baseUrl)
{
- var request = new Request("/api/system/status", baseUrl, HttpMethod.Get);
+ var request = new Request($"{ApiBaseUrl}system/status", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);
return await Api.Request(request);
@@ -217,7 +218,7 @@ namespace Ombi.Api.Sonarr
ignoreEpisodesWithoutFiles = false,
}
};
- var request = new Request("/api/seasonpass", baseUrl, HttpMethod.Post);
+ var request = new Request($"{ApiBaseUrl}seasonpass", baseUrl, HttpMethod.Post);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(seasonPass);
diff --git a/src/Ombi.Api.Sonarr/SonarrV3Api.cs b/src/Ombi.Api.Sonarr/SonarrV3Api.cs
new file mode 100644
index 000000000..64377ee4a
--- /dev/null
+++ b/src/Ombi.Api.Sonarr/SonarrV3Api.cs
@@ -0,0 +1,25 @@
+using System.Net.Http;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Ombi.Api.Sonarr.Models.V3;
+
+namespace Ombi.Api.Sonarr
+{
+ public class SonarrV3Api : SonarrApi, ISonarrV3Api
+ {
+ public SonarrV3Api(IApi api) : base(api)
+ {
+
+ }
+
+ protected override string ApiBaseUrl => "/api/v3/";
+
+ public async Task> LanguageProfiles(string apiKey, string baseUrl)
+ {
+ var request = new Request($"{ApiBaseUrl}languageprofile", baseUrl, HttpMethod.Get);
+ request.AddHeader("X-Api-Key", apiKey);
+
+ return await Api.Request>(request);
+ }
+ }
+}
diff --git a/src/Ombi.Api/Api.cs b/src/Ombi.Api/Api.cs
index 19dab7530..e405b6bca 100644
--- a/src/Ombi.Api/Api.cs
+++ b/src/Ombi.Api/Api.cs
@@ -41,7 +41,7 @@ namespace Ombi.Api
{
if (!request.IgnoreErrors)
{
- LogError(request, httpResponseMessage);
+ await LogError(request, httpResponseMessage);
}
if (request.Retry)
@@ -105,7 +105,7 @@ namespace Ombi.Api
{
if (!request.IgnoreErrors)
{
- LogError(request, httpResponseMessage);
+ await LogError(request, httpResponseMessage);
}
}
// do something with the response
@@ -126,7 +126,7 @@ namespace Ombi.Api
{
if (!request.IgnoreErrors)
{
- LogError(request, httpResponseMessage);
+ await LogError(request, httpResponseMessage);
}
}
}
@@ -149,10 +149,15 @@ namespace Ombi.Api
}
}
- private void LogError(Request request, HttpResponseMessage httpResponseMessage)
+ private async Task LogError(Request request, HttpResponseMessage httpResponseMessage)
{
Logger.LogError(LoggingEvents.Api,
$"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}, RequestUri: {request.FullUri}");
+ if (Logger.IsEnabled(LogLevel.Debug))
+ {
+ var content = await httpResponseMessage.Content.ReadAsStringAsync();
+ Logger.LogDebug(content);
+ }
}
}
}
diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj
index a37c128fb..e20dd6ccd 100644
--- a/src/Ombi.Api/Ombi.Api.csproj
+++ b/src/Ombi.Api/Ombi.Api.csproj
@@ -9,8 +9,8 @@
-
-
+
+
diff --git a/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs b/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
new file mode 100644
index 000000000..ad4c33131
--- /dev/null
+++ b/src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
@@ -0,0 +1,73 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using AutoFixture;
+using Moq;
+using NUnit.Framework;
+using Ombi.Core.Authentication;
+using Ombi.Core.Engine;
+using Ombi.Core.Engine.Interfaces;
+using Ombi.Core.Rule.Interfaces;
+using Ombi.Core.Settings;
+using Ombi.Settings.Settings.Models;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository;
+
+namespace Ombi.Core.Tests.Engine
+{
+ [TestFixture]
+ public class VoteEngineTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ F = new Fixture();
+ VoteRepository = new Mock>();
+ VoteSettings = new Mock>();
+ MusicRequestEngine = new Mock();
+ TvRequestEngine = new Mock();
+ 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"}}));
+ Rule = new Mock();
+ Engine = new VoteEngine(VoteRepository.Object, User.Object, UserManager.Object, Rule.Object, VoteSettings.Object, MusicRequestEngine.Object,
+ TvRequestEngine.Object, MovieRequestEngine.Object);
+ }
+
+ public Fixture F { get; set; }
+ public VoteEngine Engine { get; set; }
+ public Mock User { get; set; }
+ public Mock UserManager { get; set; }
+ public Mock Rule { get; set; }
+ public Mock> VoteRepository { get; set; }
+ public Mock> VoteSettings { get; set; }
+ public Mock MusicRequestEngine { get; set; }
+ public Mock TvRequestEngine { get; set; }
+ public Mock MovieRequestEngine { get; set; }
+
+ [Test]
+ [Ignore("Need to mock the user manager")]
+ public async Task New_Upvote()
+ {
+ VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings());
+ var votes = F.CreateMany().ToList();
+ votes.Add(new Votes
+ {
+ RequestId = 1,
+ RequestType = RequestType.Movie,
+ UserId = "abc"
+ });
+ VoteRepository.Setup(x => x.GetAll()).Returns(new EnumerableQuery(votes));
+ var result = await Engine.UpVote(1, RequestType.Movie);
+
+ 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);
+ }
+ }
+}
\ 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 30de4b6f0..23fc6db78 100644
--- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj
+++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj
@@ -1,15 +1,16 @@
- netcoreapp2.1
+ netcoreapp2.2
-
+
+
-
+
-
+
diff --git a/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs b/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs
index 7ff8283da..34c21e008 100644
--- a/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Request/AutoApproveRuleTests.cs
@@ -4,6 +4,7 @@ using Moq;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Store.Entities.Requests;
using NUnit.Framework;
+using Ombi.Core.Authentication;
using Ombi.Helpers;
namespace Ombi.Core.Tests.Rule.Request
@@ -16,7 +17,7 @@ namespace Ombi.Core.Tests.Rule.Request
{
PrincipalMock = new Mock();
- Rule = new AutoApproveRule(PrincipalMock.Object);
+ Rule = new AutoApproveRule(PrincipalMock.Object, null);
}
diff --git a/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs b/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs
index c9db5875a..f2781c8d2 100644
--- a/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Request/CanRequestRuleTests.cs
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Ombi.Core.Rule.Rules;
+using Ombi.Core.Rule.Rules.Request;
using Ombi.Helpers;
using Ombi.Store.Entities.Requests;
@@ -15,7 +16,7 @@ namespace Ombi.Core.Tests.Rule.Request
{
PrincipalMock = new Mock();
- Rule = new CanRequestRule(PrincipalMock.Object);
+ Rule = new CanRequestRule(PrincipalMock.Object, null);
}
diff --git a/src/Ombi.Core.Tests/Rule/Search/CouchPotatoCacheRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/CouchPotatoCacheRuleTests.cs
index 56524522b..2a8f7a520 100644
--- a/src/Ombi.Core.Tests/Rule/Search/CouchPotatoCacheRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/CouchPotatoCacheRuleTests.cs
@@ -18,13 +18,13 @@ namespace Ombi.Core.Tests.Rule.Search
[SetUp]
public void Setup()
{
- ContextMock = new Mock>();
+ ContextMock = new Mock>();
Rule = new CouchPotatoCacheRule(ContextMock.Object);
}
private CouchPotatoCacheRule Rule { get; set; }
- private Mock> ContextMock { get; set; }
+ private Mock> ContextMock { get; set; }
[Test]
public async Task Should_ReturnApproved_WhenMovieIsInCouchPotato()
diff --git a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
index 99ff5b6bd..0171e37a1 100644
--- a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
@@ -16,7 +16,7 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup()
{
ContextMock = new Mock();
- Rule = new EmbyAvailabilityRule(ContextMock.Object);
+ Rule = new EmbyAvailabilityRule(ContextMock.Object, null);
}
private EmbyAvailabilityRule Rule { get; set; }
diff --git a/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs
index 55177a6ac..5bd35473c 100644
--- a/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/PlexAvailabilityRuleTests.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using Ombi.Core.Models.Search;
@@ -14,7 +15,7 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup()
{
ContextMock = new Mock();
- Rule = new PlexAvailabilityRule(ContextMock.Object);
+ Rule = new PlexAvailabilityRule(ContextMock.Object, new Mock>().Object);
}
private PlexAvailabilityRule Rule { get; set; }
diff --git a/src/Ombi.Core.Tests/Rule/Search/RadarrCacheRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/RadarrCacheRuleTests.cs
index 914112d5b..94efe89a2 100644
--- a/src/Ombi.Core.Tests/Rule/Search/RadarrCacheRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/RadarrCacheRuleTests.cs
@@ -15,13 +15,13 @@ namespace Ombi.Core.Tests.Rule.Search
[SetUp]
public void Setup()
{
- ContextMock = new Mock>();
+ ContextMock = new Mock>();
Rule = new RadarrCacheRule(ContextMock.Object);
}
private RadarrCacheRule Rule { get; set; }
- private Mock> ContextMock { get; set; }
+ private Mock> ContextMock { get; set; }
[Test]
public async Task Should_ReturnApproved_WhenMovieIsInRadarr()
diff --git a/src/Ombi.Core/Authentication/OmbiUserManager.cs b/src/Ombi.Core/Authentication/OmbiUserManager.cs
index 185677215..2c78f39bf 100644
--- a/src/Ombi.Core/Authentication/OmbiUserManager.cs
+++ b/src/Ombi.Core/Authentication/OmbiUserManager.cs
@@ -29,6 +29,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Ombi.Api.Emby;
@@ -101,6 +102,22 @@ namespace Ombi.Core.Authentication
return true;
}
+ public async Task GetOmbiUserFromPlexToken(string plexToken)
+ {
+ var plexAccount = await _plexApi.GetAccount(plexToken);
+
+ // Check for a ombi user
+ if (plexAccount?.user != null)
+ {
+ var potentialOmbiUser = await Users.FirstOrDefaultAsync(x =>
+ x.ProviderUserId == plexAccount.user.id);
+ return potentialOmbiUser;
+ }
+
+ return null;
+ }
+
+
///
/// Sign the user into plex and make sure we can get the authentication token.
/// We do not check if the user is in the owners "friends" since they must have a local user account to get this far
diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs
index cb0047b96..d0bb74c91 100644
--- a/src/Ombi.Core/Engine/BaseMediaEngine.cs
+++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs
@@ -157,6 +157,24 @@ namespace Ombi.Core.Engine
}
}
+ private string defaultLangCode;
+ protected async Task DefaultLanguageCode(string currentCode)
+ {
+ if (currentCode.HasValue())
+ {
+ return currentCode;
+ }
+
+ var s = await GetOmbiSettings();
+ return s.DefaultLanguageCode;
+ }
+
+ private OmbiSettings ombiSettings;
+ protected async Task GetOmbiSettings()
+ {
+ return ombiSettings ?? (ombiSettings = await OmbiSettings.GetSettingsAsync());
+ }
+
public class HideResult
{
public bool Hide { get; set; }
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/IMusicRequestEngine.cs b/src/Ombi.Core/Engine/IMusicRequestEngine.cs
index 5caba8a34..02a051343 100644
--- a/src/Ombi.Core/Engine/IMusicRequestEngine.cs
+++ b/src/Ombi.Core/Engine/IMusicRequestEngine.cs
@@ -12,7 +12,7 @@ namespace Ombi.Core.Engine
{
TaskApproveAlbum(AlbumRequest request);
Task ApproveAlbumById(int requestId);
- Task DenyAlbumById(int modelId);
+ Task DenyAlbumById(int modelId, string reason);
Task> GetRequests();
Task> GetRequests(int count, int position, OrderFilterModel orderFilter);
Task GetTotal();
diff --git a/src/Ombi.Core/Engine/IVoteEngine.cs b/src/Ombi.Core/Engine/IVoteEngine.cs
new file mode 100644
index 000000000..681f333c5
--- /dev/null
+++ b/src/Ombi.Core/Engine/IVoteEngine.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Ombi.Core.Models;
+using Ombi.Core.Models.UI;
+using Ombi.Store.Entities;
+
+namespace Ombi.Core.Engine
+{
+ public interface IVoteEngine
+ {
+ Task DownVote(int requestId, RequestType requestType);
+ Task GetVoteForUser(int requestId, string userId);
+ IQueryable GetVotes(int requestId, RequestType requestType);
+ Task RemoveCurrentVote(Votes currentVote);
+ Task UpVote(int requestId, RequestType requestType);
+ Task> GetMovieViewModel();
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs
index 26bc5969c..c5cb8c45a 100644
--- a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs
@@ -7,11 +7,8 @@ using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Entities;
-using Microsoft.AspNetCore.Identity;
-using System.Linq;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
-using Ombi.Helpers;
namespace Ombi.Core.Engine.Interfaces
{
diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieEngine.cs
index 91b6404db..9b4cd9831 100644
--- a/src/Ombi.Core/Engine/Interfaces/IMovieEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/IMovieEngine.cs
@@ -10,14 +10,15 @@ namespace Ombi.Core
Task> PopularMovies();
- Task> Search(string search);
+ Task> Search(string search, int? year, string languageCode);
Task> TopRatedMovies();
Task> UpcomingMovies();
- Task LookupImdbInformation(int theMovieDbId);
+ Task LookupImdbInformation(int theMovieDbId, string langCode = null);
- Task> SimilarMovies(int theMovieDbId);
+ Task> SimilarMovies(int theMovieDbId, string langCode);
+ Task> SearchActor(string search, string langaugeCode);
}
}
\ 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 152a1d923..d741dc8bc 100644
--- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs
@@ -12,10 +12,11 @@ namespace Ombi.Core.Engine.Interfaces
Task> SearchMovieRequest(string search);
Task RemoveMovieRequest(int requestId);
+ Task RemoveAllMovieRequests();
Task UpdateMovieRequest(MovieRequests request);
Task ApproveMovie(MovieRequests request);
Task ApproveMovieById(int requestId);
- Task DenyMovieById(int modelId);
+ Task DenyMovieById(int modelId, string denyReason);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
index 03294982a..71c02af9d 100644
--- a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
@@ -12,5 +12,6 @@ namespace Ombi.Core.Engine
Task> GetArtistAlbums(string foreignArtistId);
Task> SearchAlbum(string search);
Task> SearchArtist(string search);
+ Task GetAlbumInformation(string foreignAlbumId);
}
}
\ 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 36ae7da61..63de72bd1 100644
--- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
@@ -12,7 +12,7 @@ namespace Ombi.Core.Engine.Interfaces
Task RemoveTvRequest(int requestId);
Task GetTvRequest(int requestId);
Task RequestTvShow(TvRequestViewModel tv);
- Task DenyChildRequest(int requestId);
+ Task DenyChildRequest(int requestId, string reason);
Task> GetRequestsLite(int count, int position, OrderFilterModel type);
Task> SearchTvRequest(string search);
Task UpdateTvRequest(TvRequests request);
diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs
index 9fd6033bf..456ba267a 100644
--- a/src/Ombi.Core/Engine/MovieRequestEngine.cs
+++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs
@@ -51,7 +51,7 @@ namespace Ombi.Core.Engine
///
public async Task RequestMovie(MovieRequestViewModel model)
{
- var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId);
+ var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId, model.LanguageCode);
if (movieInfo == null || movieInfo.Id == 0)
{
return new RequestEngineResult
@@ -82,7 +82,9 @@ namespace Ombi.Core.Engine
RequestedDate = DateTime.UtcNow,
Approved = false,
RequestedUserId = userDetails.Id,
- Background = movieInfo.BackdropPath
+ Background = movieInfo.BackdropPath,
+ LangCode = model.LanguageCode,
+ RequestedByAlias = model.RequestedByAlias
};
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
@@ -305,7 +307,7 @@ namespace Ombi.Core.Engine
return await ApproveMovie(request);
}
- public async Task DenyMovieById(int modelId)
+ public async Task DenyMovieById(int modelId, string denyReason)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@@ -317,12 +319,14 @@ namespace Ombi.Core.Engine
}
request.Denied = true;
+ request.DeniedReason = denyReason;
// We are denying a request
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
await MovieRepository.Update(request);
return new RequestEngineResult
{
+ Result = true,
Message = "Request successfully deleted",
};
}
@@ -416,6 +420,12 @@ namespace Ombi.Core.Engine
await MovieRepository.Delete(request);
}
+ public async Task RemoveAllMovieRequests()
+ {
+ var request = MovieRepository.GetAll();
+ await MovieRepository.DeleteRange(request);
+ }
+
public async Task UserHasRequest(string userId)
{
return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId);
@@ -483,7 +493,7 @@ namespace Ombi.Core.Engine
RequestType = RequestType.Movie,
});
- return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"};
+ return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!", RequestId = model.Id};
}
public async Task GetRemainingRequests(OmbiUser user)
diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs
index 09b4cea6b..520f052a9 100644
--- a/src/Ombi.Core/Engine/MovieSearchEngine.cs
+++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs
@@ -1,23 +1,22 @@
-using System;
-using AutoMapper;
+using AutoMapper;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
+using Ombi.Core.Authentication;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Principal;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
using Ombi.Core.Rule.Interfaces;
-using Microsoft.Extensions.Caching.Memory;
-using Ombi.Core.Authentication;
using Ombi.Core.Settings;
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
{
@@ -32,18 +31,21 @@ 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.
///
/// The movie database identifier.
///
- public async Task LookupImdbInformation(int theMovieDbId)
+ public async Task LookupImdbInformation(int theMovieDbId, string langCode = null)
{
- var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId);
+ langCode = await DefaultLanguageCode(langCode);
+ var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode);
var viewMovie = Mapper.Map(movieInfo);
return await ProcessSingleMovie(viewMovie, true);
@@ -52,31 +54,58 @@ namespace Ombi.Core.Engine
///
/// Searches the specified movie.
///
- /// The search.
- ///
- public async Task> Search(string search)
+ public async Task> Search(string search, int? year, string langaugeCode)
{
- var result = await MovieApi.SearchMovie(search);
+ langaugeCode = await DefaultLanguageCode(langaugeCode);
+ var result = await MovieApi.SearchMovie(search, year, langaugeCode);
if (result != null)
{
- return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
+ public async Task> SearchActor(string search, string langaugeCode)
+ {
+ langaugeCode = await DefaultLanguageCode(langaugeCode);
+ var people = await MovieApi.SearchByActor(search, langaugeCode);
+ var person = people?.results?.Count > 0 ? people.results.FirstOrDefault() : null;
+
+ var resultSet = new List();
+ if (person == null)
+ {
+ return resultSet;
+ }
+
+ // Get this person movie credits
+ var credits = await MovieApi.GetActorMovieCredits(person.id, langaugeCode);
+ // Grab results from both cast and crew, prefer items in cast. we can handle directors like this.
+ var movieResults = (from role in credits.cast select new { Id = role.id, Title = role.title, ReleaseDate = role.release_date }).ToList();
+ movieResults.AddRange((from job in credits.crew select new { Id = job.id, Title = job.title, ReleaseDate = job.release_date }).ToList());
+
+ movieResults = movieResults.Take(10).ToList();
+ foreach (var movieResult in movieResults)
+ {
+ resultSet.Add(await LookupImdbInformation(movieResult.Id, langaugeCode));
+ }
+
+ return resultSet;
+ }
+
///
/// Get similar movies to the id passed in
///
///
///
- public async Task> SimilarMovies(int theMovieDbId)
+ public async Task> SimilarMovies(int theMovieDbId, string langCode)
{
- var result = await MovieApi.SimilarMovies(theMovieDbId);
+ langCode = await DefaultLanguageCode(langCode);
+ var result = await MovieApi.SimilarMovies(theMovieDbId, langCode);
if (result != null)
{
Logger.LogDebug("Search Result: {result}", result);
- return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@@ -87,10 +116,15 @@ namespace Ombi.Core.Engine
///
public async Task> PopularMovies()
{
- var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
+
+ var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () =>
+ {
+ var langCode = await DefaultLanguageCode(null);
+ return await MovieApi.PopularMovies(langCode);
+ }, DateTime.Now.AddHours(12));
if (result != null)
{
- return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@@ -101,10 +135,14 @@ namespace Ombi.Core.Engine
///
public async Task> TopRatedMovies()
{
- var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
+ var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () =>
+ {
+ var langCode = await DefaultLanguageCode(null);
+ return await MovieApi.TopRated(langCode);
+ }, DateTime.Now.AddHours(12));
if (result != null)
{
- return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@@ -115,11 +153,15 @@ namespace Ombi.Core.Engine
///
public async Task> UpcomingMovies()
{
- var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () => await MovieApi.Upcoming(), DateTime.Now.AddHours(12));
+ var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () =>
+ {
+ var langCode = await DefaultLanguageCode(null);
+ return await MovieApi.Upcoming(langCode);
+ }, DateTime.Now.AddHours(12));
if (result != null)
{
Logger.LogDebug("Search Result: {result}", result);
- return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
@@ -130,15 +172,19 @@ namespace Ombi.Core.Engine
///
public async Task> NowPlayingMovies()
{
- var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
+ var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () =>
+ {
+ var langCode = await DefaultLanguageCode(null);
+ return await MovieApi.NowPlaying(langCode);
+ }, DateTime.Now.AddHours(12));
if (result != null)
{
- return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
+ return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
}
return null;
}
- private async Task> TransformMovieResultsToResponse(
+ protected async Task> TransformMovieResultsToResponse(
IEnumerable movies)
{
var viewMovies = new List();
@@ -149,24 +195,25 @@ 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)
+ if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty())
{
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
viewMovie.Id = showInfo.Id; // TheMovieDbId
viewMovie.ImdbId = showInfo.ImdbId;
- var usDates = viewMovie.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
- viewMovie.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
}
+ var usDates = viewMovie.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
+ viewMovie.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
+
viewMovie.TheMovieDbId = viewMovie.Id.ToString();
await RunSearchRules(viewMovie);
// This requires the rules to be run first to populate the RequestId property
await CheckForSubscription(viewMovie);
-
+
return viewMovie;
}
@@ -174,9 +221,13 @@ namespace Ombi.Core.Engine
{
// Check if this user requested it
var user = await GetUser();
+ if (user == null)
+ {
+ return;
+ }
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 89d1a221e..8457de515 100644
--- a/src/Ombi.Core/Engine/MusicRequestEngine.cs
+++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs
@@ -83,7 +83,8 @@ 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,
+ RequestedByAlias = model.RequestedByAlias
};
if (requestModel.Cover.IsNullOrEmpty())
{
@@ -299,7 +300,7 @@ namespace Ombi.Core.Engine
return await ApproveAlbum(request);
}
- public async Task DenyAlbumById(int modelId)
+ public async Task DenyAlbumById(int modelId, string reason)
{
var request = await MusicRepository.Find(modelId);
if (request == null)
@@ -311,6 +312,7 @@ namespace Ombi.Core.Engine
}
request.Denied = true;
+ request.DeniedReason = reason;
// We are denying a request
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
await MusicRepository.Update(request);
@@ -495,7 +497,7 @@ namespace Ombi.Core.Engine
RequestType = RequestType.Album,
});
- return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!" };
+ return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!", RequestId = model.Id };
}
diff --git a/src/Ombi.Core/Engine/MusicSearchEngine.cs b/src/Ombi.Core/Engine/MusicSearchEngine.cs
index d0e577801..c8d285766 100644
--- a/src/Ombi.Core/Engine/MusicSearchEngine.cs
+++ b/src/Ombi.Core/Engine/MusicSearchEngine.cs
@@ -60,6 +60,18 @@ namespace Ombi.Core.Engine
return vm;
}
+ public async Task GetAlbumInformation(string foreignAlbumId)
+ {
+ var settings = await GetSettings();
+ var result = await _lidarrApi.AlbumInformation(foreignAlbumId, settings.ApiKey, settings.FullUri);
+
+
+ var vm = await MapIntoAlbumVm(result, settings);
+
+
+ return vm;
+ }
+
///
/// Searches the specified artist
///
@@ -143,6 +155,48 @@ namespace Ombi.Core.Engine
return vm;
}
+
+ // TODO
+ private async Task MapIntoAlbumVm(AlbumResponse a, LidarrSettings settings)
+ {
+ var vm = new SearchAlbumViewModel
+ {
+ ForeignAlbumId = a.foreignAlbumId,
+ Monitored = a.monitored,
+ Rating = a.ratings?.value ?? 0m,
+ ReleaseDate = a.releaseDate,
+ Title = a.title,
+ Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url?.Replace("http", "https"),
+ Genres = a.genres
+ };
+ if (a.artistId > 0)
+ {
+ //TODO THEY HAVE FIXED THIS IN DEV
+ // The JSON is different for some stupid reason
+ // Need to lookup the artist now and all the images -.-"
+ var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri);
+ vm.ArtistName = artist.artistName;
+ vm.ForeignArtistId = artist.foreignArtistId;
+ }
+ else
+ {
+ //vm.ForeignArtistId = a.artistId?.foreignArtistId;
+ //vm.ArtistName = a.artist?.artistName;
+ }
+
+ vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.Replace("http", "https");
+ if (vm.Cover.IsNullOrEmpty())
+ {
+ //vm.Cover = a.remoteCover;
+ }
+
+ await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
+
+ await RunSearchRules(vm);
+
+ return vm;
+ }
+
private async Task MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings)
{
var vm = new SearchAlbumViewModel
@@ -152,7 +206,8 @@ namespace Ombi.Core.Engine
Rating = a.ratings?.value ?? 0m,
ReleaseDate = a.releaseDate,
Title = a.title,
- Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url
+ Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url?.Replace("http", "https"),
+ Genres = a.genres
};
if (a.artistId > 0)
{
@@ -169,7 +224,7 @@ namespace Ombi.Core.Engine
vm.ArtistName = a.artist?.artistName;
}
- vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url;
+ vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.Replace("http", "https");
if (vm.Cover.IsNullOrEmpty())
{
vm.Cover = a.remoteCover;
diff --git a/src/Ombi.Core/Engine/RequestEngineResult.cs b/src/Ombi.Core/Engine/RequestEngineResult.cs
index 1dc78d4b4..08f6d5129 100644
--- a/src/Ombi.Core/Engine/RequestEngineResult.cs
+++ b/src/Ombi.Core/Engine/RequestEngineResult.cs
@@ -6,5 +6,6 @@
public string Message { get; set; }
public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
public string ErrorMessage { get; set; }
+ public int RequestId { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs
index 7994b23aa..796e2fe6a 100644
--- a/src/Ombi.Core/Engine/TvRequestEngine.cs
+++ b/src/Ombi.Core/Engine/TvRequestEngine.cs
@@ -31,14 +31,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 +45,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 +82,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)
{
@@ -116,6 +112,7 @@ namespace Ombi.Core.Engine
}
// Remove the ID since this is a new child
+ // This was a TVDBID for the request rules to run
tvBuilder.ChildRequest.Id = 0;
if (!tvBuilder.ChildRequest.SeasonRequests.Any())
{
@@ -350,7 +347,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);
@@ -384,6 +380,7 @@ namespace Ombi.Core.Engine
foreach (var ep in s.Episodes)
{
ep.Approved = true;
+ ep.Requested = true;
}
}
@@ -392,7 +389,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);
}
@@ -402,7 +398,7 @@ namespace Ombi.Core.Engine
};
}
- public async Task DenyChildRequest(int requestId)
+ public async Task DenyChildRequest(int requestId, string reason)
{
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
if (request == null)
@@ -413,6 +409,7 @@ namespace Ombi.Core.Engine
};
}
request.Denied = true;
+ request.DeniedReason = reason;
await TvRepository.UpdateChild(request);
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
return new RequestEngineResult
@@ -423,9 +420,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;
}
@@ -443,16 +438,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)
@@ -604,15 +597,16 @@ namespace Ombi.Core.Engine
var result = await TvSender.Send(model);
if (result.Success)
{
- return new RequestEngineResult { Result = true };
+ return new RequestEngineResult { Result = true, RequestId = model.Id};
}
return new RequestEngineResult
{
- ErrorMessage = result.Message
+ ErrorMessage = result.Message,
+ RequestId = model.Id
};
}
- return new RequestEngineResult { Result = true };
+ return new RequestEngineResult { Result = true, RequestId = model.Id };
}
public async Task GetRemainingRequests(OmbiUser user)
diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs
index bb674a35d..3a1fead18 100644
--- a/src/Ombi.Core/Engine/TvSearchEngine.cs
+++ b/src/Ombi.Core/Engine/TvSearchEngine.cs
@@ -40,8 +40,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; }
@@ -61,7 +61,7 @@ namespace Ombi.Core.Engine
{
continue;
}
- retVal.Add(await ProcessResult(tvMazeSearch));
+ retVal.Add(ProcessResult(tvMazeSearch));
}
return retVal;
}
@@ -99,7 +99,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 +112,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,
});
}
@@ -123,7 +123,7 @@ namespace Ombi.Core.Engine
public async Task> Popular()
{
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
+ var processed = ProcessResults(result);
return processed;
}
@@ -131,35 +131,35 @@ namespace Ombi.Core.Engine
{
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
+ var processed = ProcessResults(result);
return processed;
}
public async Task> MostWatches()
{
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
+ var processed = ProcessResults(result);
return processed;
}
public async Task> Trending()
{
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
+ var processed = ProcessResults(result);
return processed;
}
- private async Task> ProcessResults(IEnumerable items)
+ protected IEnumerable ProcessResults(IEnumerable items)
{
var retVal = new List();
foreach (var tvMazeSearch in items)
{
- retVal.Add(await ProcessResult(tvMazeSearch));
+ retVal.Add(ProcessResult(tvMazeSearch));
}
return retVal;
}
- private async Task ProcessResult(T tvMazeSearch)
+ protected SearchTvShowViewModel ProcessResult(T tvMazeSearch)
{
return Mapper.Map(tvMazeSearch);
}
diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs
new file mode 100644
index 000000000..5c73e03d9
--- /dev/null
+++ b/src/Ombi.Core/Engine/VoteEngine.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Ombi.Core.Authentication;
+using Ombi.Core.Engine.Interfaces;
+using Ombi.Core.Models;
+using Ombi.Core.Models.UI;
+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
+{
+ public class VoteEngine : BaseEngine, IVoteEngine
+ {
+ public VoteEngine(IRepository votes, IPrincipal user, OmbiUserManager um, IRuleEvaluator r, ISettingsService voteSettings,
+ IMusicRequestEngine musicRequestEngine, ITvRequestEngine tvRequestEngine, IMovieRequestEngine movieRequestEngine) : base(user, um, r)
+ {
+ _voteRepository = votes;
+ _voteSettings = voteSettings;
+ _movieRequestEngine = movieRequestEngine;
+ _musicRequestEngine = musicRequestEngine;
+ _tvRequestEngine = tvRequestEngine;
+ }
+
+ private readonly IRepository _voteRepository;
+ private readonly ISettingsService _voteSettings;
+ private readonly IMusicRequestEngine _musicRequestEngine;
+ private readonly ITvRequestEngine _tvRequestEngine;
+ private readonly IMovieRequestEngine _movieRequestEngine;
+
+ public async Task> GetMovieViewModel()
+ {
+ var vm = new List();
+ var movieRequests = await _movieRequestEngine.GetRequests();
+ var tvRequestsTask = _tvRequestEngine.GetRequests();
+ var musicRequestsTask = _musicRequestEngine.GetRequests();
+ var user = await GetUser();
+ foreach (var r in movieRequests)
+ {
+ if (r.Available || r.Approved || (r.Denied ?? false))
+ {
+ continue;
+ }
+ // Make model
+ var votes = GetVotes(r.Id, RequestType.Movie);
+ var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
+ var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
+ var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
+
+ vm.Add(new VoteViewModel
+ {
+ Upvotes = upVotes,
+ Downvotes = downVotes,
+ RequestId = r.Id,
+ RequestType = RequestType.Movie,
+ Title = r.Title,
+ Image = $"https://image.tmdb.org/t/p/w500/{r.PosterPath}",
+ Background = $"https://image.tmdb.org/t/p/w1280{r.Background}",
+ Description = r.Overview,
+ AlreadyVoted = myVote != null,
+ MyVote = myVote?.VoteType ?? VoteType.Downvote
+ });
+ }
+
+ foreach (var r in await musicRequestsTask)
+ {
+ if (r.Available || r.Approved || (r.Denied ?? false))
+ {
+ continue;
+ }
+ // Make model
+ var votes = GetVotes(r.Id, RequestType.Album);
+ var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
+ var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
+ var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
+ vm.Add(new VoteViewModel
+ {
+ Upvotes = upVotes,
+ Downvotes = downVotes,
+ RequestId = r.Id,
+ RequestType = RequestType.Album,
+ Title = r.Title,
+ Image = r.Cover,
+ Background = r.Cover,
+ Description = r.ArtistName,
+ AlreadyVoted = myVote != null,
+ MyVote = myVote?.VoteType ?? VoteType.Downvote
+ });
+ }
+
+ foreach (var r in await tvRequestsTask)
+ {
+
+ foreach (var childRequests in r.ChildRequests)
+ {
+ var finalsb = new StringBuilder();
+ if (childRequests.Available || childRequests.Approved || (childRequests.Denied ?? false))
+ {
+ continue;
+ }
+ var votes = GetVotes(childRequests.Id, RequestType.TvShow);
+ // Make model
+ var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
+ var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
+ var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
+ foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber))
+ {
+ var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList();
+ var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber));
+ finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}");
+ finalsb.Append("
");
+ }
+ vm.Add(new VoteViewModel
+ {
+ Upvotes = upVotes,
+ Downvotes = downVotes,
+ RequestId = childRequests.Id,
+ RequestType = RequestType.TvShow,
+ Title = r.Title,
+ Image = r.PosterPath,
+ Background = r.Background,
+ Description = finalsb.ToString(),
+ AlreadyVoted = myVote != null,
+ MyVote = myVote?.VoteType ?? VoteType.Downvote
+ });
+ }
+ }
+
+ return vm;
+ }
+
+ public IQueryable GetVotes(int requestId, RequestType requestType)
+ {
+ return _voteRepository.GetAll().Where(x => x.RequestType == requestType && requestId == x.RequestId);
+ }
+
+ public Task GetVoteForUser(int requestId, string userId)
+ {
+ return _voteRepository.GetAll().FirstOrDefaultAsync(x => x.RequestId == requestId && x.UserId == userId);
+ }
+
+ public async Task UpVote(int requestId, RequestType requestType)
+ {
+ var voteSettings = await _voteSettings.GetSettingsAsync();
+ if (!voteSettings.Enabled)
+ {
+ return new VoteEngineResult {Result = true};
+ }
+ // How many votes does this have?!
+ var currentVotes = GetVotes(requestId, requestType);
+
+ var user = await GetUser();
+
+ // Does this user have a downvote? If so we should revert it and make it an upvote
+ var currentVote = await GetVoteForUser(requestId, user.Id);
+ if (currentVote != null && currentVote.VoteType == VoteType.Upvote)
+ {
+ return new VoteEngineResult { ErrorMessage = "You have already voted!" };
+ }
+ await RemoveCurrentVote(currentVote);
+ await _movieRequestEngine.SubscribeToRequest(requestId, requestType);
+
+ await _voteRepository.Add(new Votes
+ {
+ Date = DateTime.UtcNow,
+ RequestId = requestId,
+ RequestType = requestType,
+ UserId = user.Id,
+ VoteType = VoteType.Upvote
+ });
+
+ var upVotes = await currentVotes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
+ var downVotes = -(await currentVotes.Where(x => x.VoteType == VoteType.Downvote).CountAsync());
+
+ var totalVotes = upVotes + downVotes;
+ RequestEngineResult result = null;
+ switch (requestType)
+ {
+ case RequestType.TvShow:
+ if (totalVotes >= voteSettings.TvShowVoteMax)
+ {
+ result = await _tvRequestEngine.ApproveChildRequest(requestId);
+ }
+ break;
+ case RequestType.Movie:
+ if (totalVotes >= voteSettings.MovieVoteMax)
+ {
+ result = await _movieRequestEngine.ApproveMovieById(requestId);
+ }
+ break;
+ case RequestType.Album:
+ if (totalVotes >= voteSettings.MusicVoteMax)
+ {
+ result = await _musicRequestEngine.ApproveAlbumById(requestId);
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(requestType), requestType, null);
+ }
+
+ if (result != null && !result.Result)
+ {
+ return new VoteEngineResult
+ {
+ ErrorMessage = "Voted succesfully but could not approve!"
+ };
+ }
+
+ return new VoteEngineResult
+ {
+ Result = true
+ };
+ }
+
+ public async Task DownVote(int requestId, RequestType requestType)
+ {
+ var voteSettings = await _voteSettings.GetSettingsAsync();
+ if (!voteSettings.Enabled)
+ {
+ return new VoteEngineResult { Result = true };
+ }
+ var user = await GetUser();
+ var currentVote = await GetVoteForUser(requestId, user.Id);
+ if (currentVote != null && currentVote.VoteType == VoteType.Downvote)
+ {
+ return new VoteEngineResult { ErrorMessage = "You have already voted!" };
+ }
+ await RemoveCurrentVote(currentVote);
+
+ await _movieRequestEngine.UnSubscribeRequest(requestId, requestType);
+
+ await _voteRepository.Add(new Votes
+ {
+ Date = DateTime.UtcNow,
+ RequestId = requestId,
+ RequestType = requestType,
+ UserId = user.Id,
+ VoteType = VoteType.Downvote
+ });
+
+ return new VoteEngineResult
+ {
+ Result = true
+ };
+ }
+
+ public async Task RemoveCurrentVote(Votes currentVote)
+ {
+ if (currentVote != null)
+ {
+ await _voteRepository.Delete(currentVote);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs
index e5277e236..56d227ad0 100644
--- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs
+++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs
@@ -41,7 +41,7 @@ namespace Ombi.Core.Helpers
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
Results = await MovieDbApi.SearchTv(ShowInfo.name);
foreach (TvSearchResult result in Results) {
- if (result.Name == ShowInfo.name)
+ if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase))
{
var showIds = await MovieDbApi.GetTvExternals(result.Id);
ShowInfo.externals.imdb = showIds.imdb_id;
@@ -64,14 +64,16 @@ namespace Ombi.Core.Helpers
{
ChildRequest = new ChildRequests
{
- Id = model.TvDbId,
+ Id = model.TvDbId, // This is set to 0 after the request rules have run, the request rules needs it to identify the request
RequestType = RequestType.TvShow,
RequestedDate = DateTime.UtcNow,
Approved = false,
RequestedUserId = userId,
SeasonRequests = new List(),
Title = ShowInfo.name,
- SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.OrdinalIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
+ ReleaseYear = FirstAir,
+ RequestedByAlias = model.RequestedByAlias,
+ SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
};
return this;
diff --git a/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs b/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs
index c67e138e1..5a79d2982 100644
--- a/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs
+++ b/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs
@@ -24,10 +24,20 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
+
+using Newtonsoft.Json;
+
namespace Ombi.Core.Models.Requests
{
public class MovieRequestViewModel
{
public int TheMovieDbId { get; set; }
+ public string LanguageCode { get; set; } = "en";
+
+ ///
+ /// This is only set from a HTTP Header
+ ///
+ [JsonIgnore]
+ public string RequestedByAlias { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs b/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs
index 4c3426c9e..cd799406b 100644
--- a/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs
+++ b/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs
@@ -3,5 +3,6 @@
public class MusicAlbumRequestViewModel
{
public string ForeignAlbumId { get; set; }
+ public string RequestedByAlias { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs
index 78f9edd6d..c17925b1b 100644
--- a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs
+++ b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using Newtonsoft.Json;
namespace Ombi.Core.Models.Requests
{
@@ -9,6 +10,8 @@ namespace Ombi.Core.Models.Requests
public bool FirstSeason { get; set; }
public int TvDbId { get; set; }
public List Seasons { get; set; } = new List();
+ [JsonIgnore]
+ public string RequestedByAlias { get; set; }
}
public class SeasonsViewModel
diff --git a/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs b/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
index a494a3cb5..e9f9c5f9e 100644
--- a/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
+++ b/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
@@ -16,8 +16,13 @@ namespace Ombi.Core.Models.Search
public string Cover { get; set; }
public string Disk { get; set; }
public decimal PercentOfTracks { get; set; }
+ public object[] Genres { get; set; }
public override RequestType Type => RequestType.Album;
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;
public bool FullyAvailable => PercentOfTracks == 100;
+
+
+ // Below is from the INFO call NEED A SEPERATE VM FOR THIS IN V4 TODO
+ // TODO ADD TRACK COUNT
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Models/UI/GotifyNotificationViewModel.cs b/src/Ombi.Core/Models/UI/GotifyNotificationViewModel.cs
new file mode 100644
index 000000000..93ce66724
--- /dev/null
+++ b/src/Ombi.Core/Models/UI/GotifyNotificationViewModel.cs
@@ -0,0 +1,23 @@
+
+using System.Collections.Generic;
+using Ombi.Settings.Settings.Models.Notifications;
+using Ombi.Store.Entities;
+
+namespace Ombi.Core.Models.UI
+{
+ ///
+ /// The view model for the notification settings page
+ ///
+ ///
+ public class GotifyNotificationViewModel : GotifySettings
+ {
+ ///
+ /// Gets or sets the notification templates.
+ ///
+ ///
+ /// The notification templates.
+ ///
+ public List NotificationTemplates { get; set; }
+
+ }
+}
diff --git a/src/Ombi.Core/Models/UI/VoteViewModel.cs b/src/Ombi.Core/Models/UI/VoteViewModel.cs
new file mode 100644
index 000000000..f58db3dbc
--- /dev/null
+++ b/src/Ombi.Core/Models/UI/VoteViewModel.cs
@@ -0,0 +1,18 @@
+using Ombi.Store.Entities;
+
+namespace Ombi.Core.Models.UI
+{
+ public class VoteViewModel
+ {
+ public int RequestId { get; set; }
+ public RequestType RequestType { get; set; }
+ public string Image { get; set; }
+ public string Background { get; set; }
+ public int Upvotes { get; set; }
+ public int Downvotes { get; set; }
+ public string Title { get; set; }
+ public string Description { get; set; }
+ public bool AlreadyVoted { get; set; }
+ public VoteType MyVote { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Models/VoteEngineResult.cs b/src/Ombi.Core/Models/VoteEngineResult.cs
new file mode 100644
index 000000000..7546b0bb2
--- /dev/null
+++ b/src/Ombi.Core/Models/VoteEngineResult.cs
@@ -0,0 +1,10 @@
+namespace Ombi.Core.Models
+{
+ public class VoteEngineResult
+ {
+ public bool Result { get; set; }
+ public string Message { get; set; }
+ public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
+ public string ErrorMessage { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj
index 10e07822a..790aa8500 100644
--- a/src/Ombi.Core/Ombi.Core.csproj
+++ b/src/Ombi.Core/Ombi.Core.csproj
@@ -11,25 +11,27 @@
-
-
-
+
+
+
-
+
+
+
+
-
diff --git a/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs b/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs
index a55868db8..685f02b54 100644
--- a/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs
@@ -1,5 +1,7 @@
using System.Security.Principal;
using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Ombi.Core.Authentication;
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers;
@@ -10,28 +12,31 @@ namespace Ombi.Core.Rule.Rules.Request
{
public class AutoApproveRule : BaseRequestRule, IRules
{
- public AutoApproveRule(IPrincipal principal)
+ public AutoApproveRule(IPrincipal principal, OmbiUserManager um)
{
User = principal;
+ _manager = um;
}
private IPrincipal User { get; }
+ private readonly OmbiUserManager _manager;
- public Task Execute(BaseRequest obj)
+ public async Task Execute(BaseRequest obj)
{
- if (User.IsInRole(OmbiRoles.Admin))
+ var user = await _manager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
+ if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin))
{
obj.Approved = true;
- return Task.FromResult(Success());
+ return Success();
}
- if (obj.RequestType == RequestType.Movie && User.IsInRole(OmbiRoles.AutoApproveMovie))
+ if (obj.RequestType == RequestType.Movie && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
obj.Approved = true;
- if (obj.RequestType == RequestType.TvShow && User.IsInRole(OmbiRoles.AutoApproveTv))
+ if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
obj.Approved = true;
- if (obj.RequestType == RequestType.Album && User.IsInRole(OmbiRoles.AutoApproveMusic))
+ if (obj.RequestType == RequestType.Album && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))
obj.Approved = true;
- return Task.FromResult(Success()); // We don't really care, we just don't set the obj to approve
+ return Success(); // We don't really care, we just don't set the obj to approve
}
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs
index 1cdf03955..a2c70fcc5 100644
--- a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs
@@ -1,46 +1,52 @@
-using Ombi.Store.Entities;
+using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Ombi.Core.Authentication;
using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers;
+using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
-namespace Ombi.Core.Rule.Rules
+namespace Ombi.Core.Rule.Rules.Request
{
public class CanRequestRule : BaseRequestRule, IRules
{
- public CanRequestRule(IPrincipal principal)
+ public CanRequestRule(IPrincipal principal, OmbiUserManager manager)
{
User = principal;
+ _manager = manager;
}
private IPrincipal User { get; }
+ private readonly OmbiUserManager _manager;
- public Task Execute(BaseRequest obj)
+ public async Task Execute(BaseRequest obj)
{
- if (User.IsInRole(OmbiRoles.Admin))
- return Task.FromResult(Success());
+ var user = await _manager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
+ if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin))
+ return Success();
if (obj.RequestType == RequestType.Movie)
{
- if (User.IsInRole(OmbiRoles.RequestMovie) || User.IsInRole(OmbiRoles.AutoApproveMovie))
- return Task.FromResult(Success());
- return Task.FromResult(Fail("You do not have permissions to Request a Movie"));
+ if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMovie) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
+ return Success();
+ return Fail("You do not have permissions to Request a Movie");
}
if (obj.RequestType == RequestType.TvShow)
{
- if (User.IsInRole(OmbiRoles.RequestTv) || User.IsInRole(OmbiRoles.AutoApproveTv))
- return Task.FromResult(Success());
+ if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestTv) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
+ return Success();
}
if (obj.RequestType == RequestType.Album)
{
- if (User.IsInRole(OmbiRoles.RequestMusic) || User.IsInRole(OmbiRoles.AutoApproveMusic))
- return Task.FromResult(Success());
+ if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMusic) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))
+ return Success();
}
- return Task.FromResult(Fail("You do not have permissions to Request a TV Show"));
+ return Fail("You do not have permissions to Request a TV Show");
}
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs
new file mode 100644
index 000000000..5d7658c83
--- /dev/null
+++ b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Ombi.Core.Rule.Interfaces;
+using Ombi.Store.Entities;
+using Ombi.Store.Entities.Requests;
+using Ombi.Store.Repository;
+
+namespace Ombi.Core.Rule.Rules.Request
+{
+ public class ExistingPlexRequestRule : BaseRequestRule, IRules
+ {
+ public ExistingPlexRequestRule(IPlexContentRepository rv)
+ {
+ _plexContent = rv;
+ }
+
+ private readonly IPlexContentRepository _plexContent;
+
+ ///
+ /// We check if the request exists, if it does then we don't want to re-request it.
+ ///
+ /// The object.
+ ///
+ public async Task Execute(BaseRequest obj)
+ {
+ if (obj.RequestType == RequestType.TvShow)
+ {
+ var tvRequest = (ChildRequests) obj;
+
+ var tvContent = _plexContent.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Show);
+ // We need to do a check on the TVDBId
+ var anyTvDbMatches = await tvContent.Include(x => x.Episodes).FirstOrDefaultAsync(x => x.HasTvDb && x.TvDbId.Equals(tvRequest.Id.ToString())); // the Id on the child is the tvdbid at this point
+ if (anyTvDbMatches == null)
+ {
+ // So we do not have a TVDB Id, that really sucks.
+ // Let's try and match on the title and year of the show
+ var titleAndYearMatch = await tvContent.Include(x=> x.Episodes).FirstOrDefaultAsync(x =>
+ x.Title.Equals(tvRequest.Title, StringComparison.InvariantCultureIgnoreCase)
+ && x.ReleaseYear == tvRequest.ReleaseYear.Year.ToString());
+ if (titleAndYearMatch != null)
+ {
+ // We have a match! Suprise Motherfucker
+ return CheckExistingContent(tvRequest, titleAndYearMatch);
+ }
+
+ // We do not have this
+ return Success();
+ }
+ // looks like we have a match on the TVDbID
+ return CheckExistingContent(tvRequest, anyTvDbMatches);
+ }
+ return Success();
+ }
+
+
+ private RuleResult CheckExistingContent(ChildRequests child, PlexServerContent content)
+ {
+ foreach (var season in child.SeasonRequests)
+ {
+ var currentSeasonRequest =
+ content.Episodes.Where(x => x.SeasonNumber == season.SeasonNumber).ToList();
+ if (!currentSeasonRequest.Any())
+ {
+ continue;
+ }
+ foreach (var e in season.Episodes)
+ {
+ var hasEpisode = currentSeasonRequest.Any(x => x.EpisodeNumber == e.EpisodeNumber);
+ if (hasEpisode)
+ {
+ return Fail($"We already have episodes requested from series {child.Title}");
+ }
+ }
+ }
+
+ return Success();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs
new file mode 100644
index 000000000..9942ece63
--- /dev/null
+++ b/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs
@@ -0,0 +1,57 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Ombi.Core.Rule.Interfaces;
+using Ombi.Store.Entities;
+using Ombi.Store.Entities.Requests;
+using Ombi.Store.Repository.Requests;
+
+namespace Ombi.Core.Rule.Rules.Request
+{
+ public class ExistingTvRequestRule : BaseRequestRule, IRules
+ {
+ public ExistingTvRequestRule(ITvRequestRepository rv)
+ {
+ Tv = rv;
+ }
+
+ private ITvRequestRepository Tv { get; }
+
+ ///
+ /// We check if the request exists, if it does then we don't want to re-request it.
+ ///
+ /// The object.
+ ///
+ public async Task Execute(BaseRequest obj)
+ {
+ if (obj.RequestType == RequestType.TvShow)
+ {
+ var tv = (ChildRequests) obj;
+ var tvRequests = Tv.GetChild();
+ var currentRequest = await tvRequests.FirstOrDefaultAsync(x => x.ParentRequest.TvDbId == tv.Id); // the Id on the child is the tvdbid at this point
+ if (currentRequest == null)
+ {
+ return Success();
+ }
+ foreach (var season in tv.SeasonRequests)
+ {
+ var currentSeasonRequest =
+ currentRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
+ if (currentSeasonRequest == null)
+ {
+ continue;
+ }
+ foreach (var e in season.Episodes)
+ {
+ var hasEpisode = currentSeasonRequest.Episodes.Any(x => x.EpisodeNumber == e.EpisodeNumber);
+ if (hasEpisode)
+ {
+ return Fail($"We already have episodes requested from series {tv.Title}");
+ }
+ }
+ }
+ }
+ return Success();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Rule/Rules/Request/SonarrCacheRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/SonarrCacheRequestRule.cs
index 65750a64b..625407f3c 100644
--- a/src/Ombi.Core/Rule/Rules/Request/SonarrCacheRequestRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Request/SonarrCacheRequestRule.cs
@@ -7,12 +7,12 @@ namespace Ombi.Core.Rule.Rules.Request
{
public class SonarrCacheRequestRule : BaseRequestRule, IRules
{
- public SonarrCacheRequestRule(IOmbiContext ctx)
+ public SonarrCacheRequestRule(IExternalContext ctx)
{
_ctx = ctx;
}
- private readonly IOmbiContext _ctx;
+ private readonly IExternalContext _ctx;
public Task Execute(BaseRequest obj)
{
diff --git a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs
new file mode 100644
index 000000000..ab1a0af98
--- /dev/null
+++ b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using Ombi.Core.Models.Search;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository.Requests;
+
+namespace Ombi.Core.Rule.Rules.Search
+{
+ public static class AvailabilityRuleHelper
+ {
+ public static void CheckForUnairedEpisodes(SearchTvShowViewModel search)
+ {
+ if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
+ {
+ search.FullyAvailable = true;
+ }
+ else
+ {
+ var airedButNotAvailable = search.SeasonRequests.Any(x =>
+ x.Episodes.Any(c => !c.Available && c.AirDate <= DateTime.Now.Date && c.AirDate != DateTime.MinValue));
+ if (!airedButNotAvailable)
+ {
+ var unairedEpisodes = search.SeasonRequests.Any(x =>
+ x.Episodes.Any(c => !c.Available && c.AirDate > DateTime.Now.Date || c.AirDate != DateTime.MinValue));
+ if (unairedEpisodes)
+ {
+ search.FullyAvailable = true;
+ }
+ }
+ }
+ }
+
+ public static async Task SingleEpisodeCheck(bool useImdb, IQueryable allEpisodes, EpisodeRequests episode,
+ SeasonRequests season, PlexServerContent item, bool useTheMovieDb, bool useTvDb, ILogger log)
+ {
+ PlexEpisode epExists = null;
+ try
+ {
+
+ if (useImdb)
+ {
+ epExists = await allEpisodes.FirstOrDefaultAsync(x =>
+ x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
+ x.Series.ImdbId == item.ImdbId);
+ }
+
+ if (useTheMovieDb)
+ {
+ epExists = await allEpisodes.FirstOrDefaultAsync(x =>
+ x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
+ x.Series.TheMovieDbId == item.TheMovieDbId);
+ }
+
+ if (useTvDb)
+ {
+ epExists = await allEpisodes.FirstOrDefaultAsync(x =>
+ x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
+ x.Series.TvDbId == item.TvDbId);
+ }
+ }
+ catch (Exception e)
+ {
+ log.LogError(e, "Exception thrown when attempting to check if something is available");
+ }
+
+ if (epExists != null)
+ {
+ episode.Available = true;
+ }
+ }
+ public static async Task SingleEpisodeCheck(bool useImdb, IQueryable allEpisodes, EpisodeRequests episode,
+ SeasonRequests season, EmbyContent item, bool useTheMovieDb, bool useTvDb)
+ {
+ EmbyEpisode epExists = null;
+ if (useImdb)
+ {
+ epExists = await allEpisodes.FirstOrDefaultAsync(x =>
+ x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
+ x.Series.ImdbId == item.ImdbId);
+ }
+
+ if (useTheMovieDb)
+ {
+ epExists = await allEpisodes.FirstOrDefaultAsync(x =>
+ x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
+ x.Series.TheMovieDbId == item.TheMovieDbId);
+ }
+
+ if (useTvDb)
+ {
+ epExists = await allEpisodes.FirstOrDefaultAsync(x =>
+ x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
+ x.Series.TvDbId == item.TvDbId);
+ }
+
+ if (epExists != null)
+ {
+ episode.Available = true;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Rule/Rules/Search/CouchPotatoCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/CouchPotatoCacheRule.cs
index a6deb95d2..277266b91 100644
--- a/src/Ombi.Core/Rule/Rules/Search/CouchPotatoCacheRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Search/CouchPotatoCacheRule.cs
@@ -11,12 +11,12 @@ namespace Ombi.Core.Rule.Rules.Search
{
public class CouchPotatoCacheRule : BaseSearchRule, IRules
{
- public CouchPotatoCacheRule(IRepository ctx)
+ public CouchPotatoCacheRule(IExternalRepository ctx)
{
_ctx = ctx;
}
- private readonly IRepository _ctx;
+ private readonly IExternalRepository _ctx;
public async Task Execute(SearchViewModel obj)
{
diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs
index 486de9ea8..f80bded7a 100644
--- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs
@@ -1,10 +1,10 @@
-using System;
-using System.Linq;
-using System.Linq.Expressions;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
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.Store.Entities;
using Ombi.Store.Repository;
@@ -13,25 +13,39 @@ namespace Ombi.Core.Rule.Rules.Search
{
public class EmbyAvailabilityRule : BaseSearchRule, IRules
{
- public EmbyAvailabilityRule(IEmbyContentRepository repo)
+ public EmbyAvailabilityRule(IEmbyContentRepository repo, ISettingsService s)
{
EmbyContentRepository = repo;
+ EmbySettings = s;
}
private IEmbyContentRepository EmbyContentRepository { get; }
+ private ISettingsService EmbySettings { get; }
public async Task Execute(SearchViewModel obj)
{
EmbyContent item = null;
+ var useImdb = false;
+ var useTheMovieDb = false;
+ var useTvDb = false;
+
if (obj.ImdbId.HasValue())
{
item = await EmbyContentRepository.GetByImdbId(obj.ImdbId);
+ if (item != null)
+ {
+ useImdb = true;
+ }
}
if (item == null)
{
if (obj.TheMovieDbId.HasValue())
{
item = await EmbyContentRepository.GetByTheMovieDbId(obj.TheMovieDbId);
+ if (item != null)
+ {
+ useTheMovieDb = true;
+ }
}
if (item == null)
@@ -39,14 +53,27 @@ namespace Ombi.Core.Rule.Rules.Search
if (obj.TheTvDbId.HasValue())
{
item = await EmbyContentRepository.GetByTvDbId(obj.TheTvDbId);
+ if (item != null)
+ {
+ useTvDb = true;
+ }
}
}
}
-
+
if (item != null)
{
obj.Available = true;
- obj.EmbyUrl = item.Url;
+ var s = await EmbySettings.GetSettingsAsync();
+ var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
+ if ((server?.ServerHostname ?? string.Empty).HasValue())
+ {
+ obj.EmbyUrl = $"{server.ServerHostname}#!/itemdetails.html?id={item.EmbyId}";
+ }
+ else
+ {
+ obj.EmbyUrl = $"https://app.emby.media/#!/itemdetails.html?id={item.EmbyId}";
+ }
if (obj.Type == RequestType.TvShow)
{
@@ -59,29 +86,12 @@ namespace Ombi.Core.Rule.Rules.Search
{
foreach (var episode in season.Episodes)
{
- EmbyEpisode epExists = null;
-
- if (item.HasImdb)
- {
- epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
- && e.ImdbId == item.ImdbId);
- } if (item.HasTvDb && epExists == null)
- {
- epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
- && e.Series.TvDbId == item.TvDbId);
- } if (item.HasTheMovieDb && epExists == null)
- {
- epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
- && e.TheMovieDbId == item.TheMovieDbId);
- }
-
- if (epExists != null)
- {
- episode.Available = true;
- }
+ await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb);
}
}
}
+
+ AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
}
}
return Success();
diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs
index 965fcdfaf..2d4482ba9 100644
--- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Models.Search;
@@ -87,11 +88,11 @@ namespace Ombi.Core.Rule.Rules.Search
}
}
- if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
+ if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Available && e.AirDate > DateTime.MinValue)))
{
request.FullyAvailable = true;
}
- if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available)))
+ if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available && e.AirDate > DateTime.MinValue)))
{
request.PartlyAvailable = true;
}
diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs
index 97a27d47f..fe28c3acf 100644
--- a/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs
+++ b/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs
@@ -10,12 +10,12 @@ namespace Ombi.Core.Rule.Rules.Search
{
public class LidarrAlbumCacheRule : SpecificRule, ISpecificRule