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) + + ___ Get it on Google Play @@ -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 { - public LidarrAlbumCacheRule(IRepository db) + public LidarrAlbumCacheRule(IExternalRepository db) { _db = db; } - private readonly IRepository _db; + private readonly IExternalRepository _db; public Task Execute(object objec) { diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs index db472a951..d9667d66b 100644 --- a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs @@ -10,12 +10,12 @@ namespace Ombi.Core.Rule.Rules.Search { public class LidarrArtistCacheRule : SpecificRule, ISpecificRule { - public LidarrArtistCacheRule(IRepository db) + public LidarrArtistCacheRule(IExternalRepository db) { _db = db; } - private readonly IRepository _db; + private readonly IExternalRepository _db; public Task Execute(object objec) { diff --git a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs index 016a2817c..2a239d1d3 100644 --- a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs @@ -1,6 +1,6 @@ using System.Linq; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; using Ombi.Helpers; @@ -11,12 +11,14 @@ namespace Ombi.Core.Rule.Rules.Search { public class PlexAvailabilityRule : BaseSearchRule, IRules { - public PlexAvailabilityRule(IPlexContentRepository repo) + public PlexAvailabilityRule(IPlexContentRepository repo, ILogger log) { PlexContentRepository = repo; + Log = log; } private IPlexContentRepository PlexContentRepository { get; } + private ILogger Log { get; } public async Task Execute(SearchViewModel obj) { @@ -73,41 +75,17 @@ namespace Ombi.Core.Rule.Rules.Search { foreach (var episode in season.Episodes) { - PlexEpisode epExists = null; - if (useImdb) - { - epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && - x.Series.ImdbId == item.ImdbId.ToString()); - } - if (useTheMovieDb) - { - epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && - x.Series.TheMovieDbId == item.TheMovieDbId.ToString()); - } - if (useTvDb) - { - epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && - x.Series.TvDbId == item.TvDbId.ToString()); - } - - if (epExists != null) - { - episode.Available = true; - } + await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb, Log); } } - if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available))) - { - search.FullyAvailable = true; - } + AvailabilityRuleHelper.CheckForUnairedEpisodes(search); } } } return Success(); } + + } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs index ae3cb4782..105681c82 100644 --- a/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs @@ -9,12 +9,12 @@ namespace Ombi.Core.Rule.Rules.Search { public class RadarrCacheRule : BaseSearchRule, IRules { - public RadarrCacheRule(IRepository db) + public RadarrCacheRule(IExternalRepository db) { _db = db; } - private readonly IRepository _db; + private readonly IExternalRepository _db; public Task Execute(SearchViewModel obj) { diff --git a/src/Ombi.Core/Rule/Rules/Search/SonarrCacheSearchRule.cs b/src/Ombi.Core/Rule/Rules/Search/SonarrCacheSearchRule.cs index f9c5cd09d..03bdbe091 100644 --- a/src/Ombi.Core/Rule/Rules/Search/SonarrCacheSearchRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/SonarrCacheSearchRule.cs @@ -34,12 +34,12 @@ namespace Ombi.Core.Rule.Rules.Search { public class SonarrCacheSearchRule : BaseSearchRule, IRules { - public SonarrCacheSearchRule(IOmbiContext ctx) + public SonarrCacheSearchRule(IExternalContext ctx) { _ctx = ctx; } - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; public Task Execute(SearchViewModel obj) { diff --git a/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs index c851afb4b..7eac05d56 100644 --- a/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs @@ -10,12 +10,12 @@ namespace Ombi.Core.Rule.Rules { public class SonarrCacheRule { - public SonarrCacheRule(IOmbiContext ctx) + public SonarrCacheRule(IExternalContext ctx) { _ctx = ctx; } - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; public async Task Execute(BaseRequest obj) { diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 8b89ef7bb..567df43b5 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -19,7 +20,7 @@ namespace Ombi.Core.Senders { public MovieSender(ISettingsService radarrSettings, IRadarrApi api, ILogger log, ISettingsService dogSettings, IDogNzbApi dogApi, ISettingsService cpSettings, - ICouchPotatoApi cpApi, IRepository userProfiles) + ICouchPotatoApi cpApi, IRepository userProfiles, IRepository requestQueue, INotificationHelper notify) { RadarrSettings = radarrSettings; RadarrApi = api; @@ -29,6 +30,8 @@ namespace Ombi.Core.Senders CouchPotatoSettings = cpSettings; CouchPotatoApi = cpApi; _userProfiles = userProfiles; + _requestQueuRepository = requestQueue; + _notificationHelper = notify; } private ISettingsService RadarrSettings { get; } @@ -39,38 +42,62 @@ namespace Ombi.Core.Senders private ISettingsService CouchPotatoSettings { get; } private ICouchPotatoApi CouchPotatoApi { get; } private readonly IRepository _userProfiles; + private readonly IRepository _requestQueuRepository; + private readonly INotificationHelper _notificationHelper; public async Task Send(MovieRequests model) { - var cpSettings = await CouchPotatoSettings.GetSettingsAsync(); - //var watcherSettings = await WatcherSettings.GetSettingsAsync(); - var radarrSettings = await RadarrSettings.GetSettingsAsync(); - if (radarrSettings.Enabled) + try { - return await SendToRadarr(model, radarrSettings); - } + var cpSettings = await CouchPotatoSettings.GetSettingsAsync(); + //var watcherSettings = await WatcherSettings.GetSettingsAsync(); + var radarrSettings = await RadarrSettings.GetSettingsAsync(); + if (radarrSettings.Enabled) + { + return await SendToRadarr(model, radarrSettings); + } - var dogSettings = await DogNzbSettings.GetSettingsAsync(); - if (dogSettings.Enabled) - { - await SendToDogNzb(model, dogSettings); - return new SenderResult + var dogSettings = await DogNzbSettings.GetSettingsAsync(); + if (dogSettings.Enabled) { - Success = true, - Sent = true, - }; - } + await SendToDogNzb(model, dogSettings); + return new SenderResult + { + Success = true, + Sent = true, + }; + } - if (cpSettings.Enabled) - { - return await SendToCp(model, cpSettings, cpSettings.DefaultProfileId); + if (cpSettings.Enabled) + { + return await SendToCp(model, cpSettings, cpSettings.DefaultProfileId); + } } + catch (Exception e) + { + Log.LogError(e, "Error when sending movie to DVR app, added to the request queue"); - //if (watcherSettings.Enabled) - //{ - // return SendToWatcher(model, watcherSettings); - //} - + // Check if already in request quee + var existingQueue = await _requestQueuRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id); + if (existingQueue != null) + { + existingQueue.RetryCount++; + existingQueue.Error = e.Message; + await _requestQueuRepository.SaveChangesAsync(); + } + else + { + await _requestQueuRepository.Add(new RequestQueue + { + Dts = DateTime.UtcNow, + Error = e.Message, + RequestId = model.Id, + Type = RequestType.Movie, + RetryCount = 0 + }); + _notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue); + } + } return new SenderResult { @@ -93,21 +120,25 @@ namespace Ombi.Core.Senders private async Task SendToRadarr(MovieRequests model, RadarrSettings settings) { - + var qualityToUse = int.Parse(settings.DefaultQualityProfile); - + var rootFolderPath = settings.DefaultRootPath; var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); if (profiles != null) { - if (profiles.SonarrRootPathAnime > 0) - { - rootFolderPath = await RadarrRootPath(profiles.SonarrRootPathAnime, settings); + if (profiles.RadarrRootPath > 0) + { + var tempPath = await RadarrRootPath(profiles.RadarrRootPath, settings); + if (tempPath.HasValue()) + { + rootFolderPath = tempPath; + } } - if (profiles.SonarrQualityProfileAnime > 0) + if (profiles.RadarrQualityProfile > 0) { - qualityToUse = profiles.SonarrQualityProfileAnime; + qualityToUse = profiles.RadarrQualityProfile; } } @@ -150,7 +181,7 @@ namespace Ombi.Core.Senders // Search for it if (!settings.AddOnly) { - await RadarrApi.MovieSearch(new[] {existingMovie.id}, settings.ApiKey, settings.FullUri); + await RadarrApi.MovieSearch(new[] { existingMovie.id }, settings.ApiKey, settings.FullUri); } return new SenderResult { Success = true, Sent = true }; @@ -163,7 +194,7 @@ namespace Ombi.Core.Senders { var paths = await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); var selectedPath = paths.FirstOrDefault(x => x.id == overrideId); - return selectedPath.path; + return selectedPath?.path ?? String.Empty; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Senders/MusicSender.cs b/src/Ombi.Core/Senders/MusicSender.cs index 60e4ca6ee..0e9db9192 100644 --- a/src/Ombi.Core/Senders/MusicSender.cs +++ b/src/Ombi.Core/Senders/MusicSender.cs @@ -1,37 +1,76 @@ using System; using System.Linq; using System.Threading.Tasks; +using EnsureThat; +using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; using Ombi.Api.Lidarr.Models; -using Ombi.Api.Radarr; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; -using Serilog; +using Ombi.Store.Repository; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Ombi.Core.Senders { public class MusicSender : IMusicSender { - public MusicSender(ISettingsService lidarr, ILidarrApi lidarrApi) + public MusicSender(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, + IRepository requestQueue, INotificationHelper notify) { _lidarrSettings = lidarr; _lidarrApi = lidarrApi; + _log = log; + _requestQueueRepository = requestQueue; + _notificationHelper = notify; } private readonly ISettingsService _lidarrSettings; private readonly ILidarrApi _lidarrApi; + private readonly ILogger _log; + private readonly IRepository _requestQueueRepository; + private readonly INotificationHelper _notificationHelper; public async Task Send(AlbumRequest model) { - var settings = await _lidarrSettings.GetSettingsAsync(); - if (settings.Enabled) + try { - return await SendToLidarr(model, settings); + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await SendToLidarr(model, settings); + } + + return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" }; + } + catch (Exception e) + { + _log.LogError(e, "Exception thrown when sending a music to DVR app, added to the request queue"); + var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id); + if (existingQueue != null) + { + existingQueue.RetryCount++; + existingQueue.Error = e.Message; + await _requestQueueRepository.SaveChangesAsync(); + } + else + { + await _requestQueueRepository.Add(new RequestQueue + { + Dts = DateTime.UtcNow, + Error = e.Message, + RequestId = model.Id, + Type = RequestType.Album, + RetryCount = 0 + }); + _notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue); + } } - return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" }; + + return new SenderResult { Success = false, Sent = false, Message = "Something went wrong!" }; } private async Task SendToLidarr(AlbumRequest model, LidarrSettings settings) @@ -49,6 +88,11 @@ namespace Ombi.Core.Senders if (artist == null || artist.id <= 0) { + EnsureArg.IsNotNullOrEmpty(model.ForeignArtistId, nameof(model.ForeignArtistId)); + EnsureArg.IsNotNullOrEmpty(model.ForeignAlbumId, nameof(model.ForeignAlbumId)); + EnsureArg.IsNotNullOrEmpty(model.ArtistName, nameof(model.ArtistName)); + EnsureArg.IsNotNullOrEmpty(rootFolderPath, nameof(rootFolderPath)); + // Create artist var newArtist = new ArtistAdd { diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index e48e54c1a..9a25ca9c0 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -16,16 +16,18 @@ using Ombi.Settings.Settings.Models.External; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; +using Remotion.Linq.Parsing.Structure.IntermediateModel; namespace Ombi.Core.Senders { public class TvSender : ITvSender { - public TvSender(ISonarrApi sonarrApi, ILogger log, ISettingsService sonarrSettings, + public TvSender(ISonarrApi sonarrApi, ISonarrV3Api sonarrV3Api, ILogger log, ISettingsService sonarrSettings, ISettingsService dog, IDogNzbApi dogApi, ISettingsService srSettings, - ISickRageApi srApi, IRepository userProfiles) + ISickRageApi srApi, IRepository userProfiles, IRepository requestQueue, INotificationHelper notify) { SonarrApi = sonarrApi; + SonarrV3Api = sonarrV3Api; Logger = log; SonarrSettings = sonarrSettings; DogNzbSettings = dog; @@ -33,9 +35,12 @@ namespace Ombi.Core.Senders SickRageSettings = srSettings; SickRageApi = srApi; UserQualityProfiles = userProfiles; + _requestQueueRepository = requestQueue; + _notificationHelper = notify; } private ISonarrApi SonarrApi { get; } + private ISonarrV3Api SonarrV3Api { get; } private IDogNzbApi DogNzbApi { get; } private ISickRageApi SickRageApi { get; } private ILogger Logger { get; } @@ -43,59 +48,94 @@ namespace Ombi.Core.Senders private ISettingsService DogNzbSettings { get; } private ISettingsService SickRageSettings { get; } private IRepository UserQualityProfiles { get; } + private readonly IRepository _requestQueueRepository; + private readonly INotificationHelper _notificationHelper; public async Task Send(ChildRequests model) { - var sonarr = await SonarrSettings.GetSettingsAsync(); - if (sonarr.Enabled) + try { - var result = await SendToSonarr(model); - if (result != null) + var sonarr = await SonarrSettings.GetSettingsAsync(); + if (sonarr.Enabled) + { + var result = await SendToSonarr(model, sonarr); + if (result != null) + { + return new SenderResult + { + Sent = true, + Success = true + }; + } + } + var dog = await DogNzbSettings.GetSettingsAsync(); + if (dog.Enabled) { + var result = await SendToDogNzb(model, dog); + if (!result.Failure) + { + return new SenderResult + { + Sent = true, + Success = true + }; + } return new SenderResult { - Sent = true, - Success = true + Message = result.ErrorMessage }; } - } - var dog = await DogNzbSettings.GetSettingsAsync(); - if (dog.Enabled) - { - var result = await SendToDogNzb(model, dog); - if (!result.Failure) + var sr = await SickRageSettings.GetSettingsAsync(); + if (sr.Enabled) { + var result = await SendToSickRage(model, sr); + if (result) + { + return new SenderResult + { + Sent = true, + Success = true + }; + } return new SenderResult { - Sent = true, - Success = true + Message = "Could not send to SickRage!" }; } return new SenderResult { - Message = result.ErrorMessage + Success = true }; } - var sr = await SickRageSettings.GetSettingsAsync(); - if (sr.Enabled) + catch (Exception e) { - var result = await SendToSickRage(model, sr); - if (result) + Logger.LogError(e, "Exception thrown when sending a movie to DVR app, added to the request queue"); + // Check if already in request queue + var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id); + if (existingQueue != null) { - return new SenderResult - { - Sent = true, - Success = true - }; + existingQueue.RetryCount++; + existingQueue.Error = e.Message; + await _requestQueueRepository.SaveChangesAsync(); } - return new SenderResult + else { - Message = "Could not send to SickRage!" - }; + await _requestQueueRepository.Add(new RequestQueue + { + Dts = DateTime.UtcNow, + Error = e.Message, + RequestId = model.Id, + Type = RequestType.TvShow, + RetryCount = 0 + }); + _notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue); + } } + return new SenderResult { - Success = true + Success = false, + Message = "Something went wrong!" }; } @@ -111,13 +151,8 @@ namespace Ombi.Core.Senders /// /// /// - public async Task SendToSonarr(ChildRequests model) + public async Task SendToSonarr(ChildRequests model, SonarrSettings s) { - var s = await SonarrSettings.GetSettingsAsync(); - if (!s.Enabled) - { - return null; - } if (string.IsNullOrEmpty(s.ApiKey)) { return null; @@ -143,7 +178,7 @@ namespace Ombi.Core.Senders } if (profiles.SonarrQualityProfileAnime > 0) { - qualityToUse = profiles.SonarrQualityProfileAnime; + qualityToUse = profiles.SonarrQualityProfileAnime; } } seriesType = "anime"; @@ -163,7 +198,7 @@ namespace Ombi.Core.Senders } if (profiles.SonarrQualityProfile > 0) { - qualityToUse = profiles.SonarrQualityProfile; + qualityToUse = profiles.SonarrQualityProfile; } } seriesType = "standard"; @@ -174,7 +209,11 @@ namespace Ombi.Core.Senders { qualityToUse = model.ParentRequest.QualityOverride.Value; } - + + // Are we using v3 sonarr? + var sonarrV3 = s.V3; + var languageProfileId = s.LanguageProfile; + try { // Does the series actually exist? @@ -204,6 +243,11 @@ namespace Ombi.Core.Senders } }; + if (sonarrV3) + { + newSeries.languageProfileId = languageProfileId; + } + // Montitor the correct seasons, // If we have that season in the model then it's monitored! var seasonsToAdd = GetSeasonsToCreate(model); @@ -268,13 +312,22 @@ namespace Ombi.Core.Senders } } var seriesChanges = false; - + foreach (var season in model.SeasonRequests) { - var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber); - var sonarrEpCount = sonarrSeason.Count(); + var sonarrEpisodeList = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber).ToList(); + var sonarrEpCount = sonarrEpisodeList.Count; var ourRequestCount = season.Episodes.Count; + var ourEpisodes = season.Episodes.Select(x => x.EpisodeNumber).ToList(); + var unairedEpisodes = sonarrEpisodeList.Where(x => x.airDateUtc > DateTime.UtcNow).Select(x => x.episodeNumber).ToList(); + + //// Check if we have requested all the latest episodes, if we have then monitor + //// NOTE, not sure if needed since ombi ui displays future episodes anyway... + //ourEpisodes.AddRange(unairedEpisodes); + //var distinctEpisodes = ourEpisodes.Distinct().ToList(); + //var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes); + var existingSeason = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); if (existingSeason == null) @@ -284,7 +337,7 @@ namespace Ombi.Core.Senders } - if (sonarrEpCount == ourRequestCount) + if (sonarrEpCount == ourRequestCount /*|| !missingEpisodes.Any()*/) { // We have the same amount of requests as all of the episodes in the season. @@ -300,16 +353,16 @@ namespace Ombi.Core.Senders if (!existingSeason.monitored) { // We need to monitor it, problem being is all episodes will now be monitored - // So we need to monior the series but unmonitor every episode - // Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand) + // So we need to monitor the series but unmonitor every episode + // Except the episodes that are already monitored before we update the series (we do not want to unmonitored episodes that are monitored beforehand) existingSeason.monitored = true; var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber); sea.monitored = true; //var previouslyMonitoredEpisodes = sonarrEpList.Where(x => // x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri); - var epToUnmonitor = new List(); - var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member + var epToUnmonitored = new List(); + var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList()) { //if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber)) @@ -318,10 +371,10 @@ namespace Ombi.Core.Senders // continue; //} ep.monitored = false; - epToUnmonitor.Add(ep); + epToUnmonitored.Add(ep); } - foreach (var epToUpdate in epToUnmonitor) + foreach (var epToUpdate in epToUnmonitored) { await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); } @@ -355,7 +408,7 @@ namespace Ombi.Core.Senders var sea = new Season { seasonNumber = i, - monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0) + monitored = false }; seasonsToUpdate.Add(sea); } diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index c42621281..8eff75dbb 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -32,6 +32,7 @@ using Ombi.Api.CouchPotato; using Ombi.Api.DogNzb; using Ombi.Api.FanartTv; using Ombi.Api.Github; +using Ombi.Api.Gotify; using Ombi.Api.Lidarr; using Ombi.Api.Mattermost; using Ombi.Api.Notifications; @@ -53,6 +54,7 @@ using Ombi.Updater; using PlexContentCacher = Ombi.Schedule.Jobs.Plex; using Ombi.Api.Telegram; using Ombi.Core.Authentication; +using Ombi.Core.Engine.Demo; using Ombi.Core.Processor; using Ombi.Schedule.Jobs.Lidarr; using Ombi.Schedule.Jobs.Plex.Interfaces; @@ -92,6 +94,9 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); } public static void RegisterHttp(this IServiceCollection services) { @@ -107,6 +112,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -116,6 +122,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -128,23 +135,28 @@ namespace Ombi.DependencyInjection } public static void RegisterStore(this IServiceCollection services) { - services.AddEntityFrameworkSqlite().AddDbContext(); + services.AddDbContext(); + services.AddDbContext(); + services.AddDbContext(); services.AddScoped(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddScoped(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 + services.AddScoped(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>)); - services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(typeof(ISettingsService<>), typeof(SettingsService<>)); + services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); + services.AddScoped(typeof(IExternalRepository<>), typeof(ExternalRepository<>)); } public static void RegisterServices(this IServiceCollection services) { @@ -152,7 +164,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); @@ -161,6 +173,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -194,6 +207,8 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); } } } diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 028c37b43..ec905e718 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj new file mode 100644 index 000000000..0517af22d --- /dev/null +++ b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/src/Ombi.Helpers.Tests/PlexHelperTests.cs b/src/Ombi.Helpers.Tests/PlexHelperTests.cs new file mode 100644 index 000000000..8ecb3fa0a --- /dev/null +++ b/src/Ombi.Helpers.Tests/PlexHelperTests.cs @@ -0,0 +1,52 @@ +using System; +using NUnit.Framework; +using System.Collections.Generic; + +namespace Ombi.Helpers.Tests +{ + [TestFixture] + public class PlexHelperTests + { + + [TestCaseSource(nameof(ProviderIdGuidData))] + public string GetProviderIdFromPlexGuidTests(string guidInput, ProviderIdType type) + { + var result = PlexHelper.GetProviderIdFromPlexGuid(guidInput); + + switch (type) + { + case ProviderIdType.Imdb: + Assert.That(result.ImdbId, Is.Not.Null); + return result.ImdbId; + case ProviderIdType.TvDb: + Assert.That(result.TheTvDb, Is.Not.Null); + return result.TheTvDb; + case ProviderIdType.MovieDb: + Assert.That(result.TheMovieDb, Is.Not.Null); + return result.TheMovieDb; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + public static IEnumerable ProviderIdGuidData + { + get + { + yield return new TestCaseData("com.plexapp.agents.thetvdb://269586/2/8?lang=en", ProviderIdType.TvDb).Returns("269586").SetName("Regular TvDb Id"); + yield return new TestCaseData("com.plexapp.agents.themoviedb://390043?lang=en", ProviderIdType.MovieDb).Returns("390043").SetName("Regular MovieDb Id"); + yield return new TestCaseData("com.plexapp.agents.imdb://tt2543164?lang=en", ProviderIdType.Imdb).Returns("tt2543164").SetName("Regular Imdb Id"); + yield return new TestCaseData("com.plexapp.agents.agent47://tt2543456?lang=en", ProviderIdType.Imdb).Returns("tt2543456").SetName("Unknown IMDB agent"); + yield return new TestCaseData("com.plexapp.agents.agent47://456822/1/1?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent"); + yield return new TestCaseData("com.plexapp.agents.agent47://456822/999/999?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent, large episode and season"); + } + } + + public enum ProviderIdType + { + Imdb, + TvDb, + MovieDb + } + } +} \ No newline at end of file diff --git a/src/Ombi.Helpers/CacheService.cs b/src/Ombi.Helpers/CacheService.cs index 4eef62bda..ae57a9f61 100644 --- a/src/Ombi.Helpers/CacheService.cs +++ b/src/Ombi.Helpers/CacheService.cs @@ -28,18 +28,15 @@ namespace Ombi.Helpers return result; } - using (await _mutex.LockAsync()) + if (_memoryCache.TryGetValue(cacheKey, out result)) { - if (_memoryCache.TryGetValue(cacheKey, out result)) - { - return result; - } - - result = await factory(); - _memoryCache.Set(cacheKey, result, absoluteExpiration); - return result; } + + result = await factory(); + _memoryCache.Set(cacheKey, result, absoluteExpiration); + + return result; } public void Remove(string key) @@ -47,34 +44,34 @@ namespace Ombi.Helpers _memoryCache.Remove(key); } - - - public T GetOrAdd(string cacheKey, Func factory, DateTime absoluteExpiration) + + + public T GetOrAdd(string cacheKey, Func factory, DateTime absoluteExpiration) + { + // locks get and set internally + if (_memoryCache.TryGetValue(cacheKey, out var result)) { - // locks get and set internally - if (_memoryCache.TryGetValue(cacheKey, out var result)) + return result; + } + + lock (TypeLock.Lock) + { + if (_memoryCache.TryGetValue(cacheKey, out result)) { return result; } - lock (TypeLock.Lock) - { - if (_memoryCache.TryGetValue(cacheKey, out result)) - { - return result; - } - - result = factory(); - _memoryCache.Set(cacheKey, result, absoluteExpiration); + result = factory(); + _memoryCache.Set(cacheKey, result, absoluteExpiration); - return result; - } + return result; } + } + + private static class TypeLock + { + public static object Lock { get; } = new object(); + } - private static class TypeLock - { - public static object Lock { get; } = new object(); - } - } } diff --git a/src/Ombi.Helpers/DemoLists.cs b/src/Ombi.Helpers/DemoLists.cs new file mode 100644 index 000000000..c0d0dd77f --- /dev/null +++ b/src/Ombi.Helpers/DemoLists.cs @@ -0,0 +1,11 @@ +namespace Ombi.Config +{ + public class DemoLists + { + public bool Enabled { get; set; } + public int[] Movies { get; set; } + public int[] TvShows { get; set; } + } + + +} \ No newline at end of file diff --git a/src/Ombi.Helpers/DemoSingleton.cs b/src/Ombi.Helpers/DemoSingleton.cs new file mode 100644 index 000000000..22b6b2f31 --- /dev/null +++ b/src/Ombi.Helpers/DemoSingleton.cs @@ -0,0 +1,13 @@ +namespace Ombi.Helpers +{ + public class DemoSingleton + { + private static DemoSingleton instance; + + private DemoSingleton() { } + + public static DemoSingleton Instance => instance ?? (instance = new DemoSingleton()); + + public bool Demo { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs index 567bcfe7e..a9967f21f 100644 --- a/src/Ombi.Helpers/EmbyHelper.cs +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -7,11 +7,16 @@ namespace Ombi.Helpers { public class EmbyHelper { - public static string GetEmbyMediaUrl(string mediaId) + public static string GetEmbyMediaUrl(string mediaId, string customerServerUrl = null) { - var url = - $"http://app.emby.media/#!/itemdetails.html?id={mediaId}"; - return url; + if (customerServerUrl.HasValue()) + { + return $"{customerServerUrl}#!/itemdetails.html?id={mediaId}"; + } + else + { + return $"https://app.emby.media/#!/itemdetails.html?id={mediaId}"; + } } } } diff --git a/src/Ombi.Helpers/LoggingEvents.cs b/src/Ombi.Helpers/LoggingEvents.cs index 40ec3fd2b..0723800ab 100644 --- a/src/Ombi.Helpers/LoggingEvents.cs +++ b/src/Ombi.Helpers/LoggingEvents.cs @@ -21,6 +21,7 @@ namespace Ombi.Helpers public static EventId PlexContentCacher => new EventId(2008); public static EventId SickRageCacher => new EventId(2009); public static EventId LidarrArtistCache => new EventId(2010); + public static EventId MediaReferesh => new EventId(2011); public static EventId MovieSender => new EventId(3000); @@ -31,6 +32,7 @@ namespace Ombi.Helpers public static EventId MattermostNotification => new EventId(4004); public static EventId PushoverNotification => new EventId(4005); public static EventId TelegramNotifcation => new EventId(4006); + public static EventId GotifyNotification => new EventId(4007); public static EventId TvSender => new EventId(5000); public static EventId SonarrSender => new EventId(5001); diff --git a/src/Ombi.Helpers/NotificationAgent.cs b/src/Ombi.Helpers/NotificationAgent.cs index 8990eeba9..18f28105a 100644 --- a/src/Ombi.Helpers/NotificationAgent.cs +++ b/src/Ombi.Helpers/NotificationAgent.cs @@ -10,5 +10,6 @@ Slack = 5, Mattermost = 6, Mobile = 7, + Gotify = 8, } } \ No newline at end of file diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 2aaaa076f..5dedaff61 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/src/Ombi.Helpers/OmbiRoles.cs b/src/Ombi.Helpers/OmbiRoles.cs index 1d584d57f..02a480fdf 100644 --- a/src/Ombi.Helpers/OmbiRoles.cs +++ b/src/Ombi.Helpers/OmbiRoles.cs @@ -14,5 +14,7 @@ public const string RequestMusic = nameof(RequestMusic); public const string Disabled = nameof(Disabled); public const string ReceivesNewsletter = nameof(ReceivesNewsletter); + public const string ManageOwnRequests = nameof(ManageOwnRequests); + public const string EditCustomPage = nameof(EditCustomPage); } } \ No newline at end of file diff --git a/src/Ombi.Helpers/PlexHelper.cs b/src/Ombi.Helpers/PlexHelper.cs index 93710022f..de61b8740 100644 --- a/src/Ombi.Helpers/PlexHelper.cs +++ b/src/Ombi.Helpers/PlexHelper.cs @@ -27,12 +27,15 @@ using System; using System.Globalization; +using System.Text.RegularExpressions; namespace Ombi.Helpers { public class PlexHelper { - + private const string ImdbMatchExpression = "tt([0-9]{1,10})"; + private const string TvDbIdMatchExpression = "//[0-9]+/([0-9]{1,3})/([0-9]{1,3})"; + public static ProviderId GetProviderIdFromPlexGuid(string guid) { //com.plexapp.agents.thetvdb://269586/2/8?lang=en @@ -52,7 +55,7 @@ namespace Ombi.Helpers { TheTvDb = guidSplit[1] }; - } + } else if (guid.Contains("themoviedb", CompareOptions.IgnoreCase)) { return new ProviderId @@ -60,6 +63,7 @@ namespace Ombi.Helpers TheMovieDb = guidSplit[1] }; } + else if (guid.Contains("imdb", CompareOptions.IgnoreCase)) { return new ProviderId @@ -67,6 +71,31 @@ namespace Ombi.Helpers ImdbId = guidSplit[1] }; } + else + { + var imdbRegex = new Regex(ImdbMatchExpression, RegexOptions.Compiled); + var tvdbRegex = new Regex(TvDbIdMatchExpression, RegexOptions.Compiled); + var imdbMatch = imdbRegex.IsMatch(guid); + if (imdbMatch) + { + return new ProviderId + { + ImdbId = guidSplit[1] + }; + } + else + { + // Check if it matches the TvDb pattern + var tvdbMatch = tvdbRegex.IsMatch(guid); + if (tvdbMatch) + { + return new ProviderId + { + TheTvDb = guidSplit[1] + }; + } + } + } } return new ProviderId(); } diff --git a/src/Ombi.Helpers/StringHelper.cs b/src/Ombi.Helpers/StringHelper.cs index c198301fc..68a29e848 100644 --- a/src/Ombi.Helpers/StringHelper.cs +++ b/src/Ombi.Helpers/StringHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security; @@ -76,6 +77,49 @@ namespace Ombi.Helpers return -1; } + public static string BuildEpisodeList(IEnumerable orderedEpisodes) + { + var epSb = new StringBuilder(); + var previousEpisodes = new List(); + var previousEpisode = -1; + foreach (var ep in orderedEpisodes) + { + if (ep - 1 == previousEpisode) + { + // This is the next one + previousEpisodes.Add(ep); + } + else + { + if (previousEpisodes.Count > 1) + { + // End it + epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}, "); + } + else if (previousEpisodes.Count == 1) + { + epSb.Append($"{previousEpisodes.FirstOrDefault()}, "); + } + // New one + previousEpisodes.Clear(); + previousEpisodes.Add(ep); + } + previousEpisode = ep; + } + + if (previousEpisodes.Count > 1) + { + // Got some left over + epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}"); + } + else if (previousEpisodes.Count == 1) + { + epSb.Append(previousEpisodes.FirstOrDefault()); + } + + return epSb.ToString(); + } + public static string RemoveSpaces(this string str) { return str.Replace(" ", ""); diff --git a/src/Ombi.Mapping/Profiles/SettingsProfile.cs b/src/Ombi.Mapping/Profiles/SettingsProfile.cs index 139290f2b..f460ce78b 100644 --- a/src/Ombi.Mapping/Profiles/SettingsProfile.cs +++ b/src/Ombi.Mapping/Profiles/SettingsProfile.cs @@ -19,6 +19,7 @@ namespace Ombi.Mapping.Profiles CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } } \ No newline at end of file diff --git a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs index b29122be0..fc80de193 100644 --- a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs +++ b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs @@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates if (string.IsNullOrEmpty(_templateLocation)) { #if DEBUG - _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.0", "Templates", + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.2", "Templates", "BasicTemplate.html"); #else _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); diff --git a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html index 450e7df2a..5456743c9 100644 --- a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html +++ b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html @@ -1,10 +1,17 @@ - + + - - + + Ombi - - - - -
-
- - - - - - - - - - + + +
- - - - - - +
- -
-
-
-

{@INTRO}

+ + + + + - - - - - - - - - -
+ + + - - - - -
 
+ - \ No newline at end of file + + + + diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index de1c55533..5436dff22 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,15 +1,15 @@  - netcoreapp2.1 + netcoreapp2.2 - - - - - + + + + + diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 84e907053..77580e5e4 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -6,7 +6,6 @@ using Ombi.Api.Discord; using Ombi.Api.Discord.Models; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -57,148 +56,42 @@ namespace Ombi.Notifications.Agents protected override async Task NewRequest(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.NewRequest, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.NewRequest); } protected override async Task NewIssue(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.Issue, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.Issue); } protected override async Task IssueComment(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.IssueComment, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueComment); } protected override async Task IssueResolved(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.IssueResolved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueResolved); } protected override async Task AddedToRequestQueue(NotificationOptions model, DiscordNotificationSettings settings) { - var user = string.Empty; - var title = string.Empty; - var image = string.Empty; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - image = MovieRequest.PosterPath; - } - else if (model.RequestType == RequestType.TvShow) - { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - image = TvRequest.ParentRequest.PosterPath; - } - else if (model.RequestType == RequestType.Album) - { - user = AlbumRequest.RequestedUser.UserAlias; - title = AlbumRequest.Title; - image = AlbumRequest.Cover; - } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; - var notification = new NotificationMessage - { - Message = message - }; - notification.Other.Add("image", image); - await Send(notification, settings); + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); } protected override async Task RequestDeclined(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestDeclined, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestDeclined); } protected override async Task RequestApproved(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestApproved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestApproved); } protected override async Task AvailableRequest(NotificationOptions model, DiscordNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestAvailable, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Discord}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task Send(NotificationMessage model, DiscordNotificationSettings settings) @@ -243,5 +136,21 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + private async Task Run(NotificationOptions model, DiscordNotificationSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Discord, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Discord}"); + return; + } + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + notification.Other.Add("image", parsed.Image); + await Send(notification, settings); + } } } diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index cff1f3b80..a7f9334fb 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using MimeKit; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Notifications.Templates; using Ombi.Settings.Settings.Models; @@ -90,7 +89,6 @@ namespace Ombi.Notifications.Agents } else { - // Send to admin message.To = settings.AdminEmail; } @@ -184,37 +182,21 @@ namespace Ombi.Notifications.Agents protected override async Task AddedToRequestQueue(NotificationOptions model, EmailNotificationSettings settings) { - var email = new EmailBasicTemplate(); - var user = string.Empty; - var title = string.Empty; - var img = string.Empty; - if (model.RequestType == RequestType.Movie) + if (!model.Recipient.HasValue()) { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - img = $"https://image.tmdb.org/t/p/w300/{MovieRequest.PosterPath}"; + return; } - else + var message = await LoadTemplate(NotificationType.ItemAddedToFaultQueue, model, settings); + if (message == null) { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - img = TvRequest.ParentRequest.PosterPath; + return; } - var html = email.LoadTemplate( - $"{Customization.ApplicationName}: A request could not be added.", - $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying", img, Customization.Logo); - - var message = new NotificationMessage - { - Message = html, - Subject = $"{Customization.ApplicationName}: A request could not be added", - To = settings.AdminEmail, - }; - - var plaintext = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; + var plaintext = await LoadPlainTextMessage(NotificationType.ItemAddedToFaultQueue, model, settings); message.Other.Add("PlainTextBody", plaintext); + // Issues resolved should be sent to the user + message.To = settings.AdminEmail; await Send(message, settings); } diff --git a/src/Ombi.Notifications/Agents/GotifyNotification.cs b/src/Ombi.Notifications/Agents/GotifyNotification.cs new file mode 100644 index 000000000..e1c9fc1db --- /dev/null +++ b/src/Ombi.Notifications/Agents/GotifyNotification.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Ombi.Api.Gotify; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Notifications.Models; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.Notifications; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Notifications.Agents +{ + public class GotifyNotification : BaseNotification, IGotifyNotification + { + public GotifyNotification(IGotifyApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, + ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + { + Api = api; + Logger = log; + } + + public override string NotificationName => "GotifyNotification"; + + private IGotifyApi Api { get; } + private ILogger Logger { get; } + + protected override bool ValidateConfiguration(GotifySettings settings) + { + return settings.Enabled && !string.IsNullOrEmpty(settings.BaseUrl) && !string.IsNullOrEmpty(settings.ApplicationToken); + } + + protected override async Task NewRequest(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.NewRequest); + } + + + protected override async Task NewIssue(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.Issue); + } + + protected override async Task IssueComment(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.IssueComment); + } + + protected override async Task IssueResolved(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.IssueResolved); + } + + protected override async Task AddedToRequestQueue(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); + } + + protected override async Task RequestDeclined(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.RequestDeclined); + } + + protected override async Task RequestApproved(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.RequestApproved); + } + + protected override async Task AvailableRequest(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.RequestAvailable); + } + + protected override async Task Send(NotificationMessage model, GotifySettings settings) + { + try + { + await Api.PushAsync(settings.BaseUrl, settings.ApplicationToken, model.Subject, model.Message, settings.Priority); + } + catch (Exception e) + { + Logger.LogError(LoggingEvents.GotifyNotification, e, "Failed to send Gotify notification"); + } + } + + protected override async Task Test(NotificationOptions model, GotifySettings settings) + { + var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!"; + var notification = new NotificationMessage + { + Message = message, + }; + await Send(notification, settings); + } + + private async Task Run(NotificationOptions model, GotifySettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Gotify, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Gotify}"); + return; + } + + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + + await Send(notification, settings); + } + } +} diff --git a/src/Ombi.Notifications/Agents/Interfaces/IGotifyNotification.cs b/src/Ombi.Notifications/Agents/Interfaces/IGotifyNotification.cs new file mode 100644 index 000000000..a85421938 --- /dev/null +++ b/src/Ombi.Notifications/Agents/Interfaces/IGotifyNotification.cs @@ -0,0 +1,6 @@ +namespace Ombi.Notifications.Agents +{ + public interface IGotifyNotification : INotification + { + } +} \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/MattermostNotification.cs b/src/Ombi.Notifications/Agents/MattermostNotification.cs index 37e597854..ed3084c19 100644 --- a/src/Ombi.Notifications/Agents/MattermostNotification.cs +++ b/src/Ombi.Notifications/Agents/MattermostNotification.cs @@ -2,13 +2,10 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Ombi.Api.Discord; -using Ombi.Api.Discord.Models; using Ombi.Api.Mattermost; using Ombi.Api.Mattermost.Models; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -49,150 +46,49 @@ namespace Ombi.Notifications.Agents protected override async Task NewRequest(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.NewRequest, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - AddOtherInformation(model, notification, parsed); - //notification.Other.Add("overview", model.RequestType == RequestType.Movie ? base.MovieRequest.Overview : TvRequest.); - await Send(notification, settings); + await Run(model, settings, NotificationType.NewRequest); } private void AddOtherInformation(NotificationOptions model, NotificationMessage notification, NotificationMessageContent parsed) { - notification.Other.Add("image", parsed.Image); + notification.Other.Add("image", parsed?.Image ?? string.Empty); notification.Other.Add("title", model.RequestType == RequestType.Movie ? MovieRequest.Title : TvRequest.Title); } protected override async Task NewIssue(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.Issue, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - AddOtherInformation(model, notification, parsed); - await Send(notification, settings); + await Run(model, settings, NotificationType.Issue); } protected override async Task IssueComment(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.IssueComment, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - AddOtherInformation(model, notification, parsed); - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueComment); } protected override async Task IssueResolved(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.IssueResolved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - AddOtherInformation(model, notification, parsed); - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueResolved); } protected override async Task AddedToRequestQueue(NotificationOptions model, MattermostNotificationSettings settings) { - var user = string.Empty; - var title = string.Empty; - var image = string.Empty; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - image = MovieRequest.PosterPath; - } - else - { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - image = TvRequest.ParentRequest.PosterPath; - } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; - var notification = new NotificationMessage - { - Message = message - }; - notification.Other.Add("image", image); - await Send(notification, settings); + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); } protected override async Task RequestDeclined(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.RequestDeclined, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - AddOtherInformation(model, notification, parsed); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestDeclined); } protected override async Task RequestApproved(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.RequestApproved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - AddOtherInformation(model, notification, parsed); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestApproved); } protected override async Task AvailableRequest(NotificationOptions model, MattermostNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Mattermost, NotificationType.RequestAvailable, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Mattermost}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - AddOtherInformation(model, notification, parsed); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task Send(NotificationMessage model, MattermostNotificationSettings settings) @@ -231,5 +127,21 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + private async Task Run(NotificationOptions model, MattermostNotificationSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Mattermost, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Mattermost}"); + return; + } + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + AddOtherInformation(model, notification, parsed); + await Send(notification, settings); + } } } diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index a16785909..4e3e55fc4 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using Ombi.Api.Notifications; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -131,23 +130,18 @@ namespace Ombi.Notifications.Agents protected override async Task AddedToRequestQueue(NotificationOptions model, MobileNotificationSettings settings) { - string user; - string title; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - } - else + + var parsed = await LoadTemplate(NotificationAgent.Mobile, NotificationType.ItemAddedToFaultQueue, model); + if (parsed.Disabled) { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; + _logger.LogInformation($"Template {NotificationType.ItemAddedToFaultQueue} is disabled for {NotificationAgent.Mobile}"); + return; } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; var notification = new NotificationMessage { - Message = message + Message = parsed.Message, }; + // Get admin devices var playerIds = await GetAdmins(NotificationType.Test); await Send(playerIds, notification, settings, model); @@ -295,6 +289,5 @@ namespace Ombi.Notifications.Agents } } } - } } \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/PushbulletNotification.cs b/src/Ombi.Notifications/Agents/PushbulletNotification.cs index 6c6b1f789..91a8120b2 100644 --- a/src/Ombi.Notifications/Agents/PushbulletNotification.cs +++ b/src/Ombi.Notifications/Agents/PushbulletNotification.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Ombi.Api.Pushbullet; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -45,131 +44,43 @@ namespace Ombi.Notifications.Agents protected override async Task NewRequest(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.NewRequest, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - await Send(notification, settings); + await Run(model, settings, NotificationType.NewRequest); } + protected override async Task NewIssue(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.Issue, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.Issue); } protected override async Task IssueComment(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.IssueComment, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueComment); } protected override async Task IssueResolved(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.IssueResolved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueResolved); } protected override async Task AddedToRequestQueue(NotificationOptions model, PushbulletSettings settings) { - string user; - string title; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - } - else - { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; - var notification = new NotificationMessage - { - Message = message - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); } protected override async Task RequestDeclined(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.RequestDeclined, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestDeclined); } protected override async Task RequestApproved(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.RequestApproved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestApproved); } protected override async Task AvailableRequest(NotificationOptions model, PushbulletSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushbullet, NotificationType.RequestAvailable, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Pushbullet}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task Send(NotificationMessage model, PushbulletSettings settings) @@ -193,5 +104,22 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + private async Task Run(NotificationOptions model, PushbulletSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Pushbullet, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Pushbullet}"); + return; + } + + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + + await Send(notification, settings); + } } } diff --git a/src/Ombi.Notifications/Agents/PushoverNotification.cs b/src/Ombi.Notifications/Agents/PushoverNotification.cs index 86f91dbaa..d41e6d911 100644 --- a/src/Ombi.Notifications/Agents/PushoverNotification.cs +++ b/src/Ombi.Notifications/Agents/PushoverNotification.cs @@ -5,7 +5,6 @@ using Ombi.Api.Pushbullet; using Ombi.Api.Pushover; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -46,132 +45,42 @@ namespace Ombi.Notifications.Agents protected override async Task NewRequest(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.NewRequest, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Pushover}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - await Send(notification, settings); + await Run(model, settings, NotificationType.NewRequest); } protected override async Task NewIssue(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.Issue, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Pushover}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.Issue); } protected override async Task IssueComment(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.IssueComment, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Pushover}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueComment); } protected override async Task IssueResolved(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.IssueResolved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Pushover}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueResolved); } protected override async Task AddedToRequestQueue(NotificationOptions model, PushoverSettings settings) { - string user; - string title; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - } - else - { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; - var notification = new NotificationMessage - { - Message = message - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); } protected override async Task RequestDeclined(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.RequestDeclined, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Pushover}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestDeclined); } protected override async Task RequestApproved(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.RequestApproved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Pushover}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestApproved); } protected override async Task AvailableRequest(NotificationOptions model, PushoverSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Pushover, NotificationType.RequestAvailable, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Pushover}"); - return; - } - - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task Send(NotificationMessage model, PushoverSettings settings) @@ -196,5 +105,21 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + private async Task Run(NotificationOptions model, PushoverSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Pushover, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Pushover}"); + return; + } + + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + await Send(notification, settings); + } } } diff --git a/src/Ombi.Notifications/Agents/SlackNotification.cs b/src/Ombi.Notifications/Agents/SlackNotification.cs index ee81e9729..58b2da651 100644 --- a/src/Ombi.Notifications/Agents/SlackNotification.cs +++ b/src/Ombi.Notifications/Agents/SlackNotification.cs @@ -5,7 +5,6 @@ using Ombi.Api.Slack; using Ombi.Api.Slack.Models; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -55,138 +54,42 @@ namespace Ombi.Notifications.Agents protected override async Task NewRequest(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.NewRequest, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.NewRequest); } protected override async Task NewIssue(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.Issue, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.Issue); } protected override async Task IssueComment(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.IssueComment, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueComment); } protected override async Task IssueResolved(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.IssueResolved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueResolved); } protected override async Task AddedToRequestQueue(NotificationOptions model, SlackNotificationSettings settings) { - var user = string.Empty; - var title = string.Empty; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - } - else - { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; - var notification = new NotificationMessage - { - Message = message - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); } protected override async Task RequestDeclined(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.RequestDeclined, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task RequestApproved(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.RequestApproved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestApproved); } protected override async Task AvailableRequest(NotificationOptions model, SlackNotificationSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Slack, NotificationType.RequestAvailable, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Slack}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - notification.Other.Add("image", parsed.Image); - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task Send(NotificationMessage model, SlackNotificationSettings settings) @@ -219,5 +122,21 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + private async Task Run(NotificationOptions model, SlackNotificationSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Slack, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Slack}"); + return; + } + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + notification.Other.Add("image", parsed.Image); + await Send(notification, settings); + } } } diff --git a/src/Ombi.Notifications/Agents/TelegramNotification.cs b/src/Ombi.Notifications/Agents/TelegramNotification.cs index cf463bf99..3acfc7331 100644 --- a/src/Ombi.Notifications/Agents/TelegramNotification.cs +++ b/src/Ombi.Notifications/Agents/TelegramNotification.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -42,134 +41,42 @@ namespace Ombi.Notifications.Agents protected override async Task NewRequest(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.NewRequest, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.NewRequest} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - - await Send(notification, settings); + await Run(model, settings, NotificationType.NewRequest); } protected override async Task NewIssue(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.Issue, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.Issue} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.Issue); } protected override async Task IssueComment(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.IssueComment, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueComment} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueComment); } protected override async Task IssueResolved(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.IssueResolved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.IssueResolved} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.IssueResolved); } protected override async Task AddedToRequestQueue(NotificationOptions model, TelegramSettings settings) { - var user = string.Empty; - var title = string.Empty; - var image = string.Empty; - if (model.RequestType == RequestType.Movie) - { - user = MovieRequest.RequestedUser.UserAlias; - title = MovieRequest.Title; - image = MovieRequest.PosterPath; - } - else - { - user = TvRequest.RequestedUser.UserAlias; - title = TvRequest.ParentRequest.Title; - image = TvRequest.ParentRequest.PosterPath; - } - var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying"; - var notification = new NotificationMessage - { - Message = message - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); } protected override async Task RequestDeclined(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.RequestDeclined, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestDeclined} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestDeclined); } protected override async Task RequestApproved(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.RequestApproved, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestApproved} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message ?? string.Empty, - }; - - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestApproved); } protected override async Task AvailableRequest(NotificationOptions model, TelegramSettings settings) { - var parsed = await LoadTemplate(NotificationAgent.Telegram, NotificationType.RequestAvailable, model); - if (parsed.Disabled) - { - Logger.LogInformation($"Template {NotificationType.RequestAvailable} is disabled for {NotificationAgent.Telegram}"); - return; - } - var notification = new NotificationMessage - { - Message = parsed.Message, - }; - await Send(notification, settings); + await Run(model, settings, NotificationType.RequestAvailable); } protected override async Task Send(NotificationMessage model, TelegramSettings settings) @@ -193,5 +100,20 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + private async Task Run(NotificationOptions model, TelegramSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Telegram, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Telegram}"); + return; + } + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + await Send(notification, settings); + } } } diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index d351c8283..001f68f45 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -14,7 +13,7 @@ using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; -namespace Ombi.Notifications.Interfaces +namespace Ombi.Notifications { public abstract class BaseNotification : INotification where T : Settings.Settings.Models.Settings, new() { @@ -27,8 +26,6 @@ namespace Ombi.Notifications.Interfaces MovieRepository = movie; TvRepository = tv; CustomizationSettings = customization; - Settings.ClearCache(); - CustomizationSettings.ClearCache(); RequestSubscription = sub; _log = log; AlbumRepository = album; @@ -56,14 +53,12 @@ namespace Ombi.Notifications.Interfaces public async Task NotifyAsync(NotificationOptions model) { - Settings.ClearCache(); var configuration = await GetConfiguration(); await NotifyAsync(model, configuration); } public async Task NotifyAsync(NotificationOptions model, Settings.Settings.Models.Settings settings) { - Settings.ClearCache(); if (settings == null) await NotifyAsync(model); var notificationSettings = (T)settings; @@ -170,6 +165,24 @@ namespace Ombi.Notifications.Interfaces { return new NotificationMessageContent { Disabled = true }; } + + if (model.UserId.IsNullOrEmpty()) + { + if (model.RequestType == RequestType.Movie) + { + model.UserId = MovieRequest.RequestedUserId; + } + + if (model.RequestType == RequestType.Album) + { + model.UserId = AlbumRequest.RequestedUserId; + } + + if (model.RequestType == RequestType.TvShow) + { + model.UserId = TvRequest.RequestedUserId; + } + } var parsed = Parse(model, template, agent); return parsed; @@ -184,7 +197,7 @@ namespace Ombi.Notifications.Interfaces protected UserNotificationPreferences GetUserPreference(string userId, NotificationAgent agent) { return UserNotificationPreferences.GetAll() - .FirstOrDefault(x => x.Enabled && x.Agent == agent && x.UserId == userId); + .FirstOrDefault(x => x.Agent == agent && x.UserId == userId); } private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template, NotificationAgent agent) diff --git a/src/Ombi.Notifications/GenericEmailProvider.cs b/src/Ombi.Notifications/GenericEmailProvider.cs index 462f8918e..15f17af92 100644 --- a/src/Ombi.Notifications/GenericEmailProvider.cs +++ b/src/Ombi.Notifications/GenericEmailProvider.cs @@ -4,7 +4,9 @@ using EnsureThat; using MailKit.Net.Smtp; using Microsoft.Extensions.Logging; using MimeKit; +using MimeKit.Utils; using Ombi.Core.Settings; +using Ombi.Helpers; using Ombi.Notifications.Models; using Ombi.Notifications.Templates; using Ombi.Settings.Settings.Models; @@ -36,6 +38,15 @@ namespace Ombi.Notifications var customization = await CustomizationSettings.GetSettingsAsync(); var html = email.LoadTemplate(model.Subject, model.Message, null, customization.Logo); + + var messageId = MimeUtils.GenerateMessageId(); + if (customization.ApplicationUrl.HasValue()) + { + if (Uri.TryCreate(customization.ApplicationUrl, UriKind.RelativeOrAbsolute, out var url)) + { + messageId = MimeUtils.GenerateMessageId(url.IdnHost); + } + } var textBody = string.Empty; @@ -49,7 +60,8 @@ namespace Ombi.Notifications var message = new MimeMessage { Body = body.ToMessageBody(), - Subject = model.Subject + Subject = model.Subject, + MessageId = messageId }; message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress)); message.To.Add(new MailboxAddress(model.To, model.To)); @@ -83,22 +95,20 @@ namespace Ombi.Notifications { client.Authenticate(settings.Username, settings.Password); } - //Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated); + _log.LogDebug("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated); await client.SendAsync(message); await client.DisconnectAsync(true); } } catch (Exception e) { - //Log.Error(e); - throw new InvalidOperationException(e.Message); + _log.LogError(e, "Exception when attempting to send an email"); + throw; } } public async Task Send(NotificationMessage model, EmailNotificationSettings settings) { - try - { EnsureArg.IsNotNullOrEmpty(settings.SenderAddress); EnsureArg.IsNotNullOrEmpty(model.To); EnsureArg.IsNotNullOrEmpty(model.Message); @@ -120,9 +130,18 @@ namespace Ombi.Notifications Subject = model.Subject }; - message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress)); message.To.Add(new MailboxAddress(model.To, model.To)); + await Send(message, settings); + + } + + public async Task Send(MimeMessage message, EmailNotificationSettings settings) + { + try + { + message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress)); + using (var client = new SmtpClient()) { if (settings.DisableCertificateChecking) diff --git a/src/Ombi.Notifications/IEmailProvider.cs b/src/Ombi.Notifications/IEmailProvider.cs index 85bb91331..009e9bf96 100644 --- a/src/Ombi.Notifications/IEmailProvider.cs +++ b/src/Ombi.Notifications/IEmailProvider.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using MimeKit; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models.Notifications; @@ -8,5 +9,6 @@ namespace Ombi.Notifications { Task Send(NotificationMessage model, EmailNotificationSettings settings); Task SendAdHoc(NotificationMessage model, EmailNotificationSettings settings); + Task Send(MimeMessage message, EmailNotificationSettings settings); } } \ No newline at end of file diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index 1b655b84d..cedda3735 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -17,10 +17,6 @@ namespace Ombi.Notifications public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref) { LoadIssues(opts); - if (pref != null) - { - UserPreference = pref.Enabled ? pref.Value : string.Empty; - } string title; if (req == null) @@ -41,6 +37,10 @@ namespace Ombi.Notifications } Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; + if (pref != null) + { + UserPreference = pref.Value.HasValue() ? pref.Value : Alias; + } Title = title; RequestedDate = req?.RequestedDate.ToString("D"); if (Type.IsNullOrEmpty()) @@ -49,7 +49,8 @@ namespace Ombi.Notifications } Overview = req?.Overview; Year = req?.ReleaseDate.Year.ToString(); - + DenyReason = req?.DeniedReason; + AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; if (req?.RequestType == RequestType.Movie) { PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase) @@ -66,10 +67,7 @@ namespace Ombi.Notifications public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref) { LoadIssues(opts); - if (pref != null) - { - UserPreference = pref.Enabled ? pref.Value : string.Empty; - } + string title; if (req == null) { @@ -88,7 +86,13 @@ namespace Ombi.Notifications UserName = req?.RequestedUser?.UserName; } + AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; + DenyReason = req?.DeniedReason; Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; + if (pref != null) + { + UserPreference = pref.Value.HasValue() ? pref.Value : Alias; + } Title = title; RequestedDate = req?.RequestedDate.ToString("D"); if (Type.IsNullOrEmpty()) @@ -101,22 +105,15 @@ namespace Ombi.Notifications AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; } - public void SetupNewsletter(CustomizationSettings s, OmbiUser username) + public void SetupNewsletter(CustomizationSettings s) { ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; - RequestedUser = username.UserName; - UserName = username.UserName; - Alias = username.Alias.HasValue() ? username.Alias : username.UserName; } public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref) { LoadIssues(opts); - if (pref != null) - { - UserPreference = pref.Enabled ? pref.Value : string.Empty; - } string title; if (req == null) { @@ -126,6 +123,7 @@ namespace Ombi.Notifications { title = req?.ParentRequest.Title; } + DenyReason = req?.DeniedReason; ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; RequestedUser = req?.RequestedUser?.UserName; @@ -134,7 +132,12 @@ namespace Ombi.Notifications // Can be set if it's an issue UserName = req?.RequestedUser?.UserName; } + AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; + if (pref != null) + { + UserPreference = pref.Value.HasValue() ? pref.Value : Alias; + } Title = title; RequestedDate = req?.RequestedDate.ToString("D"); if (Type.IsNullOrEmpty()) @@ -217,7 +220,6 @@ namespace Ombi.Notifications public string UserName { get; set; } public string IssueUser => UserName; public string Alias { get; set; } - public string Title { get; set; } public string RequestedDate { get; set; } public string Type { get; set; } @@ -235,6 +237,8 @@ namespace Ombi.Notifications public string IssueSubject { get; set; } public string NewIssueComment { get; set; } public string UserPreference { get; set; } + public string DenyReason { get; set; } + public string AvailableDate { get; set; } // System Defined private string LongDate => DateTime.Now.ToString("D"); @@ -268,6 +272,9 @@ namespace Ombi.Notifications {nameof(IssueUser),IssueUser}, {nameof(UserName),UserName}, {nameof(Alias),Alias}, + {nameof(UserPreference),UserPreference}, + {nameof(DenyReason),DenyReason}, + {nameof(AvailableDate),AvailableDate}, }; } } \ No newline at end of file diff --git a/src/Ombi.Notifications/NotificationService.cs b/src/Ombi.Notifications/NotificationService.cs index d3651871f..c2985a21b 100644 --- a/src/Ombi.Notifications/NotificationService.cs +++ b/src/Ombi.Notifications/NotificationService.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; namespace Ombi.Notifications diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index 2b5c95154..3fa4b4830 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Ombi.Schedule.Tests/NewsletterTests.cs b/src/Ombi.Schedule.Tests/NewsletterTests.cs index b3c2ce98a..f729e8899 100644 --- a/src/Ombi.Schedule.Tests/NewsletterTests.cs +++ b/src/Ombi.Schedule.Tests/NewsletterTests.cs @@ -1,11 +1,8 @@ -using System.Collections.Generic; -using Moq; +using System; +using System.Collections.Generic; using NUnit.Framework; -using Ombi.Core.Settings; -using Ombi.Schedule.Jobs.Ombi; -using Ombi.Settings.Settings.Models; -using Ombi.Settings.Settings.Models.Notifications; -using Ombi.Store.Entities; +using Ombi.Helpers; +using static Ombi.Schedule.Jobs.Ombi.NewsletterJob; namespace Ombi.Schedule.Tests { @@ -15,17 +12,12 @@ namespace Ombi.Schedule.Tests [TestCaseSource(nameof(EpisodeListData))] public string BuildEpisodeListTest(List episodes) { - var emailSettings = new Mock>(); - var customziation = new Mock>(); - var newsletterSettings = new Mock>(); - var newsletter = new NewsletterJob(null, null, null, null, null, null, customziation.Object, emailSettings.Object, null, null, newsletterSettings.Object, null, null, null, null); - var ep = new List(); foreach (var i in episodes) { ep.Add(i); } - var result = newsletter.BuildEpisodeList(ep); + var result = StringHelper.BuildEpisodeList(ep); return result; } diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index ea1d17f8c..0c83dd755 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,16 +1,16 @@ - netcoreapp2.1 + netcoreapp2.2 - - + + - + - + diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 502d7b726..1f3752c78 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -7,6 +7,7 @@ using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Lidarr; using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex; +using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.Radarr; using Ombi.Schedule.Jobs.SickRage; using Ombi.Schedule.Jobs.Sonarr; @@ -21,7 +22,7 @@ namespace Ombi.Schedule IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache, ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh, INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist, - IIssuesPurge purge) + IIssuesPurge purge, IResendFailedRequests resender, IMediaDatabaseRefresh dbRefresh) { _plexContentSync = plexContentSync; _radarrSync = radarrSync; @@ -38,6 +39,8 @@ namespace Ombi.Schedule _plexRecentlyAddedSync = recentlyAddedPlex; _lidarrArtistSync = artist; _issuesPurge = purge; + _resender = resender; + _mediaDatabaseRefresh = dbRefresh; } private readonly IPlexContentSync _plexContentSync; @@ -55,6 +58,8 @@ namespace Ombi.Schedule private readonly INewsletterJob _newsletter; private readonly ILidarrArtistSync _lidarrArtistSync; private readonly IIssuesPurge _issuesPurge; + private readonly IResendFailedRequests _resender; + private readonly IMediaDatabaseRefresh _mediaDatabaseRefresh; public void Setup() { @@ -76,9 +81,10 @@ namespace Ombi.Schedule RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); + // RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s)); + RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s)); } - private bool _disposed; protected virtual void Dispose(bool disposing) { diff --git a/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs b/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs index 8606de371..5e9f13534 100644 --- a/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs +++ b/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs @@ -42,7 +42,7 @@ namespace Ombi.Schedule.Jobs.Couchpotato public class CouchPotatoSync : ICouchPotatoSync { public CouchPotatoSync(ISettingsService cpSettings, - ICouchPotatoApi api, ILogger log, IOmbiContext ctx) + ICouchPotatoApi api, ILogger log, IExternalContext ctx) { _settings = cpSettings; _api = api; @@ -54,7 +54,7 @@ namespace Ombi.Schedule.Jobs.Couchpotato private readonly ISettingsService _settings; private readonly ICouchPotatoApi _api; private readonly ILogger _log; - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; public async Task Start() { diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 7002eec41..e22c0ca51 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -28,7 +28,6 @@ namespace Ombi.Schedule.Jobs.Emby _repo = repo; _episodeSync = epSync; _metadata = metadata; - _settings.ClearCache(); } private readonly ILogger _logger; @@ -87,7 +86,7 @@ namespace Ombi.Schedule.Jobs.Emby await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); foreach (var item in movieInfo.Items) { - await ProcessMovies(item, mediaToAdd); + await ProcessMovies(item, mediaToAdd, server); } processed++; @@ -96,7 +95,7 @@ namespace Ombi.Schedule.Jobs.Emby { processed++; // Regular movie - await ProcessMovies(movie, mediaToAdd); + await ProcessMovies(movie, mediaToAdd, server); } } @@ -138,7 +137,7 @@ namespace Ombi.Schedule.Jobs.Emby Title = tvShow.Name, Type = EmbyMediaType.Series, EmbyId = tvShow.Id, - Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server.ServerHostname), AddedAt = DateTime.UtcNow }); } @@ -164,7 +163,7 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.AddRange(mediaToAdd); } - private async Task ProcessMovies(EmbyMovie movieInfo, ICollection content) + private async Task ProcessMovies(EmbyMovie movieInfo, ICollection content, EmbyServers server) { // Check if it exists var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); @@ -179,7 +178,7 @@ namespace Ombi.Schedule.Jobs.Emby Title = movieInfo.Name, Type = EmbyMediaType.Movie, EmbyId = movieInfo.Id, - Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id), + Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server.ServerHostname), AddedAt = DateTime.UtcNow, }); } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index f66ff89ab..962b08cda 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -49,7 +49,6 @@ namespace Ombi.Schedule.Jobs.Emby _settings = s; _repo = repo; _avaliabilityChecker = checker; - _settings.ClearCache(); } private readonly ISettingsService _settings; @@ -82,6 +81,13 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var ep in allEpisodes.Items) { processed++; + + if (ep.LocationType?.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase) ?? false) + { + // For some reason Emby is not respecting the `IsVirtualItem` field. + continue; + } + // Let's make sure we have the parent request, stop those pesky forign key errors, // Damn me having data integrity var parent = await _repo.GetByEmbyId(ep.SeriesId); @@ -147,4 +153,4 @@ namespace Ombi.Schedule.Jobs.Emby GC.SuppressFinalize(this); } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs index 2dadd4bd4..280a61ab4 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs @@ -50,8 +50,6 @@ namespace Ombi.Schedule.Jobs.Emby _log = log; _embySettings = embySettings; _userManagementSettings = ums; - _userManagementSettings.ClearCache(); - _embySettings.ClearCache(); } private readonly IEmbyApi _api; diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs index 2e32b6478..2a50b5b38 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs @@ -1,26 +1,23 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using Hangfire; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; -using Ombi.Api.Radarr; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; -using Serilog; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Ombi.Schedule.Jobs.Lidarr { public class LidarrAlbumSync : ILidarrAlbumSync { - public LidarrAlbumSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IOmbiContext ctx, + public LidarrAlbumSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IExternalContext ctx, IBackgroundJobClient job, ILidarrAvailabilityChecker availability) { _lidarrSettings = lidarr; @@ -29,13 +26,12 @@ namespace Ombi.Schedule.Jobs.Lidarr _ctx = ctx; _job = job; _availability = availability; - _lidarrSettings.ClearCache(); } private readonly ISettingsService _lidarrSettings; private readonly ILidarrApi _lidarrApi; private readonly ILogger _logger; - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; private readonly IBackgroundJobClient _job; private readonly ILidarrAvailabilityChecker _availability; diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs index ac6264e3d..e9a64f2a3 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs @@ -1,26 +1,23 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using Hangfire; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; -using Ombi.Api.Radarr; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; -using Serilog; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Ombi.Schedule.Jobs.Lidarr { public class LidarrArtistSync : ILidarrArtistSync { - public LidarrArtistSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IOmbiContext ctx, + public LidarrArtistSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IExternalContext ctx, IBackgroundJobClient background, ILidarrAlbumSync album) { _lidarrSettings = lidarr; @@ -29,13 +26,12 @@ namespace Ombi.Schedule.Jobs.Lidarr _ctx = ctx; _job = background; _albumSync = album; - _lidarrSettings.ClearCache(); } private readonly ISettingsService _lidarrSettings; private readonly ILidarrApi _lidarrApi; private readonly ILogger _logger; - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; private readonly IBackgroundJobClient _job; private readonly ILidarrAlbumSync _albumSync; diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs index 9db24784d..5708dad6c 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs @@ -70,4 +70,4 @@ namespace Ombi.Schedule.Jobs.Lidarr } } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs index 09b7d9858..54d1d1133 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs @@ -7,18 +7,18 @@ namespace Ombi.Schedule.Jobs.Ombi { protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url) { - sb.Append("
"); - sb.AppendFormat("", url); + sb.Append("
"); + sb.AppendFormat("", url); sb.Append(""); - sb.Append("
"); - sb.Append(""); + sb.Append("
"); + sb.Append(""); } protected virtual void AddPosterInsideTable(StringBuilder sb, string url) { sb.Append(""); - sb.Append("
"); - sb.AppendFormat("", url); + sb.Append("
"); + sb.AppendFormat("", url); } protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url) @@ -27,10 +27,10 @@ namespace Ombi.Schedule.Jobs.Ombi { sb.Append(""); sb.Append( - ""); @@ -44,16 +44,16 @@ namespace Ombi.Schedule.Jobs.Ombi protected virtual void AddInfoTable(StringBuilder sb) { sb.Append( - "
"); + ""); sb.AppendFormat("", mediaurl); sb.AppendFormat( - "", + "", url); sb.Append(""); sb.Append(""); + ""); sb.Append(""); } protected virtual void AddTitle(StringBuilder sb, string url, string title) { - sb.Append(""); - sb.Append(""); + sb.Append(""); sb.Append(""); @@ -61,30 +61,30 @@ namespace Ombi.Schedule.Jobs.Ombi protected virtual void AddParagraph(StringBuilder sb, string text) { - sb.Append(""); - sb.Append(""); + sb.Append(""); sb.Append(""); } protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary) { - sb.Append(""); - sb.Append(""); + sb.Append(""); sb.Append(""); } protected virtual void AddGenres(StringBuilder sb, string text) { - sb.Append(""); - sb.Append(""); + sb.Append(""); sb.Append(""); } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IIssuesPurge.cs similarity index 100% rename from src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs rename to src/Ombi.Schedule/Jobs/Ombi/Interfaces/IIssuesPurge.cs diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs new file mode 100644 index 000000000..11fe7c51a --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Plex.Interfaces +{ + public interface IMediaDatabaseRefresh : IBaseJob + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/INewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/INewsletterJob.cs similarity index 100% rename from src/Ombi.Schedule/Jobs/Ombi/INewsletterJob.cs rename to src/Ombi.Schedule/Jobs/Ombi/Interfaces/INewsletterJob.cs diff --git a/src/Ombi.Schedule/Jobs/Ombi/IOmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs similarity index 100% rename from src/Ombi.Schedule/Jobs/Ombi/IOmbiAutomaticUpdater.cs rename to src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs diff --git a/src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs similarity index 100% rename from src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs rename to src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs new file mode 100644 index 000000000..b55c0f69b --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public interface IResendFailedRequests + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IWelcomeEmail.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IWelcomeEmail.cs similarity index 100% rename from src/Ombi.Schedule/Jobs/Ombi/IWelcomeEmail.cs rename to src/Ombi.Schedule/Jobs/Ombi/Interfaces/IWelcomeEmail.cs diff --git a/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs similarity index 91% rename from src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs rename to src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs index 92ca31071..5af4b565d 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs @@ -28,9 +28,9 @@ namespace Ombi.Schedule.Jobs.Ombi return; } - var now = DateTime.Now.AddDays(-settings.DaysAfterResolvedToDelete).Date; + var deletionDate = DateTime.Now.AddDays(settings.DaysAfterResolvedToDelete).Date; var resolved = _issuesRepository.GetAll().Where(x => x.Status == IssueStatus.Resolved); - var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date <= now); + var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date >= deletionDate); foreach (var d in toDelete) { diff --git a/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs b/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs new file mode 100644 index 000000000..fcf8529ce --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.Extensions.Logging; +using Ombi.Api.Plex; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; +using Ombi.Schedule.Jobs.Emby; +using Ombi.Schedule.Jobs.Plex.Interfaces; +using Ombi.Store.Repository; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public class MediaDatabaseRefresh : IMediaDatabaseRefresh + { + public MediaDatabaseRefresh(ISettingsService s, ILogger log, + IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IEmbyContentSync embySync) + { + _settings = s; + _log = log; + _plexRepo = plexRepo; + _embyRepo = embyRepo; + _embyContentSync = embySync; + _settings.ClearCache(); + } + + private readonly ISettingsService _settings; + private readonly ILogger _log; + private readonly IPlexContentRepository _plexRepo; + private readonly IEmbyContentRepository _embyRepo; + private readonly IEmbyContentSync _embyContentSync; + + public async Task Start() + { + try + { + await RemovePlexData(); + await RemoveEmbyData(); + } + catch (Exception e) + { + _log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Media Data Failed"); + } + + } + + private async Task RemoveEmbyData() + { + try + { + var s = await _settings.GetSettingsAsync(); + if (!s.Enable) + { + return; + } + + const string episodeSQL = "DELETE FROM EmbyEpisode"; + const string mainSql = "DELETE FROM EmbyContent"; + await _embyRepo.ExecuteSql(episodeSQL); + await _embyRepo.ExecuteSql(mainSql); + + BackgroundJob.Enqueue(() => _embyContentSync.Start()); + } + catch (Exception e) + { + _log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Emby Data Failed"); + } + } + + private async Task RemovePlexData() + { + try + { + var s = await _settings.GetSettingsAsync(); + if (!s.Enable) + { + return; + } + + const string episodeSQL = "DELETE FROM PlexEpisode"; + const string seasonsSql = "DELETE FROM PlexSeasonsContent"; + const string mainSql = "DELETE FROM PlexServerContent"; + await _plexRepo.ExecuteSql(episodeSQL); + await _plexRepo.ExecuteSql(seasonsSql); + await _plexRepo.ExecuteSql(mainSql); + } + catch (Exception e) + { + _log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Plex Data Failed"); + } + } + + + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _plexRepo?.Dispose(); + _settings?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index f152f6b4b..12415f6e4 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -9,12 +9,14 @@ using MailKit; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using MimeKit; using Ombi.Api.Lidarr; using Ombi.Api.Lidarr.Models; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze; using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; using Ombi.Helpers; using Ombi.Notifications; using Ombi.Notifications.Models; @@ -24,6 +26,7 @@ using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.Notifications; using Ombi.Store.Entities; using Ombi.Store.Repository; +using ContentType = Ombi.Store.Entities.ContentType; namespace Ombi.Schedule.Jobs.Ombi { @@ -33,7 +36,8 @@ namespace Ombi.Schedule.Jobs.Ombi IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService custom, ISettingsService emailSettings, INotificationTemplatesRepository templateRepo, UserManager um, ISettingsService newsletter, ILogger log, - ILidarrApi lidarrApi, IRepository albumCache, ISettingsService lidarrSettings) + ILidarrApi lidarrApi, IRepository albumCache, ISettingsService lidarrSettings, + ISettingsService ombiSettings, ISettingsService plexSettings, ISettingsService embySettings) { _plex = plex; _emby = emby; @@ -46,14 +50,17 @@ namespace Ombi.Schedule.Jobs.Ombi _emailSettings = emailSettings; _newsletterSettings = newsletter; _userManager = um; - _emailSettings.ClearCache(); - _customizationSettings.ClearCache(); - _newsletterSettings.ClearCache(); _log = log; _lidarrApi = lidarrApi; _lidarrAlbumRepository = albumCache; _lidarrSettings = lidarrSettings; - _lidarrSettings.ClearCache(); + _ombiSettings = ombiSettings; + _plexSettings = plexSettings; + _embySettings = embySettings; + _ombiSettings.ClearCache(); + _plexSettings.ClearCache(); + _emailSettings.ClearCache(); + _customizationSettings.ClearCache(); } private readonly IPlexContentRepository _plex; @@ -66,11 +73,14 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly INotificationTemplatesRepository _templateRepo; private readonly ISettingsService _emailSettings; private readonly ISettingsService _newsletterSettings; + private readonly ISettingsService _ombiSettings; private readonly UserManager _userManager; private readonly ILogger _log; private readonly ILidarrApi _lidarrApi; private readonly IRepository _lidarrAlbumRepository; private readonly ISettingsService _lidarrSettings; + private readonly ISettingsService _plexSettings; + private readonly ISettingsService _embySettings; public async Task Start(NewsletterSettings settings, bool test) { @@ -98,12 +108,12 @@ namespace Ombi.Schedule.Jobs.Ombi // Get the Content var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); - var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking(); + var lidarrContent = _lidarrAlbumRepository.GetAll().Where(x => x.FullyAvailable).AsNoTracking(); var addedLog = _recentlyAddedLog.GetAll(); - var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - var addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId); + var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId).ToHashSet(); + var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId).ToHashSet(); + var addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId).ToHashSet(); var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode); @@ -126,6 +136,8 @@ namespace Ombi.Schedule.Jobs.Ombi _log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count()); _log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.Count()); + var plexSettings = await _plexSettings.GetSettingsAsync(); + var embySettings = await _embySettings.GetSettingsAsync(); var body = string.Empty; if (test) { @@ -134,11 +146,11 @@ namespace Ombi.Schedule.Jobs.Ombi var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet(); var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); - body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings); + body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings, embySettings, plexSettings); } else { - body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings); + body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings, embySettings, plexSettings); if (body.IsNullOrEmpty()) { return; @@ -162,7 +174,23 @@ namespace Ombi.Schedule.Jobs.Ombi Email = emails }); } - var emailTasks = new List(); + + var messageContent = ParseTemplate(template, customization); + var email = new NewsletterTemplate(); + + var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); + + var bodyBuilder = new BodyBuilder + { + HtmlBody = html, + }; + + var message = new MimeMessage + { + Body = bodyBuilder.ToMessageBody(), + Subject = messageContent.Subject + }; + foreach (var user in users) { // Get the users to send it to @@ -170,17 +198,13 @@ namespace Ombi.Schedule.Jobs.Ombi { continue; } - - var messageContent = ParseTemplate(template, customization, user); - var email = new NewsletterTemplate(); - - var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); - - emailTasks.Add(_email.Send( - new NotificationMessage { Message = html, Subject = messageContent.Subject, To = user.Email }, - emailSettings)); + // BCC the messages + message.Bcc.Add(new MailboxAddress(user.Email, user.Email)); } + // Send the email + await _email.Send(message, emailSettings); + // Now add all of this to the Recently Added log var recentlyAddedLog = new HashSet(); foreach (var p in plexContentMoviesToSend) @@ -234,7 +258,6 @@ namespace Ombi.Schedule.Jobs.Ombi }); } await _recentlyAddedLog.AddRange(recentlyAddedLog); - await Task.WhenAll(emailTasks.ToArray()); } else { @@ -245,7 +268,7 @@ namespace Ombi.Schedule.Jobs.Ombi { continue; } - var messageContent = ParseTemplate(template, customization, a); + var messageContent = ParseTemplate(template, customization); var email = new NewsletterTemplate(); @@ -305,19 +328,21 @@ namespace Ombi.Schedule.Jobs.Ombi return itemsToReturn; } - private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings, OmbiUser username) + private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings) { var resolver = new NotificationMessageResolver(); var curlys = new NotificationMessageCurlys(); - curlys.SetupNewsletter(settings, username); + curlys.SetupNewsletter(settings); return resolver.ParseMessage(template, curlys); } private async Task BuildHtml(IQueryable plexContentToSend, IQueryable embyContentToSend, - HashSet plexEpisodes, HashSet embyEp, HashSet albums, NewsletterSettings settings) + HashSet plexEpisodes, HashSet embyEp, HashSet albums, NewsletterSettings settings, EmbySettings embySettings, + PlexSettings plexSettings) { + var ombiSettings = await _ombiSettings.GetSettingsAsync(); var sb = new StringBuilder(); var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie); @@ -331,8 +356,16 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append(""); @@ -349,8 +382,16 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append(""); @@ -379,7 +420,7 @@ namespace Ombi.Schedule.Jobs.Ombi return sb.ToString(); } - private async Task ProcessPlexMovies(IQueryable plexContentToSend, StringBuilder sb) + private async Task ProcessPlexMovies(IQueryable plexContentToSend, StringBuilder sb, string defaultLanguageCode) { int count = 0; var ordered = plexContentToSend.OrderByDescending(x => x.AddedAt); @@ -390,7 +431,7 @@ namespace Ombi.Schedule.Jobs.Ombi { continue; } - var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId); + var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId, defaultLanguageCode); var mediaurl = content.Url; if (info == null) { @@ -453,7 +494,7 @@ namespace Ombi.Schedule.Jobs.Ombi } } - private async Task ProcessEmbyMovies(IQueryable embyContent, StringBuilder sb) + private async Task ProcessEmbyMovies(IQueryable embyContent, StringBuilder sb, string defaultLangaugeCode) { int count = 0; var ordered = embyContent.OrderByDescending(x => x.AddedAt); @@ -474,7 +515,7 @@ namespace Ombi.Schedule.Jobs.Ombi } var mediaurl = content.Url; - var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId)); + var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId), defaultLangaugeCode); if (info == null) { continue; @@ -654,7 +695,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddInfoTable(sb); var title = ""; - if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) + if (!string.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) { title = $"{t.Title} ({info.premiered.Remove(4)})"; } else @@ -677,7 +718,7 @@ namespace Ombi.Schedule.Jobs.Ombi foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var episodeString = BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); finalsb.Append("
"); } @@ -715,48 +756,7 @@ namespace Ombi.Schedule.Jobs.Ombi } } - public string BuildEpisodeList(IEnumerable orderedEpisodes) - { - var epSb = new StringBuilder(); - var previousEpisodes = new List(); - var previousEpisode = -1; - foreach (var ep in orderedEpisodes) - { - if (ep - 1 == previousEpisode) - { - // This is the next one - previousEpisodes.Add(ep); - } - else - { - if (previousEpisodes.Count > 1) - { - // End it - epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}, "); - } - else if (previousEpisodes.Count == 1) - { - epSb.Append($"{previousEpisodes.FirstOrDefault()}, "); - } - // New one - previousEpisodes.Clear(); - previousEpisodes.Add(ep); - } - previousEpisode = ep; - } - - if (previousEpisodes.Count > 1) - { - // Got some left over - epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}"); - } - else if(previousEpisodes.Count == 1) - { - epSb.Append(previousEpisodes.FirstOrDefault()); - } - - return epSb.ToString(); - } + private async Task ProcessEmbyTv(HashSet embyContent, StringBuilder sb) { @@ -841,7 +841,7 @@ namespace Ombi.Schedule.Jobs.Ombi foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var episodeString = BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); finalsb.Append("
"); } diff --git a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs index 72f0ef6f5..783fe5f9d 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs @@ -5,18 +5,13 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Net; -using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Hangfire; -using Hangfire.Console; using Hangfire.Server; using Microsoft.Extensions.Logging; - -using Ombi.Api.Service; -using Ombi.Api.Service.Models; using Ombi.Core.Processor; using Ombi.Core.Settings; using Ombi.Helpers; @@ -33,14 +28,13 @@ namespace Ombi.Schedule.Jobs.Ombi public class OmbiAutomaticUpdater : IOmbiAutomaticUpdater { public OmbiAutomaticUpdater(ILogger log, IChangeLogProcessor service, - ISettingsService s, IProcessProvider proc, IRepository appConfig) + ISettingsService s, IProcessProvider proc, IApplicationConfigRepository appConfig) { Logger = log; Processor = service; Settings = s; _processProvider = proc; _appConfig = appConfig; - Settings.ClearCache(); } private ILogger Logger { get; } @@ -48,7 +42,7 @@ namespace Ombi.Schedule.Jobs.Ombi private ISettingsService Settings { get; } private readonly IProcessProvider _processProvider; private static PerformContext Ctx { get; set; } - private readonly IRepository _appConfig; + private readonly IApplicationConfigRepository _appConfig; public string[] GetVersion() { @@ -252,9 +246,8 @@ namespace Ombi.Schedule.Jobs.Ombi private string GetArgs(UpdateSettings settings) { - var config = _appConfig.GetAll(); - var url = config.FirstOrDefault(x => x.Type == ConfigurationTypes.Url); - var storage = config.FirstOrDefault(x => x.Type == ConfigurationTypes.StoragePath); + var url = _appConfig.Get(ConfigurationTypes.Url); + var storage = _appConfig.Get(ConfigurationTypes.StoragePath); var currentLocation = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var processName = (settings.ProcessName.HasValue() ? settings.ProcessName : "Ombi"); @@ -345,7 +338,6 @@ namespace Ombi.Schedule.Jobs.Ombi if (disposing) { - _appConfig?.Dispose(); Settings?.Dispose(); } _disposed = true; diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index d58c29ddc..c9ba5c6b3 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using Hangfire; using Microsoft.Extensions.Logging; +using Ombi.Api.Emby; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze; @@ -21,7 +22,8 @@ namespace Ombi.Schedule.Jobs.Ombi { public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, ILogger log, ITvMazeApi tvApi, ISettingsService plexSettings, - IMovieDbApi movieApi, ISettingsService embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability) + IMovieDbApi movieApi, ISettingsService embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability, + IEmbyApi embyApi) { _plexRepo = plexRepo; _embyRepo = embyRepo; @@ -32,6 +34,7 @@ namespace Ombi.Schedule.Jobs.Ombi _embySettings = embySettings; _plexAvailabilityChecker = plexAvailability; _embyAvaliabilityChecker = embyAvaliability; + _embyApi = embyApi; } private readonly IPlexContentRepository _plexRepo; @@ -43,6 +46,7 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ITvMazeApi _tvApi; private readonly ISettingsService _plexSettings; private readonly ISettingsService _embySettings; + private readonly IEmbyApi _embyApi; public async Task Start() { @@ -54,11 +58,11 @@ namespace Ombi.Schedule.Jobs.Ombi { await StartPlex(); } - + var embySettings = await _embySettings.GetSettingsAsync(); if (embySettings.Enable) { - await StartEmby(); + await StartEmby(embySettings); } } catch (Exception e) @@ -123,9 +127,9 @@ namespace Ombi.Schedule.Jobs.Ombi await StartPlexTv(allTv); } - private async Task StartEmby() + private async Task StartEmby(EmbySettings s) { - await StartEmbyMovies(); + await StartEmbyMovies(s); await StartEmbyTv(); } @@ -158,7 +162,7 @@ namespace Ombi.Schedule.Jobs.Ombi _plexRepo.UpdateWithoutSave(show); } tvCount++; - if (tvCount >= 20) + if (tvCount >= 75) { await _plexRepo.SaveChangesAsync(); tvCount = 0; @@ -198,7 +202,7 @@ namespace Ombi.Schedule.Jobs.Ombi _embyRepo.UpdateWithoutSave(show); } tvCount++; - if (tvCount >= 20) + if (tvCount >= 75) { await _embyRepo.SaveChangesAsync(); tvCount = 0; @@ -229,7 +233,7 @@ namespace Ombi.Schedule.Jobs.Ombi _plexRepo.UpdateWithoutSave(movie); } movieCount++; - if (movieCount >= 20) + if (movieCount >= 75) { await _plexRepo.SaveChangesAsync(); movieCount = 0; @@ -239,31 +243,56 @@ namespace Ombi.Schedule.Jobs.Ombi await _plexRepo.SaveChangesAsync(); } - private async Task StartEmbyMovies() + private async Task StartEmbyMovies(EmbySettings settings) { var allMovies = _embyRepo.GetAll().Where(x => x.Type == EmbyMediaType.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); int movieCount = 0; foreach (var movie in allMovies) { - var hasImdb = movie.ImdbId.HasValue(); - var hasTheMovieDb = movie.TheMovieDbId.HasValue(); + movie.ImdbId.HasValue(); + movie.TheMovieDbId.HasValue(); // Movies don't really use TheTvDb - if (!hasImdb) + // Check if it even has 1 ID + if (!movie.HasImdb && !movie.HasTheMovieDb) { - var imdbId = await GetImdbId(hasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty); + // Ok this sucks, + // The only think I can think that has happened is that we scanned Emby before Emby has got the metadata + // So let's recheck emby to see if they have got the metadata now + _log.LogInformation($"Movie {movie.Title} does not have a ImdbId or TheMovieDbId, so rechecking emby"); + foreach (var server in settings.Servers) + { + _log.LogInformation($"Checking server {server.Name} for upto date metadata"); + var movieInfo = await _embyApi.GetMovieInformation(movie.EmbyId, server.ApiKey, server.AdministratorId, + server.FullUri); + + if (movieInfo.ProviderIds?.Imdb.HasValue() ?? false) + { + movie.ImdbId = movieInfo.ProviderIds.Imdb; + } + + if (movieInfo.ProviderIds?.Tmdb.HasValue() ?? false) + { + movie.TheMovieDbId = movieInfo.ProviderIds.Tmdb; + } + } + } + + if (!movie.HasImdb) + { + var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty); movie.ImdbId = imdbId; _embyRepo.UpdateWithoutSave(movie); } - if (!hasTheMovieDb) + if (!movie.HasTheMovieDb) { - var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title, true); + var id = await GetTheMovieDbId(false, movie.HasImdb, string.Empty, movie.ImdbId, movie.Title, true); movie.TheMovieDbId = id; _embyRepo.UpdateWithoutSave(movie); } movieCount++; - if (movieCount >= 20) + if (movieCount >= 75) { await _embyRepo.SaveChangesAsync(); movieCount = 0; diff --git a/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs new file mode 100644 index 000000000..bc8f93a97 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs @@ -0,0 +1,75 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core; +using Ombi.Core.Senders; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public class ResendFailedRequests : IResendFailedRequests + { + public ResendFailedRequests(IRepository queue, IMovieSender movieSender, ITvSender tvSender, IMusicSender musicSender, + IMovieRequestRepository movieRepo, ITvRequestRepository tvRepo, IMusicRequestRepository music) + { + _requestQueue = queue; + _movieSender = movieSender; + _tvSender = tvSender; + _musicSender = musicSender; + _movieRequestRepository = movieRepo; + _tvRequestRepository = tvRepo; + _musicRequestRepository = music; + } + + private readonly IRepository _requestQueue; + private readonly IMovieSender _movieSender; + private readonly ITvSender _tvSender; + private readonly IMusicSender _musicSender; + private readonly IMovieRequestRepository _movieRequestRepository; + private readonly ITvRequestRepository _tvRequestRepository; + private readonly IMusicRequestRepository _musicRequestRepository; + + public async Task Start() + { + // Get all the failed ones! + var failedRequests = _requestQueue.GetAll().Where(x => !x.Completed.HasValue); + + foreach (var request in failedRequests) + { + if (request.Type == RequestType.Movie) + { + var movieRequest = await _movieRequestRepository.GetAll().FirstOrDefaultAsync(x => x.Id == request.RequestId); + var result = await _movieSender.Send(movieRequest); + if (result.Success) + { + request.Completed = DateTime.UtcNow; + await _requestQueue.SaveChangesAsync(); + } + } + if (request.Type == RequestType.TvShow) + { + var tvRequest = await _tvRequestRepository.GetChild().FirstOrDefaultAsync(x => x.Id == request.RequestId); + var result = await _tvSender.Send(tvRequest); + if (result.Success) + { + request.Completed = DateTime.UtcNow; + await _requestQueue.SaveChangesAsync(); + } + } + if (request.Type == RequestType.Album) + { + var musicRequest = await _musicRequestRepository.GetAll().FirstOrDefaultAsync(x => x.Id == request.RequestId); + var result = await _musicSender.Send(musicRequest); + if (result.Success) + { + request.Completed = DateTime.UtcNow; + await _requestQueue.SaveChangesAsync(); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/WelcomeEmail.cs b/src/Ombi.Schedule/Jobs/Ombi/WelcomeEmail.cs index e260ebed3..f98072e9e 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/WelcomeEmail.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/WelcomeEmail.cs @@ -20,8 +20,6 @@ namespace Ombi.Schedule.Jobs.Ombi _email = provider; _templates = template; _customizationSettings = c; - email.ClearCache(); - _customizationSettings.ClearCache(); } private readonly ISettingsService _emailSettings; diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 3b855740a..3131e5963 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -58,7 +58,7 @@ namespace Ombi.Schedule.Jobs.Plex EpisodeSync = epsiodeSync; Metadata = metadataRefresh; Checker = checker; - plex.ClearCache(); + Plex.ClearCache(); } private ISettingsService Plex { get; } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs index 4f6bf2550..53c82465c 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs @@ -24,8 +24,8 @@ namespace Ombi.Schedule.Jobs.Plex _log = log; _plexSettings = plexSettings; _userManagementSettings = ums; - _userManagementSettings.ClearCache(); _plexSettings.ClearCache(); + _userManagementSettings.ClearCache(); } private readonly IPlexApi _api; diff --git a/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs b/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs index 0aea8cdc2..5c954def8 100644 --- a/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs @@ -16,7 +16,7 @@ namespace Ombi.Schedule.Jobs.Radarr { public class RadarrSync : IRadarrSync { - public RadarrSync(ISettingsService radarr, IRadarrApi radarrApi, ILogger log, IOmbiContext ctx) + public RadarrSync(ISettingsService radarr, IRadarrApi radarrApi, ILogger log, IExternalContext ctx) { RadarrSettings = radarr; RadarrApi = radarrApi; @@ -28,7 +28,7 @@ namespace Ombi.Schedule.Jobs.Radarr private ISettingsService RadarrSettings { get; } private IRadarrApi RadarrApi { get; } private ILogger Logger { get; } - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); diff --git a/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs b/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs index d2330197d..92e0c2d55 100644 --- a/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs +++ b/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -17,7 +16,7 @@ namespace Ombi.Schedule.Jobs.SickRage { public class SickRageSync : ISickRageSync { - public SickRageSync(ISettingsService s, ISickRageApi api, ILogger l, IOmbiContext ctx) + public SickRageSync(ISettingsService s, ISickRageApi api, ILogger l, IExternalContext ctx) { _settings = s; _api = api; @@ -29,7 +28,7 @@ namespace Ombi.Schedule.Jobs.SickRage private readonly ISettingsService _settings; private readonly ISickRageApi _api; private readonly ILogger _log; - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; public async Task Start() { diff --git a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs index ec5502581..e4c00c726 100644 --- a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs @@ -19,7 +19,7 @@ namespace Ombi.Schedule.Jobs.Sonarr { public class SonarrSync : ISonarrSync { - public SonarrSync(ISettingsService s, ISonarrApi api, ILogger l, IOmbiContext ctx) + public SonarrSync(ISettingsService s, ISonarrApi api, ILogger l, IExternalContext ctx) { _settings = s; _api = api; @@ -31,7 +31,7 @@ namespace Ombi.Schedule.Jobs.Sonarr private readonly ISettingsService _settings; private readonly ISonarrApi _api; private readonly ILogger _log; - private readonly IOmbiContext _ctx; + private readonly IExternalContext _ctx; public async Task Start() { diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index fa4c389d3..15128f768 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -10,14 +10,14 @@ - - + + + - @@ -35,6 +35,7 @@ + diff --git a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs index e645097ef..a34bd89e1 100644 --- a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs +++ b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs @@ -174,7 +174,7 @@ namespace Ombi.Schedule.Processor var client = new GitHubClient(Octokit.ProductHeaderValue.Parse("OmbiV3")); var releases = await client.Repository.Release.GetAll("tidusjar", "ombi"); - var latest = releases.FirstOrDefault(x => x.TagName == releaseTag); + var latest = releases.FirstOrDefault(x => x.TagName.Equals(releaseTag, StringComparison.InvariantCultureIgnoreCase)); if (latest.Name.Contains("V2", CompareOptions.IgnoreCase)) { latest = null; diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 19a415a47..6db0768aa 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/Ombi.Settings/Settings/Models/CustomPageSettings.cs b/src/Ombi.Settings/Settings/Models/CustomPageSettings.cs new file mode 100644 index 000000000..18c0125cb --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/CustomPageSettings.cs @@ -0,0 +1,9 @@ +namespace Ombi.Settings.Settings.Models +{ + public class CustomPageSettings : Settings + { + public string Title { get; set; } + public string Html { get; set; } + public string FontAwesomeIcon { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs index 120c14bd8..5f0287fc4 100644 --- a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs @@ -1,54 +1,16 @@ -using System; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json; -using Ombi.Helpers; - -namespace Ombi.Settings.Settings.Models +namespace Ombi.Settings.Settings.Models { public class CustomizationSettings : Settings { public string ApplicationName { get; set; } public string ApplicationUrl { get; set; } - public string CustomCssLink { get; set; } + public string CustomCss { get; set; } public bool EnableCustomDonations { get; set; } public string CustomDonationUrl { get; set; } public string CustomDonationMessage { get; set; } public string Logo { get; set; } - - public string PresetThemeName { get; set; } - public string PresetThemeContent { get; set; } public bool RecentlyAddedPage { get; set; } - - [NotMapped] - public string PresetThemeVersion - { - get - { - if (HasPresetTheme) - { - var parts = PresetThemeName.Split('-'); - return parts[3].Replace(".css", string.Empty); - } - return string.Empty; - } - } - - [NotMapped] - public string PresetThemeDisplayName - { - get - { - if (HasPresetTheme) - { - var parts = PresetThemeName.Split('-'); - return parts[1]; - } - return string.Empty; - } - } - - [NotMapped] - public bool HasPresetTheme => PresetThemeName.HasValue() || PresetThemeContent.HasValue(); + public bool UseCustomPage { get; set; } public void AddToUrl(string part) { diff --git a/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs b/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs index 96df77281..595733dc1 100644 --- a/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs @@ -14,6 +14,7 @@ namespace Ombi.Core.Settings.Models.External public string Name { get; set; } public string ApiKey { get; set; } public string AdministratorId { get; set; } + public string ServerHostname { get; set; } public bool EnableEpisodeSearching { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs index 0c7e17900..bbbe58fd3 100644 --- a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs @@ -18,5 +18,7 @@ public string QualityProfileAnime { get; set; } public string RootPathAnime { get; set; } public bool AddOnly { get; set; } + public bool V3 { get; set; } + public int LanguageProfile { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettings.cs b/src/Ombi.Settings/Settings/Models/JobSettings.cs index 8b283cdf7..4376088a3 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettings.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettings.cs @@ -15,5 +15,7 @@ public string Newsletter { get; set; } public string LidarrArtistSync { get; set; } public string IssuesPurge { get; set; } + public string RetryRequests { get; set; } + public string MediaDatabaseRefresh { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index 4491ca27a..e2080e3cb 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -61,7 +61,14 @@ namespace Ombi.Settings.Settings.Models { return Get(s.IssuesPurge, Cron.Daily()); } - + public static string ResendFailedRequests(JobSettings s) + { + return Get(s.RetryRequests, Cron.Daily(6)); + } + public static string MediaDatabaseRefresh(JobSettings s) + { + return Get(s.MediaDatabaseRefresh, Cron.DayInterval(5)); + } private static string Get(string settings, string defaultCron) { return settings.HasValue() ? settings : defaultCron; diff --git a/src/Ombi.Settings/Settings/Models/Notifications/GotifySettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/GotifySettings.cs new file mode 100644 index 000000000..f0325b0f2 --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/Notifications/GotifySettings.cs @@ -0,0 +1,10 @@ +namespace Ombi.Settings.Settings.Models.Notifications +{ + public class GotifySettings : Settings + { + public bool Enabled { get; set; } + public string BaseUrl { get; set; } + public string ApplicationToken { get; set; } + public sbyte Priority { get; set; } = 4; + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs index bf4d29eb4..e0787326e 100644 --- a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs +++ b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs @@ -4,11 +4,12 @@ { public string BaseUrl { get; set; } public bool CollectAnalyticData { get; set; } + public bool Set { get; set; } public bool Wizard { get; set; } public string ApiKey { get; set; } public bool IgnoreCertificateErrors { get; set; } public bool DoNotSendNotificationsForAutoApprove { get; set; } public bool HideRequestsUsers { get; set; } - + public string DefaultLanguageCode { get; set; } = "en"; } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/VoteSettings.cs b/src/Ombi.Settings/Settings/Models/VoteSettings.cs new file mode 100644 index 000000000..4d63cfea4 --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/VoteSettings.cs @@ -0,0 +1,10 @@ +namespace Ombi.Settings.Settings.Models +{ + public class VoteSettings : Settings + { + public bool Enabled { get; set; } + public int MovieVoteMax { get; set; } + public int MusicVoteMax { get; set; } + public int TvShowVoteMax { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/SettingsService.cs b/src/Ombi.Settings/Settings/SettingsService.cs index 3162c34c2..57ae482a2 100644 --- a/src/Ombi.Settings/Settings/SettingsService.cs +++ b/src/Ombi.Settings/Settings/SettingsService.cs @@ -61,7 +61,7 @@ namespace Ombi.Settings.Settings var model = obj; return model; - }, DateTime.Now.AddHours(2)); + }, DateTime.Now.AddHours(5)); } public bool SaveSettings(T model) diff --git a/src/Ombi.Store/Context/ExternalContext.cs b/src/Ombi.Store/Context/ExternalContext.cs new file mode 100644 index 000000000..ff0581091 --- /dev/null +++ b/src/Ombi.Store/Context/ExternalContext.cs @@ -0,0 +1,69 @@ +using System.IO; +using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; +using Ombi.Store.Entities; + +namespace Ombi.Store.Context +{ + public sealed class ExternalContext : DbContext, IExternalContext + { + private static bool _created; + public ExternalContext() + { + if (_created) return; + + _created = true; + Database.SetCommandTimeout(60); + Database.Migrate(); + } + + public DbSet PlexServerContent { get; set; } + public DbSet PlexSeasonsContent { get; set; } + public DbSet PlexEpisode { get; set; } + public DbSet RadarrCache { get; set; } + public DbSet CouchPotatoCache { get; set; } + public DbSet EmbyContent { get; set; } + public DbSet EmbyEpisode { get; set; } + + public DbSet SonarrCache { get; set; } + public DbSet LidarrArtistCache { get; set; } + public DbSet LidarrAlbumCache { get; set; } + public DbSet SonarrEpisodeCache { get; set; } + public DbSet SickRageCache { get; set; } + public DbSet SickRageEpisodeCache { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var i = StoragePathSingleton.Instance; + if (string.IsNullOrEmpty(i.StoragePath)) + { + i.StoragePath = string.Empty; + } + optionsBuilder.UseSqlite($"Data Source={Path.Combine(i.StoragePath, "OmbiExternal.db")}"); + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity().HasMany(x => x.Episodes) + .WithOne(x => x.Series) + .HasPrincipalKey(x => x.Key) + .HasForeignKey(x => x.GrandparentKey); + + builder.Entity() + .HasOne(p => p.Series) + .WithMany(b => b.Episodes) + .HasPrincipalKey(x => x.EmbyId) + .HasForeignKey(p => p.ParentId); + + base.OnModelCreating(builder); + } + + + public void Seed() + { + // VACUUM; + Database.ExecuteSqlCommand("VACUUM;"); + SaveChanges(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Context/IDbContext.cs b/src/Ombi.Store/Context/IDbContext.cs new file mode 100644 index 000000000..d84aaaa3d --- /dev/null +++ b/src/Ombi.Store/Context/IDbContext.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Ombi.Store.Context +{ + public interface IDbContext : IDisposable + { + EntityEntry Update(object entity); + EntityEntry Update(TEntity entity) where TEntity : class; + int SaveChanges(); + void Seed(); + Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); + DatabaseFacade Database { get; } + EntityEntry Entry(T entry) where T : class; + EntityEntry Attach(TEntity entity) where TEntity : class; + DbSet Set() where TEntity : class; + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Context/IExternalContext.cs b/src/Ombi.Store/Context/IExternalContext.cs new file mode 100644 index 000000000..3f5d79a79 --- /dev/null +++ b/src/Ombi.Store/Context/IExternalContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Ombi.Store.Entities; + +namespace Ombi.Store.Context +{ + public interface IExternalContext : IDbContext + { + DbSet CouchPotatoCache { get; set; } + DbSet EmbyContent { get; set; } + DbSet EmbyEpisode { get; set; } + DbSet LidarrAlbumCache { get; set; } + DbSet LidarrArtistCache { get; set; } + DbSet PlexEpisode { get; set; } + DbSet PlexServerContent { get; set; } + DbSet RadarrCache { get; set; } + DbSet SickRageCache { get; set; } + DbSet SickRageEpisodeCache { get; set; } + DbSet SonarrCache { get; set; } + DbSet SonarrEpisodeCache { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 2c4d809b4..a7fb3b47d 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -9,22 +9,18 @@ using Ombi.Store.Entities.Requests; namespace Ombi.Store.Context { - public interface IOmbiContext : IDisposable + public interface IOmbiContext : IDbContext { - int SaveChanges(); - Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); + + //DbSet PlexServerContent { get; set; } + //DbSet PlexEpisode { get; set; } DbSet Settings { get; set; } - DbSet PlexServerContent { get; set; } - DbSet PlexEpisode { get; set; } - DbSet RadarrCache { get; set; } - DbSet EmbyContent { get; set; } - DbSet EmbyEpisode { get; set; } - DatabaseFacade Database { get; } - EntityEntry Entry(T entry) where T : class; - EntityEntry Attach(TEntity entity) where TEntity : class; - DbSet Set() where TEntity : class; + //DbSet RadarrCache { get; set; } + //DbSet EmbyContent { get; set; } + //DbSet EmbyEpisode { get; set; } DbSet NotificationTemplates { get; set; } DbSet ApplicationConfigurations { get; set; } + DbSet Votes { get; set; } void Seed(); DbSet Audit { get; set; } DbSet MovieRequests { get; set; } @@ -35,14 +31,12 @@ namespace Ombi.Store.Context DbSet IssueCategories { get; set; } DbSet Tokens { get; set; } DbSet SonarrCache { get; set; } - DbSet SonarrEpisodeCache { get; set; } - EntityEntry Update(object entity); - EntityEntry Update(TEntity entity) where TEntity : class; - DbSet CouchPotatoCache { get; set; } - DbSet SickRageCache { get; set; } - DbSet LidarrArtistCache { get; set; } - DbSet LidarrAlbumCache { get; set; } - DbSet SickRageEpisodeCache { get; set; } + //DbSet SonarrEpisodeCache { get; set; } + //DbSet CouchPotatoCache { get; set; } + //DbSet SickRageCache { get; set; } + //DbSet LidarrArtistCache { get; set; } + //DbSet LidarrAlbumCache { get; set; } + //DbSet SickRageEpisodeCache { get; set; } DbSet RequestLogs { get; set; } DbSet RecentlyAddedLogs { get; set; } DbSet RequestSubscription { get; set; } diff --git a/src/Ombi.Store/Context/ISettingsContext.cs b/src/Ombi.Store/Context/ISettingsContext.cs new file mode 100644 index 000000000..3c209c68a --- /dev/null +++ b/src/Ombi.Store/Context/ISettingsContext.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore; +using Ombi.Store.Entities; + +namespace Ombi.Store.Context +{ + public interface ISettingsContext : IDbContext + { + DbSet ApplicationConfigurations { get; set; } + DbSet Settings { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index f34b4c57c..98f4cd9c3 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -17,14 +17,18 @@ namespace Ombi.Store.Context { if (_created) return; + _created = true; + Database.SetCommandTimeout(60); Database.Migrate(); } public DbSet NotificationTemplates { get; set; } - public DbSet Settings { get; set; } + public DbSet ApplicationConfigurations { get; set; } public DbSet PlexServerContent { get; set; } + public DbSet PlexSeasonsContent { get; set; } public DbSet PlexEpisode { get; set; } + public DbSet Settings { get; set; } public DbSet RadarrCache { get; set; } public DbSet CouchPotatoCache { get; set; } public DbSet EmbyContent { get; set; } @@ -40,6 +44,7 @@ namespace Ombi.Store.Context public DbSet IssueComments { get; set; } public DbSet RequestLogs { get; set; } public DbSet RecentlyAddedLogs { get; set; } + public DbSet Votes { get; set; } public DbSet Audit { get; set; } @@ -53,7 +58,7 @@ namespace Ombi.Store.Context public DbSet RequestSubscription { get; set; } public DbSet UserNotificationPreferences { get; set; } public DbSet UserQualityProfileses { get; set; } - public DbSet ApplicationConfigurations { get; set; } + public DbSet RequestQueue { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -84,66 +89,6 @@ namespace Ombi.Store.Context public void Seed() { - - // Add the tokens - var fanArt = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.FanartTv); - if (fanArt == null) - { - ApplicationConfigurations.Add(new ApplicationConfiguration - { - Type = ConfigurationTypes.FanartTv, - Value = "4b6d983efa54d8f45c68432521335f15" - }); - SaveChanges(); - } - var movieDb = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.FanartTv); - if (movieDb == null) - { - ApplicationConfigurations.Add(new ApplicationConfiguration - { - Type = ConfigurationTypes.TheMovieDb, - Value = "b8eabaf5608b88d0298aa189dd90bf00" - }); - SaveChanges(); - } - var notification = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.Notification); - if (notification == null) - { - ApplicationConfigurations.Add(new ApplicationConfiguration - { - Type = ConfigurationTypes.Notification, - Value = "4f0260c4-9c3d-41ab-8d68-27cb5a593f0e" - }); - SaveChanges(); - } - - // VACUUM; - Database.ExecuteSqlCommand("VACUUM;"); - - // Make sure we have the roles - var newsletterRole = Roles.Where(x => x.Name == OmbiRoles.ReceivesNewsletter); - if (!newsletterRole.Any()) - { - Roles.Add(new IdentityRole(OmbiRoles.ReceivesNewsletter) - { - NormalizedName = OmbiRoles.ReceivesNewsletter.ToUpper() - }); - SaveChanges(); - } - var requestMusicRole = Roles.Where(x => x.Name == OmbiRoles.RequestMusic); - if (!requestMusicRole.Any()) - { - Roles.Add(new IdentityRole(OmbiRoles.RequestMusic) - { - NormalizedName = OmbiRoles.RequestMusic.ToUpper() - }); - Roles.Add(new IdentityRole(OmbiRoles.AutoApproveMusic) - { - NormalizedName = OmbiRoles.AutoApproveMusic.ToUpper() - }); - SaveChanges(); - } - // Make sure we have the API User var apiUserExists = Users.Any(x => x.UserName.Equals("Api", StringComparison.CurrentCultureIgnoreCase)); if (!apiUserExists) @@ -164,6 +109,7 @@ namespace Ombi.Store.Context var allAgents = Enum.GetValues(typeof(NotificationAgent)).Cast().ToList(); var allTypes = Enum.GetValues(typeof(NotificationType)).Cast().ToList(); + var needToSave = false; foreach (var agent in allAgents) { foreach (var notificationType in allTypes) @@ -173,6 +119,8 @@ namespace Ombi.Store.Context // We already have this continue; } + + needToSave = true; NotificationTemplates notificationToAdd; switch (notificationType) { @@ -229,7 +177,15 @@ namespace Ombi.Store.Context }; break; case NotificationType.ItemAddedToFaultQueue: - continue; + notificationToAdd = new NotificationTemplates + { + NotificationType = notificationType, + Message = "Hello! The user '{UserName}' has requested {Title} but it could not be added. This has been added into the requests queue and will keep retrying", + Subject = "Item Added To Retry Queue", + Agent = agent, + Enabled = true, + }; + break; case NotificationType.WelcomeEmail: notificationToAdd = new NotificationTemplates { @@ -279,7 +235,11 @@ namespace Ombi.Store.Context NotificationTemplates.Add(notificationToAdd); } } - SaveChanges(); + + if (needToSave) + { + SaveChanges(); + } } } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/SettingsContext.cs b/src/Ombi.Store/Context/SettingsContext.cs new file mode 100644 index 000000000..6c9fad335 --- /dev/null +++ b/src/Ombi.Store/Context/SettingsContext.cs @@ -0,0 +1,69 @@ +using System.IO; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; +using Ombi.Store.Entities; + +namespace Ombi.Store.Context +{ + public sealed class SettingsContext : DbContext, ISettingsContext + { + private static bool _created; + public SettingsContext() + { + if (_created) return; + + _created = true; + Database.SetCommandTimeout(60); + Database.Migrate(); + } + + public DbSet Settings { get; set; } + public DbSet ApplicationConfigurations { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var i = StoragePathSingleton.Instance; + if (string.IsNullOrEmpty(i.StoragePath)) + { + i.StoragePath = string.Empty; + } + optionsBuilder.UseSqlite($"Data Source={Path.Combine(i.StoragePath, "OmbiSettings.db")}"); + } + + public void Seed() + { + // Add the tokens + var fanArt = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.FanartTv); + if (fanArt == null) + { + ApplicationConfigurations.Add(new ApplicationConfiguration + { + Type = ConfigurationTypes.FanartTv, + Value = "4b6d983efa54d8f45c68432521335f15" + }); + SaveChanges(); + } + var movieDb = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.FanartTv); + if (movieDb == null) + { + ApplicationConfigurations.Add(new ApplicationConfiguration + { + Type = ConfigurationTypes.TheMovieDb, + Value = "b8eabaf5608b88d0298aa189dd90bf00" + }); + SaveChanges(); + } + var notification = ApplicationConfigurations.FirstOrDefault(x => x.Type == ConfigurationTypes.Notification); + if (notification == null) + { + ApplicationConfigurations.Add(new ApplicationConfiguration + { + Type = ConfigurationTypes.Notification, + Value = "4f0260c4-9c3d-41ab-8d68-27cb5a593f0e" + }); + SaveChanges(); + } + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/OmbiUser.cs b/src/Ombi.Store/Entities/OmbiUser.cs index 801a50cb1..edb9708a9 100644 --- a/src/Ombi.Store/Entities/OmbiUser.cs +++ b/src/Ombi.Store/Entities/OmbiUser.cs @@ -34,7 +34,7 @@ namespace Ombi.Store.Entities public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue(); [NotMapped] - public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias; + public virtual string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias; [NotMapped] public bool EmailLogin { get; set; } diff --git a/src/Ombi.Store/Entities/PlexServerContent.cs b/src/Ombi.Store/Entities/PlexServerContent.cs index 14028cb57..17d8ceffb 100644 --- a/src/Ombi.Store/Entities/PlexServerContent.cs +++ b/src/Ombi.Store/Entities/PlexServerContent.cs @@ -42,13 +42,9 @@ namespace Ombi.Store.Entities public PlexMediaTypeEntity Type { get; set; } public string Url { get; set; } - - /// - /// Only used for TV Shows - /// - public virtual ICollection Seasons { get; set; } public ICollection Episodes { get; set; } + public ICollection Seasons { get; set; } /// /// Plex's internal ID for this item diff --git a/src/Ombi.Store/Entities/RequestQueue.cs b/src/Ombi.Store/Entities/RequestQueue.cs new file mode 100644 index 000000000..85f73e04b --- /dev/null +++ b/src/Ombi.Store/Entities/RequestQueue.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("RequestQueue")] + public class RequestQueue : Entity + { + public int RequestId { get; set; } + public RequestType Type { get; set; } + public DateTime Dts { get; set; } + public string Error { get; set; } + public DateTime? Completed { get; set; } + public int RetryCount { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/RequestType.cs b/src/Ombi.Store/Entities/RequestType.cs index 151453bdd..4d2d20ac4 100644 --- a/src/Ombi.Store/Entities/RequestType.cs +++ b/src/Ombi.Store/Entities/RequestType.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Ombi.Store.Entities +namespace Ombi.Store.Entities { public enum RequestType { diff --git a/src/Ombi.Store/Entities/Requests/BaseRequest.cs b/src/Ombi.Store/Entities/Requests/BaseRequest.cs index f224032f1..596041a51 100644 --- a/src/Ombi.Store/Entities/Requests/BaseRequest.cs +++ b/src/Ombi.Store/Entities/Requests/BaseRequest.cs @@ -17,6 +17,7 @@ namespace Ombi.Store.Entities.Requests public DateTime MarkedAsDenied { get; set; } public string DeniedReason { get; set; } public RequestType RequestType { get; set; } + public string RequestedByAlias { get; set; } [ForeignKey(nameof(RequestedUserId))] public OmbiUser RequestedUser { get; set; } diff --git a/src/Ombi.Store/Entities/Requests/ChildRequests.cs b/src/Ombi.Store/Entities/Requests/ChildRequests.cs index 3b5156ce5..f212aa3e7 100644 --- a/src/Ombi.Store/Entities/Requests/ChildRequests.cs +++ b/src/Ombi.Store/Entities/Requests/ChildRequests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Ombi.Store.Repository.Requests; @@ -22,6 +23,8 @@ namespace Ombi.Store.Entities.Requests [NotMapped] public bool ShowSubscribe { get; set; } + [NotMapped] + public DateTime ReleaseYear { get; set; } // Used in the ExistingPlexRequestRule.cs [ForeignKey(nameof(IssueId))] public List Issues { get; set; } diff --git a/src/Ombi.Store/Entities/Requests/MovieRequests.cs b/src/Ombi.Store/Entities/Requests/MovieRequests.cs index 675035140..677a4292c 100644 --- a/src/Ombi.Store/Entities/Requests/MovieRequests.cs +++ b/src/Ombi.Store/Entities/Requests/MovieRequests.cs @@ -1,6 +1,7 @@ -using System; +using Ombi.Helpers; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; namespace Ombi.Store.Entities.Requests { @@ -19,5 +20,14 @@ namespace Ombi.Store.Entities.Requests public int RootPathOverride { get; set; } public int QualityOverride { get; set; } + + /// + /// Only Use for setting the Language Code, Use the LanguageCode property for reading + /// + public string LangCode { get; set; } + + [NotMapped] + [JsonIgnore] + public string LanguageCode => LangCode.IsNullOrEmpty() ? "en" : LangCode; } } diff --git a/src/Ombi.Store/Entities/Requests/SeasonRequests.cs b/src/Ombi.Store/Entities/Requests/SeasonRequests.cs index 521cf5b94..6abaa4de3 100644 --- a/src/Ombi.Store/Entities/Requests/SeasonRequests.cs +++ b/src/Ombi.Store/Entities/Requests/SeasonRequests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Globalization; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; @@ -27,9 +28,10 @@ namespace Ombi.Store.Repository.Requests public bool Approved { get; set; } public bool Requested { get; set; } - public int SeasonId { get; set; } [ForeignKey(nameof(SeasonId))] public SeasonRequests Season { get; set; } + + [NotMapped] public string AirDateDisplay => AirDate == DateTime.MinValue ? "Unknown" : AirDate.ToString(CultureInfo.InvariantCulture); } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/TvRequests.cs b/src/Ombi.Store/Entities/Requests/TvRequests.cs index 432bc88ab..5e33c016f 100644 --- a/src/Ombi.Store/Entities/Requests/TvRequests.cs +++ b/src/Ombi.Store/Entities/Requests/TvRequests.cs @@ -17,10 +17,6 @@ namespace Ombi.Store.Entities.Requests public DateTime ReleaseDate { get; set; } public string Status { get; set; } - /// - /// This is so we can correctly send the right amount of seasons to Sonarr - /// - [NotMapped] public int TotalSeasons { get; set; } public List ChildRequests { get; set; } diff --git a/src/Ombi.Store/Entities/Votes.cs b/src/Ombi.Store/Entities/Votes.cs new file mode 100644 index 000000000..61e5651da --- /dev/null +++ b/src/Ombi.Store/Entities/Votes.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("Votes")] + public class Votes : Entity + { + public int RequestId { get; set; } + public VoteType VoteType { get; set; } + public RequestType RequestType { get; set; } + public string UserId { get; set; } + public DateTime Date { get; set; } + public bool Deleted { get; set; } + + [ForeignKey(nameof(UserId))] + public OmbiUser User { get; set; } + } + + public enum VoteType + { + Upvote = 0, + Downvote = 1 + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs b/src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs new file mode 100644 index 000000000..5d9b47c26 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs @@ -0,0 +1,1182 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180928201334_Votes")] + partial class Votes + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180928201334_Votes.cs b/src/Ombi.Store/Migrations/20180928201334_Votes.cs new file mode 100644 index 000000000..1c64a19aa --- /dev/null +++ b/src/Ombi.Store/Migrations/20180928201334_Votes.cs @@ -0,0 +1,46 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class Votes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Votes", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RequestId = table.Column(nullable: false), + VoteType = table.Column(nullable: false), + RequestType = table.Column(nullable: false), + UserId = table.Column(nullable: true), + Date = table.Column(nullable: false), + Deleted = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Votes", x => x.Id); + table.ForeignKey( + name: "FK_Votes_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Votes_UserId", + table: "Votes", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Votes"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20181204084915_RequestQueue.Designer.cs b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.Designer.cs new file mode 100644 index 000000000..468abb2c7 --- /dev/null +++ b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.Designer.cs @@ -0,0 +1,1204 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20181204084915_RequestQueue")] + partial class RequestQueue + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20181204084915_RequestQueue.cs b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.cs new file mode 100644 index 000000000..820ce50af --- /dev/null +++ b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class RequestQueue : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "RequestQueue", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RequestId = table.Column(nullable: false), + Type = table.Column(nullable: false), + Dts = table.Column(nullable: false), + Error = table.Column(nullable: true), + Completed = table.Column(nullable: true), + RetryCount = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RequestQueue", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RequestQueue"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20190104203305_LanguageCode.Designer.cs b/src/Ombi.Store/Migrations/20190104203305_LanguageCode.Designer.cs new file mode 100644 index 000000000..301622b41 --- /dev/null +++ b/src/Ombi.Store/Migrations/20190104203305_LanguageCode.Designer.cs @@ -0,0 +1,1206 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20190104203305_LanguageCode")] + partial class LanguageCode + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("LangCode"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20190104203305_LanguageCode.cs b/src/Ombi.Store/Migrations/20190104203305_LanguageCode.cs new file mode 100644 index 000000000..7372dc0d6 --- /dev/null +++ b/src/Ombi.Store/Migrations/20190104203305_LanguageCode.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class LanguageCode : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LangCode", + table: "MovieRequests", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LangCode", + table: "MovieRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs b/src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs new file mode 100644 index 000000000..45197bdfb --- /dev/null +++ b/src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs @@ -0,0 +1,1212 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20190116212601_RequestedByAlias")] + partial class RequestedByAlias + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.1-servicing-10028"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("LangCode"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs b/src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs new file mode 100644 index 000000000..1e55c2f6e --- /dev/null +++ b/src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class RequestedByAlias : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequestedByAlias", + table: "MovieRequests", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequestedByAlias", + table: "ChildRequests", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequestedByAlias", + table: "AlbumRequests", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RequestedByAlias", + table: "MovieRequests"); + + migrationBuilder.DropColumn( + name: "RequestedByAlias", + table: "ChildRequests"); + + migrationBuilder.DropColumn( + name: "RequestedByAlias", + table: "AlbumRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/20190216224539_Roles.Designer.cs b/src/Ombi.Store/Migrations/20190216224539_Roles.Designer.cs new file mode 100644 index 000000000..f0aeb88bc --- /dev/null +++ b/src/Ombi.Store/Migrations/20190216224539_Roles.Designer.cs @@ -0,0 +1,1212 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20190216224539_Roles")] + partial class Roles + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.1-servicing-10028"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("LangCode"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20190216224539_Roles.cs b/src/Ombi.Store/Migrations/20190216224539_Roles.cs new file mode 100644 index 000000000..9c2f91c12 --- /dev/null +++ b/src/Ombi.Store/Migrations/20190216224539_Roles.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Ombi.Helpers; + +namespace Ombi.Store.Migrations +{ + public partial class Roles : Migration + { + protected override void Up(MigrationBuilder mb) + { + // Make sure we have the roles + InsertRole(mb, OmbiRoles.ReceivesNewsletter); + InsertRole(mb, OmbiRoles.RequestMusic); + InsertRole(mb, OmbiRoles.AutoApproveMusic); + InsertRole(mb, OmbiRoles.ManageOwnRequests); + InsertRole(mb, OmbiRoles.EditCustomPage); + } + + private void InsertRole(MigrationBuilder mb, string role) + { + mb.Sql($@" +INSERT INTO AspnetRoles(Id, ConcurrencyStamp, Name, NormalizedName) +SELECT '{Guid.NewGuid().ToString()}','{Guid.NewGuid().ToString()}','{role}', '{role.ToUpper()}' +WHERE NOT EXISTS(SELECT 1 FROM AspnetRoles WHERE Name = '{role}');"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.Designer.cs b/src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.Designer.cs new file mode 100644 index 000000000..7341ff5fe --- /dev/null +++ b/src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.Designer.cs @@ -0,0 +1,1214 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20190216231519_TvRequestsTotalSeasons")] + partial class TvRequestsTotalSeasons + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.1-servicing-10028"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("LangCode"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TotalSeasons"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.cs b/src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.cs new file mode 100644 index 000000000..0bae50c15 --- /dev/null +++ b/src/Ombi.Store/Migrations/20190216231519_TvRequestsTotalSeasons.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class TvRequestsTotalSeasons : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TotalSeasons", + table: "TvRequests", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "TotalSeasons", + table: "TvRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/External/20181004134907_Inital.Designer.cs b/src/Ombi.Store/Migrations/External/20181004134907_Inital.Designer.cs new file mode 100644 index 000000000..776d3e082 --- /dev/null +++ b/src/Ombi.Store/Migrations/External/20181004134907_Inital.Designer.cs @@ -0,0 +1,312 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations.External +{ + [DbContext(typeof(ExternalContext))] + [Migration("20181004134907_Inital")] + partial class Inital + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/External/20181004134907_Inital.cs b/src/Ombi.Store/Migrations/External/20181004134907_Inital.cs new file mode 100644 index 000000000..bc4dc509e --- /dev/null +++ b/src/Ombi.Store/Migrations/External/20181004134907_Inital.cs @@ -0,0 +1,308 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.External +{ + public partial class Inital : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CouchPotatoCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + TheMovieDbId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CouchPotatoCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "EmbyContent", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(nullable: true), + ProviderId = table.Column(nullable: true), + EmbyId = table.Column(nullable: false), + Type = table.Column(nullable: false), + AddedAt = table.Column(nullable: false), + ImdbId = table.Column(nullable: true), + TheMovieDbId = table.Column(nullable: true), + TvDbId = table.Column(nullable: true), + Url = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EmbyContent", x => x.Id); + table.UniqueConstraint("AK_EmbyContent_EmbyId", x => x.EmbyId); + }); + + migrationBuilder.CreateTable( + name: "LidarrAlbumCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ArtistId = table.Column(nullable: false), + ForeignAlbumId = table.Column(nullable: true), + TrackCount = table.Column(nullable: false), + ReleaseDate = table.Column(nullable: false), + Monitored = table.Column(nullable: false), + Title = table.Column(nullable: true), + PercentOfTracks = table.Column(nullable: false), + AddedAt = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LidarrAlbumCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "LidarrArtistCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ArtistId = table.Column(nullable: false), + ArtistName = table.Column(nullable: true), + ForeignArtistId = table.Column(nullable: true), + Monitored = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LidarrArtistCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "PlexServerContent", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(nullable: true), + ReleaseYear = table.Column(nullable: true), + ImdbId = table.Column(nullable: true), + TvDbId = table.Column(nullable: true), + TheMovieDbId = table.Column(nullable: true), + Type = table.Column(nullable: false), + Url = table.Column(nullable: true), + Key = table.Column(nullable: false), + AddedAt = table.Column(nullable: false), + Quality = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexServerContent", x => x.Id); + table.UniqueConstraint("AK_PlexServerContent_Key", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "RadarrCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + TheMovieDbId = table.Column(nullable: false), + HasFile = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RadarrCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SickRageCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + TvDbId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SickRageCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SickRageEpisodeCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SeasonNumber = table.Column(nullable: false), + EpisodeNumber = table.Column(nullable: false), + TvDbId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SickRageEpisodeCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SonarrCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + TvDbId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SonarrCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SonarrEpisodeCache", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SeasonNumber = table.Column(nullable: false), + EpisodeNumber = table.Column(nullable: false), + TvDbId = table.Column(nullable: false), + HasFile = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SonarrEpisodeCache", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "EmbyEpisode", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(nullable: true), + EmbyId = table.Column(nullable: true), + EpisodeNumber = table.Column(nullable: false), + SeasonNumber = table.Column(nullable: false), + ParentId = table.Column(nullable: true), + ProviderId = table.Column(nullable: true), + AddedAt = table.Column(nullable: false), + TvDbId = table.Column(nullable: true), + ImdbId = table.Column(nullable: true), + TheMovieDbId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EmbyEpisode", x => x.Id); + table.ForeignKey( + name: "FK_EmbyEpisode_EmbyContent_ParentId", + column: x => x.ParentId, + principalTable: "EmbyContent", + principalColumn: "EmbyId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "PlexEpisode", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + EpisodeNumber = table.Column(nullable: false), + SeasonNumber = table.Column(nullable: false), + Key = table.Column(nullable: false), + Title = table.Column(nullable: true), + ParentKey = table.Column(nullable: false), + GrandparentKey = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexEpisode", x => x.Id); + table.ForeignKey( + name: "FK_PlexEpisode_PlexServerContent_GrandparentKey", + column: x => x.GrandparentKey, + principalTable: "PlexServerContent", + principalColumn: "Key", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PlexSeasonsContent", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + PlexContentId = table.Column(nullable: false), + SeasonNumber = table.Column(nullable: false), + SeasonKey = table.Column(nullable: false), + ParentKey = table.Column(nullable: false), + PlexServerContentId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexSeasonsContent", x => x.Id); + table.ForeignKey( + name: "FK_PlexSeasonsContent_PlexServerContent_PlexServerContentId", + column: x => x.PlexServerContentId, + principalTable: "PlexServerContent", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_EmbyEpisode_ParentId", + table: "EmbyEpisode", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_PlexEpisode_GrandparentKey", + table: "PlexEpisode", + column: "GrandparentKey"); + + migrationBuilder.CreateIndex( + name: "IX_PlexSeasonsContent_PlexServerContentId", + table: "PlexSeasonsContent", + column: "PlexServerContentId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CouchPotatoCache"); + + migrationBuilder.DropTable( + name: "EmbyEpisode"); + + migrationBuilder.DropTable( + name: "LidarrAlbumCache"); + + migrationBuilder.DropTable( + name: "LidarrArtistCache"); + + migrationBuilder.DropTable( + name: "PlexEpisode"); + + migrationBuilder.DropTable( + name: "PlexSeasonsContent"); + + migrationBuilder.DropTable( + name: "RadarrCache"); + + migrationBuilder.DropTable( + name: "SickRageCache"); + + migrationBuilder.DropTable( + name: "SickRageEpisodeCache"); + + migrationBuilder.DropTable( + name: "SonarrCache"); + + migrationBuilder.DropTable( + name: "SonarrEpisodeCache"); + + migrationBuilder.DropTable( + name: "EmbyContent"); + + migrationBuilder.DropTable( + name: "PlexServerContent"); + } + } +} diff --git a/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs b/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs new file mode 100644 index 000000000..4e97b5514 --- /dev/null +++ b/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs @@ -0,0 +1,310 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations.External +{ + [DbContext(typeof(ExternalContext))] + partial class ExternalContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 86694ec23..466011d43 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + .HasAnnotation("ProductVersion", "2.2.1-servicing-10028"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -508,6 +508,46 @@ namespace Ombi.Store.Migrations b.ToTable("RecentlyAddedLog"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => { b.Property("Id") @@ -543,6 +583,8 @@ namespace Ombi.Store.Migrations b.Property("RequestType"); + b.Property("RequestedByAlias"); + b.Property("RequestedDate"); b.Property("RequestedUserId"); @@ -581,6 +623,8 @@ namespace Ombi.Store.Migrations b.Property("RequestType"); + b.Property("RequestedByAlias"); + b.Property("RequestedDate"); b.Property("RequestedUserId"); @@ -691,6 +735,8 @@ namespace Ombi.Store.Migrations b.Property("IssueId"); + b.Property("LangCode"); + b.Property("MarkedAsApproved"); b.Property("MarkedAsAvailable"); @@ -707,6 +753,8 @@ namespace Ombi.Store.Migrations b.Property("RequestType"); + b.Property("RequestedByAlias"); + b.Property("RequestedDate"); b.Property("RequestedUserId"); @@ -771,6 +819,8 @@ namespace Ombi.Store.Migrations b.Property("Title"); + b.Property("TotalSeasons"); + b.Property("TvDbId"); b.HasKey("Id"); @@ -778,24 +828,6 @@ namespace Ombi.Store.Migrations b.ToTable("TvRequests"); }); - modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("RequestId"); - - b.Property("RequestType"); - - b.Property("UserId"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("RequestSubscription"); - }); - modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => { b.Property("Id") @@ -916,6 +948,30 @@ namespace Ombi.Store.Migrations b.ToTable("UserQualityProfiles"); }); + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => { b.Property("Id") @@ -1036,6 +1092,13 @@ namespace Ombi.Store.Migrations .HasForeignKey("PlexServerContentId"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") @@ -1100,28 +1163,28 @@ namespace Ombi.Store.Migrations .HasForeignKey("UserId"); }); - modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); }); - modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() + .WithMany("UserNotificationPreferences") .HasForeignKey("UserId"); }); - modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany("UserNotificationPreferences") + .WithMany() .HasForeignKey("UserId"); }); - modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() diff --git a/src/Ombi.Store/Migrations/Settings/20181004132516_Inital.Designer.cs b/src/Ombi.Store/Migrations/Settings/20181004132516_Inital.Designer.cs new file mode 100644 index 000000000..60b9f9adc --- /dev/null +++ b/src/Ombi.Store/Migrations/Settings/20181004132516_Inital.Designer.cs @@ -0,0 +1,50 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations.Settings +{ + [DbContext(typeof(SettingsContext))] + [Migration("20181004132516_Inital")] + partial class Inital + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/Settings/20181004132516_Inital.cs b/src/Ombi.Store/Migrations/Settings/20181004132516_Inital.cs new file mode 100644 index 000000000..956e0a14a --- /dev/null +++ b/src/Ombi.Store/Migrations/Settings/20181004132516_Inital.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.Settings +{ + public partial class Inital : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ApplicationConfiguration", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Type = table.Column(nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApplicationConfiguration", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "GlobalSettings", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Content = table.Column(nullable: true), + SettingsName = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_GlobalSettings", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApplicationConfiguration"); + + migrationBuilder.DropTable( + name: "GlobalSettings"); + } + } +} diff --git a/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs b/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs new file mode 100644 index 000000000..fe063ef8b --- /dev/null +++ b/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs @@ -0,0 +1,48 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations.Settings +{ + [DbContext(typeof(SettingsContext))] + partial class SettingsContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687"); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index cdbd3fe84..8dfcc1c28 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -10,11 +10,12 @@ - - - - - + + + + + + diff --git a/src/Ombi.Store/Repository/ApplicationConfigRepository.cs b/src/Ombi.Store/Repository/ApplicationConfigRepository.cs index 3b4476454..31ec3313c 100644 --- a/src/Ombi.Store/Repository/ApplicationConfigRepository.cs +++ b/src/Ombi.Store/Repository/ApplicationConfigRepository.cs @@ -8,16 +8,20 @@ namespace Ombi.Store.Repository { public class ApplicationConfigRepository : IApplicationConfigRepository { - public ApplicationConfigRepository(IOmbiContext ctx) + public ApplicationConfigRepository(ISettingsContext ctx) { Ctx = ctx; } - private IOmbiContext Ctx { get; } + private ISettingsContext Ctx { get; } - public async Task Get(ConfigurationTypes type) + public Task GetAsync(ConfigurationTypes type) { - return await Ctx.ApplicationConfigurations.FirstOrDefaultAsync(x => x.Type == type); + return Ctx.ApplicationConfigurations.FirstOrDefaultAsync(x => x.Type == type); + } + public ApplicationConfiguration Get(ConfigurationTypes type) + { + return Ctx.ApplicationConfigurations.FirstOrDefault(x => x.Type == type); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/BaseRepository.cs b/src/Ombi.Store/Repository/BaseRepository.cs new file mode 100644 index 000000000..0741a79b6 --- /dev/null +++ b/src/Ombi.Store/Repository/BaseRepository.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public class BaseRepository : IRepository where T : Entity where U : IDbContext + { + public BaseRepository(U ctx) + { + _ctx = ctx; + _db = _ctx.Set(); + } + public DbSet _db { get; } + private readonly U _ctx; + + public async Task Find(object key) + { + return await _db.FindAsync(key); + } + + public IQueryable GetAll() + { + return _db.AsQueryable(); + } + + public async Task FirstOrDefaultAsync(Expression> predicate) + { + return await _db.FirstOrDefaultAsync(predicate); + } + + public async Task AddRange(IEnumerable content, bool save = true) + { + _db.AddRange(content); + if (save) + { + await InternalSaveChanges(); + } + } + + public async Task Add(T content) + { + await _db.AddAsync(content); + await InternalSaveChanges(); + return content; + } + + public async Task Delete(T request) + { + _db.Remove(request); + await InternalSaveChanges(); + } + + public async Task DeleteRange(IEnumerable req) + { + _db.RemoveRange(req); + await InternalSaveChanges(); + } + + public async Task SaveChangesAsync() + { + return await InternalSaveChanges(); + } + + public IIncludableQueryable Include( + IQueryable source, Expression> navigationPropertyPath) + where TEntity : class + { + return source.Include(navigationPropertyPath); + } + + public async Task ExecuteSql(string sql) + { + await _ctx.Database.ExecuteSqlCommandAsync(sql); + } + + protected async Task InternalSaveChanges() + { + return await _ctx.SaveChangesAsync(); + } + + + private bool _disposed; + // Protected implementation of Dispose pattern. + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _ctx?.Dispose(); + } + + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/EmbyContentRepository.cs b/src/Ombi.Store/Repository/EmbyContentRepository.cs index c4377f929..2ada709ab 100644 --- a/src/Ombi.Store/Repository/EmbyContentRepository.cs +++ b/src/Ombi.Store/Repository/EmbyContentRepository.cs @@ -35,15 +35,15 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class EmbyContentRepository : Repository, IEmbyContentRepository + public class EmbyContentRepository : ExternalRepository, IEmbyContentRepository { - public EmbyContentRepository(IOmbiContext db):base(db) + public EmbyContentRepository(IExternalContext db):base(db) { Db = db; } - private IOmbiContext Db { get; } + private IExternalContext Db { get; } public async Task GetByImdbId(string imdbid) @@ -72,7 +72,7 @@ namespace Ombi.Store.Repository public async Task Update(EmbyContent existingContent) { Db.EmbyContent.Update(existingContent); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public IQueryable GetAllEpisodes() @@ -83,7 +83,7 @@ namespace Ombi.Store.Repository public async Task Add(EmbyEpisode content) { await Db.EmbyEpisode.AddAsync(content); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); return content; } public async Task GetEpisodeByEmbyId(string key) @@ -94,12 +94,13 @@ namespace Ombi.Store.Repository public async Task AddRange(IEnumerable content) { Db.EmbyEpisode.AddRange(content); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public void UpdateWithoutSave(EmbyContent existingContent) { Db.EmbyContent.Update(existingContent); } + } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/ExternalRepository.cs b/src/Ombi.Store/Repository/ExternalRepository.cs new file mode 100644 index 000000000..d7494afd4 --- /dev/null +++ b/src/Ombi.Store/Repository/ExternalRepository.cs @@ -0,0 +1,12 @@ +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public class ExternalRepository : BaseRepository, IExternalRepository where T : Entity + { + public ExternalRepository(IExternalContext ctx) : base(ctx) + { + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IApplicationConfigRepository.cs b/src/Ombi.Store/Repository/IApplicationConfigRepository.cs index 9e35bde7e..6aa76e357 100644 --- a/src/Ombi.Store/Repository/IApplicationConfigRepository.cs +++ b/src/Ombi.Store/Repository/IApplicationConfigRepository.cs @@ -5,6 +5,7 @@ namespace Ombi.Store.Repository { public interface IApplicationConfigRepository { - Task Get(ConfigurationTypes type); + Task GetAsync(ConfigurationTypes type); + ApplicationConfiguration Get(ConfigurationTypes type); } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IExternalRepository.cs b/src/Ombi.Store/Repository/IExternalRepository.cs new file mode 100644 index 000000000..de8b6db67 --- /dev/null +++ b/src/Ombi.Store/Repository/IExternalRepository.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public interface IExternalRepository : IDisposable where T : Entity + { + Task Find(object key); + IQueryable GetAll(); + Task FirstOrDefaultAsync(Expression> predicate); + Task AddRange(IEnumerable content, bool save = true); + Task Add(T content); + Task DeleteRange(IEnumerable req); + Task Delete(T request); + Task SaveChangesAsync(); + + IIncludableQueryable Include( + IQueryable source, Expression> navigationPropertyPath) + where TEntity : class; + + Task ExecuteSql(string sql); + DbSet _db { get; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IPlexContentRepository.cs b/src/Ombi.Store/Repository/IPlexContentRepository.cs index 381a89fa3..7bce2e75a 100644 --- a/src/Ombi.Store/Repository/IPlexContentRepository.cs +++ b/src/Ombi.Store/Repository/IPlexContentRepository.cs @@ -7,7 +7,7 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IPlexContentRepository : IRepository + public interface IPlexContentRepository : IExternalRepository { Task ContentExists(string providerId); Task Get(string providerId); diff --git a/src/Ombi.Store/Repository/NotificationTemplatesRepository.cs b/src/Ombi.Store/Repository/NotificationTemplatesRepository.cs index 175d0e6a9..8f3968dc0 100644 --- a/src/Ombi.Store/Repository/NotificationTemplatesRepository.cs +++ b/src/Ombi.Store/Repository/NotificationTemplatesRepository.cs @@ -45,7 +45,7 @@ namespace Ombi.Store.Repository Db.Attach(template); Db.Entry(template).State = EntityState.Modified; } - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task UpdateRange(IEnumerable templates) @@ -56,16 +56,21 @@ namespace Ombi.Store.Repository Db.Attach(t); Db.Entry(t).State = EntityState.Modified; } - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task Insert(NotificationTemplates entity) { var settings = await Db.NotificationTemplates.AddAsync(entity).ConfigureAwait(false); - await Db.SaveChangesAsync().ConfigureAwait(false); + await InternalSaveChanges().ConfigureAwait(false); return settings.Entity; } + private async Task InternalSaveChanges() + { + return await Db.SaveChangesAsync(); + } + private bool _disposed; // Protected implementation of Dispose pattern. protected virtual void Dispose(bool disposing) diff --git a/src/Ombi.Store/Repository/PlexContentRepository.cs b/src/Ombi.Store/Repository/PlexContentRepository.cs index e452eeb7d..37275a47c 100644 --- a/src/Ombi.Store/Repository/PlexContentRepository.cs +++ b/src/Ombi.Store/Repository/PlexContentRepository.cs @@ -36,15 +36,15 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class PlexServerContentRepository : Repository, IPlexContentRepository + public class PlexServerContentRepository : ExternalRepository, IPlexContentRepository { - public PlexServerContentRepository(IOmbiContext db) : base(db) + public PlexServerContentRepository(IExternalContext db) : base(db) { Db = db; } - private IOmbiContext Db { get; } + private IExternalContext Db { get; } public async Task ContentExists(string providerId) @@ -96,7 +96,7 @@ namespace Ombi.Store.Repository public async Task Update(PlexServerContent existingContent) { Db.PlexServerContent.Update(existingContent); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public void UpdateWithoutSave(PlexServerContent existingContent) { @@ -106,7 +106,7 @@ namespace Ombi.Store.Repository public async Task UpdateRange(IEnumerable existingContent) { Db.PlexServerContent.UpdateRange(existingContent); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public IQueryable GetAllEpisodes() @@ -127,14 +127,14 @@ namespace Ombi.Store.Repository public async Task Add(PlexEpisode content) { await Db.PlexEpisode.AddAsync(content); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); return content; } public async Task DeleteEpisode(PlexEpisode content) { Db.PlexEpisode.Remove(content); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task GetEpisodeByKey(int key) @@ -144,7 +144,7 @@ namespace Ombi.Store.Repository public async Task AddRange(IEnumerable content) { Db.PlexEpisode.AddRange(content); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Repository.cs b/src/Ombi.Store/Repository/Repository.cs index 8c07c2371..9d49ded58 100644 --- a/src/Ombi.Store/Repository/Repository.cs +++ b/src/Ombi.Store/Repository/Repository.cs @@ -1,105 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; -using Ombi.Store.Context; +using Ombi.Store.Context; using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class Repository : IRepository where T : Entity + public class Repository : BaseRepository, IRepository where T : Entity { - public Repository(IOmbiContext ctx) + public Repository(IOmbiContext ctx) : base(ctx) { - _ctx = ctx; - _db = _ctx.Set(); - } - public DbSet _db { get; } - private readonly IOmbiContext _ctx; - - public async Task Find(object key) - { - return await _db.FindAsync(key); - } - - public IQueryable GetAll() - { - return _db.AsQueryable(); - } - - public async Task FirstOrDefaultAsync(Expression> predicate) - { - return await _db.FirstOrDefaultAsync(predicate); - } - - public async Task AddRange(IEnumerable content, bool save = true) - { - _db.AddRange(content); - if (save) - { - await _ctx.SaveChangesAsync(); - } - } - - public async Task Add(T content) - { - await _db.AddAsync(content); - await _ctx.SaveChangesAsync(); - return content; - } - - public async Task Delete(T request) - { - _db.Remove(request); - await _ctx.SaveChangesAsync(); - } - - public async Task DeleteRange(IEnumerable req) - { - _db.RemoveRange(req); - await _ctx.SaveChangesAsync(); - } - - public async Task SaveChangesAsync() - { - return await _ctx.SaveChangesAsync(); - } - - public IIncludableQueryable Include( - IQueryable source, Expression> navigationPropertyPath) - where TEntity : class - { - return source.Include(navigationPropertyPath); - } - - public async Task ExecuteSql(string sql) - { - await _ctx.Database.ExecuteSqlCommandAsync(sql); - } - - - private bool _disposed; - // Protected implementation of Dispose pattern. - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - _ctx?.Dispose(); - } - - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs index d4a550528..2cea81200 100644 --- a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; using Ombi.Store.Context; using Ombi.Store.Entities.Requests; @@ -70,12 +71,12 @@ namespace Ombi.Store.Repository.Requests Db.MovieRequests.Attach(request); Db.Update(request); } - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task Save() { - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs index 59edf265a..971d53b39 100644 --- a/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; using Ombi.Store.Context; using Ombi.Store.Entities.Requests; @@ -61,12 +62,12 @@ namespace Ombi.Store.Repository.Requests Db.AlbumRequests.Attach(request); Db.Update(request); } - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task Save() { - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs index daac7d4df..91e885b37 100644 --- a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; using Ombi.Store.Context; using Ombi.Store.Entities.Requests; @@ -101,20 +102,20 @@ namespace Ombi.Store.Repository.Requests public async Task Save() { - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task Add(TvRequests request) { await Db.TvRequests.AddAsync(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); return request; } public async Task AddChild(ChildRequests request) { await Db.ChildRequests.AddAsync(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); return request; } @@ -122,33 +123,38 @@ namespace Ombi.Store.Repository.Requests public async Task Delete(TvRequests request) { Db.TvRequests.Remove(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task DeleteChild(ChildRequests request) { Db.ChildRequests.Remove(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task DeleteChildRange(IEnumerable request) { Db.ChildRequests.RemoveRange(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task Update(TvRequests request) { Db.Update(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task UpdateChild(ChildRequests request) { Db.Update(request); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); + } + + private async Task InternalSaveChanges() + { + return await Db.SaveChangesAsync(); } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/SettingsJsonRepository.cs b/src/Ombi.Store/Repository/SettingsJsonRepository.cs index 248413ccc..909a68480 100644 --- a/src/Ombi.Store/Repository/SettingsJsonRepository.cs +++ b/src/Ombi.Store/Repository/SettingsJsonRepository.cs @@ -12,13 +12,13 @@ namespace Ombi.Store.Repository { public class SettingsJsonRepository : ISettingsRepository { - public SettingsJsonRepository(IOmbiContext ctx, ICacheService mem) + public SettingsJsonRepository(ISettingsContext ctx, ICacheService mem) { Db = ctx; _cache = mem; } - private IOmbiContext Db { get; } + private ISettingsContext Db { get; } private readonly ICacheService _cache; public GlobalSettings Insert(GlobalSettings entity) @@ -62,14 +62,14 @@ namespace Ombi.Store.Repository { //_cache.Remove(GetName(entity.SettingsName)); Db.Settings.Remove(entity); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public async Task UpdateAsync(GlobalSettings entity) { //_cache.Remove(GetName(entity.SettingsName)); Db.Update(entity); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public void Delete(GlobalSettings entity) @@ -91,6 +91,11 @@ namespace Ombi.Store.Repository return $"{entity}Json"; } + private async Task InternalSaveChanges() + { + return await Db.SaveChangesAsync(); + } + private bool _disposed; protected virtual void Dispose(bool disposing) { diff --git a/src/Ombi.Store/Repository/TokenRepository.cs b/src/Ombi.Store/Repository/TokenRepository.cs index d766c5690..d0a501bd5 100644 --- a/src/Ombi.Store/Repository/TokenRepository.cs +++ b/src/Ombi.Store/Repository/TokenRepository.cs @@ -4,6 +4,7 @@ using Ombi.Store.Entities; using System; using System.Linq; using System.Threading.Tasks; +using Ombi.Helpers; namespace Ombi.Store.Repository { @@ -19,12 +20,16 @@ namespace Ombi.Store.Repository public async Task CreateToken(Tokens token) { await Db.Tokens.AddAsync(token); - await Db.SaveChangesAsync(); + await InternalSaveChanges(); } public IQueryable GetToken(string tokenId) { return Db.Tokens.Where(x => x.Token == tokenId); } + private async Task InternalSaveChanges() + { + return await Db.SaveChangesAsync(); + } } } diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 95eb09353..fdfec1cb2 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -1,18 +1,18 @@ - netcoreapp2.1 + netcoreapp2.2 false - - - - - - + + + + + + diff --git a/src/Ombi.Tests/TestStartup.cs b/src/Ombi.Tests/TestStartup.cs index fad346df0..07d54a936 100644 --- a/src/Ombi.Tests/TestStartup.cs +++ b/src/Ombi.Tests/TestStartup.cs @@ -1,71 +1,71 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using Ombi.Api.Emby; -using Ombi.Api.Plex; -using Ombi.Core.Authentication; -using Ombi.Core.Settings; -using Ombi.Core.Settings.Models.External; -using Ombi.Models.Identity; -using Ombi.Store.Context; -using Ombi.Store.Entities; -using Ombi.Store.Repository; +//using System; +//using Microsoft.AspNetCore.Builder; +//using Microsoft.AspNetCore.Hosting; +//using Microsoft.AspNetCore.Http; +//using Microsoft.AspNetCore.Http.Features.Authentication; +//using Microsoft.AspNetCore.Identity; +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Options; +//using Moq; +//using Ombi.Api.Emby; +//using Ombi.Api.Plex; +//using Ombi.Core.Authentication; +//using Ombi.Core.Settings; +//using Ombi.Core.Settings.Models.External; +//using Ombi.Models.Identity; +//using Ombi.Store.Context; +//using Ombi.Store.Entities; +//using Ombi.Store.Repository; -namespace Ombi.Tests -{ - public class TestStartup - { - public IServiceProvider ConfigureServices(IServiceCollection services) - { - var _plexApi = new Mock(); - var _embyApi = new Mock(); - var _tokenSettings = new Mock>(); - var _embySettings = new Mock>(); - var _plexSettings = new Mock>(); - var audit = new Mock(); - var tokenRepo = new Mock(); +//namespace Ombi.Tests +//{ +// public class TestStartup +// { +// public IServiceProvider ConfigureServices(IServiceCollection services) +// { +// var _plexApi = new Mock(); +// var _embyApi = new Mock(); +// var _tokenSettings = new Mock>(); +// var _embySettings = new Mock>(); +// var _plexSettings = new Mock>(); +// var audit = new Mock(); +// var tokenRepo = new Mock(); - services.AddEntityFrameworkInMemoryDatabase() - .AddDbContext(); - services.AddIdentity() - .AddEntityFrameworkStores().AddUserManager(); +// services.AddEntityFrameworkInMemoryDatabase() +// .AddDbContext(); +// services.AddIdentity() +// .AddEntityFrameworkStores().AddUserManager(); - services.AddTransient(x => _plexApi.Object); - services.AddTransient(x => _embyApi.Object); - services.AddTransient(x => _tokenSettings.Object); - services.AddTransient(x => _embySettings.Object); - services.AddTransient(x => _plexSettings.Object); - services.AddTransient(x => audit.Object); - services.AddTransient(x => tokenRepo.Object); - // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified) - var context = new DefaultHttpContext(); - context.Features.Set(new HttpAuthenticationFeature()); - services.AddSingleton(h => new HttpContextAccessor { HttpContext = context }); +// services.AddTransient(x => _plexApi.Object); +// services.AddTransient(x => _embyApi.Object); +// services.AddTransient(x => _tokenSettings.Object); +// services.AddTransient(x => _embySettings.Object); +// services.AddTransient(x => _plexSettings.Object); +// services.AddTransient(x => audit.Object); +// services.AddTransient(x => tokenRepo.Object); +// // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified) +// var context = new DefaultHttpContext(); +// context.Features.Set(new HttpAuthenticationFeature()); +// services.AddSingleton(h => new HttpContextAccessor { HttpContext = context }); - services.Configure(options => - { - options.Password.RequireDigit = false; - options.Password.RequiredLength = 1; - options.Password.RequireLowercase = false; - options.Password.RequireNonAlphanumeric = false; - options.Password.RequireUppercase = false; - options.User.AllowedUserNameCharacters = string.Empty; - }); +// services.Configure(options => +// { +// options.Password.RequireDigit = false; +// options.Password.RequiredLength = 1; +// options.Password.RequireLowercase = false; +// options.Password.RequireNonAlphanumeric = false; +// options.Password.RequireUppercase = false; +// options.User.AllowedUserNameCharacters = string.Empty; +// }); - return services.BuildServiceProvider(); +// return services.BuildServiceProvider(); - } +// } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { +// public void Configure(IApplicationBuilder app, IHostingEnvironment env) +// { - } - } -} \ No newline at end of file +// } +// } +//} \ No newline at end of file diff --git a/src/Ombi.Tests/TokenControllerTests.cs b/src/Ombi.Tests/TokenControllerTests.cs index ec0e616ac..0fb1e9200 100644 --- a/src/Ombi.Tests/TokenControllerTests.cs +++ b/src/Ombi.Tests/TokenControllerTests.cs @@ -1,60 +1,60 @@ -using System.Net.Http; -using System.Threading.Tasks; -using AutoMapper; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; -using Ombi.Api.Emby; -using Ombi.Api.Plex; -using Ombi.Controllers; -using Ombi.Core.Authentication; -using Ombi.Core.Settings; -using Ombi.Core.Settings.Models.External; -using Ombi.Models.Identity; -using Ombi.Notifications; -using Ombi.Schedule.Jobs.Ombi; -using Ombi.Settings.Settings.Models; -using Ombi.Settings.Settings.Models.Notifications; -using Ombi.Store.Context; -using Ombi.Store.Entities; -using Ombi.Store.Repository; -using Microsoft.AspNetCore.Hosting.Server; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using Ombi.Models; +//using System.Net.Http; +//using System.Threading.Tasks; +//using AutoMapper; +//using Microsoft.AspNetCore.Hosting; +//using Microsoft.AspNetCore.Http; +//using Microsoft.AspNetCore.Http.Features.Authentication; +//using Microsoft.AspNetCore.Identity; +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Options; +//using Moq; +//using NUnit.Framework; +//using Ombi.Api.Emby; +//using Ombi.Api.Plex; +//using Ombi.Controllers; +//using Ombi.Core.Authentication; +//using Ombi.Core.Settings; +//using Ombi.Core.Settings.Models.External; +//using Ombi.Models.Identity; +//using Ombi.Notifications; +//using Ombi.Schedule.Jobs.Ombi; +//using Ombi.Settings.Settings.Models; +//using Ombi.Settings.Settings.Models.Notifications; +//using Ombi.Store.Context; +//using Ombi.Store.Entities; +//using Ombi.Store.Repository; +//using Microsoft.AspNetCore.Hosting.Server; +//using Microsoft.AspNetCore.TestHost; +//using Newtonsoft.Json; +//using Ombi.Models; -namespace Ombi.Tests -{ - [TestFixture] - [Ignore("TODO")] - public class TokenControllerTests - { - [SetUp] - public void Setup() - { - _testServer = new TestServer(new WebHostBuilder() - .UseStartup()); - _client = _testServer.CreateClient(); - } +//namespace Ombi.Tests +//{ +// [TestFixture] +// [Ignore("TODO")] +// public class TokenControllerTests +// { +// [SetUp] +// public void Setup() +// { +// _testServer = new TestServer(new WebHostBuilder() +// .UseStartup()); +// _client = _testServer.CreateClient(); +// } - private TestServer _testServer; - private HttpClient _client; +// private TestServer _testServer; +// private HttpClient _client; - [Test] - public async Task GetToken_FromValid_LocalUser() - { - var model = new UserAuthModel - { - Password = "a", - Username = "a" - }; - HttpResponseMessage response = await _client.PostAsync("/api/v1/token", new StringContent(JsonConvert.SerializeObject(model)) ); - } - } -} \ No newline at end of file +// [Test] +// public async Task GetToken_FromValid_LocalUser() +// { +// var model = new UserAuthModel +// { +// Password = "a", +// Username = "a" +// }; +// HttpResponseMessage response = await _client.PostAsync("/api/v1/token", new StringContent(JsonConvert.SerializeObject(model)) ); +// } +// } +//} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index 0b24bd55c..43d8b02c1 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -8,16 +8,18 @@ namespace Ombi.Api.TheMovieDb public interface IMovieDbApi { Task GetMovieInformation(int movieId); - Task GetMovieInformationWithExtraInfo(int movieId); - Task> NowPlaying(); - Task> PopularMovies(); - Task> SearchMovie(string searchTerm); + Task GetMovieInformationWithExtraInfo(int movieId, string langCode = "en"); + Task> NowPlaying(string languageCode); + Task> PopularMovies(string languageCode); + Task> SearchMovie(string searchTerm, int? year, string languageCode); Task> SearchTv(string searchTerm); - Task> TopRated(); - Task> Upcoming(); - Task> SimilarMovies(int movieId); + Task> TopRated(string languageCode); + Task> Upcoming(string languageCode); + Task> SimilarMovies(int movieId, string langCode); Task Find(string externalId, ExternalSource source); Task GetTvExternals(int theMovieDbId); Task GetTVInfo(string themoviedbid); + Task> SearchByActor(string searchTerm, string langCode); + Task GetActorMovieCredits(int actorId, string langCode); } } \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/Models/ActorCredits.cs b/src/Ombi.TheMovieDbApi/Models/ActorCredits.cs new file mode 100644 index 000000000..cb27bc677 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/ActorCredits.cs @@ -0,0 +1,51 @@ +namespace Ombi.Api.TheMovieDb.Models +{ + public class ActorCredits + { + public Cast[] cast { get; set; } + public Crew[] crew { get; set; } + public int id { get; set; } + } + + public class Cast + { + public string character { get; set; } + public string credit_id { get; set; } + public string poster_path { get; set; } + public int id { get; set; } + public bool video { get; set; } + public int vote_count { get; set; } + public bool adult { get; set; } + public string backdrop_path { get; set; } + public int?[] genre_ids { get; set; } + public string original_language { get; set; } + public string original_title { get; set; } + public float popularity { get; set; } + public string title { get; set; } + public float vote_average { get; set; } + public string overview { get; set; } + public string release_date { get; set; } + } + + public class Crew + { + public int id { get; set; } + public string department { get; set; } + public string original_language { get; set; } + public string original_title { get; set; } + public string job { get; set; } + public string overview { get; set; } + public int vote_count { get; set; } + public bool video { get; set; } + public string release_date { get; set; } + public float vote_average { get; set; } + public string title { get; set; } + public float popularity { get; set; } + public int?[] genre_ids { get; set; } + public string backdrop_path { get; set; } + public bool adult { get; set; } + public string poster_path { get; set; } + public string credit_id { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/Models/ActorSearchResult.cs b/src/Ombi.TheMovieDbApi/Models/ActorSearchResult.cs new file mode 100644 index 000000000..b16b35ab9 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/ActorSearchResult.cs @@ -0,0 +1,33 @@ +namespace Ombi.Api.TheMovieDb.Models +{ + + public class ActorResult + { + public float popularity { get; set; } + public int id { get; set; } + public string profile_path { get; set; } + public string name { get; set; } + public Known_For[] known_for { get; set; } + public bool adult { get; set; } + } + + public class Known_For + { + public float vote_average { get; set; } + public int vote_count { get; set; } + public int id { get; set; } + public bool video { get; set; } + public string media_type { get; set; } + public string title { get; set; } + public float popularity { get; set; } + public string poster_path { get; set; } + public string original_language { get; set; } + public string original_title { get; set; } + public int[] genre_ids { get; set; } + public string backdrop_path { get; set; } + public bool adult { get; set; } + public string overview { get; set; } + public string release_date { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index af9423f5d..79ccc5bb7 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -43,6 +43,27 @@ namespace Ombi.Api.TheMovieDb return await Api.Request(request); } + public async Task> SearchByActor(string searchTerm, string langCode) + { + var request = new Request($"search/person", BaseUri, HttpMethod.Get); + request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); + request.FullUri = request.FullUri.AddQueryParameter("language", langCode); + + var result = await Api.Request>(request); + return result; + } + + public async Task GetActorMovieCredits(int actorId, string langCode) + { + var request = new Request($"person/{actorId}/movie_credits", BaseUri, HttpMethod.Get); + request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("language", langCode); + + var result = await Api.Request(request); + return result; + } + public async Task> SearchTv(string searchTerm) { var request = new Request($"search/tv", BaseUri, HttpMethod.Get); @@ -63,68 +84,80 @@ namespace Ombi.Api.TheMovieDb return await Api.Request(request); } - public async Task> SimilarMovies(int movieId) + public async Task> SimilarMovies(int movieId, string langCode) { var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + + request.FullUri = request.FullUri.AddQueryParameter("language", langCode); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task GetMovieInformationWithExtraInfo(int movieId) + public async Task GetMovieInformationWithExtraInfo(int movieId, string langCode = "en") { var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates"); + request.FullUri = request.FullUri.AddQueryParameter("language", langCode); AddRetry(request); var result = await Api.Request(request); return Mapper.Map(result); } - public async Task> SearchMovie(string searchTerm) + public async Task> SearchMovie(string searchTerm, int? year, string langageCode) { var request = new Request($"search/movie", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); + request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); + if (year.HasValue && year.Value > 0) + { + request.FullUri = request.FullUri.AddQueryParameter("year", year.Value.ToString()); + } AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> PopularMovies() + public async Task> PopularMovies(string langageCode) { var request = new Request($"movie/popular", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> TopRated() + public async Task> TopRated(string langageCode) { var request = new Request($"movie/top_rated", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> Upcoming() + public async Task> Upcoming(string langageCode) { var request = new Request($"movie/upcoming", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> NowPlaying() + public async Task> NowPlaying(string langageCode) { var request = new Request($"movie/now_playing", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index 07fb92d81..6220a100b 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - netcoreapp2.1 + netcoreapp2.2 3.0.0.0 3.0.0.0 @@ -12,17 +12,17 @@ - - - - - - - - - + + + + + + + + + - + \ No newline at end of file diff --git a/src/Ombi.sln b/src/Ombi.sln index 1dfaa1dc6..f4f683c11 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -94,7 +94,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.SickRage", "Ombi.A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Notifications", "Ombi.Api.Notifications\Ombi.Api.Notifications.csproj", "{10D1FE9D-9124-42B7-B1E1-CEB99B832618}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Lidarr", "Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj", "{4FA21A20-92F4-462C-B929-2C517A88CC56}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Helpers.Tests", "Ombi.Helpers.Tests\Ombi.Helpers.Tests.csproj", "{CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Gotify", "Ombi.Api.Gotify\Ombi.Api.Gotify.csproj", "{105EA346-766E-45B8-928B-DE6991DCB7EB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -250,6 +254,14 @@ Global {4FA21A20-92F4-462C-B929-2C517A88CC56}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.ActiveCfg = Release|Any CPU {4FA21A20-92F4-462C-B929-2C517A88CC56}.Release|Any CPU.Build.0 = Release|Any CPU + {CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3}.Release|Any CPU.Build.0 = Release|Any CPU + {105EA346-766E-45B8-928B-DE6991DCB7EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {105EA346-766E-45B8-928B-DE6991DCB7EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {105EA346-766E-45B8-928B-DE6991DCB7EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {105EA346-766E-45B8-928B-DE6991DCB7EB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -286,6 +298,8 @@ Global {94C9A366-2595-45EA-AABB-8E4A2E90EC5B} = {9293CA11-360A-4C20-A674-B9E794431BF5} {10D1FE9D-9124-42B7-B1E1-CEB99B832618} = {9293CA11-360A-4C20-A674-B9E794431BF5} {4FA21A20-92F4-462C-B929-2C517A88CC56} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3} = {6F42AB98-9196-44C4-B888-D5E409F415A1} + {105EA346-766E-45B8-928B-DE6991DCB7EB} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869} diff --git a/src/Ombi/ApiKeyMiddlewear.cs b/src/Ombi/ApiKeyMiddlewear.cs index f3c956df4..e8fa02d78 100644 --- a/src/Ombi/ApiKeyMiddlewear.cs +++ b/src/Ombi/ApiKeyMiddlewear.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Ombi.Core.Authentication; using Ombi.Core.Settings; +using Ombi.Helpers; using Ombi.Settings.Settings.Models; namespace Ombi @@ -98,6 +99,10 @@ namespace Ombi if (context.Request.Headers.Keys.Contains("UserName", StringComparer.InvariantCultureIgnoreCase)) { var username = context.Request.Headers["UserName"].FirstOrDefault(); + if (username.IsNullOrEmpty()) + { + UseApiUser(context); + } var um = context.RequestServices.GetService(); var user = await um.Users.FirstOrDefaultAsync(x => x.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase)); @@ -114,13 +119,18 @@ namespace Ombi } else { - var identity = new GenericIdentity("API"); - var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); - context.User = principal; + UseApiUser(context); } await next.Invoke(context); } } + + private void UseApiUser(HttpContext context) + { + var identity = new GenericIdentity("API"); + var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); + context.User = principal; + } } } \ No newline at end of file diff --git a/src/Ombi/Attributes/UserAttribute.cs b/src/Ombi/Attributes/UserAttribute.cs new file mode 100644 index 000000000..3ab4cef49 --- /dev/null +++ b/src/Ombi/Attributes/UserAttribute.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Authorization; +using Ombi.Helpers; + + +namespace Ombi.Attributes +{ + public class UserAttribute : AuthorizeAttribute + { + public UserAttribute() + { + Roles = "ManageOwnRequests"; + } + } +} diff --git a/src/Ombi/ClientApp/app/app.component.html b/src/Ombi/ClientApp/app/app.component.html index 24398f088..08c153f31 100644 --- a/src/Ombi/ClientApp/app/app.component.html +++ b/src/Ombi/ClientApp/app/app.component.html @@ -34,6 +34,14 @@ {{ 'NavigationBar.Requests' | translate }} +
+
diff --git a/src/Ombi/ClientApp/app/landingpage/landingpage.component.html b/src/Ombi/ClientApp/app/landingpage/landingpage.component.html index 27545b64b..03878033f 100644 --- a/src/Ombi/ClientApp/app/landingpage/landingpage.component.html +++ b/src/Ombi/ClientApp/app/landingpage/landingpage.component.html @@ -3,7 +3,7 @@
-
+
diff --git a/src/Ombi/ClientApp/app/landingpage/landingpage.component.ts b/src/Ombi/ClientApp/app/landingpage/landingpage.component.ts index 514b1754e..dc329baf3 100644 --- a/src/Ombi/ClientApp/app/landingpage/landingpage.component.ts +++ b/src/Ombi/ClientApp/app/landingpage/landingpage.component.ts @@ -37,7 +37,7 @@ export class LandingPageComponent implements OnDestroy, OnInit { }); this.timer = setInterval(() => { this.cycleBackground(); - }, 10000); + }, 15000); const base = this.location.getBaseHrefFromDOM(); if (base.length > 1) { diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index bb6afc1dd..5d5032aef 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -33,7 +33,7 @@ include the remember me checkbox
- + diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index 0c04edbad..34edcd687 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -87,7 +87,7 @@ export class LoginComponent implements OnDestroy, OnInit { }); this.timer = setInterval(() => { this.cycleBackground(); - }, 7000); + }, 15000); const base = this.location.getBaseHrefFromDOM(); if (base.length > 1) { @@ -161,7 +161,8 @@ export class LoginComponent implements OnDestroy, OnInit { } }, err => { - this.notify.error(err.statusText); + console.log(err); + this.notify.error(err.body); this.router.navigate(["login"]); }); diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index a35ede06c..6f3b4dd00 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -1,36 +1,39 @@
- + -
@@ -96,3 +117,12 @@ + + + Please enter a rejection reason, the user will be notified of this: + + + + + + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts index 19e941e9d..2c2145fb9 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts @@ -10,6 +10,11 @@ import { NotificationService, RequestService } from "../services"; export class TvRequestChildrenComponent { @Input() public childRequests: IChildRequests[]; @Input() public isAdmin: boolean; + @Input() public currentUser: string; + + public denyDisplay: boolean; + public requestToDeny: IChildRequests; + public rejectionReason: string; @Output() public requestDeleted = new EventEmitter(); @@ -56,20 +61,26 @@ export class TvRequestChildrenComponent { public deny(request: IChildRequests) { request.denied = true; + this.requestToDeny = request; + this.denyDisplay = true; request.seasonRequests.forEach((season) => { season.episodes.forEach((ep) => { ep.approved = false; }); }); - this.requestService.denyChild({ id: request.id }) + } + + public denyRequest() { + this.requestService.denyChild({ id: this.requestToDeny.id, reason: this.rejectionReason }) .subscribe(x => { + this.denyDisplay = false; if (x.result) { this.notificationService.success( `Request has been denied successfully`); } else { this.notificationService.warning("Request Denied", x.message ? x.message : x.errorMessage); - request.approved = false; + this.requestToDeny.approved = false; } }); } @@ -110,10 +121,18 @@ export class TvRequestChildrenComponent { }); } + public isRequestUser(request: IChildRequests) { + if (request.requestedUser.userName === this.currentUser) { + return true; + } + return false; + } + private removeRequestFromUi(key: IChildRequests) { const index = this.childRequests.indexOf(key, 0); if (index > -1) { this.childRequests.splice(index, 1); } } + } diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.html b/src/Ombi/ClientApp/app/requests/tvrequests.component.html index d1f9b36da..7c5e13479 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.html @@ -22,7 +22,7 @@ -
Release Date: {{node.releaseDate | date}}
+
Release Date: {{node.releaseDate | amLocal | amDateFormat: 'LL'}}
{{ 'Requests.QualityOverride' | translate }} {{node.qualityOverrideTitle}} @@ -50,7 +50,7 @@ View
-
+
@@ -66,7 +66,7 @@
-
+
@@ -99,7 +99,7 @@
- +

diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts index 144de0206..924fccd1f 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts @@ -20,6 +20,7 @@ export class TvRequestsComponent implements OnInit { public searchChanged = new Subject(); public searchText: string; public isAdmin: boolean; + public currentUser: string; public showChildDialogue = false; // This is for the child modal popup public selectedSeason: ITvRequests; public defaultPoster: string; @@ -48,6 +49,7 @@ export class TvRequestsComponent implements OnInit { private readonly platformLocation: PlatformLocation) { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + this.currentUser = this.auth.claims().name; if (this.isAdmin) { this.sonarrService.getQualityProfilesWithoutSettings() .subscribe(x => this.sonarrProfiles = x); diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index 916e2e8de..7d8a4653a 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -1,10 +1,11 @@ 
-
- +
+
-
+
  • +
    +
    + +
    +
    +
    + + + +
    +
    + + +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    -
    + +
    - -
    - -
    -
    + +
    + +
    +
    - poster + poster
    -

    {{result.title}} ({{result.releaseDate | date: 'yyyy'}})

    +

    {{result.title}} ({{result.releaseDate | amLocal | amDateFormat: 'YYYY'}})

    - - {{ 'Search.TheatricalRelease' | translate: {date: result.releaseDate | date: 'mediumDate'} }} - {{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate | date: 'mediumDate'} }} - - - - - {{result.quality}}p - - - - - - - - - -
    + + {{ + 'Search.TheatricalRelease' | translate: {date: result.releaseDate | amLocal | + amDateFormat: 'LL'} }} + {{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate | + amLocal | amDateFormat: 'LL'} }} + + + + + {{result.quality}}p + + + + + + + + + +

    {{result.overview}}

    -
    -
    - - - -
    -
    - +
    -
    - - - - - - -
    - - -
    +
    + + + + + + +
    + + + +
    -
    -
    +
    +
    @@ -117,4 +181,4 @@ + [issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"> \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.ts b/src/Ombi/ClientApp/app/search/moviesearch.component.ts index f4d19565a..90502bb93 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.ts +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.ts @@ -6,12 +6,15 @@ import { Subject } from "rxjs"; import { debounceTime, distinctUntilChanged } from "rxjs/operators"; import { AuthService } from "../auth/auth.service"; -import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../interfaces"; -import { NotificationService, RequestService, SearchService } from "../services"; +import { IIssueCategory, ILanguageRefine, IRequestEngineResult, ISearchMovieResult } from "../interfaces"; +import { NotificationService, RequestService, SearchService, SettingsService } from "../services"; + +import * as languageData from "../../other/iso-lang.json"; @Component({ selector: "movie-search", templateUrl: "./moviesearch.component.html", + styleUrls: ["./search.component.scss"], }) export class MovieSearchComponent implements OnInit { @@ -22,6 +25,11 @@ export class MovieSearchComponent implements OnInit { public result: IRequestEngineResult; public searchApplied = false; + public refineSearchEnabled = false; + public searchYear?: number; + public actorSearch: boolean; + public selectedLanguage: string; + public langauges: ILanguageRefine[]; @Input() public issueCategories: IIssueCategory[]; @Input() public issuesEnabled: boolean; @@ -36,24 +44,14 @@ export class MovieSearchComponent implements OnInit { private searchService: SearchService, private requestService: RequestService, private notificationService: NotificationService, private authService: AuthService, private readonly translate: TranslateService, private sanitizer: DomSanitizer, - private readonly platformLocation: PlatformLocation) { + private readonly platformLocation: PlatformLocation, private settingsService: SettingsService) { + this.langauges = languageData; this.searchChanged.pipe( debounceTime(600), // Wait Xms after the last event before emitting last event distinctUntilChanged(), // only emit if value is different from previous value ).subscribe(x => { this.searchText = x as string; - if (this.searchText === "") { - this.clearResults(); - return; - } - this.searchService.searchMovie(this.searchText) - .subscribe(x => { - this.movieResults = x; - this.searchApplied = true; - // Now let's load some extra info including IMDB Id - // This way the search is fast at displaying results. - this.getExtraInfo(); - }); + this.runSearch(); }); this.defaultPoster = "../../../images/default_movie_poster.png"; const base = this.platformLocation.getBaseHrefFromDOM(); @@ -70,7 +68,7 @@ export class MovieSearchComponent implements OnInit { result: false, errorMessage: "", }; - + this.settingsService.getDefaultLanguage().subscribe(x => this.selectedLanguage = x); this.popularMovies(); } @@ -87,7 +85,8 @@ export class MovieSearchComponent implements OnInit { } try { - this.requestService.requestMovie({ theMovieDbId: searchResult.id }) + const language = this.selectedLanguage && this.selectedLanguage.length > 0 ? this.selectedLanguage : "en"; + this.requestService.requestMovie({ theMovieDbId: searchResult.id, languageCode: language }) .subscribe(x => { this.result = x; if (this.result.result) { @@ -161,7 +160,8 @@ export class MovieSearchComponent implements OnInit { public similarMovies(theMovieDbId: number) { this.clearResults(); - this.searchService.similarMovies(theMovieDbId) + const lang = this.selectedLanguage && this.selectedLanguage.length > 0 ? this.selectedLanguage : ""; + this.searchService.similarMovies(theMovieDbId, lang) .subscribe(x => { this.movieResults = x; this.getExtraInfo(); @@ -172,7 +172,7 @@ export class MovieSearchComponent implements OnInit { r.subscribed = true; this.requestService.subscribeToMovie(r.requestId) .subscribe(x => { - this.notificationService.success("Subscribed To Movie!"); + this.notificationService.success(`Subscribed To Movie ${r.title}!`); }); } @@ -184,6 +184,17 @@ export class MovieSearchComponent implements OnInit { }); } + public refineOpen() { + this.refineSearchEnabled = !this.refineSearchEnabled; + if (!this.refineSearchEnabled) { + this.searchYear = undefined; + } + } + + public applyRefinedSearch() { + this.runSearch(); + } + private getExtraInfo() { this.movieResults.forEach((val, index) => { @@ -194,10 +205,18 @@ export class MovieSearchComponent implements OnInit { } val.background = this.sanitizer.bypassSecurityTrustStyle ("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")"); - this.searchService.getMovieInformation(val.id) - .subscribe(m => { - this.updateItem(val, m); - }); + + if (this.applyRefinedSearch) { + this.searchService.getMovieInformationWithRefined(val.id, this.selectedLanguage) + .subscribe(m => { + this.updateItem(val, m); + }); + } else { + this.searchService.getMovieInformation(val.id) + .subscribe(m => { + this.updateItem(val, m); + }); + } }); } @@ -214,4 +233,41 @@ export class MovieSearchComponent implements OnInit { this.movieResults = []; this.searchApplied = false; } + + private runSearch() { + if (this.searchText === "") { + this.clearResults(); + return; + } + if (this.refineOpen) { + if (!this.actorSearch) { + this.searchService.searchMovieWithRefined(this.searchText, this.searchYear, this.selectedLanguage) + .subscribe(x => { + this.movieResults = x; + this.searchApplied = true; + // Now let's load some extra info including IMDB Id + // This way the search is fast at displaying results. + this.getExtraInfo(); + }); + } else { + this.searchService.searchMovieByActor(this.searchText, this.selectedLanguage) + .subscribe(x => { + this.movieResults = x; + this.searchApplied = true; + // Now let's load some extra info including IMDB Id + // This way the search is fast at displaying results. + this.getExtraInfo(); + }); + } + } else { + this.searchService.searchMovie(this.searchText) + .subscribe(x => { + this.movieResults = x; + this.searchApplied = true; + // Now let's load some extra info including IMDB Id + // This way the search is fast at displaying results. + this.getExtraInfo(); + }); + } + } } diff --git a/src/Ombi/ClientApp/app/search/moviesearchgrid.component.html b/src/Ombi/ClientApp/app/search/moviesearchgrid.component.html index 4a817dd18..53270f943 100644 --- a/src/Ombi/ClientApp/app/search/moviesearchgrid.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearchgrid.component.html @@ -67,10 +67,10 @@
    -

    {{result.title}} ({{result.releaseDate | date: 'yyyy'}})

    +

    {{result.title}} ({{result.releaseDate | amLocal | amDateFormat: 'YYYY'}})

    - Release Date: {{result.releaseDate | date: 'dd/MM/yyyy'}} + Release Date: {{result.releaseDate | amLocal | amDateFormat: 'L'}} HomePage diff --git a/src/Ombi/ClientApp/app/search/moviesearchgrid.component.ts b/src/Ombi/ClientApp/app/search/moviesearchgrid.component.ts index 6999f37aa..4ff6fa986 100644 --- a/src/Ombi/ClientApp/app/search/moviesearchgrid.component.ts +++ b/src/Ombi/ClientApp/app/search/moviesearchgrid.component.ts @@ -65,7 +65,7 @@ export class MovieSearchGridComponent implements OnInit { } try { - this.requestService.requestMovie({ theMovieDbId: searchResult.id }) + this.requestService.requestMovie({ theMovieDbId: searchResult.id, languageCode: "en" }) .subscribe(x => { this.result = x; diff --git a/src/Ombi/ClientApp/app/search/music/albumsearch.component.html b/src/Ombi/ClientApp/app/search/music/albumsearch.component.html index d2430bd62..2b31df040 100644 --- a/src/Ombi/ClientApp/app/search/music/albumsearch.component.html +++ b/src/Ombi/ClientApp/app/search/music/albumsearch.component.html @@ -54,7 +54,7 @@ - Release Date: {{result.releaseDate | date:'yyyy-MM-dd'}} + Release Date: {{result.releaseDate | amLocal | amDateFormat: 'L'}} {{result.rating}}/10 @@ -80,7 +80,7 @@ {{ 'Common.Available' | translate }}
    -
    +
    diff --git a/src/Ombi/ClientApp/app/search/music/artistsearch.component.html b/src/Ombi/ClientApp/app/search/music/artistsearch.component.html index 77a68a841..cc1f093df 100644 --- a/src/Ombi/ClientApp/app/search/music/artistsearch.component.html +++ b/src/Ombi/ClientApp/app/search/music/artistsearch.component.html @@ -3,7 +3,7 @@
    - poster + poster
    diff --git a/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts b/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts index 54d941da8..2fd9839f2 100644 --- a/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts +++ b/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts @@ -1,14 +1,11 @@ import { PlatformLocation } from "@angular/common"; import { Component, Input, OnInit } from "@angular/core"; import { DomSanitizer } from "@angular/platform-browser"; -import { TranslateService } from "@ngx-translate/core"; import { Subject } from "rxjs"; import { debounceTime, distinctUntilChanged } from "rxjs/operators"; - -import { AuthService } from "../../auth/auth.service"; -import { IIssueCategory, IRequestEngineResult, ISearchMovieResult } from "../../interfaces"; +import { IIssueCategory, IRequestEngineResult } from "../../interfaces"; import { ISearchAlbumResult, ISearchArtistResult } from "../../interfaces/ISearchMusicResult"; -import { NotificationService, RequestService, SearchService } from "../../services"; +import { SearchService } from "../../services"; @Component({ selector: "music-search", @@ -35,10 +32,8 @@ export class MusicSearchComponent implements OnInit { public defaultPoster: string; constructor( - private searchService: SearchService, private requestService: RequestService, - private notificationService: NotificationService, private authService: AuthService, - private readonly translate: TranslateService, private sanitizer: DomSanitizer, - private readonly platformLocation: PlatformLocation) { + private searchService: SearchService, private sanitizer: DomSanitizer, + private platformLocation: PlatformLocation) { this.searchChanged.pipe( debounceTime(600), // Wait Xms after the last event before emitting last event @@ -110,45 +105,6 @@ export class MusicSearchComponent implements OnInit { this.searchChanged.next(`lidarr:${artistId}`); } - public request(searchResult: ISearchMovieResult) { - searchResult.requested = true; - searchResult.requestProcessing = true; - searchResult.showSubscribe = false; - if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) { - searchResult.approved = true; - } - - try { - this.requestService.requestMovie({ theMovieDbId: searchResult.id }) - .subscribe(x => { - this.result = x; - - if (this.result.result) { - this.translate.get("Search.RequestAdded", { title: searchResult.title }).subscribe(x => { - this.notificationService.success(x); - searchResult.processed = true; - }); - } else { - if (this.result.errorMessage && this.result.message) { - this.notificationService.warning("Request Added", `${this.result.message} - ${this.result.errorMessage}`); - } else { - this.notificationService.warning("Request Added", this.result.message ? this.result.message : this.result.errorMessage); - } - searchResult.requested = false; - searchResult.approved = false; - searchResult.processed = false; - searchResult.requestProcessing = false; - - } - }); - } catch (e) { - - searchResult.processed = false; - searchResult.requestProcessing = false; - this.notificationService.error(e); - } - } - public viewAlbumsForArtist(albums: ISearchAlbumResult[]) { this.clearArtistResults(); this.searchAlbum = true; diff --git a/src/Ombi/ClientApp/app/search/search.component.scss b/src/Ombi/ClientApp/app/search/search.component.scss new file mode 100644 index 000000000..915c2b467 --- /dev/null +++ b/src/Ombi/ClientApp/app/search/search.component.scss @@ -0,0 +1,30 @@ +@media (max-width: 978px) { + .top-spacing { + padding-top: 5% + } + .form-control-search { + width: 77%; + } + +} +@media (min-width: 979px) { + .top-spacing { + padding-top: 2% + } + .form-control-search { + width: 90%; + } +} + +.search-bar-background { + background-color: #333333; +} + +.vcenter { + display: flex; + align-items: center; +} + +.refine-option { + box-shadow: inset 0 1px 5px rgba(0,0,0,1.0); +} diff --git a/src/Ombi/ClientApp/app/search/seriesinformation.component.html b/src/Ombi/ClientApp/app/search/seriesinformation.component.html index 6cc214c8b..12059e417 100644 --- a/src/Ombi/ClientApp/app/search/seriesinformation.component.html +++ b/src/Ombi/ClientApp/app/search/seriesinformation.component.html @@ -42,8 +42,11 @@
    - + diff --git a/src/Ombi/ClientApp/app/settings/customization/customization.component.html b/src/Ombi/ClientApp/app/settings/customization/customization.component.html index 9d05edf1f..1f7a158ae 100644 --- a/src/Ombi/ClientApp/app/settings/customization/customization.component.html +++ b/src/Ombi/ClientApp/app/settings/customization/customization.component.html @@ -21,8 +21,8 @@
    - +
    @@ -36,8 +36,8 @@
    - +
    @@ -47,37 +47,39 @@
    - -
    - -
    - -
    -
    -
    - +
    -
    +
    -
    -
    +
    -
    +
    +
    + + + +
    +
    @@ -89,21 +91,14 @@
    -
    + diff --git a/src/Ombi/ClientApp/app/settings/customization/customization.component.ts b/src/Ombi/ClientApp/app/settings/customization/customization.component.ts index 098a6f5cc..0daf9ffad 100644 --- a/src/Ombi/ClientApp/app/settings/customization/customization.component.ts +++ b/src/Ombi/ClientApp/app/settings/customization/customization.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from "@angular/core"; -import { ICustomizationSettings, IThemes } from "../../interfaces"; +import { ICustomizationSettings } from "../../interfaces"; import { NotificationService } from "../../services"; import { SettingsService } from "../../services"; @@ -10,7 +10,6 @@ import { SettingsService } from "../../services"; export class CustomizationComponent implements OnInit { public settings: ICustomizationSettings; - public themes: IThemes[]; public advanced: boolean; constructor(private settingsService: SettingsService, private notificationService: NotificationService) { } @@ -18,26 +17,6 @@ export class CustomizationComponent implements OnInit { public ngOnInit() { this.settingsService.getCustomization().subscribe(x => { this.settings = x; - this.settingsService.getThemes().subscribe(t => { - this.themes = t; - - const existingTheme = this.themes.filter((item) => { - return item.fullName === this.settings.presetThemeName; - })[0]; - - if (existingTheme) { - const index = this.themes.indexOf(existingTheme, 0); - if (index > -1) { - this.themes.splice(index, 1); - } - } - if (x.hasPresetTheme) { - this.themes.unshift({displayName: x.presetThemeDisplayName, fullName: x.presetThemeName, url: existingTheme.url, version: x.presetThemeVersion}); - this.themes.unshift({displayName: "None", fullName: "None", url: "", version: ""}); - } else { - this.themes.unshift({displayName: "Please Select", fullName: "-1", url: "-1", version: ""}); - } - }); }); } @@ -62,26 +41,4 @@ export class CustomizationComponent implements OnInit { }); } - - public dropDownChange(event: any): void { - const selectedThemeFullName = event.target.value; - const selectedTheme = this.themes.filter((val) => { - return val.fullName === selectedThemeFullName; - })[0]; - - if (selectedTheme.fullName === this.settings.presetThemeName) { - return; - } - - if (selectedTheme.fullName === "None" || selectedTheme.fullName === "-1") { - this.settings.presetThemeName = ""; - this.settings.presetThemeContent = ""; - return; - } - - this.settings.presetThemeName = selectedThemeFullName; - this.settingsService.getThemeContent(selectedTheme.url).subscribe(x => { - this.settings.presetThemeContent = x; - }); - } } diff --git a/src/Ombi/ClientApp/app/settings/emby/emby.component.html b/src/Ombi/ClientApp/app/settings/emby/emby.component.html index ec966acf5..66cac8c8b 100644 --- a/src/Ombi/ClientApp/app/settings/emby/emby.component.html +++ b/src/Ombi/ClientApp/app/settings/emby/emby.component.html @@ -63,6 +63,18 @@
    + +
    + +
    + + Current URL: "{{server.serverHostname}}/#!/itemdetails.html?id=1" + Current URL: "https://app.emby.media/#!/itemdetails.html?id=1 +
    +
    diff --git a/src/Ombi/ClientApp/app/settings/failedrequests/failedrequest.component.html b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequest.component.html new file mode 100644 index 000000000..898710199 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequest.component.html @@ -0,0 +1,26 @@ + + + + +
    "); - if(url.HasValue()) sb.AppendFormat("", url); - sb.AppendFormat("

    {0}

    ", title); + sb.Append("
    "); + if(url.HasValue()) sb.AppendFormat("", url); + sb.AppendFormat("

    {0}

    ", title); if (url.HasValue()) sb.Append("
    "); sb.Append("
    "); - sb.AppendFormat("

    {0}

    ", text); + sb.Append("
    "); + sb.AppendFormat("

    {0}

    ", text); sb.Append("
    "); - sb.AppendFormat("

    {0}

    ", episodes); - sb.AppendFormat("
    {0}
    ", summary); + sb.Append("
    "); + sb.AppendFormat("

    {0}

    ", episodes); + sb.AppendFormat("
    {0}
    ", summary); sb.Append("
    "); - sb.AppendFormat("{0}", text); + sb.Append("
    "); + sb.AppendFormat("{0}", text); sb.Append("
    "); sb.Append(""); sb.Append(""); - await ProcessPlexMovies(plexMovies, sb); - await ProcessEmbyMovies(embyMovies, sb); + if (plexSettings.Enable) + { + await ProcessPlexMovies(plexMovies, sb, ombiSettings.DefaultLanguageCode); + } + + if (embySettings.Enable) + { + await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode); + } + sb.Append(""); sb.Append("
    "); sb.Append("
    "); sb.Append(""); sb.Append(""); - await ProcessPlexTv(plexEpisodes, sb); - await ProcessEmbyTv(embyEp, sb); + if (plexSettings.Enable) + { + await ProcessPlexTv(plexEpisodes, sb); + } + + if (embySettings.Enable) + { + await ProcessEmbyTv(embyEp, sb); + } + sb.Append(""); sb.Append("
    "); sb.Append("
    - {{ep.airDate | date: 'dd/MM/yyyy' }} + {{ep.airDate | amLocal | amDateFormat: 'L' }} - - - + + + + +
    -
    +
    - +
    {{ep.title}} - {{ep.airDate | date: 'dd/MM/yyyy' }} + + {{ep.airDate | amLocal | amDateFormat: 'L' }} + + {{ep.airDateDisplay }} Available diff --git a/src/Ombi/ClientApp/app/search/tvsearch.component.html b/src/Ombi/ClientApp/app/search/tvsearch.component.html index 44570687a..3fb349f88 100644 --- a/src/Ombi/ClientApp/app/search/tvsearch.component.html +++ b/src/Ombi/ClientApp/app/search/tvsearch.component.html @@ -50,14 +50,14 @@
    - poster + poster
    -

    {{node.title}} ({{node.firstAired | date: 'yyyy'}})

    +

    {{node.title}} ({{node.firstAired | amLocal | amDateFormat: 'YYYY'}})

    @@ -72,7 +72,7 @@ {{node.status}} - {{ 'Search.TvShows.AirDate' | translate }} {{node.firstAired | date: 'dd/MM/yyyy'}} + {{ 'Search.TvShows.AirDate' | translate }} {{node.firstAired | amLocal | amDateFormat: 'L'}} {{node.network}} diff --git a/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts b/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts index 756c6ea6c..bce47acf9 100644 --- a/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts @@ -5,7 +5,7 @@ import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; import { ISonarrSettings } from "../../interfaces"; -import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; +import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; import { ServiceHelpers } from "../service.helpers"; @Injectable() @@ -27,4 +27,8 @@ export class SonarrService extends ServiceHelpers { public getQualityProfilesWithoutSettings(): Observable { return this.http.get(`${this.url}/Profiles/`, {headers: this.headers}); } + + public getV3LanguageProfiles(settings: ISonarrSettings): Observable { + return this.http.post(`${this.url}/v3/languageprofiles/`, JSON.stringify(settings), {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/services/applications/tester.service.ts b/src/Ombi/ClientApp/app/services/applications/tester.service.ts index e692b9196..af619d583 100644 --- a/src/Ombi/ClientApp/app/services/applications/tester.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/tester.service.ts @@ -11,6 +11,7 @@ import { IDiscordNotifcationSettings, IEmailNotificationSettings, IEmbyServer, + IGotifyNotificationSettings, ILidarrSettings, IMattermostNotifcationSettings, IMobileNotificationTestSettings, @@ -40,7 +41,11 @@ export class TesterService extends ServiceHelpers { } public pushoverTest(settings: IPushoverNotificationSettings): Observable { - return this.http.post(`${this.url}pushover`, JSON.stringify(settings), {headers: this.headers}); + return this.http.post(`${this.url}pushover`, JSON.stringify(settings), { headers: this.headers }); + } + + public gotifyTest(settings: IGotifyNotificationSettings): Observable { + return this.http.post(`${this.url}gotify`, JSON.stringify(settings), { headers: this.headers }); } public mattermostTest(settings: IMattermostNotifcationSettings): Observable { diff --git a/src/Ombi/ClientApp/app/services/custompage.service.ts b/src/Ombi/ClientApp/app/services/custompage.service.ts new file mode 100644 index 000000000..1fc1ea028 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/custompage.service.ts @@ -0,0 +1,25 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; + +import { + ICustomPage, +} from "../interfaces"; + +import { ServiceHelpers } from "./service.helpers"; + +@Injectable() +export class CustomPageService extends ServiceHelpers { + constructor(public http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/CustomPage", platformLocation); + } + + public getCustomPage(): Observable { + return this.http.get(this.url, {headers: this.headers}); + } + + public saveCustomPage(model: ICustomPage): Observable { + return this.http.post(this.url, model, {headers: this.headers}); + } +} diff --git a/src/Ombi/ClientApp/app/services/index.ts b/src/Ombi/ClientApp/app/services/index.ts index 59a299d3e..5065ce938 100644 --- a/src/Ombi/ClientApp/app/services/index.ts +++ b/src/Ombi/ClientApp/app/services/index.ts @@ -14,3 +14,6 @@ export * from "./issues.service"; export * from "./mobile.service"; export * from "./notificationMessage.service"; export * from "./recentlyAdded.service"; +export * from "./vote.service"; +export * from "./requestretry.service"; +export * from "./custompage.service"; diff --git a/src/Ombi/ClientApp/app/services/mobile.service.ts b/src/Ombi/ClientApp/app/services/mobile.service.ts index 1f9e3fb24..9ff9b947d 100644 --- a/src/Ombi/ClientApp/app/services/mobile.service.ts +++ b/src/Ombi/ClientApp/app/services/mobile.service.ts @@ -15,4 +15,8 @@ export class MobileService extends ServiceHelpers { public getUserDeviceList(): Observable { return this.http.get(`${this.url}notification/`, {headers: this.headers}); } + + public deleteUser(userId: string): Observable { + return this.http.post(`${this.url}remove/`, userId, {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/services/request.service.ts b/src/Ombi/ClientApp/app/services/request.service.ts index 0021dc208..5353ea580 100644 --- a/src/Ombi/ClientApp/app/services/request.service.ts +++ b/src/Ombi/ClientApp/app/services/request.service.ts @@ -5,8 +5,8 @@ import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; import { TreeNode } from "primeng/primeng"; -import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, - IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces"; +import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IDenyAlbumModel, IDenyMovieModel, IFilter, + IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvDenyModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces"; import { ITvRequestViewModel } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @@ -50,7 +50,7 @@ export class RequestService extends ServiceHelpers { return this.http.post(`${this.url}Movie/Approve`, JSON.stringify(movie), {headers: this.headers}); } - public denyMovie(movie: IMovieUpdateModel): Observable { + public denyMovie(movie: IDenyMovieModel): Observable { return this.http.put(`${this.url}Movie/Deny`, JSON.stringify(movie), {headers: this.headers}); } @@ -118,7 +118,7 @@ export class RequestService extends ServiceHelpers { return this.http.put(`${this.url}tv/child`, JSON.stringify(child), {headers: this.headers}); } - public denyChild(child: ITvUpdateModel): Observable { + public denyChild(child: ITvDenyModel): Observable { return this.http.put(`${this.url}tv/deny`, JSON.stringify(child), {headers: this.headers}); } @@ -161,7 +161,7 @@ export class RequestService extends ServiceHelpers { return this.http.post(`${this.url}music/Approve`, JSON.stringify(Album), {headers: this.headers}); } - public denyAlbum(Album: IAlbumUpdateModel): Observable { + public denyAlbum(Album: IDenyAlbumModel): Observable { return this.http.put(`${this.url}music/Deny`, JSON.stringify(Album), {headers: this.headers}); } @@ -182,7 +182,7 @@ export class RequestService extends ServiceHelpers { } public removeAlbumRequest(request: IAlbumRequest): any { - this.http.delete(`${this.url}music/${request.id}`, {headers: this.headers}).subscribe(); + return this.http.delete(`${this.url}music/${request.id}`, {headers: this.headers}); } } diff --git a/src/Ombi/ClientApp/app/services/requestretry.service.ts b/src/Ombi/ClientApp/app/services/requestretry.service.ts new file mode 100644 index 000000000..e5c9cabe0 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/requestretry.service.ts @@ -0,0 +1,21 @@ +import { PlatformLocation } from "@angular/common"; +import { Injectable } from "@angular/core"; + +import { HttpClient } from "@angular/common/http"; +import { Observable } from "rxjs"; + +import { IFailedRequestsViewModel } from "../interfaces"; +import { ServiceHelpers } from "./service.helpers"; + +@Injectable() +export class RequestRetryService extends ServiceHelpers { + constructor(http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/requestretry/", platformLocation); + } + public getFailedRequests(): Observable { + return this.http.get(this.url, {headers: this.headers}); + } + public deleteFailedRequest(failedId: number): Observable { + return this.http.delete(`${this.url}/${failedId}`, {headers: this.headers}); + } +} diff --git a/src/Ombi/ClientApp/app/services/search.service.ts b/src/Ombi/ClientApp/app/services/search.service.ts index 9769ad229..5379e23e7 100644 --- a/src/Ombi/ClientApp/app/services/search.service.ts +++ b/src/Ombi/ClientApp/app/services/search.service.ts @@ -18,10 +18,15 @@ export class SearchService extends ServiceHelpers { // Movies public searchMovie(searchTerm: string): Observable { - return this.http.get(`${this.url}/Movie/` + searchTerm); + return this.http.get(`${this.url}/Movie/${searchTerm}`); } - public similarMovies(theMovieDbId: number): Observable { - return this.http.get(`${this.url}/Movie/${theMovieDbId}/similar`); + + public searchMovieWithRefined(searchTerm: string, year: number | undefined, langCode: string): Observable { + return this.http.post(`${this.url}/Movie/`, { searchTerm, year, languageCode: langCode }); + } + + public similarMovies(theMovieDbId: number, langCode: string): Observable { + return this.http.post(`${this.url}/Movie/similar`, {theMovieDbId, languageCode: langCode}); } public popularMovies(): Observable { @@ -40,34 +45,42 @@ export class SearchService extends ServiceHelpers { return this.http.get(`${this.url}/Movie/info/${theMovieDbId}`); } + public getMovieInformationWithRefined(theMovieDbId: number, langCode: string): Observable { + return this.http.post(`${this.url}/Movie/info`, { theMovieDbId, languageCode: langCode }); + } + + public searchMovieByActor(searchTerm: string, langCode: string): Observable { + return this.http.post(`${this.url}/Movie/Actor`, { searchTerm, languageCode: langCode }); + } + // TV public searchTv(searchTerm: string): Observable { - return this.http.get(`${this.url}/Tv/${searchTerm}`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/${searchTerm}`, { headers: this.headers }); } public searchTvTreeNode(searchTerm: string): Observable { - return this.http.get(`${this.url}/Tv/${searchTerm}/tree`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/${searchTerm}/tree`, { headers: this.headers }); } public getShowInformationTreeNode(theTvDbId: number): Observable { - return this.http.get(`${this.url}/Tv/info/${theTvDbId}/Tree`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/info/${theTvDbId}/Tree`, { headers: this.headers }); } public getShowInformation(theTvDbId: number): Observable { - return this.http.get(`${this.url}/Tv/info/${theTvDbId}`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/info/${theTvDbId}`, { headers: this.headers }); } public popularTv(): Observable { - return this.http.get(`${this.url}/Tv/popular`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/popular`, { headers: this.headers }); } public mostWatchedTv(): Observable { - return this.http.get(`${this.url}/Tv/mostwatched`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/mostwatched`, { headers: this.headers }); } public anticipatedTv(): Observable { - return this.http.get(`${this.url}/Tv/anticipated`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/anticipated`, { headers: this.headers }); } public trendingTv(): Observable { - return this.http.get(`${this.url}/Tv/trending`, {headers: this.headers}); + return this.http.get(`${this.url}/Tv/trending`, { headers: this.headers }); } // Music public searchArtist(searchTerm: string): Observable { diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index b4611bfa8..a80cfd772 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -14,6 +14,7 @@ import { IDogNzbSettings, IEmailNotificationSettings, IEmbySettings, + IGotifyNotificationSettings, IIssueSettings, IJobSettings, IJobSettingsViewModel, @@ -31,9 +32,9 @@ import { ISlackNotificationSettings, ISonarrSettings, ITelegramNotifcationSettings, - IThemes, IUpdateSettings, IUserManagementSettings, + IVoteSettings, } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @@ -52,6 +53,10 @@ export class SettingsService extends ServiceHelpers { return this.http.get(`${this.url}/Ombi/`, {headers: this.headers}); } + public getDefaultLanguage(): Observable { + return this.http.get(`${this.url}/defaultlanguage/`, {headers: this.headers}); + } + public saveOmbi(settings: IOmbiSettings): Observable { return this.http.post(`${this.url}/Ombi/`, JSON.stringify(settings), {headers: this.headers}); } @@ -134,14 +139,6 @@ export class SettingsService extends ServiceHelpers { return this.http.post(`${this.url}/customization`, JSON.stringify(settings), {headers: this.headers}); } - public getThemes(): Observable { - return this.http.get(`${this.url}/themes`, {headers: this.headers}); - } - - public getThemeContent(themeUrl: string): Observable { - return this.http.get(`${this.url}/themecontent?url=${themeUrl}`, {responseType: "text", headers: this.headers}); - } - public getEmailNotificationSettings(): Observable { return this.http.get(`${this.url}/notifications/email`, {headers: this.headers}); } @@ -186,6 +183,14 @@ export class SettingsService extends ServiceHelpers { .post(`${this.url}/notifications/pushover`, JSON.stringify(settings), {headers: this.headers}); } + public getGotifyNotificationSettings(): Observable { + return this.http.get(`${this.url}/notifications/gotify`, { headers: this.headers }); + } + public saveGotifyNotificationSettings(settings: IGotifyNotificationSettings): Observable { + return this.http + .post(`${this.url}/notifications/gotify`, JSON.stringify(settings), { headers: this.headers }); + } + public getSlackNotificationSettings(): Observable { return this.http.get(`${this.url}/notifications/slack`, {headers: this.headers}); } @@ -284,6 +289,18 @@ export class SettingsService extends ServiceHelpers { .post(`${this.url}/issues`, JSON.stringify(settings), {headers: this.headers}); } + public getVoteSettings(): Observable { + return this.http.get(`${this.url}/vote`, {headers: this.headers}); + } + + public voteEnabled(): Observable { + return this.http.get(`${this.url}/voteenabled`, {headers: this.headers}); + } + + public saveVoteSettings(settings: IVoteSettings): Observable { + return this.http.post(`${this.url}/vote`, JSON.stringify(settings), {headers: this.headers}); + } + public getNewsletterSettings(): Observable { return this.http.get(`${this.url}/notifications/newsletter`, {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/services/vote.service.ts b/src/Ombi/ClientApp/app/services/vote.service.ts new file mode 100644 index 000000000..375f0fc33 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/vote.service.ts @@ -0,0 +1,36 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; + +import { IVoteEngineResult, IVoteViewModel } from "../interfaces"; +import { ServiceHelpers } from "./service.helpers"; + +@Injectable() +export class VoteService extends ServiceHelpers { + constructor(public http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/Vote/", platformLocation); + } + + public async getModel(): Promise { + return await this.http.get(`${this.url}`, {headers: this.headers}).toPromise(); + } + + public async upvoteMovie(requestId: number): Promise { + return await this.http.post(`${this.url}up/movie/${requestId}`, {headers: this.headers}).toPromise(); + } + public async upvoteTv(requestId: number): Promise { + return await this.http.post(`${this.url}up/tv/${requestId}`, {headers: this.headers}).toPromise(); + } + public async upvoteAlbum(requestId: number): Promise { + return await this.http.post(`${this.url}up/album/${requestId}`, {headers: this.headers}).toPromise(); + } + public async downvoteMovie(requestId: number): Promise { + return await this.http.post(`${this.url}down/movie/${requestId}`, {headers: this.headers}).toPromise(); + } + public async downvoteTv(requestId: number): Promise { + return await this.http.post(`${this.url}down/tv/${requestId}`, {headers: this.headers}).toPromise(); + } + public async downvoteAlbum(requestId: number): Promise { + return await this.http.post(`${this.url}down/album/${requestId}`, {headers: this.headers}).toPromise(); + } +} diff --git a/src/Ombi/ClientApp/app/settings/about/about.component.html b/src/Ombi/ClientApp/app/settings/about/about.component.html index e05677c0f..fc1efde3f 100644 --- a/src/Ombi/ClientApp/app/settings/about/about.component.html +++ b/src/Ombi/ClientApp/app/settings/about/about.component.html @@ -38,7 +38,7 @@ Discord
    - https://discord.gg/ + https://discord.gg/Sa7wNWb
    + + + + + + + + + + + + + + + + + + +
    TitleTypeRetry CountError DescriptionDelete
    + {{v.title}} + {{RequestType[v.type] | humanize}}{{v.retryCount}}
    diff --git a/src/Ombi/ClientApp/app/settings/failedrequests/failedrequests.component.ts b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequests.component.ts new file mode 100644 index 000000000..a303ac713 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequests.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from "@angular/core"; +import { IFailedRequestsViewModel, RequestType } from "../../interfaces"; +import { RequestRetryService } from "../../services"; + +@Component({ + templateUrl: "./failedrequest.component.html", +}) +export class FailedRequestsComponent implements OnInit { + + public vm: IFailedRequestsViewModel[]; + public RequestType = RequestType; + + constructor(private retry: RequestRetryService) { } + + public ngOnInit() { + this.retry.getFailedRequests().subscribe(x => this.vm = x); + } + + public remove(failed: IFailedRequestsViewModel) { + this.retry.deleteFailedRequest(failed.failedId).subscribe(x => { + if(x) { + const index = this.vm.indexOf(failed); + this.vm.splice(index,1); + } + }); + } +} diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html index 1365710f0..4532af9b3 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html @@ -48,6 +48,13 @@ The Automatic Update is required + +
    + + + The Retry Requests is required + +
    @@ -98,6 +105,13 @@ The Issues Purge is required
    + +
    + + + The Media Database Refresh is required + +
    diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts index d8ce106ae..747a4bfde 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts @@ -36,6 +36,8 @@ export class JobsComponent implements OnInit { plexRecentlyAddedSync: [x.plexRecentlyAddedSync, Validators.required], lidarrArtistSync: [x.lidarrArtistSync, Validators.required], issuesPurge: [x.issuesPurge, Validators.required], + retryRequests: [x.retryRequests, Validators.required], + mediaDatabaseRefresh: [x.mediaDatabaseRefresh, Validators.required], }); }); } diff --git a/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html b/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html index 50938412f..5e898e719 100644 --- a/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html +++ b/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html @@ -19,25 +19,28 @@
    - + - The IP/Hostname is required
    - + - The Port is required +
    - + - The API Key is required +
    @@ -56,19 +59,22 @@
    - +
    - - A Default Quality Profile is required
    - +
    @@ -89,12 +97,14 @@
    - A Language profile is required
    - +
    - A Metadata profile is required
    diff --git a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html index e66d83a18..5c51c68ca 100644 --- a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html +++ b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html @@ -39,7 +39,7 @@
    - +
    diff --git a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts index a80f7adfe..91693103f 100644 --- a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts +++ b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts @@ -38,10 +38,6 @@ export class MassEmailComponent implements OnInit { this.users.forEach(u => u.selected = !u.selected); } - public selectSingleUser(user: IMassEmailUserModel) { - user.selected = !user.selected; - } - public send() { if(!this.subject) { this.missingSubject = true; diff --git a/src/Ombi/ClientApp/app/settings/notifications/gotify.component.html b/src/Ombi/ClientApp/app/settings/notifications/gotify.component.html new file mode 100644 index 000000000..9148cb880 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/notifications/gotify.component.html @@ -0,0 +1,67 @@ + + +
    +
    + Gotify Notifications +
    +
    + +
    +
    + + +
    +
    + +
    + + + + The Base URL is required +
    + +
    + + + + The Application Token is required +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    + + + +
    +
    + +
    +
    +
    +
    + + +
    + +
    +
    +
    \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/notifications/gotify.component.ts b/src/Ombi/ClientApp/app/settings/notifications/gotify.component.ts new file mode 100644 index 000000000..f6c08d41b --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/notifications/gotify.component.ts @@ -0,0 +1,68 @@ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; + +import { IGotifyNotificationSettings, INotificationTemplates, NotificationType } from "../../interfaces"; +import { TesterService } from "../../services"; +import { NotificationService } from "../../services"; +import { SettingsService } from "../../services"; + +@Component({ + templateUrl: "./gotify.component.html", +}) +export class GotifyComponent implements OnInit { + public NotificationType = NotificationType; + public templates: INotificationTemplates[]; + public form: FormGroup; + + constructor(private settingsService: SettingsService, + private notificationService: NotificationService, + private fb: FormBuilder, + private testerService: TesterService) { } + + public ngOnInit() { + this.settingsService.getGotifyNotificationSettings().subscribe(x => { + this.templates = x.notificationTemplates; + + this.form = this.fb.group({ + enabled: [x.enabled], + baseUrl: [x.baseUrl, [Validators.required]], + applicationToken: [x.applicationToken, [Validators.required]], + priority: [x.priority], + }); + }); + } + + public onSubmit(form: FormGroup) { + if (form.invalid) { + this.notificationService.error("Please check your entered values"); + return; + } + + const settings = form.value; + settings.notificationTemplates = this.templates; + + this.settingsService.saveGotifyNotificationSettings(settings).subscribe(x => { + if (x) { + this.notificationService.success("Successfully saved the Gotify settings"); + } else { + this.notificationService.success("There was an error when saving the Gotify settings"); + } + }); + + } + + public test(form: FormGroup) { + if (form.invalid) { + this.notificationService.error("Please check your entered values"); + return; + } + + this.testerService.gotifyTest(form.value).subscribe(x => { + if (x) { + this.notificationService.success("Successfully sent a Gotify message"); + } else { + this.notificationService.error("There was an error when sending the Gotify message. Please check your settings"); + } + }); + } +} diff --git a/src/Ombi/ClientApp/app/settings/notifications/mobile.component.html b/src/Ombi/ClientApp/app/settings/notifications/mobile.component.html index 5c82e03d5..2b7ea9b2c 100644 --- a/src/Ombi/ClientApp/app/settings/notifications/mobile.component.html +++ b/src/Ombi/ClientApp/app/settings/notifications/mobile.component.html @@ -35,7 +35,7 @@
    - +
    + +
    + +
    + +
    -
    -
    - -
    - +
    + +
    + -
    -
    -
    +
    +
    +
    -
    -
    +
    +
    +
    -
    -
    +
    -
    -
    - - +
    +
    + + +
    -
    -
    -
    - - +
    +
    + + +
    -
    -
    -
    - - +
    +
    + + +
    -
    -
    -
    - - +
    +
    + + +
    -
    -
    -
    - +
    + +
    + +
    +
    + +
    +
    + +
    -
    - + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts b/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts index 31a8da156..4aec1a57c 100644 --- a/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts +++ b/src/Ombi/ClientApp/app/settings/ombi/ombi.component.ts @@ -1,16 +1,19 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder, FormGroup } from "@angular/forms"; -import { IOmbiSettings } from "../../interfaces"; +import { ILanguageRefine, IOmbiSettings } from "../../interfaces"; import { NotificationService } from "../../services"; import { SettingsService } from "../../services"; +import * as languageData from "../../../other/iso-lang.json"; + @Component({ templateUrl: "./ombi.component.html", }) export class OmbiComponent implements OnInit { public form: FormGroup; + public langauges: ILanguageRefine[]; constructor(private settingsService: SettingsService, private notificationService: NotificationService, @@ -25,8 +28,10 @@ export class OmbiComponent implements OnInit { baseUrl: [x.baseUrl], doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove], hideRequestsUsers: [x.hideRequestsUsers], + defaultLanguageCode: [x.defaultLanguageCode], }); }); + this.langauges = languageData; } public refreshApiKey() { diff --git a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html index 909a64226..08a8035c2 100644 --- a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html +++ b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html @@ -1,5 +1,4 @@ - - +
    Radarr Settings @@ -19,25 +18,34 @@
    - + - - The IP/Hostname is required +
    - + - - The Port is required +
    - - - - The API Key is required + + +
    @@ -49,63 +57,73 @@
    - +
    +
    -
    - -
    -
    -
    - +
    - +
    - A Default Quality Profile is required
    -
    -
    - - -
    -
    - +
    - + -
    - A Default Root Path is required + + +
    + +
    - +
    -
    - - A Default Minimum Availability is required
    - +
    +
    +
    - +
    @@ -118,4 +136,4 @@
    -
    +
    \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/settings.module.ts b/src/Ombi/ClientApp/app/settings/settings.module.ts index f102a03fe..6fb69dc27 100644 --- a/src/Ombi/ClientApp/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/app/settings/settings.module.ts @@ -9,7 +9,7 @@ import { AuthGuard } from "../auth/auth.guard"; import { AuthService } from "../auth/auth.service"; import { CouchPotatoService, EmbyService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService, - SonarrService, TesterService, ValidationService, + RequestRetryService, SonarrService, TesterService, ValidationService, } from "../services"; import { PipeModule } from "../pipes/pipe.module"; @@ -19,6 +19,7 @@ import { CouchPotatoComponent } from "./couchpotato/couchpotato.component"; import { CustomizationComponent } from "./customization/customization.component"; import { DogNzbComponent } from "./dognzb/dognzb.component"; import { EmbyComponent } from "./emby/emby.component"; +import { FailedRequestsComponent } from "./failedrequests/failedrequests.component"; import { IssuesComponent } from "./issues/issues.component"; import { JobsComponent } from "./jobs/jobs.component"; import { LandingPageComponent } from "./landingpage/landingpage.component"; @@ -26,6 +27,7 @@ import { LidarrComponent } from "./lidarr/lidarr.component"; import { MassEmailComponent } from "./massemail/massemail.component"; import { DiscordComponent } from "./notifications/discord.component"; import { EmailNotificationComponent } from "./notifications/emailnotification.component"; +import { GotifyComponent } from "./notifications/gotify.component"; import { MattermostComponent } from "./notifications/mattermost.component"; import { MobileComponent } from "./notifications/mobile.component"; import { NewsletterComponent } from "./notifications/newsletter.component"; @@ -41,6 +43,7 @@ import { SickRageComponent } from "./sickrage/sickrage.component"; import { SonarrComponent } from "./sonarr/sonarr.component"; import { UpdateComponent } from "./update/update.component"; import { UserManagementComponent } from "./usermanagement/usermanagement.component"; +import { VoteComponent } from "./vote/vote.component"; import { WikiComponent } from "./wiki.component"; import { SettingsMenuComponent } from "./settingsmenu.component"; @@ -61,6 +64,7 @@ const routes: Routes = [ { path: "Slack", component: SlackComponent, canActivate: [AuthGuard] }, { path: "Pushover", component: PushoverComponent, canActivate: [AuthGuard] }, { path: "Pushbullet", component: PushbulletComponent, canActivate: [AuthGuard] }, + { path: "Gotify", component: GotifyComponent, canActivate: [AuthGuard] }, { path: "Mattermost", component: MattermostComponent, canActivate: [AuthGuard] }, { path: "UserManagement", component: UserManagementComponent, canActivate: [AuthGuard] }, { path: "Update", component: UpdateComponent, canActivate: [AuthGuard] }, @@ -75,6 +79,8 @@ const routes: Routes = [ { path: "MassEmail", component: MassEmailComponent, canActivate: [AuthGuard] }, { path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] }, { path: "Lidarr", component: LidarrComponent, canActivate: [AuthGuard] }, + { path: "Vote", component: VoteComponent, canActivate: [AuthGuard] }, + { path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] }, ]; @NgModule({ @@ -113,6 +119,7 @@ const routes: Routes = [ PushoverComponent, MattermostComponent, PushbulletComponent, + GotifyComponent, UserManagementComponent, UpdateComponent, AboutComponent, @@ -127,6 +134,8 @@ const routes: Routes = [ MassEmailComponent, NewsletterComponent, LidarrComponent, + VoteComponent, + FailedRequestsComponent, ], exports: [ RouterModule, @@ -146,6 +155,7 @@ const routes: Routes = [ MobileService, NotificationMessageService, LidarrService, + RequestRetryService, ], }) diff --git a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html index 0465cc8c8..81f901ecc 100644 --- a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html @@ -12,6 +12,7 @@
  • Issues
  • User Importer
  • Authentication
  • +
  • Vote
  • @@ -53,7 +54,7 @@ Music @@ -73,6 +74,7 @@
  • Pushover
  • Mattermost
  • Telegram
  • +
  • Gotify
  • @@ -83,6 +85,7 @@