mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 12:59:39 -07:00
Merge develop into recentlyadded
This commit is contained in:
commit
b55efd60c5
121 changed files with 4196 additions and 523 deletions
374
CHANGELOG.md
374
CHANGELOG.md
|
@ -2,8 +2,118 @@
|
||||||
|
|
||||||
## (unreleased)
|
## (unreleased)
|
||||||
|
|
||||||
|
### **Fixes**
|
||||||
|
|
||||||
|
- Small memory improvements in the Plex Sync. [Jamie]
|
||||||
|
|
||||||
|
- Fixed the sort issue on the user Management page. Also added sorting to the Movie Requests page. [tidusjar]
|
||||||
|
|
||||||
|
- Downgraded the angular2-jwt library since it has a bug in it. #2064. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed an issue when Plex decideds to reuse the Plex Key for a different media item... #2038. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed an issue where we might show the Imdb link when we do not have a imdbid #1797. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed the issue where we can no longer select Pending Approval in the filters #2057. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed the API key not working when attempting to get requests #2058. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #2056. [tidusjar]
|
||||||
|
|
||||||
|
- Experimental, set the Webpack base root to the ombi base path if we have it. This should hopefully fix the reverse proxy issues. [Jamie]
|
||||||
|
|
||||||
|
|
||||||
|
## v3.0.3000 (2018-03-09)
|
||||||
|
|
||||||
### **New Features**
|
### **New Features**
|
||||||
|
|
||||||
|
- Added the ability to override root and quality options in Sonarr (#2049) [Jamie]
|
||||||
|
|
||||||
|
- Added Pending Approval into the filters list. [tidusjar]
|
||||||
|
|
||||||
|
- Added the ability to hide requests that have not been made by that user (#2052) [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Louis Laureys]
|
||||||
|
|
||||||
|
### **Fixes**
|
||||||
|
|
||||||
|
- Fixed #2042. [Jamie]
|
||||||
|
|
||||||
|
|
||||||
|
## v3.0.0 (2018-03-04)
|
||||||
|
|
||||||
|
### **New Features**
|
||||||
|
|
||||||
|
- Update build.cake. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Added a similar button to the movie searches. Makes movie discoverablility easier. [tidusjar]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update ISSUE_TEMPLATE.md. [Jamie]
|
||||||
|
|
||||||
|
- Update appveyor.yml. [Jamie]
|
||||||
|
|
||||||
|
- Update ISSUE_TEMPLATE.md. [PotatoQuality]
|
||||||
|
|
||||||
|
- Update ISSUE_TEMPLATE.md. [PotatoQuality]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [PotatoQuality]
|
||||||
|
|
||||||
|
- Change the default templates to use {IssueUser} [Jamie]
|
||||||
|
|
||||||
|
- Changed the base url validation. [tidusjar]
|
||||||
|
|
||||||
|
- Added bulk editing (#1941) [Jamie]
|
||||||
|
|
||||||
|
- Change the poster size to w300 #1932. [Jamie]
|
||||||
|
|
||||||
|
- Added a default user agent on all API calls. [tidusjar]
|
||||||
|
|
||||||
|
- Update request.service.ts. [Jamie]
|
||||||
|
|
||||||
|
- Added a filter onto the movies requests page for some inital feedback. [Jamie]
|
||||||
|
|
||||||
|
- Added ordering to the User Management screen. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Added custom donation url (#1902) [m4tta]
|
||||||
|
|
||||||
|
- Changed the url scheme to make it easier to parse. [Jamie]
|
||||||
|
|
||||||
|
- Added Norwegian to the translation code, forgot to check this in. [Jamie]
|
||||||
|
|
||||||
|
- Added Norwegian to the language dropdown. [Jamie]
|
||||||
|
|
||||||
|
- Added the stuff needed for omBlur. [tidusjar]
|
||||||
|
|
||||||
|
- Update README.md (#1872) [xnaas]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update plex.component.html. [Jamie]
|
||||||
|
|
||||||
|
- Change plus to list in menu (#1855) [Louis Laureys]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
|
- Update README.md. [Jamie]
|
||||||
|
|
||||||
- Added user request limits, We can now set the limit for a user. [tidusjar]
|
- Added user request limits, We can now set the limit for a user. [tidusjar]
|
||||||
|
|
||||||
- Updated the UI JWT framework. [Jamie]
|
- Updated the UI JWT framework. [Jamie]
|
||||||
|
@ -284,6 +394,270 @@
|
||||||
|
|
||||||
### **Fixes**
|
### **Fixes**
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) (#2020) [Jamie]
|
||||||
|
|
||||||
|
- Publish 32bit build of windows. [tidusjar]
|
||||||
|
|
||||||
|
- Fixing incorrect filter translation targets (#1987) [Jono Cairns]
|
||||||
|
|
||||||
|
- New Crowdin translations (#2017) [Jamie]
|
||||||
|
|
||||||
|
- Fixed #1997. [tidusjar]
|
||||||
|
|
||||||
|
- We now show the digital release date in the search if available #1962. [tidusjar]
|
||||||
|
|
||||||
|
- Css fixes (#2014) [Louis Laureys]
|
||||||
|
|
||||||
|
- API improvements. [Jamie]
|
||||||
|
|
||||||
|
- Fix #1599 (#2008) [Louis Laureys]
|
||||||
|
|
||||||
|
- Issue button fix (#2006) [Louis Laureys]
|
||||||
|
|
||||||
|
- Fixed #1886 #1865. [Jamie]
|
||||||
|
|
||||||
|
- Fixed the outstanding issue on #1995. [Jamie]
|
||||||
|
|
||||||
|
- Fixed an issue for #1951. [tidusjar]
|
||||||
|
|
||||||
|
- Try and fuzzy match the title and release if we cannot get the tvdb id or imdbid (depends on the media agents in Plex) #1951. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1989 #1719. [Jamie]
|
||||||
|
|
||||||
|
- Small changes that might fix #1985 but doubt it. [Jamie]
|
||||||
|
|
||||||
|
- Should fix #1975. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1789. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1968. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1978. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1954. [tidusjar]
|
||||||
|
|
||||||
|
- Small changes to the auto updater, let's see how this works. [Jamie]
|
||||||
|
|
||||||
|
- Fixed build. [Jamie]
|
||||||
|
|
||||||
|
- Fixed the update check for the master build. [Jamie]
|
||||||
|
|
||||||
|
- Removed accidently merged files. [Jamie]
|
||||||
|
|
||||||
|
- Create CODE_OF_CONDUCT.md. [Jamie]
|
||||||
|
|
||||||
|
- Windows installation guide link update. [PotatoQuality]
|
||||||
|
|
||||||
|
- Fixed the issue comment issue #1914 also added another variable for issues {IssueUser} which is the user that reported the issue. [Jamie]
|
||||||
|
|
||||||
|
- Fix #1914. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1914. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed build and added logging. [TidusJar]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1934) [Jamie]
|
||||||
|
|
||||||
|
- Potential fix for #1942. [Jamie]
|
||||||
|
|
||||||
|
- Quick change to the Emby Availability rule to make it in line slightly with the Plex one. #1950. [Jamie]
|
||||||
|
|
||||||
|
- Turn off mobile notifications. [tidusjar]
|
||||||
|
|
||||||
|
- FIXED PLEX!!!!! [tidusjar]
|
||||||
|
|
||||||
|
- Batch the PlexContentSync and increase the plex episode batch size. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed the migration issue, it's too difficult to migrate the tables. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1942. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed checkboxes style. [Jamie]
|
||||||
|
|
||||||
|
- These are not the droids you are looking for. [Jamie]
|
||||||
|
|
||||||
|
- Fixed the wrong translation and see if we can VACUUM the db. [tidusjar]
|
||||||
|
|
||||||
|
- More translations and added a check on the baseurl to ensure it starts with a '/' [Jamie]
|
||||||
|
|
||||||
|
- More translations. [Jamie]
|
||||||
|
|
||||||
|
- Fixed #1878 and added a Request all button when selecting episodes. [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (German) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (German) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Danish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Italian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (German) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Swedish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Spanish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (French) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Danish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Italian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (German) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Swedish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Spanish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (French) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Danish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Danish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Italian) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (German) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Dutch) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Swedish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Spanish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (French) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Danish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Swedish) [Jamie]
|
||||||
|
|
||||||
|
- New translations en.json (Norwegian) [Jamie]
|
||||||
|
|
||||||
|
- Working on the movie matching. Stop dupes #1869. [tidusjar]
|
||||||
|
|
||||||
|
- Delete plex episodes on every run due to a bug, need to spend quite a bit of time on this. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed the issue where we were always adding emby episodes. Also fixed #1933. [tidusjar]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1906) [Jamie]
|
||||||
|
|
||||||
|
- Add plain password for emby login (#1925) [dorian ALKOUM]
|
||||||
|
|
||||||
|
- Fixed #1924. [Jamie]
|
||||||
|
|
||||||
|
- Fixed the issue where I knocked out the ordering of notifications, oops. [tidusjar]
|
||||||
|
|
||||||
|
- #1914 for the issue resolved notification. [Jamie]
|
||||||
|
|
||||||
|
- #1916. [Jamie]
|
||||||
|
|
||||||
|
- Remove the placeholder. [Jamie]
|
||||||
|
|
||||||
|
- Feature arm (#1909) [Jamie]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1897) [Jamie]
|
||||||
|
|
||||||
|
- Fix logo cut off on login screen (#1896) [Louis Laureys]
|
||||||
|
|
||||||
|
- E-Mails: Only add poster table row if img is set (#1899) [Louis Laureys]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1884) [Jamie]
|
||||||
|
|
||||||
|
- Fix mobile layout (#1888) [Louis Laureys]
|
||||||
|
|
||||||
|
- Smal changes to the api. [tidusjar]
|
||||||
|
|
||||||
|
- OmBlur. [tidusjar]
|
||||||
|
|
||||||
|
- Hide the password field if it's not needed #1815. [Jamie]
|
||||||
|
|
||||||
|
- Should fix #1885. [Jamie]
|
||||||
|
|
||||||
|
- Make user management table responsive (#1882) [Louis Laureys]
|
||||||
|
|
||||||
|
- Fixed some stuff for omBlur. [Jamie]
|
||||||
|
|
||||||
|
- Some work... No one take a look at this, it's a suprise. [Jamie]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1858) [Jamie]
|
||||||
|
|
||||||
|
- When requesting Anime, we now mark it correctly as Anime in Sonarr. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1879 and added the spans. [tidusjar]
|
||||||
|
|
||||||
|
- Some work on the auto updater #1460. [tidusjar]
|
||||||
|
|
||||||
|
- Removed the potential locking. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1863. [tidusjar]
|
||||||
|
|
||||||
|
- Moved the update check code from the External azure service into Ombi at /api/v1/update/BRANCH. [Jamie]
|
||||||
|
|
||||||
|
- Fixed the UI erroring out, also dont show tv with no externals. [tidusjar]
|
||||||
|
|
||||||
|
- More memory management and improvements. [tidusjar]
|
||||||
|
|
||||||
|
- These are not needed, added accidentally (#1860) [Louis Laureys]
|
||||||
|
|
||||||
|
- Some memory management improvements. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1857. [tidusjar]
|
||||||
|
|
||||||
|
- Delete old v2 ombi from v3 branch. [tidusjar]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1840) [Jamie]
|
||||||
|
|
||||||
|
- Better login backgrounds! (#1852) [Louis Laureys]
|
||||||
|
|
||||||
|
- Fixed #1851. [tidusjar]
|
||||||
|
|
||||||
|
- Fixed #1826. [tidusjar]
|
||||||
|
|
||||||
|
- Redo change #1848. [tidusjar]
|
||||||
|
|
||||||
|
- Fix the issue for welcome emails not sending. [tidusjar]
|
||||||
|
|
||||||
|
- Fix typo (#1845) [Kyle Lucy]
|
||||||
|
|
||||||
|
- Fix user mentions in Slack notifications (#1846) [Aljosa Asanovic]
|
||||||
|
|
||||||
|
- If Radarr/Sonarr has noticed that the media is available, then mark it as available in the UI. [Jamie]
|
||||||
|
|
||||||
|
- Fixed #1835. [Jamie]
|
||||||
|
|
||||||
|
- Enable Multi MIME and add alt tags to images (#1838) [Louis Laureys]
|
||||||
|
|
||||||
|
- New Crowdin translations (#1816) [Jamie]
|
||||||
|
|
||||||
|
- Fixed #1832. [tidusjar]
|
||||||
|
|
||||||
|
- Switch to use a single HTTPClient rather than a new one every request !dev. [tidusjar]
|
||||||
|
|
||||||
|
- Fix non-admin rights (#1820) [Rob Gökemeijer]
|
||||||
|
|
||||||
|
- Fix duplicated "Requests" element ID on new Issues link (#1817) [Shoghi Cervantes]
|
||||||
|
|
||||||
|
- Add the Issue Reporting functionality (#1811) [Jamie]
|
||||||
|
|
||||||
|
- Removed the forum. [tidusjar]
|
||||||
|
|
||||||
- #1659 Made the option to ignore notifcations for auto approve. [Jamie]
|
- #1659 Made the option to ignore notifcations for auto approve. [Jamie]
|
||||||
|
|
||||||
- New Crowdin translations (#1806) [Jamie]
|
- New Crowdin translations (#1806) [Jamie]
|
||||||
|
|
15
README.md
15
README.md
|
@ -9,17 +9,16 @@ ____
|
||||||
[](https://patreon.com/tidusjar/Ombi)
|
[](https://patreon.com/tidusjar/Ombi)
|
||||||
[](https://paypal.me/PlexRequestsNet)
|
[](https://paypal.me/PlexRequestsNet)
|
||||||
|
|
||||||
[](https://patreon.com/tidusjar/Ombi)
|
|
||||||
[](https://paypal.me/PlexRequestsNet)
|
|
||||||
___
|
___
|
||||||
|
|
||||||
|
|
||||||
[](https://forums.ombi.io/viewforum.php?f=10) [](https://forums.ombi.io/posting.php?mode=post&f=20)
|
[](https://forums.ombi.io/viewforum.php?f=10) [](https://forums.ombi.io/posting.php?mode=post&f=20)
|
||||||
|
|
||||||
| Service | Master (V2) | Open Beta (V3 - Recommended) |
|
|
||||||
|
| Service | Stable | Develop |
|
||||||
|----------|:---------------------------:|:----------------------------:|
|
|----------|:---------------------------:|:----------------------------:|
|
||||||
| AppVeyor | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/DotNetCore) |
|
| AppVeyor | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) |
|
||||||
| Download |[](https://github.com/tidusjar/Ombi/releases) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/DotNetCore/artifacts) |
|
| Download |[](https://github.com/tidusjar/Ombi/releases) | [](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) |
|
||||||
# Features
|
# Features
|
||||||
Here are some of the features Ombi V3 has:
|
Here are some of the features Ombi V3 has:
|
||||||
* Now working without crashes on Linux.
|
* Now working without crashes on Linux.
|
||||||
|
@ -72,10 +71,9 @@ We are planning to bring back these features in V3 but for now you can find a li
|
||||||
| Login page | Yes (brand new) | Yes |
|
| Login page | Yes (brand new) | Yes |
|
||||||
| Custom Notification Messages | Yes | No |
|
| Custom Notification Messages | Yes | No |
|
||||||
| Sending newsletters | Planned | Yes |
|
| Sending newsletters | Planned | Yes |
|
||||||
| Send a Mass Email | Planned | Yes |
|
| Send a Mass Email | Yes | Yes |
|
||||||
| SickRage | Yes | Yes |
|
| SickRage | Yes | Yes |
|
||||||
| CouchPotato | Yes | Yes |
|
| CouchPotato | Yes | Yes |
|
||||||
| Watcher | Planned | Yes |
|
|
||||||
| DogNzb | Yes | No |
|
| DogNzb | Yes | No |
|
||||||
| Issues | Yes | Yes |
|
| Issues | Yes | Yes |
|
||||||
| Headphones | No (support dropped) | Yes |
|
| Headphones | No (support dropped) | Yes |
|
||||||
|
@ -94,8 +92,9 @@ Search the existing requests to see if your suggestion has already been submitte
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
[Click Here](https://github.com/tidusjar/Ombi/wiki/Installation)
|
[Installation Guide](https://github.com/tidusjar/Ombi/wiki/Installation)
|
||||||
[Here for Reverse Proxy Config Examples](https://github.com/tidusjar/Ombi/wiki/Reverse-Proxy-Examples)
|
[Here for Reverse Proxy Config Examples](https://github.com/tidusjar/Ombi/wiki/Reverse-Proxy-Examples)
|
||||||
|
[PlexGuide.com - Ombi Deployment & 101 Demonstration!](https://www.youtube.com/watch?v=QPNlqqkjNJw&feature=youtu.be)
|
||||||
|
|
||||||
# Contributors
|
# Contributors
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,12 @@ after_build:
|
||||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz"
|
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip"
|
||||||
|
|
||||||
|
|
||||||
|
# appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#cache:
|
#cache:
|
||||||
#- '%USERPROFILE%\.nuget\packages'
|
#- '%USERPROFILE%\.nuget\packages'
|
||||||
|
|
57
build.cake
57
build.cake
|
@ -3,7 +3,7 @@
|
||||||
#addin "Cake.Gulp"
|
#addin "Cake.Gulp"
|
||||||
#addin "Cake.Npm"
|
#addin "Cake.Npm"
|
||||||
#addin "SharpZipLib"
|
#addin "SharpZipLib"
|
||||||
#addin "Cake.Compression"
|
#addin nuget:?package=Cake.Compression&version=0.1.4
|
||||||
#addin "Cake.Incubator"
|
#addin "Cake.Incubator"
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -26,26 +26,29 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
|
||||||
var solutionFile = "Ombi.sln"; // Solution file if needed
|
var solutionFile = "Ombi.sln"; // Solution file if needed
|
||||||
GitVersion versionInfo = null;
|
GitVersion versionInfo = null;
|
||||||
|
|
||||||
|
var frameworkVer = "netcoreapp2.0";
|
||||||
|
|
||||||
var buildSettings = new DotNetCoreBuildSettings
|
var buildSettings = new DotNetCoreBuildSettings
|
||||||
{
|
{
|
||||||
Framework = "netcoreapp2.0",
|
Framework = frameworkVer,
|
||||||
Configuration = "Release",
|
Configuration = "Release",
|
||||||
OutputDirectory = Directory(buildDir),
|
OutputDirectory = Directory(buildDir),
|
||||||
};
|
};
|
||||||
|
|
||||||
var publishSettings = new DotNetCorePublishSettings
|
var publishSettings = new DotNetCorePublishSettings
|
||||||
{
|
{
|
||||||
Framework = "netcoreapp2.0",
|
Framework = frameworkVer,
|
||||||
Configuration = "Release",
|
Configuration = "Release",
|
||||||
OutputDirectory = Directory(buildDir),
|
OutputDirectory = Directory(buildDir),
|
||||||
};
|
};
|
||||||
|
|
||||||
var artifactsFolder = buildDir + "/netcoreapp2.0/";
|
var artifactsFolder = buildDir + "/"+frameworkVer+"/";
|
||||||
var windowsArtifactsFolder = artifactsFolder + "win10-x64/published";
|
var windowsArtifactsFolder = artifactsFolder + "win10-x64/published";
|
||||||
var windows32BitArtifactsFolder = artifactsFolder + "win10-x32/published";
|
var windows32BitArtifactsFolder = artifactsFolder + "win10-x86/published";
|
||||||
var osxArtifactsFolder = artifactsFolder + "osx-x64/published";
|
var osxArtifactsFolder = artifactsFolder + "osx-x64/published";
|
||||||
var linuxArtifactsFolder = artifactsFolder + "linux-x64/published";
|
var linuxArtifactsFolder = artifactsFolder + "linux-x64/published";
|
||||||
var linuxArmArtifactsFolder = artifactsFolder + "linux-arm/published";
|
var linuxArmArtifactsFolder = artifactsFolder + "linux-arm/published";
|
||||||
|
var linuxArm64BitArtifactsFolder = artifactsFolder + "linux-arm64/published";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,6 +107,10 @@ Task("SetVersionInfo")
|
||||||
{
|
{
|
||||||
fullVer = fullVer.Replace("_","");
|
fullVer = fullVer.Replace("_","");
|
||||||
}
|
}
|
||||||
|
if(fullVer.Contains("/"))
|
||||||
|
{
|
||||||
|
fullVer = fullVer.Replace("/","");
|
||||||
|
}
|
||||||
|
|
||||||
buildSettings.ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.AssemblySemVer);
|
buildSettings.ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.AssemblySemVer);
|
||||||
buildSettings.ArgumentCustomization = args => args.Append("/p:FullVer=" + fullVer);
|
buildSettings.ArgumentCustomization = args => args.Append("/p:FullVer=" + fullVer);
|
||||||
|
@ -161,35 +168,38 @@ Task("Package")
|
||||||
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
|
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
|
||||||
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
|
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
|
||||||
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
|
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
|
||||||
|
//GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("Publish")
|
Task("Publish")
|
||||||
.IsDependentOn("PrePublish")
|
.IsDependentOn("PrePublish")
|
||||||
.IsDependentOn("Publish-Windows")
|
.IsDependentOn("Publish-Windows")
|
||||||
|
.IsDependentOn("Publish-Windows-32bit")
|
||||||
.IsDependentOn("Publish-OSX")
|
.IsDependentOn("Publish-OSX")
|
||||||
.IsDependentOn("Publish-Linux")
|
.IsDependentOn("Publish-Linux")
|
||||||
.IsDependentOn("Publish-Linux-ARM")
|
.IsDependentOn("Publish-Linux-ARM")
|
||||||
|
//.IsDependentOn("Publish-Linux-ARM-64Bit")
|
||||||
.IsDependentOn("Package");
|
.IsDependentOn("Package");
|
||||||
|
|
||||||
Task("Publish-Windows")
|
Task("Publish-Windows")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
publishSettings.Runtime = "win10-x64";
|
publishSettings.Runtime = "win10-x64";
|
||||||
publishSettings.OutputDirectory = Directory(buildDir) + Directory("netcoreapp2.0/win10-x64/published");
|
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published");
|
||||||
|
|
||||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||||
CopyFile(buildDir + "/netcoreapp2.0/win10-x64/Swagger.xml", buildDir + "/netcoreapp2.0/win10-x64/published/Swagger.xml");
|
CopyFile(buildDir + "/"+frameworkVer+"/win10-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x64/published/Swagger.xml");
|
||||||
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("Publish-Windows-32bit")
|
Task("Publish-Windows-32bit")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
publishSettings.Runtime = "win10-x32";
|
publishSettings.Runtime = "win10-x86";
|
||||||
publishSettings.OutputDirectory = Directory(buildDir) + Directory("netcoreapp2.0/win10-x32/published");
|
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/win10-x86/published");
|
||||||
|
|
||||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||||
CopyFile(buildDir + "/netcoreapp2.0/win10-x32/Swagger.xml", buildDir + "/netcoreapp2.0/win10-x32/published/Swagger.xml");
|
CopyFile(buildDir + "/"+frameworkVer+"/win10-x86/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x86/published/Swagger.xml");
|
||||||
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -197,10 +207,10 @@ Task("Publish-OSX")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
publishSettings.Runtime = "osx-x64";
|
publishSettings.Runtime = "osx-x64";
|
||||||
publishSettings.OutputDirectory = Directory(buildDir) + Directory("netcoreapp2.0/osx-x64/published");
|
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/osx-x64/published");
|
||||||
|
|
||||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||||
CopyFile(buildDir + "/netcoreapp2.0/osx-x64/Swagger.xml", buildDir + "/netcoreapp2.0/osx-x64/published/Swagger.xml");
|
CopyFile(buildDir + "/"+frameworkVer+"/osx-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/osx-x64/published/Swagger.xml");
|
||||||
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -208,10 +218,10 @@ Task("Publish-Linux")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
publishSettings.Runtime = "linux-x64";
|
publishSettings.Runtime = "linux-x64";
|
||||||
publishSettings.OutputDirectory = Directory(buildDir) + Directory("netcoreapp2.0/linux-x64/published");
|
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-x64/published");
|
||||||
|
|
||||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||||
CopyFile(buildDir + "/netcoreapp2.0/linux-x64/Swagger.xml", buildDir + "/netcoreapp2.0/linux-x64/published/Swagger.xml");
|
CopyFile(buildDir + "/"+frameworkVer+"/linux-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-x64/published/Swagger.xml");
|
||||||
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -219,12 +229,25 @@ Task("Publish-Linux-ARM")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
publishSettings.Runtime = "linux-arm";
|
publishSettings.Runtime = "linux-arm";
|
||||||
publishSettings.OutputDirectory = Directory(buildDir) + Directory("netcoreapp2.0/linux-arm/published");
|
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm/published");
|
||||||
|
|
||||||
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||||
CopyFile(
|
CopyFile(
|
||||||
buildDir + "/netcoreapp2.0/linux-arm/Swagger.xml",
|
buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml",
|
||||||
buildDir + "/netcoreapp2.0/linux-arm/published/Swagger.xml");
|
buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml");
|
||||||
|
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("Publish-Linux-ARM-64Bit")
|
||||||
|
.Does(() =>
|
||||||
|
{
|
||||||
|
publishSettings.Runtime = "linux-arm64";
|
||||||
|
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm64/published");
|
||||||
|
|
||||||
|
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
|
||||||
|
CopyFile(
|
||||||
|
buildDir + "/"+frameworkVer+"/linux-arm64/Swagger.xml",
|
||||||
|
buildDir + "/"+frameworkVer+"/linux-arm64/published/Swagger.xml");
|
||||||
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Ombi.Api.Plex.Models
|
||||||
public int leafCount { get; set; }
|
public int leafCount { get; set; }
|
||||||
public int viewedLeafCount { get; set; }
|
public int viewedLeafCount { get; set; }
|
||||||
public int childCount { get; set; }
|
public int childCount { get; set; }
|
||||||
public int addedAt { get; set; }
|
public long addedAt { get; set; }
|
||||||
public int updatedAt { get; set; }
|
public int updatedAt { get; set; }
|
||||||
public Genre[] Genre { get; set; }
|
public Genre[] Genre { get; set; }
|
||||||
//public Role[] Role { get; set; }
|
//public Role[] Role { get; set; }
|
||||||
|
|
9
src/Ombi.Api.Radarr/CommandResult.cs
Normal file
9
src/Ombi.Api.Radarr/CommandResult.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ombi.Api.Radarr
|
||||||
|
{
|
||||||
|
public class CommandResult
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,9 @@ namespace Ombi.Api.Radarr
|
||||||
Task<List<RadarrProfile>> GetProfiles(string apiKey, string baseUrl);
|
Task<List<RadarrProfile>> GetProfiles(string apiKey, string baseUrl);
|
||||||
Task<List<RadarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
|
Task<List<RadarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
|
||||||
Task<SystemStatus> SystemStatus(string apiKey, string baseUrl);
|
Task<SystemStatus> SystemStatus(string apiKey, string baseUrl);
|
||||||
|
Task<MovieResponse> GetMovie(int id, string apiKey, string baseUrl);
|
||||||
|
Task<MovieResponse> UpdateMovie(MovieResponse movie, string apiKey, string baseUrl);
|
||||||
|
Task<bool> MovieSearch(int[] movieIds, string apiKey, string baseUrl);
|
||||||
Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability);
|
Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,10 +17,7 @@ namespace Ombi.Api.Radarr.Models
|
||||||
public bool monitored { get; set; }
|
public bool monitored { get; set; }
|
||||||
public int tmdbId { get; set; }
|
public int tmdbId { get; set; }
|
||||||
public List<string> images { get; set; }
|
public List<string> images { get; set; }
|
||||||
public string cleanTitle { get; set; }
|
|
||||||
public string imdbId { get; set; }
|
|
||||||
public string titleSlug { get; set; }
|
public string titleSlug { get; set; }
|
||||||
public int id { get; set; }
|
|
||||||
public int year { get; set; }
|
public int year { get; set; }
|
||||||
public string minimumAvailability { get; set; }
|
public string minimumAvailability { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,10 @@
|
||||||
public class RadarrError
|
public class RadarrError
|
||||||
{
|
{
|
||||||
public string message { get; set; }
|
public string message { get; set; }
|
||||||
public string description { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RadarrErrorResponse
|
public class RadarrErrorResponse
|
||||||
{
|
{
|
||||||
public string propertyName { get; set; }
|
|
||||||
public string errorMessage { get; set; }
|
public string errorMessage { get; set; }
|
||||||
public object attemptedValue { get; set; }
|
|
||||||
public FormattedMessagePlaceholderValues formattedMessagePlaceholderValues { get; set; }
|
|
||||||
}
|
|
||||||
public class FormattedMessagePlaceholderValues
|
|
||||||
{
|
|
||||||
public string propertyName { get; set; }
|
|
||||||
public object propertyValue { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -53,6 +52,23 @@ namespace Ombi.Api.Radarr
|
||||||
return await Api.Request<List<MovieResponse>>(request);
|
return await Api.Request<List<MovieResponse>>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<MovieResponse> GetMovie(int id, string apiKey, string baseUrl)
|
||||||
|
{
|
||||||
|
var request = new Request($"/api/movie/{id}", baseUrl, HttpMethod.Get);
|
||||||
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
|
return await Api.Request<MovieResponse>(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MovieResponse> UpdateMovie(MovieResponse movie, string apiKey, string baseUrl)
|
||||||
|
{
|
||||||
|
var request = new Request($"/api/movie/", baseUrl, HttpMethod.Put);
|
||||||
|
AddHeaders(request, apiKey);
|
||||||
|
request.AddJsonBody(movie);
|
||||||
|
|
||||||
|
return await Api.Request<MovieResponse>(request);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability)
|
public async Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability)
|
||||||
{
|
{
|
||||||
var request = new Request("/api/movie", baseUrl, HttpMethod.Post);
|
var request = new Request("/api/movie", baseUrl, HttpMethod.Post);
|
||||||
|
@ -66,7 +82,7 @@ namespace Ombi.Api.Radarr
|
||||||
titleSlug = title,
|
titleSlug = title,
|
||||||
monitored = true,
|
monitored = true,
|
||||||
year = year,
|
year = year,
|
||||||
minimumAvailability = minimumAvailability,
|
minimumAvailability = minimumAvailability
|
||||||
};
|
};
|
||||||
|
|
||||||
if (searchNow)
|
if (searchNow)
|
||||||
|
@ -81,9 +97,9 @@ namespace Ombi.Api.Radarr
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
request.AddJsonBody(options);
|
request.AddJsonBody(options);
|
||||||
|
|
||||||
|
var response = await Api.RequestContent(request);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await Api.RequestContent(request);
|
|
||||||
if (response.Contains("\"message\":"))
|
if (response.Contains("\"message\":"))
|
||||||
{
|
{
|
||||||
var error = JsonConvert.DeserializeObject<RadarrError>(response);
|
var error = JsonConvert.DeserializeObject<RadarrError>(response);
|
||||||
|
@ -98,11 +114,24 @@ namespace Ombi.Api.Radarr
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (JsonSerializationException jse)
|
||||||
{
|
{
|
||||||
Logger.LogError(LoggingEvents.RadarrApi, jse, "Error When adding movie to Radarr");
|
Logger.LogError(LoggingEvents.RadarrApi, jse, "Error When adding movie to Radarr, Reponse: {0}", response);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> MovieSearch(int[] movieIds, string apiKey, string baseUrl)
|
||||||
|
{
|
||||||
|
var result = await Command(apiKey, baseUrl, new { name = "MoviesSearch", movieIds });
|
||||||
|
return result != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<CommandResult> Command(string apiKey, string baseUrl, object body)
|
||||||
|
{
|
||||||
|
var request = new Request($"/api/Command/", baseUrl, HttpMethod.Post);
|
||||||
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
|
request.AddJsonBody(body);
|
||||||
|
return await Api.Request<CommandResult>(request);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the required headers and also the authorization header
|
/// Adds the required headers and also the authorization header
|
||||||
|
|
|
@ -8,11 +8,6 @@ namespace Ombi.Api.Sonarr.Models
|
||||||
public class CommandResult
|
public class CommandResult
|
||||||
{
|
{
|
||||||
public string name { get; set; }
|
public string name { get; set; }
|
||||||
public DateTime startedOn { get; set; }
|
|
||||||
public DateTime stateChangeTime { get; set; }
|
|
||||||
public bool sendUpdatesToClient { get; set; }
|
|
||||||
public string state { get; set; }
|
|
||||||
public int id { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,10 @@ namespace Ombi.Api.Sonarr
|
||||||
var request = new Request($"/api/series/{id}", baseUrl, HttpMethod.Get);
|
var request = new Request($"/api/series/{id}", baseUrl, HttpMethod.Get);
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
var result = await Api.Request<SonarrSeries>(request);
|
var result = await Api.Request<SonarrSeries>(request);
|
||||||
result.seasons.ToList().RemoveAt(0);
|
if (result?.seasons?.Length > 0)
|
||||||
|
{
|
||||||
|
result?.seasons?.ToList().RemoveAt(0);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,8 @@ namespace Ombi.Core.Authentication
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<bool> CheckPlexPasswordAsync(OmbiUser user, string password)
|
private async Task<bool> CheckPlexPasswordAsync(OmbiUser user, string password)
|
||||||
{
|
{
|
||||||
var result = await _plexApi.SignIn(new UserRequest { password = password, login = user.UserName });
|
var login = user.EmailLogin ? user.Email : user.UserName;
|
||||||
|
var result = await _plexApi.SignIn(new UserRequest { password = password, login = login });
|
||||||
if (result.user?.authentication_token != null)
|
if (result.user?.authentication_token != null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -14,6 +14,8 @@ using Ombi.Store.Repository.Requests;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -24,14 +26,18 @@ namespace Ombi.Core.Engine
|
||||||
private Dictionary<int, TvRequests> _dbTv;
|
private Dictionary<int, TvRequests> _dbTv;
|
||||||
|
|
||||||
protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService,
|
protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService,
|
||||||
IRuleEvaluator rules, OmbiUserManager um) : base(identity, um, rules)
|
IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings) : base(identity, um, rules)
|
||||||
{
|
{
|
||||||
RequestService = requestService;
|
RequestService = requestService;
|
||||||
|
Cache = cache;
|
||||||
|
OmbiSettings = ombiSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IRequestServiceMain RequestService { get; }
|
protected IRequestServiceMain RequestService { get; }
|
||||||
protected IMovieRequestRepository MovieRepository => RequestService.MovieRequestService;
|
protected IMovieRequestRepository MovieRepository => RequestService.MovieRequestService;
|
||||||
protected ITvRequestRepository TvRepository => RequestService.TvRequestService;
|
protected ITvRequestRepository TvRepository => RequestService.TvRequestService;
|
||||||
|
protected readonly ICacheService Cache;
|
||||||
|
protected readonly ISettingsService<OmbiSettings> OmbiSettings;
|
||||||
|
|
||||||
protected async Task<Dictionary<int, MovieRequests>> GetMovieRequests()
|
protected async Task<Dictionary<int, MovieRequests>> GetMovieRequests()
|
||||||
{
|
{
|
||||||
|
@ -99,5 +105,30 @@ namespace Ombi.Core.Engine
|
||||||
Pending = pendingMovies + pendingTv
|
Pending = pendingMovies + pendingTv
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task<HideResult> HideFromOtherUsers()
|
||||||
|
{
|
||||||
|
if (await IsInRole(OmbiRoles.Admin) || await IsInRole(OmbiRoles.PowerUser))
|
||||||
|
{
|
||||||
|
return new HideResult();
|
||||||
|
}
|
||||||
|
var settings = await Cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await OmbiSettings.GetSettingsAsync());
|
||||||
|
var result = new HideResult
|
||||||
|
{
|
||||||
|
Hide = settings.HideRequestsUsers
|
||||||
|
};
|
||||||
|
if (settings.HideRequestsUsers)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
result.UserId = user.Id;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HideResult
|
||||||
|
{
|
||||||
|
public bool Hide { get; set; }
|
||||||
|
public string UserId { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using Ombi.Core.Rule;
|
using System;
|
||||||
|
using Ombi.Core.Rule;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -10,6 +11,7 @@ using Microsoft.AspNetCore.Identity;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine.Interfaces
|
namespace Ombi.Core.Engine.Interfaces
|
||||||
{
|
{
|
||||||
|
@ -30,6 +32,13 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
private OmbiUser _user;
|
private OmbiUser _user;
|
||||||
protected async Task<OmbiUser> GetUser()
|
protected async Task<OmbiUser> GetUser()
|
||||||
{
|
{
|
||||||
|
if (IsApiUser)
|
||||||
|
{
|
||||||
|
return new OmbiUser
|
||||||
|
{
|
||||||
|
UserName = Username,
|
||||||
|
};
|
||||||
|
}
|
||||||
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username));
|
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +49,10 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
|
|
||||||
protected async Task<bool> IsInRole(string roleName)
|
protected async Task<bool> IsInRole(string roleName)
|
||||||
{
|
{
|
||||||
|
if (IsApiUser && roleName != OmbiRoles.Disabled)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return await UserManager.IsInRoleAsync(await GetUser(), roleName);
|
return await UserManager.IsInRoleAsync(await GetUser(), roleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,5 +72,7 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
var ruleResults = await Rules.StartSpecificRules(model, rule);
|
var ruleResults = await Rules.StartSpecificRules(model, rule);
|
||||||
return ruleResults;
|
return ruleResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsApiUser => Username.Equals("Api", StringComparison.CurrentCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,5 +17,7 @@ namespace Ombi.Core
|
||||||
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
||||||
|
|
||||||
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
|
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
|
||||||
|
|
||||||
|
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,6 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
||||||
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
||||||
Task<RequestEngineResult> DenyMovieById(int modelId);
|
Task<RequestEngineResult> DenyMovieById(int modelId);
|
||||||
IEnumerable<MovieRequests> Filter(FilterViewModel vm);
|
Task<IEnumerable<MovieRequests>> Filter(FilterViewModel vm);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,9 +10,13 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm);
|
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> SearchTreeNode(string searchTerm);
|
||||||
Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid);
|
Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid);
|
||||||
Task<SearchTvShowViewModel> GetShowInformation(int tvdbid);
|
Task<SearchTvShowViewModel> GetShowInformation(int tvdbid);
|
||||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> Popular();
|
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree();
|
||||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> Anticipated();
|
Task<IEnumerable<SearchTvShowViewModel>> Popular();
|
||||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatches();
|
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree();
|
||||||
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> Trending();
|
Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
|
||||||
|
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree();
|
||||||
|
Task<IEnumerable<SearchTvShowViewModel>> MostWatches();
|
||||||
|
Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree();
|
||||||
|
Task<IEnumerable<SearchTvShowViewModel>> Trending();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using Ombi.Api.TheMovieDb;
|
using Ombi.Api.TheMovieDb;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Models.Search;
|
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using System;
|
using System;
|
||||||
|
@ -11,9 +10,12 @@ using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Core.Engine.Interfaces;
|
using Ombi.Core.Engine.Interfaces;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
|
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
|
||||||
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
|
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
|
||||||
OmbiUserManager manager, IRepository<RequestLog> rl) : base(user, requestService, r, manager)
|
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings) : base(user, requestService, r, manager, cache, ombiSettings)
|
||||||
{
|
{
|
||||||
MovieApi = movieApi;
|
MovieApi = movieApi;
|
||||||
NotificationHelper = helper;
|
NotificationHelper = helper;
|
||||||
|
@ -45,7 +47,7 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<RequestEngineResult> RequestMovie(MovieRequestViewModel model)
|
public async Task<RequestEngineResult> RequestMovie(MovieRequestViewModel model)
|
||||||
{
|
{
|
||||||
var movieInfo = await MovieApi.GetMovieInformation(model.TheMovieDbId);
|
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId);
|
||||||
if (movieInfo == null || movieInfo.Id == 0)
|
if (movieInfo == null || movieInfo.Id == 0)
|
||||||
{
|
{
|
||||||
return new RequestEngineResult
|
return new RequestEngineResult
|
||||||
|
@ -78,6 +80,9 @@ namespace Ombi.Core.Engine
|
||||||
Background = movieInfo.BackdropPath
|
Background = movieInfo.BackdropPath
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||||
|
requestModel.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
|
||||||
|
|
||||||
var ruleResults = (await RunRequestRules(requestModel)).ToList();
|
var ruleResults = (await RunRequestRules(requestModel)).ToList();
|
||||||
if (ruleResults.Any(x => !x.Success))
|
if (ruleResults.Any(x => !x.Success))
|
||||||
{
|
{
|
||||||
|
@ -122,7 +127,16 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<MovieRequests>> GetRequests(int count, int position)
|
public async Task<IEnumerable<MovieRequests>> GetRequests(int count, int position)
|
||||||
{
|
{
|
||||||
var allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).ToListAsync();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
List<MovieRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).Skip(position).Take(count).ToListAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).ToListAsync();
|
||||||
|
}
|
||||||
allRequests.ForEach(x =>
|
allRequests.ForEach(x =>
|
||||||
{
|
{
|
||||||
x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
|
x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
|
||||||
|
@ -136,7 +150,16 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<MovieRequests>> GetRequests()
|
public async Task<IEnumerable<MovieRequests>> GetRequests()
|
||||||
{
|
{
|
||||||
var allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
List<MovieRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).ToListAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
||||||
|
}
|
||||||
return allRequests;
|
return allRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +170,16 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search)
|
public async Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search)
|
||||||
{
|
{
|
||||||
var allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
List<MovieRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).ToListAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = await MovieRepository.GetWithUser().ToListAsync();
|
||||||
|
}
|
||||||
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
|
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
|
||||||
results.ForEach(x =>
|
results.ForEach(x =>
|
||||||
{
|
{
|
||||||
|
@ -335,9 +367,10 @@ namespace Ombi.Core.Engine
|
||||||
return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" };
|
return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<MovieRequests> Filter(FilterViewModel vm)
|
public async Task<IEnumerable<MovieRequests>> Filter(FilterViewModel vm)
|
||||||
{
|
{
|
||||||
var requests = MovieRepository.GetWithUser();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
var requests = shouldHide.Hide ? MovieRepository.GetWithUser(shouldHide.UserId) : MovieRepository.GetWithUser();
|
||||||
switch (vm.AvailabilityFilter)
|
switch (vm.AvailabilityFilter)
|
||||||
{
|
{
|
||||||
case FilterType.None:
|
case FilterType.None:
|
||||||
|
|
|
@ -12,26 +12,26 @@ using System.Threading.Tasks;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public class MovieSearchEngine : BaseMediaEngine, IMovieEngine
|
public class MovieSearchEngine : BaseMediaEngine, IMovieEngine
|
||||||
{
|
{
|
||||||
public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
||||||
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem)
|
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s)
|
||||||
: base(identity, service, r, um)
|
: base(identity, service, r, um, mem, s)
|
||||||
{
|
{
|
||||||
MovieApi = movApi;
|
MovieApi = movApi;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
MemCache = mem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMovieDbApi MovieApi { get; }
|
private IMovieDbApi MovieApi { get; }
|
||||||
private IMapper Mapper { get; }
|
private IMapper Mapper { get; }
|
||||||
private ILogger<MovieSearchEngine> Logger { get; }
|
private ILogger<MovieSearchEngine> Logger { get; }
|
||||||
private ICacheService MemCache { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lookups the imdb information.
|
/// Lookups the imdb information.
|
||||||
|
@ -40,7 +40,7 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId)
|
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId)
|
||||||
{
|
{
|
||||||
var movieInfo = await MovieApi.GetMovieInformationWithVideo(theMovieDbId);
|
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId);
|
||||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
|
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
|
||||||
|
|
||||||
return await ProcessSingleMovie(viewMovie, true);
|
return await ProcessSingleMovie(viewMovie, true);
|
||||||
|
@ -63,13 +63,29 @@ namespace Ombi.Core.Engine
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get similar movies to the id passed in
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="theMovieDbId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId)
|
||||||
|
{
|
||||||
|
var result = await MovieApi.SimilarMovies(theMovieDbId);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
|
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets popular movies.
|
/// Gets popular movies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Search Result: {result}", result);
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
|
@ -84,7 +100,7 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Search Result: {result}", result);
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
|
@ -99,7 +115,7 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.UpcomingMovies, async () => await MovieApi.Upcoming(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () => await MovieApi.Upcoming(), DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Search Result: {result}", result);
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
|
@ -114,7 +130,7 @@ namespace Ombi.Core.Engine
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Search Result: {result}", result);
|
Logger.LogDebug("Search Result: {result}", result);
|
||||||
|
@ -141,6 +157,8 @@ namespace Ombi.Core.Engine
|
||||||
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
|
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
|
||||||
viewMovie.Id = showInfo.Id; // TheMovieDbId
|
viewMovie.Id = showInfo.Id; // TheMovieDbId
|
||||||
viewMovie.ImdbId = showInfo.ImdbId;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewMovie.TheMovieDbId = viewMovie.Id.ToString();
|
viewMovie.TheMovieDbId = viewMovie.Id.ToString();
|
||||||
|
|
|
@ -17,6 +17,8 @@ using Ombi.Core.Helpers;
|
||||||
using Ombi.Core.Rule;
|
using Ombi.Core.Rule;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
using Ombi.Core.Senders;
|
using Ombi.Core.Senders;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user,
|
public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user,
|
||||||
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
|
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
|
||||||
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl) : base(user, requestService, rule, manager)
|
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache) : base(user, requestService, rule, manager, cache, settings)
|
||||||
{
|
{
|
||||||
TvApi = tvApi;
|
TvApi = tvApi;
|
||||||
NotificationHelper = helper;
|
NotificationHelper = helper;
|
||||||
|
@ -128,45 +130,136 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
public async Task<IEnumerable<TvRequests>> GetRequests(int count, int position)
|
public async Task<IEnumerable<TvRequests>> GetRequests(int count, int position)
|
||||||
{
|
{
|
||||||
var allRequests = await TvRepository.Get()
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
List<TvRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = await TvRepository.Get(shouldHide.UserId)
|
||||||
.Include(x => x.ChildRequests)
|
.Include(x => x.ChildRequests)
|
||||||
.ThenInclude(x => x.SeasonRequests)
|
.ThenInclude(x => x.SeasonRequests)
|
||||||
.ThenInclude(x => x.Episodes)
|
.ThenInclude(x => x.Episodes)
|
||||||
.Skip(position).Take(count).ToListAsync();
|
.Skip(position).Take(count).ToListAsync();
|
||||||
|
|
||||||
|
// Filter out children
|
||||||
|
|
||||||
|
FilterChildren(allRequests, shouldHide);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = await TvRepository.Get()
|
||||||
|
.Include(x => x.ChildRequests)
|
||||||
|
.ThenInclude(x => x.SeasonRequests)
|
||||||
|
.ThenInclude(x => x.Episodes)
|
||||||
|
.Skip(position).Take(count).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
return allRequests;
|
return allRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
|
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> GetRequestsTreeNode(int count, int position)
|
||||||
{
|
{
|
||||||
var allRequests = await TvRepository.Get()
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
List<TvRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = await TvRepository.Get(shouldHide.UserId)
|
||||||
.Include(x => x.ChildRequests)
|
.Include(x => x.ChildRequests)
|
||||||
.ThenInclude(x => x.SeasonRequests)
|
.ThenInclude(x => x.SeasonRequests)
|
||||||
.ThenInclude(x => x.Episodes)
|
.ThenInclude(x => x.Episodes)
|
||||||
.Skip(position).Take(count).ToListAsync();
|
.Skip(position).Take(count).ToListAsync();
|
||||||
|
|
||||||
|
FilterChildren(allRequests, shouldHide);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = await TvRepository.Get()
|
||||||
|
.Include(x => x.ChildRequests)
|
||||||
|
.ThenInclude(x => x.SeasonRequests)
|
||||||
|
.ThenInclude(x => x.Episodes)
|
||||||
|
.Skip(position).Take(count).ToListAsync();
|
||||||
|
}
|
||||||
return ParseIntoTreeNode(allRequests);
|
return ParseIntoTreeNode(allRequests);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TvRequests>> GetRequests()
|
public async Task<IEnumerable<TvRequests>> GetRequests()
|
||||||
{
|
{
|
||||||
var allRequests = TvRepository.Get();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
IQueryable<TvRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||||
|
|
||||||
|
FilterChildren(allRequests, shouldHide);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = TvRepository.Get();
|
||||||
|
}
|
||||||
|
|
||||||
return await allRequests.ToListAsync();
|
return await allRequests.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void FilterChildren(IEnumerable<TvRequests> allRequests, HideResult shouldHide)
|
||||||
|
{
|
||||||
|
// Filter out children
|
||||||
|
foreach (var t in allRequests)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < t.ChildRequests.Count; j++)
|
||||||
|
{
|
||||||
|
var child = t.ChildRequests[j];
|
||||||
|
if (child.RequestedUserId != shouldHide.UserId)
|
||||||
|
{
|
||||||
|
t.ChildRequests.RemoveAt(j);
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId)
|
public async Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId)
|
||||||
{
|
{
|
||||||
return await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
List<ChildRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = await TvRepository.GetChild(shouldHide.UserId).Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TvRequests>> SearchTvRequest(string search)
|
public async Task<IEnumerable<TvRequests>> SearchTvRequest(string search)
|
||||||
{
|
{
|
||||||
var allRequests = TvRepository.Get();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
IQueryable<TvRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = TvRepository.Get();
|
||||||
|
}
|
||||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search)
|
public async Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search)
|
||||||
{
|
{
|
||||||
var allRequests = TvRepository.Get();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
IQueryable<TvRequests> allRequests;
|
||||||
|
if (shouldHide.Hide)
|
||||||
|
{
|
||||||
|
allRequests = TvRepository.Get(shouldHide.UserId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
allRequests = TvRepository.Get();
|
||||||
|
}
|
||||||
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
|
||||||
return ParseIntoTreeNode(results);
|
return ParseIntoTreeNode(results);
|
||||||
}
|
}
|
||||||
|
@ -177,6 +270,13 @@ namespace Ombi.Core.Engine
|
||||||
var allRequests = TvRepository.Get();
|
var allRequests = TvRepository.Get();
|
||||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id);
|
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id);
|
||||||
|
|
||||||
|
results.TvDbId = request.TvDbId;
|
||||||
|
results.ImdbId = request.ImdbId;
|
||||||
|
results.Overview = request.Overview;
|
||||||
|
results.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath);
|
||||||
|
results.QualityOverride = request.QualityOverride;
|
||||||
|
results.RootFolder = request.RootFolder;
|
||||||
|
|
||||||
await TvRepository.Update(results);
|
await TvRepository.Update(results);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ using Ombi.Store.Repository.Requests;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -26,8 +27,8 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService<PlexSettings> plexSettings,
|
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService<PlexSettings> plexSettings,
|
||||||
ISettingsService<EmbySettings> embySettings, IPlexContentRepository repo, IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um,
|
ISettingsService<EmbySettings> embySettings, IPlexContentRepository repo, IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um,
|
||||||
ICacheService memCache)
|
ICacheService memCache, ISettingsService<OmbiSettings> s)
|
||||||
: base(identity, service, r, um)
|
: base(identity, service, r, um, memCache, s)
|
||||||
{
|
{
|
||||||
TvMazeApi = tvMaze;
|
TvMazeApi = tvMaze;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
|
@ -36,7 +37,6 @@ namespace Ombi.Core.Engine
|
||||||
PlexContentRepo = repo;
|
PlexContentRepo = repo;
|
||||||
TraktApi = trakt;
|
TraktApi = trakt;
|
||||||
EmbyContentRepo = embyRepo;
|
EmbyContentRepo = embyRepo;
|
||||||
MemCache = memCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ITvMazeApi TvMazeApi { get; }
|
private ITvMazeApi TvMazeApi { get; }
|
||||||
|
@ -46,7 +46,6 @@ namespace Ombi.Core.Engine
|
||||||
private IPlexContentRepository PlexContentRepo { get; }
|
private IPlexContentRepository PlexContentRepo { get; }
|
||||||
private IEmbyContentRepository EmbyContentRepo { get; }
|
private IEmbyContentRepository EmbyContentRepo { get; }
|
||||||
private ITraktApi TraktApi { get; }
|
private ITraktApi TraktApi { get; }
|
||||||
private ICacheService MemCache { get; }
|
|
||||||
|
|
||||||
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm)
|
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm)
|
||||||
{
|
{
|
||||||
|
@ -122,33 +121,61 @@ namespace Ombi.Core.Engine
|
||||||
return ParseIntoTreeNode(result);
|
return ParseIntoTreeNode(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> Popular()
|
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> PopularTree()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||||
var processed = await ProcessResults(result);
|
var processed = await ProcessResults(result);
|
||||||
return processed.Select(ParseIntoTreeNode).ToList();
|
return processed.Select(ParseIntoTreeNode).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> Anticipated()
|
public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||||
|
var processed = await ProcessResults(result);
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> AnticipatedTree()
|
||||||
|
{
|
||||||
|
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
|
||||||
|
var processed = await ProcessResults(result);
|
||||||
|
return processed.Select(ParseIntoTreeNode).ToList();
|
||||||
|
}
|
||||||
|
public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated()
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
|
||||||
|
var processed = await ProcessResults(result);
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatchesTree()
|
||||||
|
{
|
||||||
|
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||||
|
var processed = await ProcessResults(result);
|
||||||
|
return processed.Select(ParseIntoTreeNode).ToList();
|
||||||
|
}
|
||||||
|
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches()
|
||||||
|
{
|
||||||
|
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||||
|
var processed = await ProcessResults(result);
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> TrendingTree()
|
||||||
|
{
|
||||||
|
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||||
var processed = await ProcessResults(result);
|
var processed = await ProcessResults(result);
|
||||||
return processed.Select(ParseIntoTreeNode).ToList();
|
return processed.Select(ParseIntoTreeNode).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> MostWatches()
|
public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
|
||||||
{
|
{
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||||
var processed = await ProcessResults(result);
|
var processed = await ProcessResults(result);
|
||||||
return processed.Select(ParseIntoTreeNode).ToList();
|
return processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<TreeNode<SearchTvShowViewModel>>> Trending()
|
|
||||||
{
|
|
||||||
var result = await MemCache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
|
||||||
var processed = await ProcessResults(result);
|
|
||||||
return processed.Select(ParseIntoTreeNode).ToList();
|
|
||||||
}
|
|
||||||
private static TreeNode<SearchTvShowViewModel> ParseIntoTreeNode(SearchTvShowViewModel result)
|
private static TreeNode<SearchTvShowViewModel> ParseIntoTreeNode(SearchTvShowViewModel result)
|
||||||
{
|
{
|
||||||
return new TreeNode<SearchTvShowViewModel>
|
return new TreeNode<SearchTvShowViewModel>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Net.Mail;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Ombi.Core.Helpers
|
namespace Ombi.Core.Helpers
|
||||||
|
@ -31,12 +32,11 @@ namespace Ombi.Core.Helpers
|
||||||
// Return true if strIn is in valid e-mail format.
|
// Return true if strIn is in valid e-mail format.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Regex.IsMatch(strIn,
|
// ReSharper disable once ObjectCreationAsStatement
|
||||||
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
|
new MailAddress(strIn);
|
||||||
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
|
return true;
|
||||||
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
|
||||||
}
|
}
|
||||||
catch (RegexMatchTimeoutException)
|
catch (FormatException)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
10
src/Ombi.Core/IMassEmailSender.cs
Normal file
10
src/Ombi.Core/IMassEmailSender.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Senders
|
||||||
|
{
|
||||||
|
public interface IMassEmailSender
|
||||||
|
{
|
||||||
|
Task<bool> SendMassEmail(MassEmailModel model);
|
||||||
|
}
|
||||||
|
}
|
40
src/Ombi.Core/Models/MassEmailModel.cs
Normal file
40
src/Ombi.Core/Models/MassEmailModel.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2018 Jamie Rees
|
||||||
|
// File: MassEmailModel.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Models
|
||||||
|
{
|
||||||
|
public class MassEmailModel
|
||||||
|
{
|
||||||
|
public string Subject { get; set; }
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
public List<OmbiUser> Users { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Core.Models.Search
|
namespace Ombi.Core.Models.Search
|
||||||
|
@ -25,5 +26,7 @@ namespace Ombi.Core.Models.Search
|
||||||
public int RootPathOverride { get; set; }
|
public int RootPathOverride { get; set; }
|
||||||
public int QualityOverride { get; set; }
|
public int QualityOverride { get; set; }
|
||||||
public override RequestType Type => RequestType.Movie;
|
public override RequestType Type => RequestType.Movie;
|
||||||
|
public ReleaseDatesDto ReleaseDates { get; set; }
|
||||||
|
public DateTime? DigitalReleaseDate { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
95
src/Ombi.Core/Senders/MassEmailSender.cs
Normal file
95
src/Ombi.Core/Senders/MassEmailSender.cs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2018 Jamie Rees
|
||||||
|
// File: MassEmailSender.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Notifications;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Senders
|
||||||
|
{
|
||||||
|
public class MassEmailSender : IMassEmailSender
|
||||||
|
{
|
||||||
|
public MassEmailSender(IEmailProvider emailProvider, ISettingsService<CustomizationSettings> custom, ISettingsService<EmailNotificationSettings> email,
|
||||||
|
ILogger<MassEmailSender> log, OmbiUserManager manager)
|
||||||
|
{
|
||||||
|
_email = emailProvider;
|
||||||
|
_customizationService = custom;
|
||||||
|
_emailService = email;
|
||||||
|
_log = log;
|
||||||
|
_userManager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IEmailProvider _email;
|
||||||
|
private readonly ISettingsService<CustomizationSettings> _customizationService;
|
||||||
|
private readonly ISettingsService<EmailNotificationSettings> _emailService;
|
||||||
|
private readonly ILogger<MassEmailSender> _log;
|
||||||
|
private readonly OmbiUserManager _userManager;
|
||||||
|
|
||||||
|
public async Task<bool> SendMassEmail(MassEmailModel model)
|
||||||
|
{
|
||||||
|
var customization = await _customizationService.GetSettingsAsync();
|
||||||
|
var email = await _emailService.GetSettingsAsync();
|
||||||
|
var messagesSent = new List<Task>();
|
||||||
|
foreach (var user in model.Users)
|
||||||
|
{
|
||||||
|
var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id);
|
||||||
|
if (!fullUser.Email.HasValue())
|
||||||
|
{
|
||||||
|
_log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var resolver = new NotificationMessageResolver();
|
||||||
|
var curlys = new NotificationMessageCurlys();
|
||||||
|
curlys.Setup(fullUser, customization);
|
||||||
|
var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject };
|
||||||
|
var content = resolver.ParseMessage(template, curlys);
|
||||||
|
var msg = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = content.Message,
|
||||||
|
To = fullUser.Email,
|
||||||
|
Subject = content.Subject
|
||||||
|
};
|
||||||
|
messagesSent.Add(_email.SendAdHoc(msg, email));
|
||||||
|
_log.LogInformation("Sent mass email to user {0} @ {1}", fullUser.UserName, fullUser.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(messagesSent);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,7 +95,15 @@ namespace Ombi.Core.Senders
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootFolderPath = model.RootPathOverride <= 0 ? settings.DefaultRootPath : await RadarrRootPath(model.RootPathOverride, settings);
|
var rootFolderPath = model.RootPathOverride <= 0 ? settings.DefaultRootPath : await RadarrRootPath(model.RootPathOverride, settings);
|
||||||
var result = await RadarrApi.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year, qualityToUse, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly, settings.MinimumAvailability);
|
|
||||||
|
// Check if the movie already exists? Since it could be unmonitored
|
||||||
|
var movies = await RadarrApi.GetMovies(settings.ApiKey, settings.FullUri);
|
||||||
|
var existingMovie = movies.FirstOrDefault(x => x.tmdbId == model.TheMovieDbId);
|
||||||
|
if (existingMovie == null)
|
||||||
|
{
|
||||||
|
var result = await RadarrApi.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year,
|
||||||
|
qualityToUse, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly,
|
||||||
|
settings.MinimumAvailability);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.Error?.message))
|
if (!string.IsNullOrEmpty(result.Error?.message))
|
||||||
{
|
{
|
||||||
|
@ -108,6 +116,20 @@ namespace Ombi.Core.Senders
|
||||||
}
|
}
|
||||||
return new SenderResult { Success = true, Sent = false };
|
return new SenderResult { Success = true, Sent = false };
|
||||||
}
|
}
|
||||||
|
// We have the movie, check if we can request it or change the status
|
||||||
|
if (!existingMovie.monitored)
|
||||||
|
{
|
||||||
|
// let's set it to monitored and search for it
|
||||||
|
existingMovie.monitored = true;
|
||||||
|
await RadarrApi.UpdateMovie(existingMovie, settings.ApiKey, settings.FullUri);
|
||||||
|
// Search for it
|
||||||
|
await RadarrApi.MovieSearch(new[] { existingMovie.id }, settings.ApiKey, settings.FullUri);
|
||||||
|
|
||||||
|
return new SenderResult { Success = true, Sent = true };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SenderResult { Success = false, Sent = false, Message = "Movie is already monitored" };
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> RadarrRootPath(int overrideId, RadarrSettings settings)
|
private async Task<string> RadarrRootPath(int overrideId, RadarrSettings settings)
|
||||||
{
|
{
|
||||||
|
|
|
@ -105,9 +105,8 @@ namespace Ombi.Core.Senders
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
/// <param name="s"></param>
|
||||||
/// <param name="model"></param>
|
/// <param name="model"></param>
|
||||||
/// <param name="qualityId">This is for any qualities overriden from the UI</param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<NewSeries> SendToSonarr(ChildRequests model, string qualityId = null)
|
public async Task<NewSeries> SendToSonarr(ChildRequests model)
|
||||||
{
|
{
|
||||||
var s = await SonarrSettings.GetSettingsAsync();
|
var s = await SonarrSettings.GetSettingsAsync();
|
||||||
if (!s.Enabled)
|
if (!s.Enabled)
|
||||||
|
@ -118,15 +117,12 @@ namespace Ombi.Core.Senders
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var qualityProfile = 0;
|
|
||||||
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
|
|
||||||
{
|
|
||||||
int.TryParse(qualityId, out qualityProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qualityProfile <= 0)
|
int.TryParse(s.QualityProfile, out var qualityToUse);
|
||||||
|
|
||||||
|
if (model.ParentRequest.QualityOverride.HasValue)
|
||||||
{
|
{
|
||||||
int.TryParse(s.QualityProfile, out qualityProfile);
|
qualityToUse = model.ParentRequest.QualityOverride.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the root path from the rootfolder selected.
|
// Get the root path from the rootfolder selected.
|
||||||
|
@ -151,7 +147,7 @@ namespace Ombi.Core.Senders
|
||||||
monitored = true,
|
monitored = true,
|
||||||
seasonFolder = s.SeasonFolders,
|
seasonFolder = s.SeasonFolders,
|
||||||
rootFolderPath = rootFolderPath,
|
rootFolderPath = rootFolderPath,
|
||||||
qualityProfileId = qualityProfile,
|
qualityProfileId = qualityToUse,
|
||||||
titleSlug = model.ParentRequest.Title,
|
titleSlug = model.ParentRequest.Title,
|
||||||
addOptions = new AddOptions
|
addOptions = new AddOptions
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,6 +81,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IMovieSender, MovieSender>();
|
services.AddTransient<IMovieSender, MovieSender>();
|
||||||
services.AddTransient<IRecentlyAddedEngine, RecentlyAddedEngine>();
|
services.AddTransient<IRecentlyAddedEngine, RecentlyAddedEngine>();
|
||||||
services.AddTransient<ITvSender, TvSender>();
|
services.AddTransient<ITvSender, TvSender>();
|
||||||
|
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||||
}
|
}
|
||||||
public static void RegisterHttp(this IServiceCollection services)
|
public static void RegisterHttp(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ombi.Helpers
|
||||||
var version = Assembly.GetEntryAssembly()
|
var version = Assembly.GetEntryAssembly()
|
||||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||||
.InformationalVersion;
|
.InformationalVersion;
|
||||||
return version.Equals("1.0.0") ? "3.0.0-DotNetCore" : version;
|
return version.Equals("1.0.0") ? "3.0.0-develop" : version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -42,7 +42,19 @@ namespace Ombi.Mapping.Profiles
|
||||||
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime))
|
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime))
|
||||||
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status))
|
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status))
|
||||||
.ForMember(dest => dest.Tagline, opts => opts.MapFrom(src => src.tagline))
|
.ForMember(dest => dest.Tagline, opts => opts.MapFrom(src => src.tagline))
|
||||||
.ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count));
|
.ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count))
|
||||||
|
.ForMember(dest => dest.ReleaseDates, opts => opts.MapFrom(src => src.release_dates));
|
||||||
|
|
||||||
|
CreateMap<ReleaseDates, ReleaseDatesDto>()
|
||||||
|
.ForMember(x => x.Results, o => o.MapFrom(src => src.results));
|
||||||
|
|
||||||
|
CreateMap<ReleaseResults, ReleaseResultsDto>()
|
||||||
|
.ForMember(x => x.ReleaseDate, o => o.MapFrom(s => s.release_dates))
|
||||||
|
.ForMember(x => x.IsoCode, o => o.MapFrom(s => s.iso_3166_1));
|
||||||
|
CreateMap<ReleaseDate, ReleaseDateDto>()
|
||||||
|
.ForMember(x => x.ReleaseDate, o => o.MapFrom(s => s.release_date))
|
||||||
|
.ForMember(x => x.Type, o => o.MapFrom(s => s.Type));
|
||||||
|
|
||||||
CreateMap<Genre, GenreDto>();
|
CreateMap<Genre, GenreDto>();
|
||||||
|
|
||||||
CreateMap<MovieSearchResult, SearchMovieViewModel>().ReverseMap();
|
CreateMap<MovieSearchResult, SearchMovieViewModel>().ReverseMap();
|
||||||
|
|
|
@ -81,11 +81,14 @@ namespace Ombi.Notifications
|
||||||
IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty;
|
IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty;
|
||||||
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
|
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
|
||||||
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
|
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
|
||||||
IssueUser = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
|
RequestedUser = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User Defined
|
// User Defined
|
||||||
public string RequestedUser { get; set; }
|
public string RequestedUser { get; set; }
|
||||||
|
public string UserName => RequestedUser;
|
||||||
|
public string IssueUser => RequestedUser;
|
||||||
|
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string RequestedDate { get; set; }
|
public string RequestedDate { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
@ -102,7 +105,6 @@ namespace Ombi.Notifications
|
||||||
public string IssueStatus { get; set; }
|
public string IssueStatus { get; set; }
|
||||||
public string IssueSubject { get; set; }
|
public string IssueSubject { get; set; }
|
||||||
public string NewIssueComment { get; set; }
|
public string NewIssueComment { get; set; }
|
||||||
public string IssueUser { get; set; }
|
|
||||||
|
|
||||||
// System Defined
|
// System Defined
|
||||||
private string LongDate => DateTime.Now.ToString("D");
|
private string LongDate => DateTime.Now.ToString("D");
|
||||||
|
@ -134,6 +136,7 @@ namespace Ombi.Notifications
|
||||||
{nameof(IssueSubject),IssueSubject},
|
{nameof(IssueSubject),IssueSubject},
|
||||||
{nameof(NewIssueComment),NewIssueComment},
|
{nameof(NewIssueComment),NewIssueComment},
|
||||||
{nameof(IssueUser),IssueUser},
|
{nameof(IssueUser),IssueUser},
|
||||||
|
{nameof(UserName),UserName},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ namespace Ombi.Notifications
|
||||||
private List<INotification> NotificationAgents { get; }
|
private List<INotification> NotificationAgents { get; }
|
||||||
private ILogger<NotificationService> Log { get; }
|
private ILogger<NotificationService> Log { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>^
|
||||||
/// Sends a notification to the user. This one is used in normal notification scenarios
|
/// Sends a notification to the user. This one is used in normal notification scenarios
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The model.</param>
|
/// <param name="model">The model.</param>
|
||||||
|
|
|
@ -94,6 +94,13 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
var existingEmbyUser = allUsers.FirstOrDefault(x => x.ProviderUserId == embyUser.Id);
|
var existingEmbyUser = allUsers.FirstOrDefault(x => x.ProviderUserId == embyUser.Id);
|
||||||
if (existingEmbyUser == null)
|
if (existingEmbyUser == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!embyUser.ConnectUserName.HasValue() && !embyUser.Name.HasValue())
|
||||||
|
{
|
||||||
|
_log.LogInformation("Could not create Emby user since the have no username, PlexUserId: {0}", embyUser.Id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Create this users
|
// Create this users
|
||||||
// We do not store a password against the user since they will authenticate via Plex
|
// We do not store a password against the user since they will authenticate via Plex
|
||||||
var newUser = new OmbiUser
|
var newUser = new OmbiUser
|
||||||
|
|
|
@ -106,14 +106,23 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
// Let's download the correct zip
|
// Let's download the correct zip
|
||||||
var desc = RuntimeInformation.OSDescription;
|
var desc = RuntimeInformation.OSDescription;
|
||||||
var proce = RuntimeInformation.ProcessArchitecture;
|
var process = RuntimeInformation.ProcessArchitecture;
|
||||||
|
|
||||||
Logger.LogDebug(LoggingEvents.Updater, "OS Information: {0} {1}", desc, proce);
|
Logger.LogDebug(LoggingEvents.Updater, "OS Information: {0} {1}", desc, process);
|
||||||
Downloads download;
|
Downloads download;
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
Logger.LogDebug(LoggingEvents.Updater, "We are Windows");
|
Logger.LogDebug(LoggingEvents.Updater, "We are Windows");
|
||||||
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("windows.zip", CompareOptions.IgnoreCase));
|
if (process == Architecture.X64)
|
||||||
|
{
|
||||||
|
download = updates.Downloads.FirstOrDefault(x =>
|
||||||
|
x.Name.Contains("windows.", CompareOptions.IgnoreCase));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
download = updates.Downloads.FirstOrDefault(x =>
|
||||||
|
x.Name.Contains("windows-32bit", CompareOptions.IgnoreCase));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
{
|
{
|
||||||
|
@ -123,13 +132,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogDebug(LoggingEvents.Updater, "We are linux");
|
Logger.LogDebug(LoggingEvents.Updater, "We are linux");
|
||||||
if (RuntimeInformation.OSDescription.Contains("arm", CompareOptions.IgnoreCase))
|
if (process == Architecture.Arm)
|
||||||
{
|
{
|
||||||
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm", CompareOptions.IgnoreCase));
|
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm.", CompareOptions.IgnoreCase));
|
||||||
|
} else if (process == Architecture.Arm64)
|
||||||
|
{
|
||||||
|
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("arm64.", CompareOptions.IgnoreCase));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("linux", CompareOptions.IgnoreCase));
|
download = updates.Downloads.FirstOrDefault(x => x.Name.Contains("linux.", CompareOptions.IgnoreCase));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (download == null)
|
if (download == null)
|
||||||
|
@ -247,24 +259,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
sb.Append($"--windowsServiceName \"{settings.WindowsServiceName}\" ");
|
sb.Append($"--windowsServiceName \"{settings.WindowsServiceName}\" ");
|
||||||
}
|
}
|
||||||
var sb2 = new StringBuilder();
|
var sb2 = new StringBuilder();
|
||||||
var hasStartupArgs = false;
|
|
||||||
if (url?.Value.HasValue() ?? false)
|
if (url?.Value.HasValue() ?? false)
|
||||||
{
|
{
|
||||||
hasStartupArgs = true;
|
sb2.Append($" --host {url.Value}");
|
||||||
sb2.Append(url.Value);
|
|
||||||
}
|
}
|
||||||
if (storage?.Value.HasValue() ?? false)
|
if (storage?.Value.HasValue() ?? false)
|
||||||
{
|
{
|
||||||
hasStartupArgs = true;
|
sb2.Append($" --storage {storage.Value}");
|
||||||
sb2.Append(storage.Value);
|
|
||||||
}
|
|
||||||
if (hasStartupArgs)
|
|
||||||
{
|
|
||||||
sb.Append($"--startupArgs {sb2.ToString()}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
//return string.Join(" ", currentLocation, processName, url?.Value ?? string.Empty, storage?.Value ?? string.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunScript(UpdateSettings settings, string downloadUrl)
|
private void RunScript(UpdateSettings settings, string downloadUrl)
|
||||||
|
|
|
@ -67,6 +67,16 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
|
seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!seriesEpisodes.Any())
|
||||||
|
{
|
||||||
|
// Let's try and match the series by name
|
||||||
|
seriesEpisodes = plexEpisodes.Where(x =>
|
||||||
|
x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase) &&
|
||||||
|
x.Series.ReleaseYear == child.ParentRequest.ReleaseDate.Year.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var season in child.SeasonRequests)
|
foreach (var season in child.SeasonRequests)
|
||||||
{
|
{
|
||||||
foreach (var episode in season.Episodes)
|
foreach (var episode in season.Episodes)
|
||||||
|
|
|
@ -30,6 +30,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Plex.Models;
|
using Ombi.Api.Plex.Models;
|
||||||
|
@ -81,7 +82,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(LoggingEvents.Cacher, e, "Exception thrown when attempting to cache the Plex Content");
|
Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content");
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation("Starting EP Cacher");
|
Logger.LogInformation("Starting EP Cacher");
|
||||||
|
@ -92,13 +93,26 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
foreach (var servers in plexSettings.Servers ?? new List<PlexServers>())
|
foreach (var servers in plexSettings.Servers ?? new List<PlexServers>())
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Starting to cache the content on server {0}", servers.Name);
|
||||||
|
await ProcessServer(servers);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content in server {0}", servers.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessServer(PlexServers servers)
|
||||||
|
{
|
||||||
Logger.LogInformation("Getting all content from server {0}", servers.Name);
|
Logger.LogInformation("Getting all content from server {0}", servers.Name);
|
||||||
var allContent = await GetAllContent(servers);
|
var allContent = await GetAllContent(servers);
|
||||||
Logger.LogInformation("We found {0} items", allContent.Count);
|
Logger.LogInformation("We found {0} items", allContent.Count);
|
||||||
|
|
||||||
// Let's now process this.
|
// Let's now process this.
|
||||||
var contentToAdd = new List<PlexServerContent>();
|
var contentToAdd = new HashSet<PlexServerContent>();
|
||||||
foreach (var content in allContent)
|
foreach (var content in allContent)
|
||||||
{
|
{
|
||||||
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
|
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
|
||||||
|
@ -127,15 +141,50 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
&& x.ReleaseYear == show.year.ToString()
|
&& x.ReleaseYear == show.year.ToString()
|
||||||
&& x.Type == PlexMediaTypeEntity.Show);
|
&& x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
|
||||||
if (existingContent == null)
|
// Just double check the rating key, since this is our unique constraint
|
||||||
|
var existingKey = await Repo.GetByKey(show.ratingKey);
|
||||||
|
|
||||||
|
if (existingKey != null)
|
||||||
{
|
{
|
||||||
// Just check the key
|
// Damn son.
|
||||||
var hasSameKey = await Repo.GetByKey(show.ratingKey);
|
// Let's check if they match up
|
||||||
if (hasSameKey != null)
|
var doesMatch = show.title.Equals(existingKey.Title,
|
||||||
|
StringComparison.CurrentCulture);
|
||||||
|
if (!doesMatch)
|
||||||
{
|
{
|
||||||
existingContent = hasSameKey;
|
// Something fucked up on Plex at somepoint... Damn, rebuild of lib maybe?
|
||||||
|
// Lets delete the matching key
|
||||||
|
await Repo.Delete(existingKey);
|
||||||
|
existingKey = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (existingContent != null)
|
||||||
|
{
|
||||||
|
// Just check the key
|
||||||
|
if (existingKey != null)
|
||||||
|
{
|
||||||
|
// The rating key is all good!
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This means the rating key has changed somehow.
|
||||||
|
// Should probably delete this and get the new one
|
||||||
|
var oldKey = existingContent.Key;
|
||||||
|
Repo.DeleteWithoutSave(existingContent);
|
||||||
|
|
||||||
|
// Because we have changed the rating key, we need to change all children too
|
||||||
|
var episodeToChange = Repo.GetAllEpisodes().Where(x => x.GrandparentKey == oldKey);
|
||||||
|
if (episodeToChange.Any())
|
||||||
|
{
|
||||||
|
foreach (var e in episodeToChange)
|
||||||
|
{
|
||||||
|
Repo.DeleteWithoutSave(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Repo.SaveChangesAsync();
|
||||||
|
existingContent = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// The ratingKey keeps changing...
|
// The ratingKey keeps changing...
|
||||||
//var existingContent = await Repo.GetByKey(show.ratingKey);
|
//var existingContent = await Repo.GetByKey(show.ratingKey);
|
||||||
|
@ -143,12 +192,14 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogInformation("We already have show {0} checking for new seasons", existingContent.Title);
|
Logger.LogInformation("We already have show {0} checking for new seasons",
|
||||||
|
existingContent.Title);
|
||||||
// Ok so we have it, let's check if there are any new seasons
|
// Ok so we have it, let's check if there are any new seasons
|
||||||
var itemAdded = false;
|
var itemAdded = false;
|
||||||
foreach (var season in seasonsContent)
|
foreach (var season in seasonsContent)
|
||||||
{
|
{
|
||||||
var seasonExists = existingContent.Seasons.FirstOrDefault(x => x.SeasonKey == season.SeasonKey);
|
var seasonExists =
|
||||||
|
existingContent.Seasons.FirstOrDefault(x => x.SeasonKey == season.SeasonKey);
|
||||||
|
|
||||||
if (seasonExists != null)
|
if (seasonExists != null)
|
||||||
{
|
{
|
||||||
|
@ -164,19 +215,23 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.LogError(LoggingEvents.PlexContentCacher, e, "Exception when adding new seasons to title {0}", existingContent.Title);
|
Logger.LogError(LoggingEvents.PlexContentCacher, e,
|
||||||
|
"Exception when adding new seasons to title {0}", existingContent.Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
Logger.LogInformation("New show {0}, so add it", show.title);
|
Logger.LogInformation("New show {0}, so add it", show.title);
|
||||||
|
|
||||||
// Get the show metadata... This sucks since the `metadata` var contains all information about the show
|
// Get the show metadata... This sucks since the `metadata` var contains all information about the show
|
||||||
// But it does not contain the `guid` property that we need to pull out thetvdb id...
|
// But it does not contain the `guid` property that we need to pull out thetvdb id...
|
||||||
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
|
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
|
||||||
show.ratingKey);
|
show.ratingKey);
|
||||||
var providerIds = PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata.FirstOrDefault().guid);
|
var providerIds =
|
||||||
|
PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata.FirstOrDefault()
|
||||||
|
.guid);
|
||||||
|
|
||||||
var item = new PlexServerContent
|
var item = new PlexServerContent
|
||||||
{
|
{
|
||||||
|
@ -201,10 +256,46 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
item.TvDbId = providerIds.TheTvDb;
|
item.TvDbId = providerIds.TheTvDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let's just double check to make sure we do not have it now we have some id's
|
||||||
|
var existingImdb = false;
|
||||||
|
var existingMovieDbId = false;
|
||||||
|
var existingTvDbId = false;
|
||||||
|
if (item.ImdbId.HasValue())
|
||||||
|
{
|
||||||
|
existingImdb = await Repo.GetAll().AnyAsync(x =>
|
||||||
|
x.ImdbId == item.ImdbId && x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
}
|
||||||
|
if (item.TheMovieDbId.HasValue())
|
||||||
|
{
|
||||||
|
existingMovieDbId = await Repo.GetAll().AnyAsync(x =>
|
||||||
|
x.TheMovieDbId == item.TheMovieDbId && x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
}
|
||||||
|
if (item.TvDbId.HasValue())
|
||||||
|
{
|
||||||
|
existingTvDbId = await Repo.GetAll().AnyAsync(x =>
|
||||||
|
x.TvDbId == item.TvDbId && x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
}
|
||||||
|
if (existingImdb || existingTvDbId || existingMovieDbId)
|
||||||
|
{
|
||||||
|
// We already have it!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
item.Seasons.ToList().AddRange(seasonsContent);
|
item.Seasons.ToList().AddRange(seasonsContent);
|
||||||
|
|
||||||
contentToAdd.Add(item);
|
contentToAdd.Add(item);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError(LoggingEvents.PlexContentCacher, e, "Exception when adding tv show {0}",
|
||||||
|
show.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (contentToAdd.Count > 500)
|
||||||
|
{
|
||||||
|
await Repo.AddRange(contentToAdd);
|
||||||
|
contentToAdd.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase))
|
if (content.viewGroup.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase))
|
||||||
|
@ -214,6 +305,8 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
// Let's check if we have this movie
|
// Let's check if we have this movie
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title
|
var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title
|
||||||
&& x.ReleaseYear == movie.year.ToString()
|
&& x.ReleaseYear == movie.year.ToString()
|
||||||
&& x.Type == PlexMediaTypeEntity.Movie);
|
&& x.Type == PlexMediaTypeEntity.Movie);
|
||||||
|
@ -263,11 +356,23 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
}
|
}
|
||||||
contentToAdd.Add(item);
|
contentToAdd.Add(item);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError(LoggingEvents.PlexContentCacher, e, "Exception when adding new Movie {0}",
|
||||||
|
movie.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentToAdd.Count > 500)
|
||||||
|
{
|
||||||
|
await Repo.AddRange(contentToAdd);
|
||||||
|
contentToAdd.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (contentToAdd.Count > 500)
|
if (contentToAdd.Count > 500)
|
||||||
{
|
{
|
||||||
await Repo.AddRange(contentToAdd);
|
await Repo.AddRange(contentToAdd);
|
||||||
contentToAdd = new List<PlexServerContent>();
|
contentToAdd.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,8 +380,6 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
await Repo.AddRange(contentToAdd);
|
await Repo.AddRange(contentToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -48,8 +48,9 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
foreach (var server in s.Servers)
|
foreach (var server in s.Servers)
|
||||||
{
|
{
|
||||||
await Cache(server);
|
await Cache(server);
|
||||||
BackgroundJob.Enqueue(() => _availabilityChecker.Start());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BackgroundJob.Enqueue(() => _availabilityChecker.Start());
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +62,6 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
if (!Validate(settings))
|
if (!Validate(settings))
|
||||||
{
|
{
|
||||||
|
|
||||||
_log.LogWarning("Validation failed");
|
_log.LogWarning("Validation failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -100,21 +100,25 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
var currentPosition = 0;
|
var currentPosition = 0;
|
||||||
var resultCount = settings.EpisodeBatchSize == 0 ? 150 : settings.EpisodeBatchSize;
|
var resultCount = settings.EpisodeBatchSize == 0 ? 150 : settings.EpisodeBatchSize;
|
||||||
|
var currentEpisodes = _repo.GetAllEpisodes();
|
||||||
var episodes = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount);
|
var episodes = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount);
|
||||||
_log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Total Epsiodes found for {episodes.MediaContainer.librarySectionTitle} = {episodes.MediaContainer.totalSize}");
|
_log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Total Epsiodes found for {episodes.MediaContainer.librarySectionTitle} = {episodes.MediaContainer.totalSize}");
|
||||||
|
|
||||||
// Delete all the episodes because we cannot uniquly match an episode to series every time,
|
// Delete all the episodes because we cannot uniquly match an episode to series every time,
|
||||||
// see comment below.
|
// see comment below.
|
||||||
await _repo.ExecuteSql("DELETE FROM PlexEpisode");
|
|
||||||
|
|
||||||
await ProcessEpsiodes(episodes);
|
// 12.03.2017 - I think we should be able to match them now
|
||||||
|
//await _repo.ExecuteSql("DELETE FROM PlexEpisode");
|
||||||
|
|
||||||
|
await ProcessEpsiodes(episodes, currentEpisodes);
|
||||||
currentPosition += resultCount;
|
currentPosition += resultCount;
|
||||||
|
|
||||||
while (currentPosition < episodes.MediaContainer.totalSize)
|
while (currentPosition < episodes.MediaContainer.totalSize)
|
||||||
{
|
{
|
||||||
var ep = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition,
|
var ep = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition,
|
||||||
resultCount);
|
resultCount);
|
||||||
await ProcessEpsiodes(ep);
|
|
||||||
|
await ProcessEpsiodes(ep, currentEpisodes);
|
||||||
_log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Processed {resultCount} more episodes. Total Remaining {episodes.MediaContainer.totalSize - currentPosition}");
|
_log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Processed {resultCount} more episodes. Total Remaining {episodes.MediaContainer.totalSize - currentPosition}");
|
||||||
currentPosition += resultCount;
|
currentPosition += resultCount;
|
||||||
}
|
}
|
||||||
|
@ -124,10 +128,11 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
await _repo.SaveChangesAsync();
|
await _repo.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessEpsiodes(PlexContainer episodes)
|
private async Task ProcessEpsiodes(PlexContainer episodes, IQueryable<PlexEpisode> currentEpisodes)
|
||||||
{
|
{
|
||||||
var ep = new HashSet<PlexEpisode>();
|
var ep = new HashSet<PlexEpisode>();
|
||||||
|
try
|
||||||
|
{
|
||||||
foreach (var episode in episodes?.MediaContainer?.Metadata ?? new Metadata[] { })
|
foreach (var episode in episodes?.MediaContainer?.Metadata ?? new Metadata[] { })
|
||||||
{
|
{
|
||||||
// I don't think we need to get the metadata, we only need to get the metadata if we need the provider id (TheTvDbid). Why do we need it for episodes?
|
// I don't think we need to get the metadata, we only need to get the metadata if we need the provider id (TheTvDbid). Why do we need it for episodes?
|
||||||
|
@ -135,12 +140,31 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
//var metadata = _api.GetEpisodeMetaData(server.PlexAuthToken, server.FullUri, episode.ratingKey);
|
//var metadata = _api.GetEpisodeMetaData(server.PlexAuthToken, server.FullUri, episode.ratingKey);
|
||||||
|
|
||||||
// This does seem to work, it looks like we can somehow get different rating, grandparent and parent keys with episodes. Not sure how.
|
// This does seem to work, it looks like we can somehow get different rating, grandparent and parent keys with episodes. Not sure how.
|
||||||
//var epExists = currentEpisodes.Any(x => episode.ratingKey == x.Key &&
|
var epExists = currentEpisodes.Any(x => episode.ratingKey == x.Key &&
|
||||||
// episode.grandparentRatingKey == x.GrandparentKey);
|
episode.grandparentRatingKey == x.GrandparentKey);
|
||||||
//if (epExists)
|
if (epExists)
|
||||||
//{
|
{
|
||||||
// continue;
|
continue;
|
||||||
//}
|
}
|
||||||
|
|
||||||
|
// Let's check if we have the parent
|
||||||
|
var seriesExists = await _repo.GetByKey(episode.grandparentRatingKey);
|
||||||
|
if (seriesExists == null)
|
||||||
|
{
|
||||||
|
// Ok let's try and match it to a title. TODO (This is experimental)
|
||||||
|
seriesExists = await _repo.GetAll().FirstOrDefaultAsync(x =>
|
||||||
|
x.Title.Equals(episode.grandparentTitle, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
if (seriesExists == null)
|
||||||
|
{
|
||||||
|
_log.LogWarning(
|
||||||
|
"The episode title {0} we cannot find the parent series. The episode grandparentKey = {1}, grandparentTitle = {2}",
|
||||||
|
episode.title, episode.grandparentRatingKey, episode.grandparentTitle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the rating key to the correct one
|
||||||
|
episode.grandparentRatingKey = seriesExists.Key;
|
||||||
|
}
|
||||||
|
|
||||||
ep.Add(new PlexEpisode
|
ep.Add(new PlexEpisode
|
||||||
{
|
{
|
||||||
|
@ -155,6 +179,12 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
|
|
||||||
await _repo.AddRange(ep);
|
await _repo.AddRange(ep);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool Validate(PlexServers settings)
|
private bool Validate(PlexServers settings)
|
||||||
{
|
{
|
||||||
|
|
|
@ -75,6 +75,12 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
var existingPlexUser = allUsers.FirstOrDefault(x => x.ProviderUserId == plexUser.Id);
|
var existingPlexUser = allUsers.FirstOrDefault(x => x.ProviderUserId == plexUser.Id);
|
||||||
if (existingPlexUser == null)
|
if (existingPlexUser == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!plexUser.Username.HasValue())
|
||||||
|
{
|
||||||
|
_log.LogInformation("Could not create Plex user since the have no username, PlexUserId: {0}", plexUser.Id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Create this users
|
// Create this users
|
||||||
// We do not store a password against the user since they will authenticate via Plex
|
// We do not store a password against the user since they will authenticate via Plex
|
||||||
var newUser = new OmbiUser
|
var newUser = new OmbiUser
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace Ombi.Settings.Settings.Models
|
||||||
{
|
{
|
||||||
public string ApplicationName { get; set; }
|
public string ApplicationName { get; set; }
|
||||||
public string ApplicationUrl { get; set; }
|
public string ApplicationUrl { get; set; }
|
||||||
|
public bool Mobile { get; set; }
|
||||||
public string CustomCssLink { get; set; }
|
public string CustomCssLink { get; set; }
|
||||||
public bool EnableCustomDonations { get; set; }
|
public bool EnableCustomDonations { get; set; }
|
||||||
public string CustomDonationUrl { get; set; }
|
public string CustomDonationUrl { get; set; }
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
public bool IgnoreCertificateErrors { get; set; }
|
public bool IgnoreCertificateErrors { get; set; }
|
||||||
public bool DoNotSendNotificationsForAutoApprove { get; set; }
|
public bool DoNotSendNotificationsForAutoApprove { get; set; }
|
||||||
|
public bool HideRequestsUsers { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Store.Entities
|
namespace Ombi.Store.Entities
|
||||||
|
@ -32,5 +33,29 @@ namespace Ombi.Store.Entities
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool EmailLogin { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string PasswordHash
|
||||||
|
{
|
||||||
|
get => base.PasswordHash;
|
||||||
|
set => base.PasswordHash = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string SecurityStamp
|
||||||
|
{
|
||||||
|
get => base.SecurityStamp;
|
||||||
|
set => base.SecurityStamp = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string ConcurrencyStamp
|
||||||
|
{
|
||||||
|
get => base.ConcurrencyStamp;
|
||||||
|
set => base.ConcurrencyStamp = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,11 +10,14 @@ namespace Ombi.Store.Entities.Requests
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public string PosterPath { get; set; }
|
public string PosterPath { get; set; }
|
||||||
public DateTime ReleaseDate { get; set; }
|
public DateTime ReleaseDate { get; set; }
|
||||||
|
public DateTime? DigitalReleaseDate { get; set; }
|
||||||
public string Status { get; set; }
|
public string Status { get; set; }
|
||||||
public string Background { get; set; }
|
public string Background { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool Released => DateTime.UtcNow > ReleaseDate;
|
public bool Released => DateTime.UtcNow > ReleaseDate;
|
||||||
|
[NotMapped]
|
||||||
|
public bool DigitalRelease => DigitalReleaseDate.HasValue && DigitalReleaseDate > DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,6 +8,7 @@ namespace Ombi.Store.Entities.Requests
|
||||||
{
|
{
|
||||||
public int TvDbId { get; set; }
|
public int TvDbId { get; set; }
|
||||||
public string ImdbId { get; set; }
|
public string ImdbId { get; set; }
|
||||||
|
public int? QualityOverride { get; set; }
|
||||||
public int? RootFolder { get; set; }
|
public int? RootFolder { get; set; }
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
1
src/Ombi.Store/Migration.txt
Normal file
1
src/Ombi.Store/Migration.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
dotnet ef migrations add Inital --context OmbiContext --startup-project ../Ombi/Ombi.csproj
|
916
src/Ombi.Store/Migrations/20180228114507_DigitalRelease.Designer.cs
generated
Normal file
916
src/Ombi.Store/Migrations/20180228114507_DigitalRelease.Designer.cs
generated
Normal file
|
@ -0,0 +1,916 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Store.Context;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Entities.Requests;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(OmbiContext))]
|
||||||
|
[Migration("20180228114507_DigitalRelease")]
|
||||||
|
partial class DigitalRelease
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ApplicationConfiguration");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AuditArea");
|
||||||
|
|
||||||
|
b.Property<int>("AuditType");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateTime");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("User");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Audit");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CouchPotatoCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmbyContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("EmbyEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Content");
|
||||||
|
|
||||||
|
b.Property<string>("SettingsName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("GlobalSettings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("Agent");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<string>("Message");
|
||||||
|
|
||||||
|
b.Property<int>("NotificationType");
|
||||||
|
|
||||||
|
b.Property<string>("Subject");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("NotificationTemplates");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("PlayerId");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("NotificationUserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("Alias");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyConnectUserId");
|
||||||
|
|
||||||
|
b.Property<int?>("EpisodeRequestLimit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastLoggedIn");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<int?>("MovieRequestLimit");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderUserId");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserAccessToken");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<int>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<int>("GrandparentKey");
|
||||||
|
|
||||||
|
b.Property<int>("Key");
|
||||||
|
|
||||||
|
b.Property<int>("ParentKey");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GrandparentKey");
|
||||||
|
|
||||||
|
b.ToTable("PlexEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("ParentKey");
|
||||||
|
|
||||||
|
b.Property<int>("PlexContentId");
|
||||||
|
|
||||||
|
b.Property<int?>("PlexServerContentId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonKey");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlexServerContentId");
|
||||||
|
|
||||||
|
b.ToTable("PlexSeasonsContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<int>("Key");
|
||||||
|
|
||||||
|
b.Property<string>("Quality");
|
||||||
|
|
||||||
|
b.Property<string>("ReleaseYear");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Url");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexServerContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RadarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Approved");
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<bool?>("Denied");
|
||||||
|
|
||||||
|
b.Property<string>("DeniedReason");
|
||||||
|
|
||||||
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<int>("ParentRequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestedDate");
|
||||||
|
|
||||||
|
b.Property<string>("RequestedUserId");
|
||||||
|
|
||||||
|
b.Property<int>("SeriesType");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentRequestId");
|
||||||
|
|
||||||
|
b.HasIndex("RequestedUserId");
|
||||||
|
|
||||||
|
b.ToTable("ChildRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("IssueCategory");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Comment");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Date");
|
||||||
|
|
||||||
|
b.Property<int?>("IssuesId");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IssuesId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("IssueComments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<int>("IssueCategoryId");
|
||||||
|
|
||||||
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ResovledDate");
|
||||||
|
|
||||||
|
b.Property<int>("Status");
|
||||||
|
|
||||||
|
b.Property<string>("Subject");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Approved");
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<string>("Background");
|
||||||
|
|
||||||
|
b.Property<bool?>("Denied");
|
||||||
|
|
||||||
|
b.Property<string>("DeniedReason");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DigitalReleaseDate");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<string>("Overview");
|
||||||
|
|
||||||
|
b.Property<string>("PosterPath");
|
||||||
|
|
||||||
|
b.Property<int>("QualityOverride");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestedDate");
|
||||||
|
|
||||||
|
b.Property<string>("RequestedUserId");
|
||||||
|
|
||||||
|
b.Property<int>("RootPathOverride");
|
||||||
|
|
||||||
|
b.Property<string>("Status");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RequestedUserId");
|
||||||
|
|
||||||
|
b.ToTable("MovieRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeCount");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestDate");
|
||||||
|
|
||||||
|
b.Property<int>("RequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RequestLog");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<string>("Overview");
|
||||||
|
|
||||||
|
b.Property<string>("PosterPath");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate");
|
||||||
|
|
||||||
|
b.Property<int?>("RootFolder");
|
||||||
|
|
||||||
|
b.Property<string>("Status");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("TvRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Token");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Tokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AirDate");
|
||||||
|
|
||||||
|
b.Property<bool>("Approved");
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("Requested");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Url");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("EpisodeRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("ChildRequestId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ChildRequestId");
|
||||||
|
|
||||||
|
b.ToTable("SeasonRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", 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.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.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Ombi.Store/Migrations/20180228114507_DigitalRelease.cs
Normal file
25
src/Ombi.Store/Migrations/20180228114507_DigitalRelease.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
public partial class DigitalRelease : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "DigitalReleaseDate",
|
||||||
|
table: "MovieRequests",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "DigitalReleaseDate",
|
||||||
|
table: "MovieRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
918
src/Ombi.Store/Migrations/20180307131304_SonarrOverrides.Designer.cs
generated
Normal file
918
src/Ombi.Store/Migrations/20180307131304_SonarrOverrides.Designer.cs
generated
Normal file
|
@ -0,0 +1,918 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Store.Context;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Entities.Requests;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(OmbiContext))]
|
||||||
|
[Migration("20180307131304_SonarrOverrides")]
|
||||||
|
partial class SonarrOverrides
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ApplicationConfiguration");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AuditArea");
|
||||||
|
|
||||||
|
b.Property<int>("AuditType");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateTime");
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<string>("User");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Audit");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CouchPotatoCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmbyContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("EmbyEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Content");
|
||||||
|
|
||||||
|
b.Property<string>("SettingsName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("GlobalSettings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("Agent");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<string>("Message");
|
||||||
|
|
||||||
|
b.Property<int>("NotificationType");
|
||||||
|
|
||||||
|
b.Property<string>("Subject");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("NotificationTemplates");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("PlayerId");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("NotificationUserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("Alias");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyConnectUserId");
|
||||||
|
|
||||||
|
b.Property<int?>("EpisodeRequestLimit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastLoggedIn");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<int?>("MovieRequestLimit");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderUserId");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserAccessToken");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<int>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<int>("GrandparentKey");
|
||||||
|
|
||||||
|
b.Property<int>("Key");
|
||||||
|
|
||||||
|
b.Property<int>("ParentKey");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GrandparentKey");
|
||||||
|
|
||||||
|
b.ToTable("PlexEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("ParentKey");
|
||||||
|
|
||||||
|
b.Property<int>("PlexContentId");
|
||||||
|
|
||||||
|
b.Property<int?>("PlexServerContentId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonKey");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlexServerContentId");
|
||||||
|
|
||||||
|
b.ToTable("PlexSeasonsContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<int>("Key");
|
||||||
|
|
||||||
|
b.Property<string>("Quality");
|
||||||
|
|
||||||
|
b.Property<string>("ReleaseYear");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Url");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexServerContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RadarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Approved");
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<bool?>("Denied");
|
||||||
|
|
||||||
|
b.Property<string>("DeniedReason");
|
||||||
|
|
||||||
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<int>("ParentRequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestedDate");
|
||||||
|
|
||||||
|
b.Property<string>("RequestedUserId");
|
||||||
|
|
||||||
|
b.Property<int>("SeriesType");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentRequestId");
|
||||||
|
|
||||||
|
b.HasIndex("RequestedUserId");
|
||||||
|
|
||||||
|
b.ToTable("ChildRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("IssueCategory");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Comment");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Date");
|
||||||
|
|
||||||
|
b.Property<int?>("IssuesId");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IssuesId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("IssueComments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Description");
|
||||||
|
|
||||||
|
b.Property<int>("IssueCategoryId");
|
||||||
|
|
||||||
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ResovledDate");
|
||||||
|
|
||||||
|
b.Property<int>("Status");
|
||||||
|
|
||||||
|
b.Property<string>("Subject");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Approved");
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<string>("Background");
|
||||||
|
|
||||||
|
b.Property<bool?>("Denied");
|
||||||
|
|
||||||
|
b.Property<string>("DeniedReason");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DigitalReleaseDate");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<int?>("IssueId");
|
||||||
|
|
||||||
|
b.Property<string>("Overview");
|
||||||
|
|
||||||
|
b.Property<string>("PosterPath");
|
||||||
|
|
||||||
|
b.Property<int>("QualityOverride");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestedDate");
|
||||||
|
|
||||||
|
b.Property<string>("RequestedUserId");
|
||||||
|
|
||||||
|
b.Property<int>("RootPathOverride");
|
||||||
|
|
||||||
|
b.Property<string>("Status");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RequestedUserId");
|
||||||
|
|
||||||
|
b.ToTable("MovieRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeCount");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestDate");
|
||||||
|
|
||||||
|
b.Property<int>("RequestId");
|
||||||
|
|
||||||
|
b.Property<int>("RequestType");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RequestLog");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<string>("Overview");
|
||||||
|
|
||||||
|
b.Property<string>("PosterPath");
|
||||||
|
|
||||||
|
b.Property<int?>("QualityOverride");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate");
|
||||||
|
|
||||||
|
b.Property<int?>("RootFolder");
|
||||||
|
|
||||||
|
b.Property<string>("Status");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("TvRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Token");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Tokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AirDate");
|
||||||
|
|
||||||
|
b.Property<bool>("Approved");
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("Requested");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Url");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("EpisodeRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("ChildRequestId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ChildRequestId");
|
||||||
|
|
||||||
|
b.ToTable("SeasonRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", 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.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.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Ombi.Store/Migrations/20180307131304_SonarrOverrides.cs
Normal file
25
src/Ombi.Store/Migrations/20180307131304_SonarrOverrides.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
public partial class SonarrOverrides : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "QualityOverride",
|
||||||
|
table: "TvRequests",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "QualityOverride",
|
||||||
|
table: "TvRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -553,6 +553,8 @@ namespace Ombi.Store.Migrations
|
||||||
|
|
||||||
b.Property<string>("DeniedReason");
|
b.Property<string>("DeniedReason");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DigitalReleaseDate");
|
||||||
|
|
||||||
b.Property<string>("ImdbId");
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
b.Property<int?>("IssueId");
|
b.Property<int?>("IssueId");
|
||||||
|
@ -619,6 +621,8 @@ namespace Ombi.Store.Migrations
|
||||||
|
|
||||||
b.Property<string>("PosterPath");
|
b.Property<string>("PosterPath");
|
||||||
|
|
||||||
|
b.Property<int?>("QualityOverride");
|
||||||
|
|
||||||
b.Property<DateTime>("ReleaseDate");
|
b.Property<DateTime>("ReleaseDate");
|
||||||
|
|
||||||
b.Property<int?>("RootFolder");
|
b.Property<int?>("RootFolder");
|
||||||
|
|
|
@ -19,5 +19,8 @@ namespace Ombi.Store.Repository
|
||||||
Task AddRange(IEnumerable<PlexEpisode> content);
|
Task AddRange(IEnumerable<PlexEpisode> content);
|
||||||
IEnumerable<PlexServerContent> GetWhereContentByCustom(Expression<Func<PlexServerContent, bool>> predicate);
|
IEnumerable<PlexServerContent> GetWhereContentByCustom(Expression<Func<PlexServerContent, bool>> predicate);
|
||||||
Task<PlexServerContent> GetFirstContentByCustom(Expression<Func<PlexServerContent, bool>> predicate);
|
Task<PlexServerContent> GetFirstContentByCustom(Expression<Func<PlexServerContent, bool>> predicate);
|
||||||
|
Task DeleteEpisode(PlexEpisode content);
|
||||||
|
void DeleteWithoutSave(PlexServerContent content);
|
||||||
|
void DeleteWithoutSave(PlexEpisode content);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -103,12 +103,29 @@ namespace Ombi.Store.Repository
|
||||||
return Db.PlexEpisode.Include(x => x.Series).AsQueryable();
|
return Db.PlexEpisode.Include(x => x.Series).AsQueryable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteWithoutSave(PlexServerContent content)
|
||||||
|
{
|
||||||
|
Db.PlexServerContent.Remove(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteWithoutSave(PlexEpisode content)
|
||||||
|
{
|
||||||
|
Db.PlexEpisode.Remove(content);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PlexEpisode> Add(PlexEpisode content)
|
public async Task<PlexEpisode> Add(PlexEpisode content)
|
||||||
{
|
{
|
||||||
await Db.PlexEpisode.AddAsync(content);
|
await Db.PlexEpisode.AddAsync(content);
|
||||||
await Db.SaveChangesAsync();
|
await Db.SaveChangesAsync();
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteEpisode(PlexEpisode content)
|
||||||
|
{
|
||||||
|
Db.PlexEpisode.Remove(content);
|
||||||
|
await Db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PlexEpisode> GetEpisodeByKey(int key)
|
public async Task<PlexEpisode> GetEpisodeByKey(int key)
|
||||||
{
|
{
|
||||||
return await Db.PlexEpisode.FirstOrDefaultAsync(x => x.Key == key);
|
return await Db.PlexEpisode.FirstOrDefaultAsync(x => x.Key == key);
|
||||||
|
|
|
@ -11,5 +11,7 @@ namespace Ombi.Store.Repository.Requests
|
||||||
Task Update(MovieRequests request);
|
Task Update(MovieRequests request);
|
||||||
Task Save();
|
Task Save();
|
||||||
IQueryable<MovieRequests> GetWithUser();
|
IQueryable<MovieRequests> GetWithUser();
|
||||||
|
IQueryable<MovieRequests> GetWithUser(string userId);
|
||||||
|
IQueryable<MovieRequests> GetAll(string userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,11 +14,13 @@ namespace Ombi.Store.Repository.Requests
|
||||||
Task Delete(TvRequests request);
|
Task Delete(TvRequests request);
|
||||||
Task DeleteChild(ChildRequests request);
|
Task DeleteChild(ChildRequests request);
|
||||||
IQueryable<TvRequests> Get();
|
IQueryable<TvRequests> Get();
|
||||||
|
IQueryable<TvRequests> Get(string userId);
|
||||||
Task<TvRequests> GetRequestAsync(int tvDbId);
|
Task<TvRequests> GetRequestAsync(int tvDbId);
|
||||||
TvRequests GetRequest(int tvDbId);
|
TvRequests GetRequest(int tvDbId);
|
||||||
Task Update(TvRequests request);
|
Task Update(TvRequests request);
|
||||||
Task UpdateChild(ChildRequests request);
|
Task UpdateChild(ChildRequests request);
|
||||||
IQueryable<ChildRequests> GetChild();
|
IQueryable<ChildRequests> GetChild();
|
||||||
|
IQueryable<ChildRequests> GetChild(string userId);
|
||||||
Task Save();
|
Task Save();
|
||||||
Task DeleteChildRange(IEnumerable<ChildRequests> request);
|
Task DeleteChildRange(IEnumerable<ChildRequests> request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,11 @@ namespace Ombi.Store.Repository.Requests
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IQueryable<MovieRequests> GetAll(string userId)
|
||||||
|
{
|
||||||
|
return GetWithUser().Where(x => x.RequestedUserId == userId);
|
||||||
|
}
|
||||||
|
|
||||||
public MovieRequests GetRequest(int theMovieDbId)
|
public MovieRequests GetRequest(int theMovieDbId)
|
||||||
{
|
{
|
||||||
return Db.MovieRequests.Where(x => x.TheMovieDbId == theMovieDbId)
|
return Db.MovieRequests.Where(x => x.TheMovieDbId == theMovieDbId)
|
||||||
|
@ -48,6 +53,16 @@ namespace Ombi.Store.Repository.Requests
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IQueryable<MovieRequests> GetWithUser(string userId)
|
||||||
|
{
|
||||||
|
return Db.MovieRequests
|
||||||
|
.Where(x => x.RequestedUserId == userId)
|
||||||
|
.Include(x => x.RequestedUser)
|
||||||
|
.ThenInclude(x => x.NotificationUserIds)
|
||||||
|
.AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Update(MovieRequests request)
|
public async Task Update(MovieRequests request)
|
||||||
{
|
{
|
||||||
if (Db.Entry(request).State == EntityState.Detached)
|
if (Db.Entry(request).State == EntityState.Detached)
|
||||||
|
|
|
@ -48,6 +48,18 @@ namespace Ombi.Store.Repository.Requests
|
||||||
.ThenInclude(x => x.Episodes)
|
.ThenInclude(x => x.Episodes)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IQueryable<TvRequests> Get(string userId)
|
||||||
|
{
|
||||||
|
return Db.TvRequests
|
||||||
|
.Include(x => x.ChildRequests)
|
||||||
|
.ThenInclude(x => x.RequestedUser)
|
||||||
|
.Include(x => x.ChildRequests)
|
||||||
|
.ThenInclude(x => x.SeasonRequests)
|
||||||
|
.ThenInclude(x => x.Episodes)
|
||||||
|
.Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId))
|
||||||
|
.AsQueryable();
|
||||||
|
}
|
||||||
public IQueryable<ChildRequests> GetChild()
|
public IQueryable<ChildRequests> GetChild()
|
||||||
{
|
{
|
||||||
return Db.ChildRequests
|
return Db.ChildRequests
|
||||||
|
@ -58,6 +70,17 @@ namespace Ombi.Store.Repository.Requests
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IQueryable<ChildRequests> GetChild(string userId)
|
||||||
|
{
|
||||||
|
return Db.ChildRequests
|
||||||
|
.Where(x => x.RequestedUserId == userId)
|
||||||
|
.Include(x => x.RequestedUser)
|
||||||
|
.Include(x => x.ParentRequest)
|
||||||
|
.Include(x => x.SeasonRequests)
|
||||||
|
.ThenInclude(x => x.Episodes)
|
||||||
|
.AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Save()
|
public async Task Save()
|
||||||
{
|
{
|
||||||
await Db.SaveChangesAsync();
|
await Db.SaveChangesAsync();
|
||||||
|
|
|
@ -8,11 +8,12 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public interface IMovieDbApi
|
public interface IMovieDbApi
|
||||||
{
|
{
|
||||||
Task<MovieResponseDto> GetMovieInformation(int movieId);
|
Task<MovieResponseDto> GetMovieInformation(int movieId);
|
||||||
Task<MovieResponseDto> GetMovieInformationWithVideo(int movieId);
|
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId);
|
||||||
Task<List<MovieSearchResult>> NowPlaying();
|
Task<List<MovieSearchResult>> NowPlaying();
|
||||||
Task<List<MovieSearchResult>> PopularMovies();
|
Task<List<MovieSearchResult>> PopularMovies();
|
||||||
Task<List<MovieSearchResult>> SearchMovie(string searchTerm);
|
Task<List<MovieSearchResult>> SearchMovie(string searchTerm);
|
||||||
Task<List<MovieSearchResult>> TopRated();
|
Task<List<MovieSearchResult>> TopRated();
|
||||||
Task<List<MovieSearchResult>> Upcoming();
|
Task<List<MovieSearchResult>> Upcoming();
|
||||||
|
Task<List<MovieSearchResult>> SimilarMovies(int movieId);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,6 +24,10 @@
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ombi.TheMovieDbApi.Models
|
namespace Ombi.TheMovieDbApi.Models
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -54,5 +58,26 @@ namespace Ombi.TheMovieDbApi.Models
|
||||||
public bool video { get; set; }
|
public bool video { get; set; }
|
||||||
public float vote_average { get; set; }
|
public float vote_average { get; set; }
|
||||||
public int vote_count { get; set; }
|
public int vote_count { get; set; }
|
||||||
|
public ReleaseDates release_dates { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseDates
|
||||||
|
{
|
||||||
|
public List<ReleaseResults> results { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseResults
|
||||||
|
{
|
||||||
|
public string iso_3166_1 { get; set; }
|
||||||
|
public List<ReleaseDate> release_dates { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseDate
|
||||||
|
{
|
||||||
|
public string Certification { get; set; }
|
||||||
|
public string iso_639_1 { get; set; }
|
||||||
|
public string note { get; set; }
|
||||||
|
public DateTime release_date { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
namespace Ombi.Api.TheMovieDb.Models
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ombi.Api.TheMovieDb.Models
|
||||||
{
|
{
|
||||||
public class MovieResponseDto
|
public class MovieResponseDto
|
||||||
{
|
{
|
||||||
|
@ -23,5 +26,33 @@
|
||||||
public bool Video { get; set; }
|
public bool Video { get; set; }
|
||||||
public float VoteAverage { get; set; }
|
public float VoteAverage { get; set; }
|
||||||
public int VoteCount { get; set; }
|
public int VoteCount { get; set; }
|
||||||
|
public ReleaseDatesDto ReleaseDates { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseDatesDto
|
||||||
|
{
|
||||||
|
public List<ReleaseResultsDto> Results { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseResultsDto
|
||||||
|
{
|
||||||
|
public string IsoCode { get; set; }
|
||||||
|
public List<ReleaseDateDto> ReleaseDate { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReleaseDateDto
|
||||||
|
{
|
||||||
|
public DateTime ReleaseDate { get; set; }
|
||||||
|
public ReleaseDateType Type { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ReleaseDateType
|
||||||
|
{
|
||||||
|
Premiere = 1,
|
||||||
|
TheatricalLimited = 2,
|
||||||
|
Theatrical = 3,
|
||||||
|
Digital = 4,
|
||||||
|
Physical = 5,
|
||||||
|
Tv = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,11 +30,20 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return Mapper.Map<MovieResponseDto>(result);
|
return Mapper.Map<MovieResponseDto>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MovieResponseDto> GetMovieInformationWithVideo(int movieId)
|
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId)
|
||||||
|
{
|
||||||
|
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
|
||||||
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos");
|
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates");
|
||||||
var result = await Api.Request<MovieResponse>(request);
|
var result = await Api.Request<MovieResponse>(request);
|
||||||
return Mapper.Map<MovieResponseDto>(result);
|
return Mapper.Map<MovieResponseDto>(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
@ -78,12 +79,22 @@ namespace Ombi.Updater
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var startupArgsBuilder = new StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(options.Host))
|
||||||
|
{
|
||||||
|
startupArgsBuilder.Append($"--host {options.Host} ");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(options.Storage))
|
||||||
|
{
|
||||||
|
startupArgsBuilder.Append($"--storage {options.Storage}");
|
||||||
|
}
|
||||||
|
|
||||||
var start = new ProcessStartInfo
|
var start = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
FileName = Path.Combine(options.ApplicationPath, fileName),
|
FileName = Path.Combine(options.ApplicationPath, fileName),
|
||||||
WorkingDirectory = options.ApplicationPath,
|
WorkingDirectory = options.ApplicationPath,
|
||||||
Arguments = options.StartupArgs
|
Arguments = startupArgsBuilder.ToString()
|
||||||
};
|
};
|
||||||
using (var proc = new Process { StartInfo = start })
|
using (var proc = new Process { StartInfo = start })
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RuntimeIdentifiers>win10-x64;win10-x32;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>3.0.0.0</FileVersion>
|
<FileVersion>3.0.0.0</FileVersion>
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using CommandLine;
|
using CommandLine;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
|
||||||
using ILogger = Serilog.ILogger;
|
|
||||||
|
|
||||||
namespace Ombi.Updater
|
namespace Ombi.Updater
|
||||||
{
|
{
|
||||||
|
@ -78,8 +74,10 @@ namespace Ombi.Updater
|
||||||
public string ApplicationPath { get; set; }
|
public string ApplicationPath { get; set; }
|
||||||
[Option("processId", Required = false)]
|
[Option("processId", Required = false)]
|
||||||
public int OmbiProcessId { get; set; }
|
public int OmbiProcessId { get; set; }
|
||||||
[Option("startupArgs", Required = false)]
|
[Option("host", Required = false)]
|
||||||
public string StartupArgs { get; set; }
|
public string Host { get; set; }
|
||||||
|
[Option("storage", Required = false)]
|
||||||
|
public string Storage { get; set; }
|
||||||
[Option("windowsServiceName", Required = false)]
|
[Option("windowsServiceName", Required = false)]
|
||||||
public string WindowsServiceName { get; set; }
|
public string WindowsServiceName { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
<a [routerLink]="['/usermanagement/updatedetails']">
|
<a [routerLink]="['/usermanagement/updatedetails']">
|
||||||
<i class="fa fa-key"></i>{{ 'NavigationBar.UpdateDetails' | translate }}</a>
|
<i class="fa fa-key"></i>{{ 'NavigationBar.UpdateDetails' | translate }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="showMobileLink" [routerLinkActive]="['active']">
|
<li *ngIf="customizationSettings.mobile" [routerLinkActive]="['active']">
|
||||||
<a href="#" (click)="openMobileApp($event)">
|
<a href="#" (click)="openMobileApp($event)">
|
||||||
<i class="fa fa-mobile"></i>{{ 'NavigationBar.OpenMobileApp' | translate }}</a>
|
<i class="fa fa-mobile"></i>{{ 'NavigationBar.OpenMobileApp' | translate }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { PlatformLocation } from "@angular/common";
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { NavigationStart, Router } from "@angular/router";
|
import { NavigationStart, Router } from "@angular/router";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { AuthService } from "./auth/auth.service";
|
import { AuthService } from "./auth/auth.service";
|
||||||
|
@ -22,7 +23,6 @@ export class AppComponent implements OnInit {
|
||||||
public updateAvailable: boolean;
|
public updateAvailable: boolean;
|
||||||
public currentUrl: string;
|
public currentUrl: string;
|
||||||
public userAccessToken: string;
|
public userAccessToken: string;
|
||||||
public showMobileLink = false;
|
|
||||||
|
|
||||||
private checkedForUpdate: boolean;
|
private checkedForUpdate: boolean;
|
||||||
|
|
||||||
|
@ -32,7 +32,14 @@ export class AppComponent implements OnInit {
|
||||||
private readonly settingsService: SettingsService,
|
private readonly settingsService: SettingsService,
|
||||||
private readonly jobService: JobService,
|
private readonly jobService: JobService,
|
||||||
public readonly translate: TranslateService,
|
public readonly translate: TranslateService,
|
||||||
private readonly identityService: IdentityService) {
|
private readonly identityService: IdentityService,
|
||||||
|
private readonly platformLocation: PlatformLocation) {
|
||||||
|
|
||||||
|
const base = this.platformLocation.getBaseHrefFromDOM();
|
||||||
|
if (base.length > 1) {
|
||||||
|
__webpack_public_path__ = base + "/dist/";
|
||||||
|
}
|
||||||
|
|
||||||
this.translate.addLangs(["en", "de", "fr","da","es","it","nl","sv","no"]);
|
this.translate.addLangs(["en", "de", "fr","da","es","it","nl","sv","no"]);
|
||||||
// this language will be used as a fallback when a translation isn't found in the current language
|
// this language will be used as a fallback when a translation isn't found in the current language
|
||||||
this.translate.setDefaultLang("en");
|
this.translate.setDefaultLang("en");
|
||||||
|
|
|
@ -1,28 +1,5 @@
|
||||||
import { IUser } from "./IUser";
|
import { IUser } from "./IUser";
|
||||||
|
|
||||||
export interface IMediaBase {
|
|
||||||
imdbId: string;
|
|
||||||
id: number;
|
|
||||||
providerId: number;
|
|
||||||
title: string;
|
|
||||||
overview: string;
|
|
||||||
posterPath: string;
|
|
||||||
releaseDate: Date;
|
|
||||||
status: string;
|
|
||||||
requestedDate: Date;
|
|
||||||
approved: boolean;
|
|
||||||
type: RequestType;
|
|
||||||
requested: boolean;
|
|
||||||
available: boolean;
|
|
||||||
otherMessage: string;
|
|
||||||
adminNote: string;
|
|
||||||
requestedUser: string;
|
|
||||||
issueId: number;
|
|
||||||
denied: boolean;
|
|
||||||
deniedReason: string;
|
|
||||||
released: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum RequestType {
|
export enum RequestType {
|
||||||
movie = 1,
|
movie = 1,
|
||||||
tvShow = 2,
|
tvShow = 2,
|
||||||
|
@ -34,7 +11,9 @@ export interface IMovieRequests extends IFullBaseRequest {
|
||||||
theMovieDbId: number;
|
theMovieDbId: number;
|
||||||
rootPathOverride: number;
|
rootPathOverride: number;
|
||||||
qualityOverride: number;
|
qualityOverride: number;
|
||||||
|
digitalReleaseDate: Date;
|
||||||
|
|
||||||
|
// For the UI
|
||||||
rootPathOverrideTitle: string;
|
rootPathOverrideTitle: string;
|
||||||
qualityOverrideTitle: string;
|
qualityOverrideTitle: string;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +63,11 @@ export interface ITvRequests {
|
||||||
releaseDate: Date;
|
releaseDate: Date;
|
||||||
status: string;
|
status: string;
|
||||||
childRequests: IChildRequests[];
|
childRequests: IChildRequests[];
|
||||||
|
qualityOverride: number;
|
||||||
|
|
||||||
|
// For UI display
|
||||||
|
qualityOverrideTitle: string;
|
||||||
|
rootPathOverrideTitle: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChildRequests extends IBaseRequest {
|
export interface IChildRequests extends IBaseRequest {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
available: boolean;
|
available: boolean;
|
||||||
plexUrl: string;
|
plexUrl: string;
|
||||||
quality: string;
|
quality: string;
|
||||||
|
digitalReleaseDate: Date;
|
||||||
|
|
||||||
// for the UI
|
// for the UI
|
||||||
requestProcessing: boolean;
|
requestProcessing: boolean;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export interface IOmbiSettings extends ISettings {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
ignoreCertificateErrors: boolean;
|
ignoreCertificateErrors: boolean;
|
||||||
doNotSendNotificationsForAutoApprove: boolean;
|
doNotSendNotificationsForAutoApprove: boolean;
|
||||||
|
hideRequestsUsers: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUpdateSettings extends ISettings {
|
export interface IUpdateSettings extends ISettings {
|
||||||
|
@ -96,6 +97,7 @@ export interface ICustomizationSettings extends ISettings {
|
||||||
applicationName: string;
|
applicationName: string;
|
||||||
applicationUrl: string;
|
applicationUrl: string;
|
||||||
logo: string;
|
logo: string;
|
||||||
|
mobile: boolean;
|
||||||
customCssLink: string;
|
customCssLink: string;
|
||||||
enableCustomDonations: boolean;
|
enableCustomDonations: boolean;
|
||||||
customDonationUrl: string;
|
customDonationUrl: string;
|
||||||
|
|
|
@ -49,3 +49,14 @@ export interface IMobileUsersViewModel {
|
||||||
username: string;
|
username: string;
|
||||||
devices: number;
|
devices: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMassEmailUserModel {
|
||||||
|
user: IUser;
|
||||||
|
selected: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMassEmailModel {
|
||||||
|
subject: string;
|
||||||
|
body: string;
|
||||||
|
users: IUser[];
|
||||||
|
}
|
||||||
|
|
|
@ -13,15 +13,15 @@
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('issue.status')">
|
<th (click)="setOrder('status')">
|
||||||
<a [translate]="'Issues.Status'"></a>
|
<a [translate]="'Issues.Status'"></a>
|
||||||
<span *ngIf="order === 'issue.status'">
|
<span *ngIf="order === 'status'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('issue.reportedUser')">
|
<th (click)="setOrder('reportedUser')">
|
||||||
<a [translate]="'Issues.ReportedBy'"></a>
|
<a [translate]="'Issues.ReportedBy'"></a>
|
||||||
<span *ngIf="order === 'issue.reportedUser'">
|
<span *ngIf="order === 'reportedUser'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
div.centered {
|
@media only screen and (max-width: 992px) {
|
||||||
|
div.centered {
|
||||||
|
max-height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100%;
|
||||||
|
padding: 50% 12.5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.centered {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Component, Input } from "@angular/core";
|
// import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
import { IMediaBase } from "../interfaces";
|
// import { IMediaBase } from "../interfaces";
|
||||||
|
|
||||||
@Component({
|
// @Component({
|
||||||
selector: "request-card",
|
// selector: "request-card",
|
||||||
templateUrl: "./request-card.component.html",
|
// templateUrl: "./request-card.component.html",
|
||||||
})
|
// })
|
||||||
export class RequestCardComponent {
|
// export class RequestCardComponent {
|
||||||
@Input() public request: IMediaBase;
|
// @Input() public request: IMediaBase;
|
||||||
}
|
// }
|
||||||
|
|
|
@ -3,8 +3,50 @@
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" (keyup)="search($event)">
|
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" (keyup)="search($event)">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = true" >
|
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay" >
|
||||||
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}</button>
|
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||||
|
<li>
|
||||||
|
<a (click)="setOrder('requestedDate')">{{ 'Requests.SortRequestDate' | translate }}
|
||||||
|
<span *ngIf="order === 'requestedDate'">
|
||||||
|
<span [hidden]="reverse"><small><i class="fa fa-arrow-down" aria-hidden="true"></i></small></span>
|
||||||
|
<span [hidden]="!reverse"><small><i class="fa fa-arrow-up" aria-hidden="true"></i></small></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a (click)="setOrder('title')">{{ 'Requests.SortTitle' | translate}}
|
||||||
|
<span *ngIf="order === 'title'">
|
||||||
|
<span [hidden]="reverse"><small><i class="fa fa-arrow-down" aria-hidden="true"></i></small></span>
|
||||||
|
<span [hidden]="!reverse"><small><i class="fa fa-arrow-up" aria-hidden="true"></i></small></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a (click)="setOrder('releaseDate')">{{ 'Requests.TheatricalReleaseSort' | translate }}
|
||||||
|
<span *ngIf="order === 'releaseDate'">
|
||||||
|
<span [hidden]="reverse"><small><i class="fa fa-arrow-down" aria-hidden="true"></i></small></span>
|
||||||
|
<span [hidden]="!reverse"><small><i class="fa fa-arrow-up" aria-hidden="true"></i></small></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a (click)="setOrder('requestedUser.userAlias')">{{ 'Requests.SortRequestedBy' | translate }}
|
||||||
|
<span *ngIf="order === 'requestedUser.userAlias'">
|
||||||
|
<span [hidden]="reverse"><small><i class="fa fa-arrow-down" aria-hidden="true"></i></small></span>
|
||||||
|
<span [hidden]="!reverse"><small><i class="fa fa-arrow-up" aria-hidden="true"></i></small></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a (click)="setOrder('status')">{{ 'Requests.SortStatus' | translate }}
|
||||||
|
<span *ngIf="order === 'status'">
|
||||||
|
<span [hidden]="reverse"><small><i class="fa fa-arrow-down" aria-hidden="true"></i></small></span>
|
||||||
|
<span [hidden]="!reverse"><small><i class="fa fa-arrow-up" aria-hidden="true"></i></small></span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +59,7 @@
|
||||||
<div infinite-scroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="100" (scrolled)="loadMore()">
|
<div infinite-scroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="100" (scrolled)="loadMore()">
|
||||||
|
|
||||||
|
|
||||||
<div *ngFor="let request of movieRequests">
|
<div *ngFor="let request of movieRequests | orderBy: order : reverse : 'case-insensitive'">
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -66,7 +108,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate }} {{request.releaseDate | date}}</div>
|
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate | date: 'mediumDate'} }}</div>
|
||||||
|
<div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">{{ 'Requests.DigitalRelease' | translate: {date: request.digitalReleaseDate | date: 'mediumDate'} }}</div>
|
||||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | date}}</div>
|
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | date}}</div>
|
||||||
<br />
|
<br />
|
||||||
<div *ngIf="isAdmin">
|
<div *ngIf="isAdmin">
|
||||||
|
@ -168,7 +211,7 @@
|
||||||
|
|
||||||
|
|
||||||
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
|
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
|
||||||
<h3>{{ 'Filter.Filter' | translate }}</h3>
|
<h3>{{ 'Requests.Filter' | translate }}</h3>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
|
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
|
||||||
|
@ -198,7 +241,14 @@
|
||||||
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
|
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="radio">
|
||||||
|
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)">
|
||||||
|
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter()">
|
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter()">
|
||||||
<i class="fa fa-filter"></i> {{ 'Requests.ClearFilter' | translate }}</button>
|
<i class="fa fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}</button>
|
||||||
</p-sidebar>
|
</p-sidebar>
|
|
@ -36,6 +36,9 @@ export class MovieRequestsComponent implements OnInit {
|
||||||
public filter: IFilter;
|
public filter: IFilter;
|
||||||
public filterType = FilterType;
|
public filterType = FilterType;
|
||||||
|
|
||||||
|
public order: string = "requestedDate";
|
||||||
|
public reverse = false;
|
||||||
|
|
||||||
private currentlyLoaded: number;
|
private currentlyLoaded: number;
|
||||||
private amountToLoad: number;
|
private amountToLoad: number;
|
||||||
|
|
||||||
|
@ -172,6 +175,14 @@ export class MovieRequestsComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setOrder(value: string) {
|
||||||
|
if (this.order === value) {
|
||||||
|
this.reverse = !this.reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.order = value;
|
||||||
|
}
|
||||||
|
|
||||||
private loadRequests(amountToLoad: number, currentlyLoaded: number) {
|
private loadRequests(amountToLoad: number, currentlyLoaded: number) {
|
||||||
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1)
|
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
import { OrderModule } from "ngx-order-pipe";
|
||||||
|
|
||||||
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ import { TvRequestsComponent } from "./tvrequests.component";
|
||||||
|
|
||||||
import { SidebarModule, TreeTableModule } from "primeng/primeng";
|
import { SidebarModule, TreeTableModule } from "primeng/primeng";
|
||||||
|
|
||||||
import { IdentityService, RadarrService, RequestService } from "../services";
|
import { IdentityService, RadarrService, RequestService, SonarrService } from "../services";
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from "../auth/auth.guard";
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ const routes: Routes = [
|
||||||
TreeTableModule,
|
TreeTableModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
SidebarModule,
|
SidebarModule,
|
||||||
|
OrderModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
RequestComponent,
|
RequestComponent,
|
||||||
|
@ -48,6 +50,7 @@ const routes: Routes = [
|
||||||
IdentityService,
|
IdentityService,
|
||||||
RequestService,
|
RequestService,
|
||||||
RadarrService,
|
RadarrService,
|
||||||
|
SonarrService,
|
||||||
],
|
],
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -51,17 +51,61 @@
|
||||||
|
|
||||||
|
|
||||||
<div>Release Date: {{node.data.releaseDate | date}}</div>
|
<div>Release Date: {{node.data.releaseDate | date}}</div>
|
||||||
|
<div *ngIf="isAdmin">
|
||||||
|
<div *ngIf="node.data.qualityOverrideTitle">{{ 'Requests.QualityOverride' | translate }}
|
||||||
|
<span>{{node.data.qualityOverrideTitle}} </span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="node.data.rootPathOverrideTitle">{{ 'Requests.RootFolderOverride' | translate }}
|
||||||
|
<span>{{node.data.rootPathOverrideTitle}} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-3 col-sm-push-3 small-padding">
|
<div class="col-sm-3 col-sm-push-3 small-padding">
|
||||||
|
|
||||||
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab($event)"><i class="fa fa-plus"></i> View</button>
|
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab($event)"><i class="fa fa-plus"></i> View</button>
|
||||||
|
<div *ngIf="isAdmin">
|
||||||
|
<!--Sonarr Root Folder-->
|
||||||
|
<div *ngIf="sonarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
|
||||||
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
|
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}</button>
|
||||||
|
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li *ngFor="let folder of sonarrRootFolders">
|
||||||
|
<a href="#" (click)="selectRootFolder(node.data, folder, $event)">{{folder.path}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--Sonarr Quality Profiles -->
|
||||||
|
<div *ngIf="sonarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
|
||||||
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
|
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}</button>
|
||||||
|
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li *ngFor="let profile of sonarrProfiles">
|
||||||
|
<a href="#" (click)="selectQualityProfile(node.data, profile, $event)">{{profile.name}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||||
<div *ngIf="node.leaf">
|
<div *ngIf="node.leaf">
|
||||||
<tvrequests-children [childRequests]="node.data" [isAdmin] ="isAdmin" (requestDeleted)="childRequestDeleted($event)" [issueCategories]="issueCategories" [issuesEnabled]="issuesEnabled" [issueProviderId]="node.data.tvDbId" ></tvrequests-children>
|
<tvrequests-children [childRequests]="node.data" [isAdmin] ="isAdmin"
|
||||||
|
(requestDeleted)="childRequestDeleted($event)"
|
||||||
|
[issueCategories]="issueCategories" [issuesEnabled]="issuesEnabled"
|
||||||
|
[issueProviderId]="node.data.tvDbId"></tvrequests-children>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</p-column>
|
</p-column>
|
||||||
|
|
|
@ -11,10 +11,10 @@ import "rxjs/add/operator/distinctUntilChanged";
|
||||||
import "rxjs/add/operator/map";
|
import "rxjs/add/operator/map";
|
||||||
|
|
||||||
import { AuthService } from "../auth/auth.service";
|
import { AuthService } from "../auth/auth.service";
|
||||||
import { RequestService } from "../services";
|
import { NotificationService, RequestService, SonarrService } from "../services";
|
||||||
|
|
||||||
import { TreeNode } from "primeng/primeng";
|
import { TreeNode } from "primeng/primeng";
|
||||||
import { IIssueCategory, ITvRequests } from "../interfaces";
|
import { IIssueCategory, ISonarrProfile, ISonarrRootFolder, ITvRequests } from "../interfaces";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "tv-requests",
|
selector: "tv-requests",
|
||||||
|
@ -34,13 +34,18 @@ export class TvRequestsComponent implements OnInit {
|
||||||
@Input() public issuesEnabled: boolean;
|
@Input() public issuesEnabled: boolean;
|
||||||
public issueProviderId: string;
|
public issueProviderId: string;
|
||||||
|
|
||||||
|
public sonarrProfiles: ISonarrProfile[] = [];
|
||||||
|
public sonarrRootFolders: ISonarrRootFolder[] = [];
|
||||||
|
|
||||||
private currentlyLoaded: number;
|
private currentlyLoaded: number;
|
||||||
private amountToLoad: number;
|
private amountToLoad: number;
|
||||||
|
|
||||||
constructor(private requestService: RequestService,
|
constructor(private requestService: RequestService,
|
||||||
private auth: AuthService,
|
private auth: AuthService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private imageService: ImageService) {
|
private imageService: ImageService,
|
||||||
|
private sonarrService: SonarrService,
|
||||||
|
private notificationService: NotificationService) {
|
||||||
this.searchChanged
|
this.searchChanged
|
||||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||||
.distinctUntilChanged() // only emit if value is different from previous value
|
.distinctUntilChanged() // only emit if value is different from previous value
|
||||||
|
@ -54,9 +59,11 @@ export class TvRequestsComponent implements OnInit {
|
||||||
.subscribe(m => {
|
.subscribe(m => {
|
||||||
this.tvRequests = m;
|
this.tvRequests = m;
|
||||||
this.tvRequests.forEach((val) => this.loadBackdrop(val));
|
this.tvRequests.forEach((val) => this.loadBackdrop(val));
|
||||||
|
this.tvRequests.forEach((val) => this.setOverride(val.data));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public openClosestTab(el: any) {
|
public openClosestTab(el: any) {
|
||||||
const rowclass = "undefined ng-star-inserted";
|
const rowclass = "undefined ng-star-inserted";
|
||||||
el = el.toElement || el.relatedTarget || el.target;
|
el = el.toElement || el.relatedTarget || el.target;
|
||||||
|
@ -83,11 +90,18 @@ export class TvRequestsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
|
|
||||||
|
const profile = <ISonarrProfile>{name:"test",id:1 };
|
||||||
|
const folder = <ISonarrRootFolder>{path:"testpath", id:1};
|
||||||
|
|
||||||
|
this.sonarrProfiles.push(profile);
|
||||||
|
this.sonarrRootFolders.push(folder);
|
||||||
this.amountToLoad = 1000;
|
this.amountToLoad = 1000;
|
||||||
this.currentlyLoaded = 1000;
|
this.currentlyLoaded = 1000;
|
||||||
this.tvRequests = [];
|
this.tvRequests = [];
|
||||||
this.loadInit();
|
|
||||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||||
|
|
||||||
|
this.loadInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadMore() {
|
public loadMore() {
|
||||||
|
@ -117,14 +131,72 @@ export class TvRequestsComponent implements OnInit {
|
||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public selectRootFolder(searchResult: ITvRequests, rootFolderSelected: ISonarrRootFolder, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
searchResult.rootFolder = rootFolderSelected.id;
|
||||||
|
this.setOverride(searchResult);
|
||||||
|
this.updateRequest(searchResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectQualityProfile(searchResult: ITvRequests, profileSelected: ISonarrProfile, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
searchResult.qualityOverride = profileSelected.id;
|
||||||
|
this.setOverride(searchResult);
|
||||||
|
this.updateRequest(searchResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setOverride(req: ITvRequests): void {
|
||||||
|
this.setQualityOverrides(req);
|
||||||
|
this.setRootFolderOverrides(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateRequest(request: ITvRequests) {
|
||||||
|
this.requestService.updateTvRequest(request)
|
||||||
|
.subscribe(x => {
|
||||||
|
this.notificationService.success("Request Updated");
|
||||||
|
this.setOverride(x);
|
||||||
|
request = x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setQualityOverrides(req: ITvRequests): void {
|
||||||
|
if (this.sonarrProfiles) {
|
||||||
|
const profile = this.sonarrProfiles.filter((p) => {
|
||||||
|
return p.id === req.qualityOverride;
|
||||||
|
});
|
||||||
|
if (profile.length > 0) {
|
||||||
|
req.qualityOverrideTitle = profile[0].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private setRootFolderOverrides(req: ITvRequests): void {
|
||||||
|
if (this.sonarrRootFolders) {
|
||||||
|
const path = this.sonarrRootFolders.filter((folder) => {
|
||||||
|
return folder.id === req.rootFolder;
|
||||||
|
});
|
||||||
|
if (path.length > 0) {
|
||||||
|
req.rootPathOverrideTitle = path[0].path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private loadInit() {
|
private loadInit() {
|
||||||
this.requestService.getTvRequestsTree(this.amountToLoad, 0)
|
this.requestService.getTvRequestsTree(this.amountToLoad, 0)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.tvRequests = x;
|
this.tvRequests = x;
|
||||||
this.tvRequests.forEach((val, index) => {
|
this.tvRequests.forEach((val, index) => {
|
||||||
this.loadBackdrop(val);
|
this.loadBackdrop(val);
|
||||||
|
this.setOverride(val.data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(this.isAdmin) {
|
||||||
|
this.sonarrService.getQualityProfilesWithoutSettings()
|
||||||
|
.subscribe(x => this.sonarrProfiles = x);
|
||||||
|
|
||||||
|
this.sonarrService.getRootFoldersWithoutSettings()
|
||||||
|
.subscribe(x => this.sonarrRootFolders = x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetSearch() {
|
private resetSearch() {
|
||||||
|
|
|
@ -42,7 +42,8 @@
|
||||||
<h4>{{result.title}} ({{result.releaseDate | date: 'yyyy'}})</h4>
|
<h4>{{result.title}} ({{result.releaseDate | date: 'yyyy'}})</h4>
|
||||||
</a>
|
</a>
|
||||||
<span class="tags">
|
<span class="tags">
|
||||||
<span *ngIf="result.releaseDate" class="label label-info" id="releaseDateLabel" target="_blank">{{ 'Search.ReleaseDate' | translate }} {{result.releaseDate | date: 'dd/MM/yyyy'}}</span>
|
<span *ngIf="result.releaseDate" class="label label-info" id="releaseDateLabel" target="_blank">{{ 'Search.TheatricalRelease' | translate: {date: result.releaseDate | date: 'mediumDate'} }}</span>
|
||||||
|
<span *ngIf="result.digitalReleaseDate" class="label label-info" id="releaseDateLabel" target="_blank">{{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate | date: 'mediumDate'} }}</span>
|
||||||
|
|
||||||
<a *ngIf="result.homepage" href="{{result.homepage}}" id="homePageLabel" target="_blank"><span class="label label-info" [translate]="'Search.Movies.HomePage'"></span></a>
|
<a *ngIf="result.homepage" href="{{result.homepage}}" id="homePageLabel" target="_blank"><span class="label label-info" [translate]="'Search.Movies.HomePage'"></span></a>
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@
|
||||||
<i *ngIf="result.processed && !result.requestProcessing" class="fa fa-check"></i>{{ 'Common.Request' | translate }}</button>
|
<i *ngIf="result.processed && !result.requestProcessing" class="fa fa-check"></i>{{ 'Common.Request' | translate }}</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
<button style="text-align: right" class="btn btn-sm btn-info-outline" (click)="similarMovies(result.id)"> <i class="fa fa-eye"></i> {{ 'Search.Similar' | translate }}</button>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<div *ngIf="result.available">
|
<div *ngIf="result.available">
|
||||||
|
|
|
@ -146,6 +146,15 @@ export class MovieSearchComponent implements OnInit {
|
||||||
this.issueProviderId = req.id.toString();
|
this.issueProviderId = req.id.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public similarMovies(theMovieDbId: number) {
|
||||||
|
this.clearResults();
|
||||||
|
this.searchService.similarMovies(theMovieDbId)
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.getExtraInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private getExtraInfo() {
|
private getExtraInfo() {
|
||||||
|
|
||||||
this.movieResults.forEach((val, index) => {
|
this.movieResults.forEach((val, index) => {
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
<div class="col-sm-8 small-padding">
|
<div class="col-sm-8 small-padding">
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<a href="http://www.imdb.com/title/{{node.data.imdbId}}/" target="_blank">
|
<a *ngIf="node.data.imdbId" href="http://www.imdb.com/title/{{node.data.imdbId}}/" target="_blank">
|
||||||
<h4>{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})</h4>
|
<h4>{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})</h4>
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -20,4 +20,11 @@ export class SonarrService extends ServiceHelpers {
|
||||||
public getQualityProfiles(settings: ISonarrSettings): Observable<ISonarrProfile[]> {
|
public getQualityProfiles(settings: ISonarrSettings): Observable<ISonarrProfile[]> {
|
||||||
return this.http.post<ISonarrProfile[]>(`${this.url}/Profiles/`, JSON.stringify(settings), {headers: this.headers});
|
return this.http.post<ISonarrProfile[]>(`${this.url}/Profiles/`, JSON.stringify(settings), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getRootFoldersWithoutSettings(): Observable<ISonarrRootFolder[]> {
|
||||||
|
return this.http.get<ISonarrRootFolder[]>(`${this.url}/RootFolders/`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
public getQualityProfilesWithoutSettings(): Observable<ISonarrProfile[]> {
|
||||||
|
return this.http.get<ISonarrProfile[]>(`${this.url}/Profiles/`, {headers: this.headers});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
export * from "./applications";
|
|
||||||
export * from "./helpers";
|
|
||||||
export * from "./identity.service";
|
|
||||||
export * from "./image.service";
|
|
||||||
export * from "./landingpage.service";
|
|
||||||
export * from "./notification.service";
|
|
||||||
export * from "./request.service";
|
|
||||||
export * from "./search.service";
|
|
||||||
export * from "./service.helpers";
|
|
||||||
export * from "./settings.service";
|
|
||||||
export * from "./status.service";
|
|
||||||
export * from "./job.service";
|
|
||||||
export * from "./issues.service";
|
|
||||||
export * from "./mobile.service";
|
|
||||||
export * from "./recentlyAdded.service";
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { PlatformLocation } from "@angular/common";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs/Rx";
|
||||||
|
|
||||||
|
import { IMassEmailModel } from "./../interfaces";
|
||||||
|
|
||||||
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NotificationMessageService extends ServiceHelpers {
|
||||||
|
constructor(http: HttpClient, public platformLocation: PlatformLocation) {
|
||||||
|
super(http, "/api/v1/notifications/", platformLocation);
|
||||||
|
}
|
||||||
|
public sendMassEmail(model: IMassEmailModel): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}massemail/`, JSON.stringify(model) ,{headers: this.headers});
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,9 @@ export class SearchService extends ServiceHelpers {
|
||||||
public searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> {
|
public searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/` + searchTerm);
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/` + searchTerm);
|
||||||
}
|
}
|
||||||
|
public similarMovies(theMovieDbId: number): Observable<ISearchMovieResult[]> {
|
||||||
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${theMovieDbId}/similar`);
|
||||||
|
}
|
||||||
|
|
||||||
public popularMovies(): Observable<ISearchMovieResult[]> {
|
public popularMovies(): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/Popular`);
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/Popular`);
|
||||||
|
@ -54,15 +57,15 @@ export class SearchService extends ServiceHelpers {
|
||||||
}
|
}
|
||||||
|
|
||||||
public popularTv(): Observable<TreeNode[]> {
|
public popularTv(): Observable<TreeNode[]> {
|
||||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/popular`, {headers: this.headers});
|
return this.http.get<TreeNode[]>(`${this.url}/Tv/popular/tree`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
public mostWatchedTv(): Observable<TreeNode[]> {
|
public mostWatchedTv(): Observable<TreeNode[]> {
|
||||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/mostwatched`, {headers: this.headers});
|
return this.http.get<TreeNode[]>(`${this.url}/Tv/mostwatched/tree`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
public anticipatedTv(): Observable<TreeNode[]> {
|
public anticipatedTv(): Observable<TreeNode[]> {
|
||||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/anticipated`, {headers: this.headers});
|
return this.http.get<TreeNode[]>(`${this.url}/Tv/anticipated/tree`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
public trendingTv(): Observable<TreeNode[]> {
|
public trendingTv(): Observable<TreeNode[]> {
|
||||||
return this.http.get<TreeNode[]>(`${this.url}/Tv/trending`, {headers: this.headers});
|
return this.http.get<TreeNode[]>(`${this.url}/Tv/trending/tree`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,12 @@
|
||||||
|
|
||||||
<fieldset *ngIf="settings">
|
<fieldset *ngIf="settings">
|
||||||
<legend>Customization</legend>
|
<legend>Customization</legend>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2 col-md-push-10">
|
||||||
|
<span style="vertical-align: top;">Advanced</span>
|
||||||
|
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-md-5">
|
<div class="col-md-5">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="applicationName" class="control-label">Application Name</label>
|
<label for="applicationName" class="control-label">Application Name</label>
|
||||||
|
@ -20,6 +26,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngIf="advanced">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="enable" [(ngModel)]="settings.mobile" [checked]="settings.mobile">
|
||||||
|
<label for="enable">Enable Mobile</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="logo" class="control-label">Custom Logo</label>
|
<label for="logo" class="control-label">Custom Logo</label>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -11,6 +11,7 @@ export class CustomizationComponent implements OnInit {
|
||||||
|
|
||||||
public settings: ICustomizationSettings;
|
public settings: ICustomizationSettings;
|
||||||
public themes: IThemes[];
|
public themes: IThemes[];
|
||||||
|
public advanced: boolean;
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService, private notificationService: NotificationService) { }
|
constructor(private settingsService: SettingsService, private notificationService: NotificationService) { }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<settings-menu></settings-menu>
|
||||||
|
|
||||||
|
<wiki [url]="'https://github.com/tidusjar/Ombi/wiki/Mass-Email'"></wiki>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Mass Email</legend>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control form-control-custom " id="subject" name="subject" placeholder="Subject" [(ngModel)]="subject" [ngClass]="{'form-error': missingSubject}">
|
||||||
|
<small *ngIf="missingSubject" class="error-text">Hey! We need a subject!</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" >
|
||||||
|
<textarea rows="10" type="text" class="form-control-custom form-control " id="themeContent" name="themeContent" [(ngModel)]="message"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="logo" class="control-label">Message Preview</label>
|
||||||
|
<br/>
|
||||||
|
<small>May appear differently on email clients</small>
|
||||||
|
<hr/>
|
||||||
|
<div [innerHTML]="message"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button type="submit" id="save" (click)="send()" class="btn btn-primary-outline">Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<!--Users Section-->
|
||||||
|
<label class="control-label">Recipients</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="all" (click)="selectAllUsers()">
|
||||||
|
<label for="all">Select All</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngFor="let u of users">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="{{u.user.id}}" [(ngModel)]="u.selected" (click)="selectSingleUser(u)">
|
||||||
|
<label for="{{u.user.id}}">{{u.user.userName}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</fieldset>
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import { IMassEmailModel, IMassEmailUserModel } from "../../interfaces";
|
||||||
|
import { IdentityService, NotificationMessageService, NotificationService, SettingsService } from "../../services";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./massemail.component.html",
|
||||||
|
})
|
||||||
|
export class MassEmailComponent implements OnInit {
|
||||||
|
|
||||||
|
public users: IMassEmailUserModel[] = [];
|
||||||
|
public message: string;
|
||||||
|
public subject: string;
|
||||||
|
|
||||||
|
public missingSubject = false;
|
||||||
|
|
||||||
|
public emailEnabled: boolean;
|
||||||
|
|
||||||
|
constructor(private readonly notification: NotificationService,
|
||||||
|
private readonly identityService: IdentityService,
|
||||||
|
private readonly notificationMessageService: NotificationMessageService,
|
||||||
|
private readonly settingsService: SettingsService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.identityService.getUsers().subscribe(x => {
|
||||||
|
x.forEach(u => {
|
||||||
|
this.users.push({
|
||||||
|
user: u,
|
||||||
|
selected: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.settingsService.getEmailSettingsEnabled().subscribe(x => this.emailEnabled = x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectAllUsers() {
|
||||||
|
this.users.forEach(u => u.selected = !u.selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectSingleUser(user: IMassEmailUserModel) {
|
||||||
|
user.selected = !user.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public send() {
|
||||||
|
if(!this.subject) {
|
||||||
|
this.missingSubject = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!this.emailEnabled) {
|
||||||
|
this.notification.error("You have not yet setup your email notifications, do that first!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.missingSubject = false;
|
||||||
|
// Where(x => x.selected).Select(x => x.user)
|
||||||
|
const selectedUsers = this.users.filter(u => {
|
||||||
|
return u.selected;
|
||||||
|
}).map(u => u.user);
|
||||||
|
|
||||||
|
if(selectedUsers.length <=0) {
|
||||||
|
this.notification.error("You need to select at least one user to send the email");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = <IMassEmailModel>{
|
||||||
|
users: selectedUsers,
|
||||||
|
subject: this.subject,
|
||||||
|
body: this.message,
|
||||||
|
};
|
||||||
|
this.notification.info("Sending","Sending mass email... Please wait");
|
||||||
|
this.notificationMessageService.sendMassEmail(model).subscribe(x => {
|
||||||
|
this.notification.success("We have sent the mass email to the users selected!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,6 +53,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="hideRequestsUsers" name="hideRequestsUsers" formControlName="hideRequestsUsers">
|
||||||
|
<label for="hideRequestsUsers">Hide requests from other users</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="ignoreCertificateErrors" name="ignoreCertificateErrors" formControlName="ignoreCertificateErrors">
|
<input type="checkbox" id="ignoreCertificateErrors" name="ignoreCertificateErrors" formControlName="ignoreCertificateErrors">
|
||||||
|
|
|
@ -24,6 +24,7 @@ export class OmbiComponent implements OnInit {
|
||||||
ignoreCertificateErrors: [x.ignoreCertificateErrors],
|
ignoreCertificateErrors: [x.ignoreCertificateErrors],
|
||||||
baseUrl: [x.baseUrl],
|
baseUrl: [x.baseUrl],
|
||||||
doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove],
|
doNotSendNotificationsForAutoApprove: [x.doNotSendNotificationsForAutoApprove],
|
||||||
|
hideRequestsUsers: [x.hideRequestsUsers],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,7 +42,7 @@ export class OmbiComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = <IOmbiSettings>form.value;
|
const result = <IOmbiSettings>form.value;
|
||||||
if(result.baseUrl.length > 0) {
|
if(result.baseUrl && result.baseUrl.length > 0) {
|
||||||
if(!result.baseUrl.startsWith("/")) {
|
if(!result.baseUrl.startsWith("/")) {
|
||||||
this.notificationService.error("Please ensure your base url starts with a '/'");
|
this.notificationService.error("Please ensure your base url starts with a '/'");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { ClipboardModule } from "ngx-clipboard/dist";
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from "../auth/auth.guard";
|
||||||
import { AuthService } from "../auth/auth.service";
|
import { AuthService } from "../auth/auth.service";
|
||||||
import { CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, PlexService, RadarrService, SonarrService, TesterService, ValidationService } from "../services";
|
import { CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||||
|
SonarrService, TesterService, ValidationService } from "../services";
|
||||||
|
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
import { AboutComponent } from "./about/about.component";
|
import { AboutComponent } from "./about/about.component";
|
||||||
|
@ -19,6 +20,7 @@ import { EmbyComponent } from "./emby/emby.component";
|
||||||
import { IssuesComponent } from "./issues/issues.component";
|
import { IssuesComponent } from "./issues/issues.component";
|
||||||
import { JobsComponent } from "./jobs/jobs.component";
|
import { JobsComponent } from "./jobs/jobs.component";
|
||||||
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
||||||
|
import { MassEmailComponent } from "./massemail/massemail.component";
|
||||||
import { DiscordComponent } from "./notifications/discord.component";
|
import { DiscordComponent } from "./notifications/discord.component";
|
||||||
import { EmailNotificationComponent } from "./notifications/emailnotification.component";
|
import { EmailNotificationComponent } from "./notifications/emailnotification.component";
|
||||||
import { MattermostComponent } from "./notifications/mattermost.component";
|
import { MattermostComponent } from "./notifications/mattermost.component";
|
||||||
|
@ -66,6 +68,7 @@ const routes: Routes = [
|
||||||
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
|
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
|
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
|
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: "MassEmail", component: MassEmailComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -114,6 +117,7 @@ const routes: Routes = [
|
||||||
IssuesComponent,
|
IssuesComponent,
|
||||||
AuthenticationComponent,
|
AuthenticationComponent,
|
||||||
MobileComponent,
|
MobileComponent,
|
||||||
|
MassEmailComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
@ -131,6 +135,7 @@ const routes: Routes = [
|
||||||
PlexService,
|
PlexService,
|
||||||
EmbyService,
|
EmbyService,
|
||||||
MobileService,
|
MobileService,
|
||||||
|
NotificationMessageService,
|
||||||
],
|
],
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Email']">Email</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Email']">Email</a></li>
|
||||||
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/MassEmail']">Mass Email</a></li>
|
||||||
<!--<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Newsletter']">Newsletter</a></li>-->
|
<!--<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Newsletter']">Newsletter</a></li>-->
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Discord']">Discord</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Discord']">Discord</a></li>
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Slack']">Slack</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Slack']">Slack</a></li>
|
||||||
|
|
|
@ -16,44 +16,45 @@
|
||||||
</td>
|
</td>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('u.userName')">
|
<th (click)="setOrder('userName')">
|
||||||
<a>
|
<a>
|
||||||
Username
|
Username
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="order === 'u.userName'">
|
<span *ngIf="order === 'userName'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('u.alias')">
|
<th (click)="setOrder('alias')">
|
||||||
<a>
|
<a>
|
||||||
Alias
|
Alias
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="order === 'u.alias'">
|
<span *ngIf="order === 'alias'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('u.emailAddress')">
|
<th (click)="setOrder('emailAddress')">
|
||||||
<a>
|
<a>
|
||||||
Email
|
Email
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="order === 'u.emailAddress'">
|
<span *ngIf="order === 'emailAddress'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span>
|
||||||
|
<span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Roles
|
Roles
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('u.lastLoggedIn')">
|
<th (click)="setOrder('lastLoggedIn')">
|
||||||
<a> Last Logged In</a>
|
<a> Last Logged In</a>
|
||||||
<span *ngIf="order === 'u.lastLoggedIn'">
|
<span *ngIf="order === 'lastLoggedIn'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th (click)="setOrder('u.userType')">
|
<th (click)="setOrder('userType')">
|
||||||
<a>
|
<a>
|
||||||
User Type
|
User Type
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="order === 'u.userType'">
|
<span *ngIf="order === 'userType'">
|
||||||
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class UserManagementComponent implements OnInit {
|
||||||
public emailSettings: IEmailNotificationSettings;
|
public emailSettings: IEmailNotificationSettings;
|
||||||
public customizationSettings: ICustomizationSettings;
|
public customizationSettings: ICustomizationSettings;
|
||||||
|
|
||||||
public order: string = "u.userName";
|
public order: string = "userName";
|
||||||
public reverse = false;
|
public reverse = false;
|
||||||
|
|
||||||
public showBulkEdit = false;
|
public showBulkEdit = false;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import '../base.scss';
|
$primary-colour: #df691a;
|
||||||
$primary-colour: #df691a;
|
|
||||||
$primary-colour-outline: #ff761b;
|
$primary-colour-outline: #ff761b;
|
||||||
$bg-colour: #333333;
|
$bg-colour: #333333;
|
||||||
$bg-colour-disabled: #252424;
|
$bg-colour-disabled: #252424;
|
||||||
|
|
|
@ -22,6 +22,49 @@ $i: !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
|
||||||
|
.table-usermanagement {
|
||||||
|
/* Force table to not be like tables anymore */
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
thead, tbody, th, td, tr {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide table headers (but not display: none;, for accessibility) */
|
||||||
|
thead tr {
|
||||||
|
position: absolute;
|
||||||
|
top: -9999px;
|
||||||
|
left: -9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
/* Behave like a "row" */
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 50% $i;
|
||||||
|
min-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:before {
|
||||||
|
/* Now like a table header */
|
||||||
|
position: absolute;
|
||||||
|
/* Top/left values mimic padding */
|
||||||
|
top: 6px;
|
||||||
|
left: 6px;
|
||||||
|
width: 45%;
|
||||||
|
padding-right: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
/* Label the data */
|
||||||
|
.td-labelled:before {
|
||||||
|
content: attr(data-label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 48em) {
|
@media (max-width: 48em) {
|
||||||
.home {
|
.home {
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
|
@ -841,8 +884,13 @@ textarea {
|
||||||
border: 1px solid $form-color-lighter;
|
border: 1px solid $form-color-lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-treetable tfoot td, .ui-treetable th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-treetable tbody td {
|
.ui-treetable tbody td {
|
||||||
white-space: inherit;
|
white-space: inherit;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
table a:not(.btn) {
|
table a:not(.btn) {
|
||||||
|
@ -897,49 +945,6 @@ a > h4:hover {
|
||||||
padding-top:15px;
|
padding-top:15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
|
||||||
|
|
||||||
.table-usermanagement {
|
|
||||||
/* Force table to not be like tables anymore */
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
thead, tbody, th, td, tr {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide table headers (but not display: none;, for accessibility) */
|
|
||||||
thead tr {
|
|
||||||
position: absolute;
|
|
||||||
top: -9999px;
|
|
||||||
left: -9999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
/* Behave like a "row" */
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 50% $i;
|
|
||||||
min-height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:before {
|
|
||||||
/* Now like a table header */
|
|
||||||
position: absolute;
|
|
||||||
/* Top/left values mimic padding */
|
|
||||||
top: 6px;
|
|
||||||
left: 6px;
|
|
||||||
width: 45%;
|
|
||||||
padding-right: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
/* Label the data */
|
|
||||||
.td-labelled:before {
|
|
||||||
content: attr(data-label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchWidth {
|
.searchWidth {
|
||||||
width: 94%;
|
width: 94%;
|
||||||
}
|
}
|
|
@ -46,5 +46,27 @@ namespace Ombi.Controllers.External
|
||||||
{
|
{
|
||||||
return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Sonarr profiles.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("Profiles")]
|
||||||
|
public async Task<IEnumerable<SonarrProfile>> GetProfiles()
|
||||||
|
{
|
||||||
|
var settings = await SonarrSettings.GetSettingsAsync();
|
||||||
|
return await SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Sonarr root folders.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("RootFolders")]
|
||||||
|
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders()
|
||||||
|
{
|
||||||
|
var settings = await SonarrSettings.GetSettingsAsync();
|
||||||
|
return await SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
@ -472,8 +472,18 @@ namespace Ombi.Controllers
|
||||||
// Get the roles
|
// Get the roles
|
||||||
var userRoles = await UserManager.GetRolesAsync(user);
|
var userRoles = await UserManager.GetRolesAsync(user);
|
||||||
|
|
||||||
|
// Am I modifying myself?
|
||||||
|
var modifyingSelf = user.UserName.Equals(User.Identity.Name, StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
foreach (var role in userRoles)
|
foreach (var role in userRoles)
|
||||||
{
|
{
|
||||||
|
if (modifyingSelf && role.Equals(OmbiRoles.Admin))
|
||||||
|
{
|
||||||
|
// We do not want to remove the admin role from yourself, this must be an accident
|
||||||
|
var claim = ui.Claims.FirstOrDefault(x => x.Value == OmbiRoles.Admin && x.Enabled);
|
||||||
|
ui.Claims.Remove(claim);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
await UserManager.RemoveFromRoleAsync(user, role);
|
await UserManager.RemoveFromRoleAsync(user, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,25 +623,54 @@ namespace Ombi.Controllers
|
||||||
return defaultMessage;
|
return defaultMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have the user
|
|
||||||
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
|
|
||||||
// We now need to email the user with this token
|
|
||||||
var emailSettings = await EmailSettings.GetSettingsAsync();
|
|
||||||
var customizationSettings = await CustomizationSettings.GetSettingsAsync();
|
var customizationSettings = await CustomizationSettings.GetSettingsAsync();
|
||||||
var appName = (string.IsNullOrEmpty(customizationSettings.ApplicationName)
|
var appName = (string.IsNullOrEmpty(customizationSettings.ApplicationName)
|
||||||
? "Ombi"
|
? "Ombi"
|
||||||
: customizationSettings.ApplicationName);
|
: customizationSettings.ApplicationName);
|
||||||
|
|
||||||
|
var emailSettings = await EmailSettings.GetSettingsAsync();
|
||||||
|
|
||||||
customizationSettings.AddToUrl("/token?token=");
|
customizationSettings.AddToUrl("/token?token=");
|
||||||
var url = customizationSettings.ApplicationUrl;
|
var url = customizationSettings.ApplicationUrl;
|
||||||
|
|
||||||
|
if (user.UserType == UserType.PlexUser)
|
||||||
|
{
|
||||||
|
await EmailProvider.SendAdHoc(new NotificationMessage
|
||||||
|
{
|
||||||
|
To = user.Email,
|
||||||
|
Subject = $"{appName} Password Reset",
|
||||||
|
Message =
|
||||||
|
$"You recently made a request to reset your {appName} account. Please click the link below to complete the process.<br/><br/>" +
|
||||||
|
$"<a href=\"https://www.plex.tv/sign-in/password-reset/\"> Reset </a>"
|
||||||
|
}, emailSettings);
|
||||||
|
}
|
||||||
|
else if (user.UserType == UserType.EmbyUser && user.IsEmbyConnect)
|
||||||
|
{
|
||||||
|
await EmailProvider.SendAdHoc(new NotificationMessage
|
||||||
|
{
|
||||||
|
To = user.Email,
|
||||||
|
Subject = $"{appName} Password Reset",
|
||||||
|
Message =
|
||||||
|
$"You recently made a request to reset your {appName} account.<br/><br/>" +
|
||||||
|
$"To reset your password you need to go to <a href=\"https://emby.media/community/index.php\">Emby.Media</a> and then click on your Username > Edit Profile > Email and Password"
|
||||||
|
}, emailSettings);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We have the user
|
||||||
|
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
|
||||||
|
var encodedToken = WebUtility.UrlEncode(token);
|
||||||
|
|
||||||
await EmailProvider.SendAdHoc(new NotificationMessage
|
await EmailProvider.SendAdHoc(new NotificationMessage
|
||||||
{
|
{
|
||||||
To = user.Email,
|
To = user.Email,
|
||||||
Subject = $"{appName} Password Reset",
|
Subject = $"{appName} Password Reset",
|
||||||
Message = $"You recently made a request to reset your {appName} account. Please click the link below to complete the process.<br/><br/>" +
|
Message =
|
||||||
$"<a href=\"{url}{token}\"> Reset </a>"
|
$"You recently made a request to reset your {appName} account. Please click the link below to complete the process.<br/><br/>" +
|
||||||
|
$"<a href=\"{url}{encodedToken}\"> Reset </a>"
|
||||||
}, emailSettings);
|
}, emailSettings);
|
||||||
|
}
|
||||||
|
|
||||||
return defaultMessage;
|
return defaultMessage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Ombi.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
private ILogger Logger { get; }
|
private ILogger Logger { get; }
|
||||||
private const string Message = "Exception: {0} at {1}. Stacktrade {2}";
|
private const string Message = "Exception: {0} at {1}. Stacktrace {2}";
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Log([FromBody]UiLoggingModel l)
|
public IActionResult Log([FromBody]UiLoggingModel l)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue