mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
update poc quartz with develop
This commit is contained in:
commit
e9ead2eddb
402 changed files with 30984 additions and 4844 deletions
48
.github/ISSUE_TEMPLATE.md
vendored
48
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,48 +0,0 @@
|
|||
<!---
|
||||
|
||||
!! Please use the Support / bug report template, otherwise we will close the Github issue !!
|
||||
|
||||
Version 2.X is not supported anymore. Please don't open a issue for the 2.x version.
|
||||
See https://github.com/tidusjar/Ombi/issues/1455 for more information.
|
||||
|
||||
(Pleas submit a feature request over here: http://feathub.com/tidusjar/Ombi)
|
||||
|
||||
|
||||
--->
|
||||
|
||||
#### Ombi build Version:
|
||||
|
||||
V 3.0.XX
|
||||
|
||||
#### Update Branch:
|
||||
|
||||
Open Beta
|
||||
|
||||
#### Media Sever:
|
||||
|
||||
Plex/Emby
|
||||
|
||||
#### Media Server Version:
|
||||
|
||||
<!-- If appropriate --->
|
||||
|
||||
#### Operating System:
|
||||
|
||||
(Place text here)
|
||||
|
||||
|
||||
#### Ombi Applicable Logs (from `/logs/` directory or the Admin page):
|
||||
|
||||
```
|
||||
|
||||
(Logs go here. Don't remove the ' tags for showing your logs correctly. Please make sure you remove any personal information from the logs)
|
||||
|
||||
```
|
||||
|
||||
#### Problem Description:
|
||||
|
||||
(Place text here)
|
||||
|
||||
#### Reproduction Steps:
|
||||
|
||||
Please include any steps to reproduce the issue, this the request that is causing the problem etc.
|
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs (Logs directory where Ombi is located)**
|
||||
If applicable, a snippet of the logs that seems relevant to the bug if present.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
|
||||
**Ombi Version (please complete the following information):**
|
||||
- Version [e.g. 3.0.1158]
|
||||
- Media Server [e.g. Plex]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
475
CHANGELOG.md
475
CHANGELOG.md
|
@ -1,6 +1,479 @@
|
|||
# Changelog
|
||||
|
||||
## (unreleased)
|
||||
## v3.0.4256 (2019-02-18)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Update discord link to follow the scheme of other links. [Tom McClellan]
|
||||
|
||||
- Update issue templates. [Jamie]
|
||||
|
||||
- Update README.md. [Jamie]
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
- Added the functionality to remove a user from getting notifications to their mobile device #2780. [tidusjar]
|
||||
|
||||
- Added a demo mode, this will only show movies and shows that are in the public domain. Dam that stupid fruit company. [tidusjar]
|
||||
|
||||
- Added Actor Searching for Movies! [TidusJar]
|
||||
|
||||
- Added the ability to change where the View on Emby link goes to #2730. [TidusJar]
|
||||
|
||||
- Added the request queue to the notifications UI so you can turn it off per notification agent #2747. [TidusJar]
|
||||
|
||||
- Added new classes to the posters #2732. [TidusJar]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- Fix: src/Ombi/package.json to reduce vulnerabilities. [snyk-bot]
|
||||
|
||||
- Fixed #2801 this is when a season is not correctly monitored in sonarr when approved by an admin. [tidusjar]
|
||||
|
||||
- Small improvements to try and mitigate #2750. [tidusjar]
|
||||
|
||||
- Removed some areas where we clear out the cache. This should help with DB locking #2750. [tidusjar]
|
||||
|
||||
- Fixed #2810. [tidusjar]
|
||||
|
||||
- Cannot create an issue comment with the API #2811. [tidusjar]
|
||||
|
||||
- Set the local domain if the Application URL is set for the HELO or EHLO commands. #2636. [tidusjar]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- Delete ISSUE_TEMPLATE.md. [Jamie]
|
||||
|
||||
- More minor grammatical edits. [Andrew Metzger]
|
||||
|
||||
- Minor grammatical edits. [Andrew Metzger]
|
||||
|
||||
- Fixed #2802 the issue where "Issues" were not being deleted correctly. [tidusjar]
|
||||
|
||||
- Fixed #2797. [tidusjar]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- Fixed #2786. [tidusjar]
|
||||
|
||||
- Fixed #2756. [tidusjar]
|
||||
|
||||
- Ignore the UserName header as part of the Api is the value is an empty string. [tidusjar]
|
||||
|
||||
- Fixed #2759. [tidusjar]
|
||||
|
||||
- Did #2756. [TidusJar]
|
||||
|
||||
- Fixed the exception that sometimes makes ombi fallover. [TidusJar]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- Log the error to the ui to figure out what's going on with #2755. [tidusjar]
|
||||
|
||||
- Small fix when denying a request with a reason, we wasn't updating the ui. [TidusJar]
|
||||
|
||||
- Make sure we can only set the ApiAlias when using the API Key. [tidusjar]
|
||||
|
||||
- #2363 Added the ability to pass any username into the API using the ApiAlias header. [tidusjar]
|
||||
|
||||
- Removed the Add user to Plex from Ombi. [tidusjar]
|
||||
|
||||
|
||||
## v3.0.4119 (2019-01-09)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
- Added a page where the admin can write/style/basically do whatever they want with e.g. FAQ for the users #2715 This needs to be enabled in the Customization Settings and then it's all configured on the page. [TidusJar]
|
||||
|
||||
- Updated the AspnetCore.App package to remove the CVE-2019-0564 vulnerability. [TidusJar]
|
||||
|
||||
- Added a global language flag that now applies to the search by default. [tidusjar]
|
||||
|
||||
- Updated the frontend packages (Using Angular 7 now) [TidusJar]
|
||||
|
||||
- Added capture of anonymous analytical data. [tidusjar]
|
||||
|
||||
- Added {AvailableDate} as a Notification Variable, this is the date the request was marked as available. See here: https://github.com/tidusjar/Ombi/wiki/Notification-Template-Variables. [tidusjar]
|
||||
|
||||
- Added the ability to search movies via the movie db with a different language! [tidusjar]
|
||||
|
||||
- Added the ability to specify a year when searching for movies. [tidusjar]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update NewsletterTemplate.html. [d1slact0r]
|
||||
|
||||
- Update HtmlTemplateGenerator.cs. [d1slact0r]
|
||||
|
||||
- Updated boostrap #2694. [Jamie]
|
||||
|
||||
- Added the ability to deny a request with a reason. [TidusJar]
|
||||
|
||||
- Update EmbyEpisodeSync.cs. [Jamie]
|
||||
|
||||
- Updated to .net core 2.2 and included a linux-arm64 build. [TidusJar]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- There is now a new Job in ombi that will clear out the Plex/Emby data and recache. This will prevent the issues going forward that we have when Ombi and the Media server fall out of sync with deletions/updates #2641 #2362 #1566. [TidusJar]
|
||||
|
||||
- Potentially fix #2726. [TidusJar]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- Fixed #2725 and #2721. [TidusJar]
|
||||
|
||||
- Made the newsletter use the default lanuage code set in the Ombi settings for movie information. [TidusJar]
|
||||
|
||||
- Save the language code against the request so we can use it later e.g. Sending to the DVR apps. [tidusjar]
|
||||
|
||||
- Fixed #2716. [tidusjar]
|
||||
|
||||
- Make the newsletter BCC the users rather than creating a million newsletters (Hopefully will stop SMTP providers from marking as spam). This does mean that the custom user customization in the newsletter will no longer work. [TidusJar]
|
||||
|
||||
- If we don't know the Plex agent, then see if it's a ImdbId, if it's not check the string for any episode and season hints #2695. [tidusjar]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- New translations en.json (Polish) [Jamie]
|
||||
|
||||
- New translations en.json (Norwegian) [Jamie]
|
||||
|
||||
- New translations en.json (Italian) [Jamie]
|
||||
|
||||
- New translations en.json (German) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Danish) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- Made the search results the language specified in the search refinement. [tidusjar]
|
||||
|
||||
- Fixed #2704. [tidusjar]
|
||||
|
||||
- Now it is fixed :) [d1slact0r]
|
||||
|
||||
- Android please be nice now. [d1slact0r]
|
||||
|
||||
- Fixed title bit better. [d1slact0r]
|
||||
|
||||
- Fixed titles. [d1slact0r]
|
||||
|
||||
- This should fix the build for sure (stupid quotes) [d1slact0r]
|
||||
|
||||
- Fixes build. [d1slact0r]
|
||||
|
||||
- Rewritten the whole newsletter template. [d1slact0r]
|
||||
|
||||
- Fixed #2697. [tidusjar]
|
||||
|
||||
- Add linux-arm runtime identifier. [aptalca]
|
||||
|
||||
- Add back arm packages. [aptalca]
|
||||
|
||||
- Add arm32 package. [aptalca]
|
||||
|
||||
- Fixed #2691. [tidusjar]
|
||||
|
||||
- Fixed linting. [TidusJar]
|
||||
|
||||
- Fixed the Plex OAuth when going through the wizard. [TidusJar]
|
||||
|
||||
- Fixed #2678. [TidusJar]
|
||||
|
||||
- Deny reason for movie requests. [TidusJar]
|
||||
|
||||
- Set the landing and login pages background refresh to 15 seconds rather than 10 and 7. [TidusJar]
|
||||
|
||||
- Fixed a bug with us thinking future dated emby episodes are not available, Consoldated the emby and plex search rules (since they have the same logic) [TidusJar]
|
||||
|
||||
- Fixed build. [TidusJar]
|
||||
|
||||
|
||||
## v3.0.4036 (2018-12-11)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Changelog. [Jamie]
|
||||
|
||||
- Added Sonarr v3 #2359. [TidusJar]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- !changelog. [Jamie]
|
||||
|
||||
- Fixed a missing translation. [Jamie]
|
||||
|
||||
- Fixed a potential security vulnerability. [Jamie]
|
||||
|
||||
- Sorted out some of the settings pages, trying to make it consistent. [Jamie]
|
||||
|
||||
- #2669 Fixed missing translations. [TidusJar]
|
||||
|
||||
- Maps alias email variable for welcome emails. [Victor Usoltsev]
|
||||
|
||||
- Increased the logo size on the landing page to match the container below it. [Jamie]
|
||||
|
||||
- Think the request queue is done! [Jamie]
|
||||
|
||||
- Finished off the job. [TidusJar]
|
||||
|
||||
|
||||
## v3.0.3988 (2018-11-23)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Updated the emby api since we no longer need the extra parameters to send to emby to log in a local user #2546. [Jamie]
|
||||
|
||||
- Added the ability to get the ombi user via a Plex Token #2591. [Jamie]
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- !changelog. [Jamie]
|
||||
|
||||
- Made the subscribe/unsubscribe button more obvious on the UI #2309. [Jamie]
|
||||
|
||||
- Fixed #2603. [Jamie]
|
||||
|
||||
- Fixed the issue with the user overrides #2646. [Jamie]
|
||||
|
||||
- Fixed the issue where we could sometimes allow the request of a whole series when the user shouldn't be able to. [Jamie]
|
||||
|
||||
- Fixed the issue where we were marking episodes as available with the Emby connection when they have not yet aired #2417 #2623. [TidusJar]
|
||||
|
||||
- Fixed the issue where we were marking the whole season as wanted in Sonarr rather than the individual episode #2629. [TidusJar]
|
||||
|
||||
- Fixed #2623. [Jamie]
|
||||
|
||||
- Fixed #2633. [TidusJar]
|
||||
|
||||
- Fixed #2639. [Jamie]
|
||||
|
||||
- Show the TV show as available when we have all the episodes but future episodes have not aired. #2585. [Jamie]
|
||||
|
||||
|
||||
## v3.0.3945 (2018-10-25)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Update Readme for Lidarr. [Qstick]
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- Fixed the issue with mobile notifications. [Jamie]
|
||||
|
||||
- Fixed #2514. [Jamie]
|
||||
|
||||
|
||||
## v3.0.3923 (2018-10-19)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- Fixed #2601. [Jamie]
|
||||
|
||||
|
||||
## v3.0.3919 (2018-10-17)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Added automation tests for the voting feature. [TidusJar]
|
||||
|
||||
- Update LidarrAvailabilityChecker.cs. [Jamie]
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
- Changes language selector to always show native language name. [Victor Usoltsev]
|
||||
|
||||
- Updated test dependancies. [TidusJar]
|
||||
|
||||
- Added in the external repo so we can rip out external stuff. [TidusJar]
|
||||
|
||||
- Added the ability to purge/remove issues. [TidusJar]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- New translations en.json (Polish) [Jamie]
|
||||
|
||||
- New translations en.json (Norwegian) [Jamie]
|
||||
|
||||
- New translations en.json (Italian) [Jamie]
|
||||
|
||||
- New translations en.json (German) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Danish) [Jamie]
|
||||
|
||||
- When a users requests content and the voting is enabled, the user who requested is an automatic +1 vote. [TidusJar]
|
||||
|
||||
- Revert, no idea how this happened. [TidusJar]
|
||||
|
||||
- Fixed the build. Thanks Matt! [TidusJar]
|
||||
|
||||
- Fixes untickable mass email checkboxes in Safari. [Victor Usoltsev]
|
||||
|
||||
- [ImgBot] optimizes images. [ImgBotApp]
|
||||
|
||||
- Revert "Feature/purge issues" [Jamie]
|
||||
|
||||
- Fixed the issue where user preferences was not being inported into some notifications. [TidusJar]
|
||||
|
||||
- New role to enable users to remove their own requests. [Anojh]
|
||||
|
||||
- Users can now remove their own requests. [Anojh]
|
||||
|
||||
- New translations en.json (Danish) [Jamie]
|
||||
|
||||
- Fixed lidarr newsletter bug. [Jamie]
|
||||
|
||||
- Potentially fix the user profiles issue. [Jamie]
|
||||
|
||||
- Hides Radarr options on movie requests page if only 1 option available. [Victor Usoltsev]
|
||||
|
||||
- Hides Sonarr options on tv requests page if only 1 option available. [Victor Usoltsev]
|
||||
|
||||
- Fixed the issue where we could not delete users #2558. [TidusJar]
|
||||
|
||||
- New translations en.json (German) [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- New translations en.json (Polish) [Jamie]
|
||||
|
||||
- New translations en.json (Norwegian) [Jamie]
|
||||
|
||||
- New translations en.json (Italian) [Jamie]
|
||||
|
||||
- New translations en.json (German) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Danish) [Jamie]
|
||||
|
||||
- Subscribe the user to the request when they vote on it. [TidusJar]
|
||||
|
||||
- Fixed #2555. [Jamie]
|
||||
|
||||
- Fixed #2549. [Jamie]
|
||||
|
||||
- Removed the pinID from the OAuth url #2548. [Jamie]
|
||||
|
||||
- Put the issue purge limit on the issues page. [Jamie]
|
||||
|
||||
- Date and times are now in the local users date time. [TidusJar]
|
||||
|
||||
- Fixed the migration. [TidusJar]
|
||||
|
||||
- ExternalContext migrations. [TidusJar]
|
||||
|
||||
- The settings have now been split out of the main db. [TidusJar]
|
||||
|
||||
- Search for the Lidarr Album when it's a new artist. [TidusJar]
|
||||
|
||||
- The album in Lidarr does not need to be marked as monitored for us to pick up it's available. Fixes #2536. [Jamie]
|
||||
|
||||
- Truncate the request title. [Jamie]
|
||||
|
||||
- Fixed #2535. [Jamie]
|
||||
|
||||
|
||||
## v3.0.3795 (2018-09-23)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
|
|
21
README.md
21
README.md
|
@ -9,6 +9,15 @@ ____
|
|||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
___
|
||||
|
||||
[](https://twitter.com/intent/follow?screen_name=tidusjar)
|
||||
|
||||
Follow me developing Ombi!
|
||||
|
||||
[](https://www.twitch.tv/tidusjar)
|
||||
|
||||
|
||||
___
|
||||
<a href='https://play.google.com/store/apps/details?id=com.tidusjar.Ombi&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img width="150" alt='Get it on Google Play' src='https://play.google.com/intl/en_gb/badges/images/generic/en_badge_web_generic.png'/></a>
|
||||
|
||||
|
@ -32,7 +41,7 @@ ___
|
|||
# Features
|
||||
Here are some of the features Ombi V3 has:
|
||||
* Now working without crashes on Linux.
|
||||
* Lets users request Movies and TV Shows (whether it being the entire series, an entire season, or even single episodes.)
|
||||
* Lets users request Movies, Music, and TV Shows (whether it being the entire series, an entire season, or even single episodes.)
|
||||
* Easily manage your requests
|
||||
* User management system (supports plex.tv, Emby and local accounts)
|
||||
* A landing page that will give you the availability of your Plex/Emby server and also add custom notification text to inform your users of downtime.
|
||||
|
@ -50,6 +59,7 @@ We integrate with the following applications:
|
|||
* Emby
|
||||
* Sonarr
|
||||
* Radarr
|
||||
* Lidarr
|
||||
* DogNzb
|
||||
* Couch Potato
|
||||
|
||||
|
@ -58,6 +68,7 @@ We integrate with the following applications:
|
|||
Supported notifications:
|
||||
* SMTP Notifications (Email)
|
||||
* Discord
|
||||
* Gotify
|
||||
* Slack
|
||||
* Pushbullet
|
||||
* Pushover
|
||||
|
@ -87,6 +98,7 @@ We are planning to bring back these features in V3 but for now you can find a li
|
|||
| DogNzb | Yes | No |
|
||||
| Issues | Yes | Yes |
|
||||
| Headphones | No | Yes |
|
||||
| Lidarr | Yes | No |
|
||||
|
||||
# Feature Requests
|
||||
Feature requests are handled on FeatHub.
|
||||
|
@ -115,13 +127,12 @@ Please feel free to submit a pull request!
|
|||
# Donation
|
||||
If you feel like donating you can donate with the below buttons!
|
||||
|
||||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
[](https://patreon.com/tidusjar/Ombi)
|
||||
[](https://paypal.me/PlexRequestsNet)
|
||||
|
||||
### A massive thanks to everyone for all their help!
|
||||
|
||||
## Stats
|
||||
[](https://waffle.io/tidusjar/PlexRequests.Net/metrics/throughput)
|
||||
|
||||
### Sponsors ###
|
||||
- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
|
||||
|
|
24
appveyor.yml
24
appveyor.yml
|
@ -3,34 +3,46 @@ configuration: Release
|
|||
os: Visual Studio 2017
|
||||
environment:
|
||||
nodejs_version: "9.8.0"
|
||||
typescript_version: "3.0.1"
|
||||
github_auth_token:
|
||||
secure: H/7uCrjmWHGJxgN3l9fbhhdVjvvWI8VVF4ZzQqeXuJwAf+PgSNBdxv4SS+rMQ+RH
|
||||
sonarrcloudtoken:
|
||||
secure: WGkIog4wuMSx1q5vmSOgIBXMtI/leMpLbZhi9MJnJdBBuDfcv12zwXg3LQwY0WbE
|
||||
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
|
||||
- cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\3.0;%path%
|
||||
- cmd: tsc -v
|
||||
build_script:
|
||||
# - dotnet tool install --global dotnet-sonarscanner
|
||||
#- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { dotnet sonarscanner begin /k:"tidusjar_Ombi" /d:sonar.organization="tidusjar-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="$env.sonarrcloud_token" /d:sonar.analysis.mode="preview" /d:sonar.github.pullRequest="$env:APPVEYOR_PULL_REQUEST_NUMBER" /d:sonar.github.repository="https://github.com/tidusjar/ombi" /d:sonar.github.oauth="$env.github_auth_token" }
|
||||
# - ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { dotnet sonarscanner begin /k:"tidusjar_Ombi" /d:sonar.organization="tidusjar-github" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="$env.SONARRCLOUDTOKEN" }
|
||||
- ps: ./build.ps1 --settings_skipverification=true
|
||||
# - dotnet sonarscanner end /d:sonar.login="%sonarrcloudtoken%"
|
||||
|
||||
test: off
|
||||
|
||||
after_build:
|
||||
- cmd: >-
|
||||
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip"
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows.zip"
|
||||
|
||||
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz"
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\osx.tar.gz"
|
||||
|
||||
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz"
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux.tar.gz"
|
||||
|
||||
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz"
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm.tar.gz"
|
||||
|
||||
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip"
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\windows-32bit.zip"
|
||||
|
||||
|
||||
# appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz"
|
||||
appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.2\linux-arm64.tar.gz"
|
||||
|
||||
|
||||
|
||||
|
|
10
build.cake
10
build.cake
|
@ -3,7 +3,7 @@
|
|||
#addin "Cake.Gulp"
|
||||
#addin "SharpZipLib"
|
||||
#addin nuget:?package=Cake.Compression&version=0.1.4
|
||||
#addin "Cake.Incubator"
|
||||
#addin "Cake.Incubator&version=3.1.0"
|
||||
#addin "Cake.Yarn"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
|
|||
var solutionFile = "Ombi.sln"; // Solution file if needed
|
||||
GitVersion versionInfo = null;
|
||||
|
||||
var frameworkVer = "netcoreapp2.1";
|
||||
var frameworkVer = "netcoreapp2.2";
|
||||
|
||||
var buildSettings = new DotNetCoreBuildSettings
|
||||
{
|
||||
|
@ -81,9 +81,9 @@ Task("SetVersionInfo")
|
|||
|
||||
versionInfo = GitVersion(settings);
|
||||
|
||||
Information("GitResults -> {0}", versionInfo.Dump());
|
||||
// Information("GitResults -> {0}", versionInfo.Dump());
|
||||
|
||||
Information(@"Build:{0}",AppVeyor.Environment.Build.Dump());
|
||||
//Information(@"Build:{0}",AppVeyor.Environment.Build.Dump());
|
||||
|
||||
var buildVersion = string.Empty;
|
||||
if(string.IsNullOrEmpty(AppVeyor.Environment.Build.Version))
|
||||
|
@ -151,7 +151,7 @@ Task("Package")
|
|||
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
|
||||
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
|
||||
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
|
||||
//GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
|
||||
GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
|
||||
});
|
||||
|
||||
Task("Publish")
|
||||
|
|
291
package-lock.json
generated
291
package-lock.json
generated
|
@ -1,291 +0,0 @@
|
|||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "1.0.3"
|
||||
}
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"requires": {
|
||||
"chalk": "1.1.3",
|
||||
"esutils": "2.0.2",
|
||||
"js-tokens": "3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
||||
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
|
||||
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "5.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"requires": {
|
||||
"color-convert": "1.9.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
|
||||
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
|
||||
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
|
||||
"requires": {
|
||||
"argparse": "1.0.10",
|
||||
"esprima": "4.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
|
||||
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
|
||||
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
|
||||
"requires": {
|
||||
"path-parse": "1.0.5"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
|
||||
"integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz",
|
||||
"integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=",
|
||||
"requires": {
|
||||
"babel-code-frame": "6.26.0",
|
||||
"builtin-modules": "1.1.1",
|
||||
"chalk": "2.4.1",
|
||||
"commander": "2.15.1",
|
||||
"diff": "3.5.0",
|
||||
"glob": "7.1.2",
|
||||
"js-yaml": "3.12.0",
|
||||
"minimatch": "3.0.4",
|
||||
"resolve": "1.7.1",
|
||||
"semver": "5.5.0",
|
||||
"tslib": "1.9.2",
|
||||
"tsutils": "2.27.1"
|
||||
}
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "2.27.1",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz",
|
||||
"integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
|
||||
"requires": {
|
||||
"tslib": "1.9.2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,8 +53,6 @@ namespace Ombi.Api.Emby
|
|||
{
|
||||
username,
|
||||
pw = password,
|
||||
password = password.GetSha1Hash().ToLower(),
|
||||
passwordMd5 = password.CalcuateMd5Hash()
|
||||
};
|
||||
|
||||
request.AddJsonBody(body);
|
||||
|
@ -98,7 +96,7 @@ namespace Ombi.Api.Emby
|
|||
|
||||
request.AddQueryString("Fields", "ProviderIds,Overview");
|
||||
|
||||
request.AddQueryString("VirtualItem", "False");
|
||||
request.AddQueryString("IsVirtualItem", "False");
|
||||
|
||||
return await Api.Request<EmbyItemContainer<EmbyMovie>>(request);
|
||||
}
|
||||
|
@ -149,7 +147,7 @@ namespace Ombi.Api.Emby
|
|||
request.AddQueryString("IncludeItemTypes", type);
|
||||
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
|
||||
|
||||
request.AddQueryString("VirtualItem", "False");
|
||||
request.AddQueryString("IsVirtualItem", "False");
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
|
@ -167,7 +165,7 @@ namespace Ombi.Api.Emby
|
|||
request.AddQueryString("startIndex", startIndex.ToString());
|
||||
request.AddQueryString("limit", count.ToString());
|
||||
|
||||
request.AddQueryString("VirtualItem", "False");
|
||||
request.AddQueryString("IsVirtualItem", "False");
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
|
||||
|
|
|
@ -24,16 +24,5 @@ namespace Ombi.Api.Github
|
|||
request.AddHeader("User-Agent", "Ombi");
|
||||
return await _api.Request<List<CakeThemes>>(request);
|
||||
}
|
||||
|
||||
public async Task<string> GetThemesRawContent(string url)
|
||||
{
|
||||
var sections = url.Split('/');
|
||||
var lastPart = sections.Last();
|
||||
url = url.Replace(lastPart, string.Empty);
|
||||
var request = new Request(lastPart, url, HttpMethod.Get);
|
||||
request.AddHeader("Accept", "application/vnd.github.v3+json");
|
||||
request.AddHeader("User-Agent", "Ombi");
|
||||
return await _api.RequestContent(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,5 @@ namespace Ombi.Api.Github
|
|||
public interface IGithubApi
|
||||
{
|
||||
Task<List<CakeThemes>> GetCakeThemes();
|
||||
Task<string> GetThemesRawContent(string url);
|
||||
}
|
||||
}
|
36
src/Ombi.Api.Gotify/GotifyApi.cs
Normal file
36
src/Ombi.Api.Gotify/GotifyApi.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Gotify
|
||||
{
|
||||
public class GotifyApi : IGotifyApi
|
||||
{
|
||||
public GotifyApi(IApi api)
|
||||
{
|
||||
_api = api;
|
||||
}
|
||||
|
||||
private readonly IApi _api;
|
||||
|
||||
public async Task PushAsync(string baseUrl, string accessToken, string subject, string body, sbyte priority)
|
||||
{
|
||||
var request = new Request("/message", baseUrl, HttpMethod.Post);
|
||||
request.AddQueryString("token", accessToken);
|
||||
|
||||
request.AddHeader("Access-Token", accessToken);
|
||||
request.ApplicationJsonContentType();
|
||||
|
||||
|
||||
var jsonBody = new
|
||||
{
|
||||
message = body,
|
||||
title = subject,
|
||||
priority = priority
|
||||
};
|
||||
|
||||
request.AddJsonBody(jsonBody);
|
||||
|
||||
await _api.Request(request);
|
||||
}
|
||||
}
|
||||
}
|
9
src/Ombi.Api.Gotify/IGotifyApi.cs
Normal file
9
src/Ombi.Api.Gotify/IGotifyApi.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Gotify
|
||||
{
|
||||
public interface IGotifyApi
|
||||
{
|
||||
Task PushAsync(string endpoint, string accessToken, string subject, string body, sbyte priority);
|
||||
}
|
||||
}
|
15
src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj
Normal file
15
src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj
Normal file
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyVersion>3.0.0.0</AssemblyVersion>
|
||||
<FileVersion>3.0.0.0</FileVersion>
|
||||
<Version></Version>
|
||||
<PackageVersion></PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -23,5 +23,6 @@ namespace Ombi.Api.Lidarr
|
|||
Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl);
|
||||
Task<LidarrStatus> Status(string apiKey, string baseUrl);
|
||||
Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl);
|
||||
Task<AlbumResponse> AlbumInformation(string albumId, string apiKey, string baseUrl);
|
||||
}
|
||||
}
|
|
@ -105,6 +105,32 @@ namespace Ombi.Api.Lidarr
|
|||
return Api.Request<List<AlbumResponse>>(request);
|
||||
}
|
||||
|
||||
public async Task<AlbumResponse> AlbumInformation(string albumId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("foreignAlbumId", albumId);
|
||||
AddHeaders(request, apiKey);
|
||||
var albums = await Api.Request<List<AlbumResponse>>(request);
|
||||
return albums.Where(x => x.foreignAlbumId.Equals(albumId, StringComparison.InvariantCultureIgnoreCase))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// THIS ONLY SUPPORTS ALBUMS THAT THE ARTIST IS IN LIDARR
|
||||
/// </summary>
|
||||
/// <param name="albumId"></param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public Task<List<LidarrTrack>> GetTracksForAlbum(int albumId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("albumId", albumId.ToString());
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LidarrTrack>>(request);
|
||||
}
|
||||
|
||||
public Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post);
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumLookup
|
||||
{
|
||||
public string title { get; set; }
|
||||
public string status { get; set; }
|
||||
public string artistType { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public List<LidarrLinks> links { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string foreignAlbumId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
|
|
12
src/Ombi.Api.Lidarr/Models/LidarrLinks.cs
Normal file
12
src/Ombi.Api.Lidarr/Models/LidarrLinks.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrLinks
|
||||
{
|
||||
public string url { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/LidarrRatings.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/LidarrRatings.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrRatings
|
||||
{
|
||||
public int votes { get; set; }
|
||||
public decimal value { get; set; }
|
||||
}
|
||||
}
|
22
src/Ombi.Api.Lidarr/Models/LidarrTrack.cs
Normal file
22
src/Ombi.Api.Lidarr/Models/LidarrTrack.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrTrack
|
||||
{
|
||||
public int artistId { get; set; }
|
||||
public int trackFileId { get; set; }
|
||||
public int albumId { get; set; }
|
||||
public bool _explicit { get; set; }
|
||||
public int absoluteTrackNumber { get; set; }
|
||||
public string trackNumber { get; set; }
|
||||
public string title { get; set; }
|
||||
public int duration { get; set; }
|
||||
public int mediumNumber { get; set; }
|
||||
public bool hasFile { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Ombi.Api.Notifications
|
|||
{
|
||||
return null;
|
||||
}
|
||||
var id = await _appConfig.Get(ConfigurationTypes.Notification);
|
||||
var id = await _appConfig.GetAsync(ConfigurationTypes.Notification);
|
||||
var request = new Request(string.Empty, ApiUrl, HttpMethod.Post);
|
||||
|
||||
var body = new OneSignalNotificationBody
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
13
src/Ombi.Api.Sonarr/ISonarrV3Api.cs
Normal file
13
src/Ombi.Api.Sonarr/ISonarrV3Api.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using System.Net.Http;
|
||||
using Ombi.Api.Sonarr.Models.V3;
|
||||
|
||||
namespace Ombi.Api.Sonarr
|
||||
{
|
||||
public interface ISonarrV3Api : ISonarrApi
|
||||
{
|
||||
Task<IEnumerable<LanguageProfiles>> LanguageProfiles(string apiKey, string baseUrl);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,9 @@ namespace Ombi.Api.Sonarr.Models
|
|||
public int id { get; set; }
|
||||
public List<SonarrImage> images { get; set; }
|
||||
|
||||
// V3 Property
|
||||
public int languageProfileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is for us
|
||||
/// </summary>
|
||||
|
|
|
@ -44,6 +44,7 @@ namespace Ombi.Api.Sonarr.Models
|
|||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int id { get; set; }
|
||||
public DateTime nextAiring { get; set; }
|
||||
}
|
||||
|
|
30
src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs
Normal file
30
src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
namespace Ombi.Api.Sonarr.Models.V3
|
||||
{
|
||||
public class LanguageProfiles
|
||||
{
|
||||
public string name { get; set; }
|
||||
public bool upgradeAllowed { get; set; }
|
||||
public Cutoff cutoff { get; set; }
|
||||
public Languages[] languages { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class Cutoff
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class Languages
|
||||
{
|
||||
public Language languages { get; set; }
|
||||
public bool allowed { get; set; }
|
||||
}
|
||||
|
||||
public class Language
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -16,18 +16,19 @@ namespace Ombi.Api.Sonarr
|
|||
Api = api;
|
||||
}
|
||||
|
||||
private IApi Api { get; }
|
||||
protected IApi Api { get; }
|
||||
protected virtual string ApiBaseUrl => "/api/";
|
||||
|
||||
public async Task<IEnumerable<SonarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/profile", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}profile", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
return await Api.Request<List<SonarrProfile>>(request);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}rootfolder", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
return await Api.Request<List<SonarrRootFolder>>(request);
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SonarrSeries>> GetSeries(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/series", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}series", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
var results = await Api.Request<List<SonarrSeries>>(request);
|
||||
|
||||
|
@ -63,7 +64,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<SonarrSeries> GetSeriesById(int id, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/series/{id}", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}series/{id}", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
var result = await Api.Request<SonarrSeries>(request);
|
||||
if (result?.seasons?.Length > 0)
|
||||
|
@ -82,7 +83,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<SonarrSeries> UpdateSeries(SonarrSeries updated, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/series/", baseUrl, HttpMethod.Put);
|
||||
var request = new Request($"{ApiBaseUrl}series/", baseUrl, HttpMethod.Put);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(updated);
|
||||
return await Api.Request<SonarrSeries>(request);
|
||||
|
@ -94,7 +95,7 @@ namespace Ombi.Api.Sonarr
|
|||
{
|
||||
return new NewSeries { ErrorMessages = new List<string> { seriesToAdd.Validate() } };
|
||||
}
|
||||
var request = new Request("/api/series/", baseUrl, HttpMethod.Post);
|
||||
var request = new Request($"{ApiBaseUrl}series/", baseUrl, HttpMethod.Post);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(seriesToAdd);
|
||||
|
@ -120,7 +121,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<Episode>> GetEpisodes(int seriesId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
return await Api.Request<List<Episode>>(request);
|
||||
}
|
||||
|
@ -134,14 +135,14 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<Episode> GetEpisodeById(int episodeId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/Episode/{episodeId}", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}Episode/{episodeId}", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
return await Api.Request<Episode>(request);
|
||||
}
|
||||
|
||||
public async Task<EpisodeUpdateResult> UpdateEpisode(Episode episodeToUpdate, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/Episode/", baseUrl, HttpMethod.Put);
|
||||
var request = new Request($"{ApiBaseUrl}Episode/", baseUrl, HttpMethod.Put);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(episodeToUpdate);
|
||||
return await Api.Request<EpisodeUpdateResult>(request);
|
||||
|
@ -189,7 +190,7 @@ namespace Ombi.Api.Sonarr
|
|||
|
||||
private async Task<CommandResult> Command(string apiKey, string baseUrl, object body)
|
||||
{
|
||||
var request = new Request($"/api/Command/", baseUrl, HttpMethod.Post);
|
||||
var request = new Request($"{ApiBaseUrl}Command/", baseUrl, HttpMethod.Post);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(body);
|
||||
return await Api.Request<CommandResult>(request);
|
||||
|
@ -197,7 +198,7 @@ namespace Ombi.Api.Sonarr
|
|||
|
||||
public async Task<SystemStatus> SystemStatus(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/system/status", baseUrl, HttpMethod.Get);
|
||||
var request = new Request($"{ApiBaseUrl}system/status", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<SystemStatus>(request);
|
||||
|
@ -217,7 +218,7 @@ namespace Ombi.Api.Sonarr
|
|||
ignoreEpisodesWithoutFiles = false,
|
||||
}
|
||||
};
|
||||
var request = new Request("/api/seasonpass", baseUrl, HttpMethod.Post);
|
||||
var request = new Request($"{ApiBaseUrl}seasonpass", baseUrl, HttpMethod.Post);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(seasonPass);
|
||||
|
||||
|
|
25
src/Ombi.Api.Sonarr/SonarrV3Api.cs
Normal file
25
src/Ombi.Api.Sonarr/SonarrV3Api.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Sonarr.Models.V3;
|
||||
|
||||
namespace Ombi.Api.Sonarr
|
||||
{
|
||||
public class SonarrV3Api : SonarrApi, ISonarrV3Api
|
||||
{
|
||||
public SonarrV3Api(IApi api) : base(api)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override string ApiBaseUrl => "/api/v3/";
|
||||
|
||||
public async Task<IEnumerable<LanguageProfiles>> LanguageProfiles(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiBaseUrl}languageprofile", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<List<LanguageProfiles>>(request);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
await LogError(request, httpResponseMessage);
|
||||
}
|
||||
|
||||
if (request.Retry)
|
||||
|
@ -105,7 +105,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
await LogError(request, httpResponseMessage);
|
||||
}
|
||||
}
|
||||
// do something with the response
|
||||
|
@ -126,7 +126,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!request.IgnoreErrors)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
await LogError(request, httpResponseMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,10 +149,15 @@ namespace Ombi.Api
|
|||
}
|
||||
}
|
||||
|
||||
private void LogError(Request request, HttpResponseMessage httpResponseMessage)
|
||||
private async Task LogError(Request request, HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
Logger.LogError(LoggingEvents.Api,
|
||||
$"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}, RequestUri: {request.FullUri}");
|
||||
if (Logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
var content = await httpResponseMessage.Content.ReadAsStringAsync();
|
||||
Logger.LogDebug(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="Polly" Version="6.1.0" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
|
73
src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
Normal file
73
src/Ombi.Core.Tests/Engine/VoteEngineTests.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Tests.Engine
|
||||
{
|
||||
[TestFixture]
|
||||
public class VoteEngineTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
F = new Fixture();
|
||||
VoteRepository = new Mock<IRepository<Votes>>();
|
||||
VoteSettings = new Mock<ISettingsService<VoteSettings>>();
|
||||
MusicRequestEngine = new Mock<IMusicRequestEngine>();
|
||||
TvRequestEngine = new Mock<ITvRequestEngine>();
|
||||
MovieRequestEngine = new Mock<IMovieRequestEngine>();
|
||||
MovieRequestEngine = new Mock<IMovieRequestEngine>();
|
||||
User = new Mock<IPrincipal>();
|
||||
UserManager = new Mock<OmbiUserManager>();
|
||||
UserManager.Setup(x => x.Users)
|
||||
.Returns(new EnumerableQuery<OmbiUser>(new List<OmbiUser> {new OmbiUser {Id = "abc"}}));
|
||||
Rule = new Mock<IRuleEvaluator>();
|
||||
Engine = new VoteEngine(VoteRepository.Object, User.Object, UserManager.Object, Rule.Object, VoteSettings.Object, MusicRequestEngine.Object,
|
||||
TvRequestEngine.Object, MovieRequestEngine.Object);
|
||||
}
|
||||
|
||||
public Fixture F { get; set; }
|
||||
public VoteEngine Engine { get; set; }
|
||||
public Mock<IPrincipal> User { get; set; }
|
||||
public Mock<OmbiUserManager> UserManager { get; set; }
|
||||
public Mock<IRuleEvaluator> Rule { get; set; }
|
||||
public Mock<IRepository<Votes>> VoteRepository { get; set; }
|
||||
public Mock<ISettingsService<VoteSettings>> VoteSettings { get; set; }
|
||||
public Mock<IMusicRequestEngine> MusicRequestEngine { get; set; }
|
||||
public Mock<ITvRequestEngine> TvRequestEngine { get; set; }
|
||||
public Mock<IMovieRequestEngine> MovieRequestEngine { get; set; }
|
||||
|
||||
[Test]
|
||||
[Ignore("Need to mock the user manager")]
|
||||
public async Task New_Upvote()
|
||||
{
|
||||
VoteSettings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new VoteSettings());
|
||||
var votes = F.CreateMany<Votes>().ToList();
|
||||
votes.Add(new Votes
|
||||
{
|
||||
RequestId = 1,
|
||||
RequestType = RequestType.Movie,
|
||||
UserId = "abc"
|
||||
});
|
||||
VoteRepository.Setup(x => x.GetAll()).Returns(new EnumerableQuery<Votes>(votes));
|
||||
var result = await Engine.UpVote(1, RequestType.Movie);
|
||||
|
||||
Assert.That(result.Result, Is.True);
|
||||
VoteRepository.Verify(x => x.Add(It.Is<Votes>(c => c.UserId == "abc" && c.VoteType == VoteType.Upvote)), Times.Once);
|
||||
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Once);
|
||||
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.9.0" />
|
||||
<PackageReference Include="AutoFixture" Version="4.5.0" />
|
||||
<PackageReference Include="Moq" Version="4.10.0" />
|
||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"></packagereference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -4,6 +4,7 @@ using Moq;
|
|||
using Ombi.Core.Rule.Rules.Request;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Core.Tests.Rule.Request
|
||||
|
@ -16,7 +17,7 @@ namespace Ombi.Core.Tests.Rule.Request
|
|||
{
|
||||
|
||||
PrincipalMock = new Mock<IPrincipal>();
|
||||
Rule = new AutoApproveRule(PrincipalMock.Object);
|
||||
Rule = new AutoApproveRule(PrincipalMock.Object, null);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Core.Rule.Rules;
|
||||
using Ombi.Core.Rule.Rules.Request;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
|
@ -15,7 +16,7 @@ namespace Ombi.Core.Tests.Rule.Request
|
|||
{
|
||||
|
||||
PrincipalMock = new Mock<IPrincipal>();
|
||||
Rule = new CanRequestRule(PrincipalMock.Object);
|
||||
Rule = new CanRequestRule(PrincipalMock.Object, null);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,13 +18,13 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
ContextMock = new Mock<IRepository<CouchPotatoCache>>();
|
||||
ContextMock = new Mock<IExternalRepository<CouchPotatoCache>>();
|
||||
Rule = new CouchPotatoCacheRule(ContextMock.Object);
|
||||
|
||||
}
|
||||
|
||||
private CouchPotatoCacheRule Rule { get; set; }
|
||||
private Mock<IRepository<CouchPotatoCache>> ContextMock { get; set; }
|
||||
private Mock<IExternalRepository<CouchPotatoCache>> ContextMock { get; set; }
|
||||
|
||||
[Test]
|
||||
public async Task Should_ReturnApproved_WhenMovieIsInCouchPotato()
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
public void Setup()
|
||||
{
|
||||
ContextMock = new Mock<IEmbyContentRepository>();
|
||||
Rule = new EmbyAvailabilityRule(ContextMock.Object);
|
||||
Rule = new EmbyAvailabilityRule(ContextMock.Object, null);
|
||||
}
|
||||
|
||||
private EmbyAvailabilityRule Rule { get; set; }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Core.Models.Search;
|
||||
|
@ -14,7 +15,7 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
public void Setup()
|
||||
{
|
||||
ContextMock = new Mock<IPlexContentRepository>();
|
||||
Rule = new PlexAvailabilityRule(ContextMock.Object);
|
||||
Rule = new PlexAvailabilityRule(ContextMock.Object, new Mock<ILogger<PlexAvailabilityRule>>().Object);
|
||||
}
|
||||
|
||||
private PlexAvailabilityRule Rule { get; set; }
|
||||
|
|
|
@ -15,13 +15,13 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
ContextMock = new Mock<IRepository<RadarrCache>>();
|
||||
ContextMock = new Mock<IExternalRepository<RadarrCache>>();
|
||||
Rule = new RadarrCacheRule(ContextMock.Object);
|
||||
|
||||
}
|
||||
|
||||
private RadarrCacheRule Rule { get; set; }
|
||||
private Mock<IRepository<RadarrCache>> ContextMock { get; set; }
|
||||
private Mock<IExternalRepository<RadarrCache>> ContextMock { get; set; }
|
||||
|
||||
[Test]
|
||||
public async Task Should_ReturnApproved_WhenMovieIsInRadarr()
|
||||
|
|
|
@ -29,6 +29,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.Emby;
|
||||
|
@ -101,6 +102,22 @@ namespace Ombi.Core.Authentication
|
|||
return true;
|
||||
}
|
||||
|
||||
public async Task<OmbiUser> GetOmbiUserFromPlexToken(string plexToken)
|
||||
{
|
||||
var plexAccount = await _plexApi.GetAccount(plexToken);
|
||||
|
||||
// Check for a ombi user
|
||||
if (plexAccount?.user != null)
|
||||
{
|
||||
var potentialOmbiUser = await Users.FirstOrDefaultAsync(x =>
|
||||
x.ProviderUserId == plexAccount.user.id);
|
||||
return potentialOmbiUser;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sign the user into plex and make sure we can get the authentication token.
|
||||
/// <remarks>We do not check if the user is in the owners "friends" since they must have a local user account to get this far</remarks>
|
||||
|
|
|
@ -157,6 +157,24 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
private string defaultLangCode;
|
||||
protected async Task<string> DefaultLanguageCode(string currentCode)
|
||||
{
|
||||
if (currentCode.HasValue())
|
||||
{
|
||||
return currentCode;
|
||||
}
|
||||
|
||||
var s = await GetOmbiSettings();
|
||||
return s.DefaultLanguageCode;
|
||||
}
|
||||
|
||||
private OmbiSettings ombiSettings;
|
||||
protected async Task<OmbiSettings> GetOmbiSettings()
|
||||
{
|
||||
return ombiSettings ?? (ombiSettings = await OmbiSettings.GetSettingsAsync());
|
||||
}
|
||||
|
||||
public class HideResult
|
||||
{
|
||||
public bool Hide { get; set; }
|
||||
|
|
106
src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs
Normal file
106
src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Config;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine.Demo
|
||||
{
|
||||
public class DemoMovieSearchEngine : MovieSearchEngine, IDemoMovieSearchEngine
|
||||
{
|
||||
public DemoMovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
||||
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s,
|
||||
IRepository<RequestSubscription> sub, IOptions<DemoLists> lists)
|
||||
: base(identity, service, movApi, mapper, logger, r, um, mem, s, sub)
|
||||
{
|
||||
_demoLists = lists.Value;
|
||||
}
|
||||
|
||||
private readonly DemoLists _demoLists;
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
|
||||
{
|
||||
var result = await MovieApi.SearchMovie(search, null, "en");
|
||||
|
||||
for (var i = 0; i < result.Count; i++)
|
||||
{
|
||||
if (!_demoLists.Movies.Contains(result[i].Id))
|
||||
{
|
||||
result.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
if(result.Count > 0)
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
||||
{
|
||||
var rand = new Random();
|
||||
var responses = new List<SearchMovieViewModel>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var item = rand.Next(_demoLists.Movies.Length);
|
||||
var movie = _demoLists.Movies[item];
|
||||
if (responses.Any(x => x.Id == movie))
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
var movieResult = await MovieApi.GetMovieInformationWithExtraInfo(movie);
|
||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieResult);
|
||||
|
||||
responses.Add(await ProcessSingleMovie(viewMovie));
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
||||
{
|
||||
return await NowPlayingMovies();
|
||||
}
|
||||
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
||||
{
|
||||
return await NowPlayingMovies();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
||||
{
|
||||
|
||||
return await NowPlayingMovies();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDemoMovieSearchEngine
|
||||
{
|
||||
Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> Search(string search);
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
||||
|
||||
}
|
||||
}
|
96
src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs
Normal file
96
src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using AutoMapper;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.Trakt;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Config;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Core.Engine.Demo
|
||||
{
|
||||
public class DemoTvSearchEngine : TvSearchEngine, IDemoTvSearchEngine
|
||||
{
|
||||
|
||||
public DemoTvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
||||
ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings, IPlexContentRepository repo,
|
||||
IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
|
||||
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists)
|
||||
: base(identity, service, tvMaze, mapper, plexSettings, embySettings, repo, embyRepo, trakt, r, um, memCache, s, sub)
|
||||
{
|
||||
_demoLists = lists.Value;
|
||||
}
|
||||
|
||||
private readonly DemoLists _demoLists;
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string search)
|
||||
{
|
||||
var searchResult = await TvMazeApi.Search(search);
|
||||
|
||||
for (var i = 0; i < searchResult.Count; i++)
|
||||
{
|
||||
if (!_demoLists.TvShows.Contains(searchResult[i].show?.externals?.thetvdb ?? 0))
|
||||
{
|
||||
searchResult.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (searchResult != null)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in searchResult)
|
||||
{
|
||||
if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> NowPlayingMovies()
|
||||
{
|
||||
var rand = new Random();
|
||||
var responses = new List<SearchTvShowViewModel>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var item = rand.Next(_demoLists.TvShows.Length);
|
||||
var tv = _demoLists.TvShows[item];
|
||||
if (responses.Any(x => x.Id == tv))
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
var movieResult = await TvMazeApi.ShowLookup(tv);
|
||||
responses.Add(ProcessResult(movieResult));
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public interface IDemoTvSearchEngine
|
||||
{
|
||||
Task<IEnumerable<SearchTvShowViewModel>> Search(string search);
|
||||
Task<IEnumerable<SearchTvShowViewModel>> NowPlayingMovies();
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
Task<RequestEngineResult>ApproveAlbum(AlbumRequest request);
|
||||
Task<RequestEngineResult> ApproveAlbumById(int requestId);
|
||||
Task<RequestEngineResult> DenyAlbumById(int modelId);
|
||||
Task<RequestEngineResult> DenyAlbumById(int modelId, string reason);
|
||||
Task<IEnumerable<AlbumRequest>> GetRequests();
|
||||
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, OrderFilterModel orderFilter);
|
||||
Task<int> GetTotal();
|
||||
|
|
19
src/Ombi.Core/Engine/IVoteEngine.cs
Normal file
19
src/Ombi.Core/Engine/IVoteEngine.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public interface IVoteEngine
|
||||
{
|
||||
Task<VoteEngineResult> DownVote(int requestId, RequestType requestType);
|
||||
Task<Votes> GetVoteForUser(int requestId, string userId);
|
||||
IQueryable<Votes> GetVotes(int requestId, RequestType requestType);
|
||||
Task RemoveCurrentVote(Votes currentVote);
|
||||
Task<VoteEngineResult> UpVote(int requestId, RequestType requestType);
|
||||
Task<List<VoteViewModel>> GetMovieViewModel();
|
||||
}
|
||||
}
|
|
@ -7,11 +7,8 @@ using Ombi.Core.Models.Search;
|
|||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Core.Engine.Interfaces
|
||||
{
|
||||
|
|
|
@ -10,14 +10,15 @@ namespace Ombi.Core
|
|||
|
||||
Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> Search(string search);
|
||||
Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string languageCode);
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
||||
|
||||
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
|
||||
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = null);
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId);
|
||||
Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId, string langCode);
|
||||
Task<IEnumerable<SearchMovieViewModel>> SearchActor(string search, string langaugeCode);
|
||||
}
|
||||
}
|
|
@ -12,10 +12,11 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search);
|
||||
|
||||
Task RemoveMovieRequest(int requestId);
|
||||
Task RemoveAllMovieRequests();
|
||||
|
||||
Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
|
||||
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
||||
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
||||
Task<RequestEngineResult> DenyMovieById(int modelId);
|
||||
Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason);
|
||||
}
|
||||
}
|
|
@ -12,5 +12,6 @@ namespace Ombi.Core.Engine
|
|||
Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId);
|
||||
Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search);
|
||||
Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search);
|
||||
Task<SearchAlbumViewModel> GetAlbumInformation(string foreignAlbumId);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task RemoveTvRequest(int requestId);
|
||||
Task<TvRequests> GetTvRequest(int requestId);
|
||||
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
|
||||
Task<RequestEngineResult> DenyChildRequest(int requestId);
|
||||
Task<RequestEngineResult> DenyChildRequest(int requestId, string reason);
|
||||
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
||||
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
||||
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace Ombi.Core.Engine
|
|||
/// <returns></returns>
|
||||
public async Task<RequestEngineResult> RequestMovie(MovieRequestViewModel model)
|
||||
{
|
||||
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId);
|
||||
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(model.TheMovieDbId, model.LanguageCode);
|
||||
if (movieInfo == null || movieInfo.Id == 0)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
|
@ -82,7 +82,9 @@ namespace Ombi.Core.Engine
|
|||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
RequestedUserId = userDetails.Id,
|
||||
Background = movieInfo.BackdropPath
|
||||
Background = movieInfo.BackdropPath,
|
||||
LangCode = model.LanguageCode,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
};
|
||||
|
||||
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
|
@ -305,7 +307,7 @@ namespace Ombi.Core.Engine
|
|||
return await ApproveMovie(request);
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> DenyMovieById(int modelId)
|
||||
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason)
|
||||
{
|
||||
var request = await MovieRepository.Find(modelId);
|
||||
if (request == null)
|
||||
|
@ -317,12 +319,14 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
request.Denied = true;
|
||||
request.DeniedReason = denyReason;
|
||||
// We are denying a request
|
||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||
await MovieRepository.Update(request);
|
||||
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = true,
|
||||
Message = "Request successfully deleted",
|
||||
};
|
||||
}
|
||||
|
@ -416,6 +420,12 @@ namespace Ombi.Core.Engine
|
|||
await MovieRepository.Delete(request);
|
||||
}
|
||||
|
||||
public async Task RemoveAllMovieRequests()
|
||||
{
|
||||
var request = MovieRepository.GetAll();
|
||||
await MovieRepository.DeleteRange(request);
|
||||
}
|
||||
|
||||
public async Task<bool> UserHasRequest(string userId)
|
||||
{
|
||||
return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId);
|
||||
|
@ -483,7 +493,7 @@ namespace Ombi.Core.Engine
|
|||
RequestType = RequestType.Movie,
|
||||
});
|
||||
|
||||
return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"};
|
||||
return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!", RequestId = model.Id};
|
||||
}
|
||||
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -32,18 +31,21 @@ namespace Ombi.Core.Engine
|
|||
Logger = logger;
|
||||
}
|
||||
|
||||
private IMovieDbApi MovieApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
protected IMovieDbApi MovieApi { get; }
|
||||
protected IMapper Mapper { get; }
|
||||
private ILogger<MovieSearchEngine> Logger { get; }
|
||||
|
||||
protected const int MovieLimit = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Lookups the imdb information.
|
||||
/// </summary>
|
||||
/// <param name="theMovieDbId">The movie database identifier.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId)
|
||||
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId, string langCode = null)
|
||||
{
|
||||
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId);
|
||||
langCode = await DefaultLanguageCode(langCode);
|
||||
var movieInfo = await MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode);
|
||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
|
||||
|
||||
return await ProcessSingleMovie(viewMovie, true);
|
||||
|
@ -52,31 +54,58 @@ namespace Ombi.Core.Engine
|
|||
/// <summary>
|
||||
/// Searches the specified movie.
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year, string langaugeCode)
|
||||
{
|
||||
var result = await MovieApi.SearchMovie(search);
|
||||
langaugeCode = await DefaultLanguageCode(langaugeCode);
|
||||
var result = await MovieApi.SearchMovie(search, year, langaugeCode);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> SearchActor(string search, string langaugeCode)
|
||||
{
|
||||
langaugeCode = await DefaultLanguageCode(langaugeCode);
|
||||
var people = await MovieApi.SearchByActor(search, langaugeCode);
|
||||
var person = people?.results?.Count > 0 ? people.results.FirstOrDefault() : null;
|
||||
|
||||
var resultSet = new List<SearchMovieViewModel>();
|
||||
if (person == null)
|
||||
{
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
// Get this person movie credits
|
||||
var credits = await MovieApi.GetActorMovieCredits(person.id, langaugeCode);
|
||||
// Grab results from both cast and crew, prefer items in cast. we can handle directors like this.
|
||||
var movieResults = (from role in credits.cast select new { Id = role.id, Title = role.title, ReleaseDate = role.release_date }).ToList();
|
||||
movieResults.AddRange((from job in credits.crew select new { Id = job.id, Title = job.title, ReleaseDate = job.release_date }).ToList());
|
||||
|
||||
movieResults = movieResults.Take(10).ToList();
|
||||
foreach (var movieResult in movieResults)
|
||||
{
|
||||
resultSet.Add(await LookupImdbInformation(movieResult.Id, langaugeCode));
|
||||
}
|
||||
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get similar movies to the id passed in
|
||||
/// </summary>
|
||||
/// <param name="theMovieDbId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId)
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> SimilarMovies(int theMovieDbId, string langCode)
|
||||
{
|
||||
var result = await MovieApi.SimilarMovies(theMovieDbId);
|
||||
langCode = await DefaultLanguageCode(langCode);
|
||||
var result = await MovieApi.SimilarMovies(theMovieDbId, langCode);
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -87,10 +116,15 @@ namespace Ombi.Core.Engine
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
|
||||
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () =>
|
||||
{
|
||||
var langCode = await DefaultLanguageCode(null);
|
||||
return await MovieApi.PopularMovies(langCode);
|
||||
}, DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -101,10 +135,14 @@ namespace Ombi.Core.Engine
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () =>
|
||||
{
|
||||
var langCode = await DefaultLanguageCode(null);
|
||||
return await MovieApi.TopRated(langCode);
|
||||
}, DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -115,11 +153,15 @@ namespace Ombi.Core.Engine
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () => await MovieApi.Upcoming(), DateTime.Now.AddHours(12));
|
||||
var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () =>
|
||||
{
|
||||
var langCode = await DefaultLanguageCode(null);
|
||||
return await MovieApi.Upcoming(langCode);
|
||||
}, DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -130,15 +172,19 @@ namespace Ombi.Core.Engine
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
|
||||
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () =>
|
||||
{
|
||||
var langCode = await DefaultLanguageCode(null);
|
||||
return await MovieApi.NowPlaying(langCode);
|
||||
}, DateTime.Now.AddHours(12));
|
||||
if (result != null)
|
||||
{
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||
IEnumerable<MovieSearchResult> movies)
|
||||
{
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
|
@ -149,16 +195,17 @@ namespace Ombi.Core.Engine
|
|||
return viewMovies;
|
||||
}
|
||||
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
|
||||
protected async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
|
||||
{
|
||||
if (lookupExtraInfo)
|
||||
if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty())
|
||||
{
|
||||
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
|
||||
viewMovie.Id = showInfo.Id; // TheMovieDbId
|
||||
viewMovie.ImdbId = showInfo.ImdbId;
|
||||
}
|
||||
|
||||
var usDates = viewMovie.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
viewMovie.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate;
|
||||
}
|
||||
|
||||
viewMovie.TheMovieDbId = viewMovie.Id.ToString();
|
||||
|
||||
|
@ -174,9 +221,13 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
// Check if this user requested it
|
||||
var user = await GetUser();
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var request = await RequestService.MovieRequestService.GetAll()
|
||||
.AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id);
|
||||
if (request)
|
||||
if (request || viewModel.Available)
|
||||
{
|
||||
viewModel.ShowSubscribe = false;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
|
|||
Title = album.title,
|
||||
Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
|
||||
Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url,
|
||||
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty
|
||||
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
};
|
||||
if (requestModel.Cover.IsNullOrEmpty())
|
||||
{
|
||||
|
@ -299,7 +300,7 @@ namespace Ombi.Core.Engine
|
|||
return await ApproveAlbum(request);
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> DenyAlbumById(int modelId)
|
||||
public async Task<RequestEngineResult> DenyAlbumById(int modelId, string reason)
|
||||
{
|
||||
var request = await MusicRepository.Find(modelId);
|
||||
if (request == null)
|
||||
|
@ -311,6 +312,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
request.Denied = true;
|
||||
request.DeniedReason = reason;
|
||||
// We are denying a request
|
||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||
await MusicRepository.Update(request);
|
||||
|
@ -495,7 +497,7 @@ namespace Ombi.Core.Engine
|
|||
RequestType = RequestType.Album,
|
||||
});
|
||||
|
||||
return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!" };
|
||||
return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!", RequestId = model.Id };
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -60,6 +60,18 @@ namespace Ombi.Core.Engine
|
|||
return vm;
|
||||
}
|
||||
|
||||
public async Task<SearchAlbumViewModel> GetAlbumInformation(string foreignAlbumId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.AlbumInformation(foreignAlbumId, settings.ApiKey, settings.FullUri);
|
||||
|
||||
|
||||
var vm = await MapIntoAlbumVm(result, settings);
|
||||
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the specified artist
|
||||
/// </summary>
|
||||
|
@ -143,6 +155,48 @@ namespace Ombi.Core.Engine
|
|||
return vm;
|
||||
}
|
||||
|
||||
|
||||
// TODO
|
||||
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumResponse a, LidarrSettings settings)
|
||||
{
|
||||
var vm = new SearchAlbumViewModel
|
||||
{
|
||||
ForeignAlbumId = a.foreignAlbumId,
|
||||
Monitored = a.monitored,
|
||||
Rating = a.ratings?.value ?? 0m,
|
||||
ReleaseDate = a.releaseDate,
|
||||
Title = a.title,
|
||||
Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url?.Replace("http", "https"),
|
||||
Genres = a.genres
|
||||
};
|
||||
if (a.artistId > 0)
|
||||
{
|
||||
//TODO THEY HAVE FIXED THIS IN DEV
|
||||
// The JSON is different for some stupid reason
|
||||
// Need to lookup the artist now and all the images -.-"
|
||||
var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri);
|
||||
vm.ArtistName = artist.artistName;
|
||||
vm.ForeignArtistId = artist.foreignArtistId;
|
||||
}
|
||||
else
|
||||
{
|
||||
//vm.ForeignArtistId = a.artistId?.foreignArtistId;
|
||||
//vm.ArtistName = a.artist?.artistName;
|
||||
}
|
||||
|
||||
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.Replace("http", "https");
|
||||
if (vm.Cover.IsNullOrEmpty())
|
||||
{
|
||||
//vm.Cover = a.remoteCover;
|
||||
}
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
|
||||
|
||||
await RunSearchRules(vm);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings)
|
||||
{
|
||||
var vm = new SearchAlbumViewModel
|
||||
|
@ -152,7 +206,8 @@ namespace Ombi.Core.Engine
|
|||
Rating = a.ratings?.value ?? 0m,
|
||||
ReleaseDate = a.releaseDate,
|
||||
Title = a.title,
|
||||
Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url
|
||||
Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url?.Replace("http", "https"),
|
||||
Genres = a.genres
|
||||
};
|
||||
if (a.artistId > 0)
|
||||
{
|
||||
|
@ -169,7 +224,7 @@ namespace Ombi.Core.Engine
|
|||
vm.ArtistName = a.artist?.artistName;
|
||||
}
|
||||
|
||||
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url;
|
||||
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.Replace("http", "https");
|
||||
if (vm.Cover.IsNullOrEmpty())
|
||||
{
|
||||
vm.Cover = a.remoteCover;
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
public string Message { get; set; }
|
||||
public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
|
||||
public string ErrorMessage { get; set; }
|
||||
public int RequestId { get; set; }
|
||||
}
|
||||
}
|
|
@ -31,14 +31,13 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
|
||||
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
||||
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
|
||||
IRepository<RequestSubscription> sub) : base(user, requestService, rule, manager, cache, settings, sub)
|
||||
{
|
||||
TvApi = tvApi;
|
||||
MovieDbApi = movApi;
|
||||
NotificationHelper = helper;
|
||||
TvSender = sender;
|
||||
Audit = audit;
|
||||
_requestLog = rl;
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,6 @@ namespace Ombi.Core.Engine
|
|||
private ITvMazeApi TvApi { get; }
|
||||
private IMovieDbApi MovieDbApi { get; }
|
||||
private ITvSender TvSender { get; }
|
||||
private IAuditRepository Audit { get; }
|
||||
private readonly IRepository<RequestLog> _requestLog;
|
||||
|
||||
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
||||
|
@ -84,8 +82,6 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
await Audit.Record(AuditType.Added, AuditArea.TvRequest, $"Added Request {tvBuilder.ChildRequest.Title}", Username);
|
||||
|
||||
var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.TvDbId);
|
||||
if (existingRequest != null)
|
||||
{
|
||||
|
@ -116,6 +112,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
// Remove the ID since this is a new child
|
||||
// This was a TVDBID for the request rules to run
|
||||
tvBuilder.ChildRequest.Id = 0;
|
||||
if (!tvBuilder.ChildRequest.SeasonRequests.Any())
|
||||
{
|
||||
|
@ -350,7 +347,6 @@ namespace Ombi.Core.Engine
|
|||
|
||||
public async Task<TvRequests> UpdateTvRequest(TvRequests request)
|
||||
{
|
||||
await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username);
|
||||
var allRequests = TvRepository.Get();
|
||||
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id);
|
||||
|
||||
|
@ -384,6 +380,7 @@ namespace Ombi.Core.Engine
|
|||
foreach (var ep in s.Episodes)
|
||||
{
|
||||
ep.Approved = true;
|
||||
ep.Requested = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,7 +389,6 @@ namespace Ombi.Core.Engine
|
|||
if (request.Approved)
|
||||
{
|
||||
NotificationHelper.Notify(request, NotificationType.RequestApproved);
|
||||
await Audit.Record(AuditType.Approved, AuditArea.TvRequest, $"Approved Request {request.Title}", Username);
|
||||
// Autosend
|
||||
await TvSender.Send(request);
|
||||
}
|
||||
|
@ -402,7 +398,7 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> DenyChildRequest(int requestId)
|
||||
public async Task<RequestEngineResult> DenyChildRequest(int requestId, string reason)
|
||||
{
|
||||
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
if (request == null)
|
||||
|
@ -413,6 +409,7 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
request.Denied = true;
|
||||
request.DeniedReason = reason;
|
||||
await TvRepository.UpdateChild(request);
|
||||
NotificationHelper.Notify(request, NotificationType.RequestDeclined);
|
||||
return new RequestEngineResult
|
||||
|
@ -423,8 +420,6 @@ namespace Ombi.Core.Engine
|
|||
|
||||
public async Task<ChildRequests> UpdateChildRequest(ChildRequests request)
|
||||
{
|
||||
await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username);
|
||||
|
||||
await TvRepository.UpdateChild(request);
|
||||
return request;
|
||||
}
|
||||
|
@ -443,7 +438,6 @@ namespace Ombi.Core.Engine
|
|||
// Delete the parent
|
||||
TvRepository.Db.TvRequests.Remove(parent);
|
||||
}
|
||||
await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username);
|
||||
|
||||
await TvRepository.Db.SaveChangesAsync();
|
||||
}
|
||||
|
@ -451,7 +445,6 @@ namespace Ombi.Core.Engine
|
|||
public async Task RemoveTvRequest(int requestId)
|
||||
{
|
||||
var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username);
|
||||
await TvRepository.Delete(request);
|
||||
}
|
||||
|
||||
|
@ -604,15 +597,16 @@ namespace Ombi.Core.Engine
|
|||
var result = await TvSender.Send(model);
|
||||
if (result.Success)
|
||||
{
|
||||
return new RequestEngineResult { Result = true };
|
||||
return new RequestEngineResult { Result = true, RequestId = model.Id};
|
||||
}
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = result.Message
|
||||
ErrorMessage = result.Message,
|
||||
RequestId = model.Id
|
||||
};
|
||||
}
|
||||
|
||||
return new RequestEngineResult { Result = true };
|
||||
return new RequestEngineResult { Result = true, RequestId = model.Id };
|
||||
}
|
||||
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace Ombi.Core.Engine
|
|||
EmbyContentRepo = embyRepo;
|
||||
}
|
||||
|
||||
private ITvMazeApi TvMazeApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
protected ITvMazeApi TvMazeApi { get; }
|
||||
protected IMapper Mapper { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
private IPlexContentRepository PlexContentRepo { get; }
|
||||
|
@ -61,7 +61,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(await ProcessResult(tvMazeSearch));
|
||||
retVal.Add(ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
Url = e.url,
|
||||
Title = e.name,
|
||||
AirDate = DateTime.Parse(e.airstamp ?? DateTime.MinValue.ToString()),
|
||||
AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
|
||||
EpisodeNumber = e.number,
|
||||
|
||||
});
|
||||
|
@ -112,7 +112,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
Url = e.url,
|
||||
Title = e.name,
|
||||
AirDate = DateTime.Parse(e.airstamp ?? DateTime.MinValue.ToString()),
|
||||
AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
|
||||
EpisodeNumber = e.number,
|
||||
});
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ namespace Ombi.Core.Engine
|
|||
public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
var processed = ProcessResults(result);
|
||||
return processed;
|
||||
}
|
||||
|
||||
|
@ -131,35 +131,35 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
|
||||
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
var processed = ProcessResults(result);
|
||||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
var processed = ProcessResults(result);
|
||||
return processed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
|
||||
{
|
||||
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
|
||||
var processed = await ProcessResults(result);
|
||||
var processed = ProcessResults(result);
|
||||
return processed;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items)
|
||||
protected IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in items)
|
||||
{
|
||||
retVal.Add(await ProcessResult(tvMazeSearch));
|
||||
retVal.Add(ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult<T>(T tvMazeSearch)
|
||||
protected SearchTvShowViewModel ProcessResult<T>(T tvMazeSearch)
|
||||
{
|
||||
return Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
}
|
||||
|
|
263
src/Ombi.Core/Engine/VoteEngine.cs
Normal file
263
src/Ombi.Core/Engine/VoteEngine.cs
Normal file
|
@ -0,0 +1,263 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class VoteEngine : BaseEngine, IVoteEngine
|
||||
{
|
||||
public VoteEngine(IRepository<Votes> votes, IPrincipal user, OmbiUserManager um, IRuleEvaluator r, ISettingsService<VoteSettings> voteSettings,
|
||||
IMusicRequestEngine musicRequestEngine, ITvRequestEngine tvRequestEngine, IMovieRequestEngine movieRequestEngine) : base(user, um, r)
|
||||
{
|
||||
_voteRepository = votes;
|
||||
_voteSettings = voteSettings;
|
||||
_movieRequestEngine = movieRequestEngine;
|
||||
_musicRequestEngine = musicRequestEngine;
|
||||
_tvRequestEngine = tvRequestEngine;
|
||||
}
|
||||
|
||||
private readonly IRepository<Votes> _voteRepository;
|
||||
private readonly ISettingsService<VoteSettings> _voteSettings;
|
||||
private readonly IMusicRequestEngine _musicRequestEngine;
|
||||
private readonly ITvRequestEngine _tvRequestEngine;
|
||||
private readonly IMovieRequestEngine _movieRequestEngine;
|
||||
|
||||
public async Task<List<VoteViewModel>> GetMovieViewModel()
|
||||
{
|
||||
var vm = new List<VoteViewModel>();
|
||||
var movieRequests = await _movieRequestEngine.GetRequests();
|
||||
var tvRequestsTask = _tvRequestEngine.GetRequests();
|
||||
var musicRequestsTask = _musicRequestEngine.GetRequests();
|
||||
var user = await GetUser();
|
||||
foreach (var r in movieRequests)
|
||||
{
|
||||
if (r.Available || r.Approved || (r.Denied ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Make model
|
||||
var votes = GetVotes(r.Id, RequestType.Movie);
|
||||
var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
|
||||
var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
|
||||
var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
|
||||
|
||||
vm.Add(new VoteViewModel
|
||||
{
|
||||
Upvotes = upVotes,
|
||||
Downvotes = downVotes,
|
||||
RequestId = r.Id,
|
||||
RequestType = RequestType.Movie,
|
||||
Title = r.Title,
|
||||
Image = $"https://image.tmdb.org/t/p/w500/{r.PosterPath}",
|
||||
Background = $"https://image.tmdb.org/t/p/w1280{r.Background}",
|
||||
Description = r.Overview,
|
||||
AlreadyVoted = myVote != null,
|
||||
MyVote = myVote?.VoteType ?? VoteType.Downvote
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var r in await musicRequestsTask)
|
||||
{
|
||||
if (r.Available || r.Approved || (r.Denied ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Make model
|
||||
var votes = GetVotes(r.Id, RequestType.Album);
|
||||
var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
|
||||
var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
|
||||
var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
|
||||
vm.Add(new VoteViewModel
|
||||
{
|
||||
Upvotes = upVotes,
|
||||
Downvotes = downVotes,
|
||||
RequestId = r.Id,
|
||||
RequestType = RequestType.Album,
|
||||
Title = r.Title,
|
||||
Image = r.Cover,
|
||||
Background = r.Cover,
|
||||
Description = r.ArtistName,
|
||||
AlreadyVoted = myVote != null,
|
||||
MyVote = myVote?.VoteType ?? VoteType.Downvote
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var r in await tvRequestsTask)
|
||||
{
|
||||
|
||||
foreach (var childRequests in r.ChildRequests)
|
||||
{
|
||||
var finalsb = new StringBuilder();
|
||||
if (childRequests.Available || childRequests.Approved || (childRequests.Denied ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var votes = GetVotes(childRequests.Id, RequestType.TvShow);
|
||||
// Make model
|
||||
var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
|
||||
var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
|
||||
var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
|
||||
foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber))
|
||||
{
|
||||
var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList();
|
||||
var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber));
|
||||
finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}");
|
||||
finalsb.Append("<br />");
|
||||
}
|
||||
vm.Add(new VoteViewModel
|
||||
{
|
||||
Upvotes = upVotes,
|
||||
Downvotes = downVotes,
|
||||
RequestId = childRequests.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
Title = r.Title,
|
||||
Image = r.PosterPath,
|
||||
Background = r.Background,
|
||||
Description = finalsb.ToString(),
|
||||
AlreadyVoted = myVote != null,
|
||||
MyVote = myVote?.VoteType ?? VoteType.Downvote
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
public IQueryable<Votes> GetVotes(int requestId, RequestType requestType)
|
||||
{
|
||||
return _voteRepository.GetAll().Where(x => x.RequestType == requestType && requestId == x.RequestId);
|
||||
}
|
||||
|
||||
public Task<Votes> GetVoteForUser(int requestId, string userId)
|
||||
{
|
||||
return _voteRepository.GetAll().FirstOrDefaultAsync(x => x.RequestId == requestId && x.UserId == userId);
|
||||
}
|
||||
|
||||
public async Task<VoteEngineResult> UpVote(int requestId, RequestType requestType)
|
||||
{
|
||||
var voteSettings = await _voteSettings.GetSettingsAsync();
|
||||
if (!voteSettings.Enabled)
|
||||
{
|
||||
return new VoteEngineResult {Result = true};
|
||||
}
|
||||
// How many votes does this have?!
|
||||
var currentVotes = GetVotes(requestId, requestType);
|
||||
|
||||
var user = await GetUser();
|
||||
|
||||
// Does this user have a downvote? If so we should revert it and make it an upvote
|
||||
var currentVote = await GetVoteForUser(requestId, user.Id);
|
||||
if (currentVote != null && currentVote.VoteType == VoteType.Upvote)
|
||||
{
|
||||
return new VoteEngineResult { ErrorMessage = "You have already voted!" };
|
||||
}
|
||||
await RemoveCurrentVote(currentVote);
|
||||
await _movieRequestEngine.SubscribeToRequest(requestId, requestType);
|
||||
|
||||
await _voteRepository.Add(new Votes
|
||||
{
|
||||
Date = DateTime.UtcNow,
|
||||
RequestId = requestId,
|
||||
RequestType = requestType,
|
||||
UserId = user.Id,
|
||||
VoteType = VoteType.Upvote
|
||||
});
|
||||
|
||||
var upVotes = await currentVotes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
|
||||
var downVotes = -(await currentVotes.Where(x => x.VoteType == VoteType.Downvote).CountAsync());
|
||||
|
||||
var totalVotes = upVotes + downVotes;
|
||||
RequestEngineResult result = null;
|
||||
switch (requestType)
|
||||
{
|
||||
case RequestType.TvShow:
|
||||
if (totalVotes >= voteSettings.TvShowVoteMax)
|
||||
{
|
||||
result = await _tvRequestEngine.ApproveChildRequest(requestId);
|
||||
}
|
||||
break;
|
||||
case RequestType.Movie:
|
||||
if (totalVotes >= voteSettings.MovieVoteMax)
|
||||
{
|
||||
result = await _movieRequestEngine.ApproveMovieById(requestId);
|
||||
}
|
||||
break;
|
||||
case RequestType.Album:
|
||||
if (totalVotes >= voteSettings.MusicVoteMax)
|
||||
{
|
||||
result = await _musicRequestEngine.ApproveAlbumById(requestId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(requestType), requestType, null);
|
||||
}
|
||||
|
||||
if (result != null && !result.Result)
|
||||
{
|
||||
return new VoteEngineResult
|
||||
{
|
||||
ErrorMessage = "Voted succesfully but could not approve!"
|
||||
};
|
||||
}
|
||||
|
||||
return new VoteEngineResult
|
||||
{
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<VoteEngineResult> DownVote(int requestId, RequestType requestType)
|
||||
{
|
||||
var voteSettings = await _voteSettings.GetSettingsAsync();
|
||||
if (!voteSettings.Enabled)
|
||||
{
|
||||
return new VoteEngineResult { Result = true };
|
||||
}
|
||||
var user = await GetUser();
|
||||
var currentVote = await GetVoteForUser(requestId, user.Id);
|
||||
if (currentVote != null && currentVote.VoteType == VoteType.Downvote)
|
||||
{
|
||||
return new VoteEngineResult { ErrorMessage = "You have already voted!" };
|
||||
}
|
||||
await RemoveCurrentVote(currentVote);
|
||||
|
||||
await _movieRequestEngine.UnSubscribeRequest(requestId, requestType);
|
||||
|
||||
await _voteRepository.Add(new Votes
|
||||
{
|
||||
Date = DateTime.UtcNow,
|
||||
RequestId = requestId,
|
||||
RequestType = requestType,
|
||||
UserId = user.Id,
|
||||
VoteType = VoteType.Downvote
|
||||
});
|
||||
|
||||
return new VoteEngineResult
|
||||
{
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
public async Task RemoveCurrentVote(Votes currentVote)
|
||||
{
|
||||
if (currentVote != null)
|
||||
{
|
||||
await _voteRepository.Delete(currentVote);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ namespace Ombi.Core.Helpers
|
|||
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
|
||||
Results = await MovieDbApi.SearchTv(ShowInfo.name);
|
||||
foreach (TvSearchResult result in Results) {
|
||||
if (result.Name == ShowInfo.name)
|
||||
if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var showIds = await MovieDbApi.GetTvExternals(result.Id);
|
||||
ShowInfo.externals.imdb = showIds.imdb_id;
|
||||
|
@ -64,14 +64,16 @@ namespace Ombi.Core.Helpers
|
|||
{
|
||||
ChildRequest = new ChildRequests
|
||||
{
|
||||
Id = model.TvDbId,
|
||||
Id = model.TvDbId, // This is set to 0 after the request rules have run, the request rules needs it to identify the request
|
||||
RequestType = RequestType.TvShow,
|
||||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
RequestedUserId = userId,
|
||||
SeasonRequests = new List<SeasonRequests>(),
|
||||
Title = ShowInfo.name,
|
||||
SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.OrdinalIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
|
||||
ReleaseYear = FirstAir,
|
||||
RequestedByAlias = model.RequestedByAlias,
|
||||
SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
|
||||
};
|
||||
|
||||
return this;
|
||||
|
|
|
@ -24,10 +24,20 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public class MovieRequestViewModel
|
||||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
public string LanguageCode { get; set; } = "en";
|
||||
|
||||
/// <summary>
|
||||
/// This is only set from a HTTP Header
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
}
|
|
@ -3,5 +3,6 @@
|
|||
public class MusicAlbumRequestViewModel
|
||||
{
|
||||
public string ForeignAlbumId { get; set; }
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
|
@ -9,6 +10,8 @@ namespace Ombi.Core.Models.Requests
|
|||
public bool FirstSeason { get; set; }
|
||||
public int TvDbId { get; set; }
|
||||
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsViewModel
|
||||
|
|
|
@ -16,8 +16,13 @@ namespace Ombi.Core.Models.Search
|
|||
public string Cover { get; set; }
|
||||
public string Disk { get; set; }
|
||||
public decimal PercentOfTracks { get; set; }
|
||||
public object[] Genres { get; set; }
|
||||
public override RequestType Type => RequestType.Album;
|
||||
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;
|
||||
public bool FullyAvailable => PercentOfTracks == 100;
|
||||
|
||||
|
||||
// Below is from the INFO call NEED A SEPERATE VM FOR THIS IN V4 TODO
|
||||
// TODO ADD TRACK COUNT
|
||||
}
|
||||
}
|
23
src/Ombi.Core/Models/UI/GotifyNotificationViewModel.cs
Normal file
23
src/Ombi.Core/Models/UI/GotifyNotificationViewModel.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// The view model for the notification settings page
|
||||
/// </summary>
|
||||
/// <seealso cref="GotifyNotificationSettings" />
|
||||
public class GotifyNotificationViewModel : GotifySettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the notification templates.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The notification templates.
|
||||
/// </value>
|
||||
public List<NotificationTemplates> NotificationTemplates { get; set; }
|
||||
|
||||
}
|
||||
}
|
18
src/Ombi.Core/Models/UI/VoteViewModel.cs
Normal file
18
src/Ombi.Core/Models/UI/VoteViewModel.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
public class VoteViewModel
|
||||
{
|
||||
public int RequestId { get; set; }
|
||||
public RequestType RequestType { get; set; }
|
||||
public string Image { get; set; }
|
||||
public string Background { get; set; }
|
||||
public int Upvotes { get; set; }
|
||||
public int Downvotes { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool AlreadyVoted { get; set; }
|
||||
public VoteType MyVote { get; set; }
|
||||
}
|
||||
}
|
10
src/Ombi.Core/Models/VoteEngineResult.cs
Normal file
10
src/Ombi.Core/Models/VoteEngineResult.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ombi.Core.Models
|
||||
{
|
||||
public class VoteEngineResult
|
||||
{
|
||||
public bool Result { get; set; }
|
||||
public string Message { get; set; }
|
||||
public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
|
@ -11,25 +11,27 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.22" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.2" />
|
||||
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Trakt\Ombi.Api.Trakt.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Helpers;
|
||||
|
@ -10,28 +12,31 @@ namespace Ombi.Core.Rule.Rules.Request
|
|||
{
|
||||
public class AutoApproveRule : BaseRequestRule, IRules<BaseRequest>
|
||||
{
|
||||
public AutoApproveRule(IPrincipal principal)
|
||||
public AutoApproveRule(IPrincipal principal, OmbiUserManager um)
|
||||
{
|
||||
User = principal;
|
||||
_manager = um;
|
||||
}
|
||||
|
||||
private IPrincipal User { get; }
|
||||
private readonly OmbiUserManager _manager;
|
||||
|
||||
public Task<RuleResult> Execute(BaseRequest obj)
|
||||
public async Task<RuleResult> Execute(BaseRequest obj)
|
||||
{
|
||||
if (User.IsInRole(OmbiRoles.Admin))
|
||||
var user = await _manager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
|
||||
if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin))
|
||||
{
|
||||
obj.Approved = true;
|
||||
return Task.FromResult(Success());
|
||||
return Success();
|
||||
}
|
||||
|
||||
if (obj.RequestType == RequestType.Movie && User.IsInRole(OmbiRoles.AutoApproveMovie))
|
||||
if (obj.RequestType == RequestType.Movie && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
|
||||
obj.Approved = true;
|
||||
if (obj.RequestType == RequestType.TvShow && User.IsInRole(OmbiRoles.AutoApproveTv))
|
||||
if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
|
||||
obj.Approved = true;
|
||||
if (obj.RequestType == RequestType.Album && User.IsInRole(OmbiRoles.AutoApproveMusic))
|
||||
if (obj.RequestType == RequestType.Album && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))
|
||||
obj.Approved = true;
|
||||
return Task.FromResult(Success()); // We don't really care, we just don't set the obj to approve
|
||||
return Success(); // We don't really care, we just don't set the obj to approve
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +1,52 @@
|
|||
using Ombi.Store.Entities;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules
|
||||
namespace Ombi.Core.Rule.Rules.Request
|
||||
{
|
||||
public class CanRequestRule : BaseRequestRule, IRules<BaseRequest>
|
||||
{
|
||||
public CanRequestRule(IPrincipal principal)
|
||||
public CanRequestRule(IPrincipal principal, OmbiUserManager manager)
|
||||
{
|
||||
User = principal;
|
||||
_manager = manager;
|
||||
}
|
||||
|
||||
private IPrincipal User { get; }
|
||||
private readonly OmbiUserManager _manager;
|
||||
|
||||
public Task<RuleResult> Execute(BaseRequest obj)
|
||||
public async Task<RuleResult> Execute(BaseRequest obj)
|
||||
{
|
||||
if (User.IsInRole(OmbiRoles.Admin))
|
||||
return Task.FromResult(Success());
|
||||
var user = await _manager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
|
||||
if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin))
|
||||
return Success();
|
||||
|
||||
if (obj.RequestType == RequestType.Movie)
|
||||
{
|
||||
if (User.IsInRole(OmbiRoles.RequestMovie) || User.IsInRole(OmbiRoles.AutoApproveMovie))
|
||||
return Task.FromResult(Success());
|
||||
return Task.FromResult(Fail("You do not have permissions to Request a Movie"));
|
||||
if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMovie) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
|
||||
return Success();
|
||||
return Fail("You do not have permissions to Request a Movie");
|
||||
}
|
||||
|
||||
if (obj.RequestType == RequestType.TvShow)
|
||||
{
|
||||
if (User.IsInRole(OmbiRoles.RequestTv) || User.IsInRole(OmbiRoles.AutoApproveTv))
|
||||
return Task.FromResult(Success());
|
||||
if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestTv) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
|
||||
return Success();
|
||||
}
|
||||
|
||||
if (obj.RequestType == RequestType.Album)
|
||||
{
|
||||
if (User.IsInRole(OmbiRoles.RequestMusic) || User.IsInRole(OmbiRoles.AutoApproveMusic))
|
||||
return Task.FromResult(Success());
|
||||
if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMusic) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))
|
||||
return Success();
|
||||
}
|
||||
|
||||
return Task.FromResult(Fail("You do not have permissions to Request a TV Show"));
|
||||
return Fail("You do not have permissions to Request a TV Show");
|
||||
}
|
||||
}
|
||||
}
|
82
src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs
Normal file
82
src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Request
|
||||
{
|
||||
public class ExistingPlexRequestRule : BaseRequestRule, IRules<BaseRequest>
|
||||
{
|
||||
public ExistingPlexRequestRule(IPlexContentRepository rv)
|
||||
{
|
||||
_plexContent = rv;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plexContent;
|
||||
|
||||
/// <summary>
|
||||
/// We check if the request exists, if it does then we don't want to re-request it.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RuleResult> Execute(BaseRequest obj)
|
||||
{
|
||||
if (obj.RequestType == RequestType.TvShow)
|
||||
{
|
||||
var tvRequest = (ChildRequests) obj;
|
||||
|
||||
var tvContent = _plexContent.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Show);
|
||||
// We need to do a check on the TVDBId
|
||||
var anyTvDbMatches = await tvContent.Include(x => x.Episodes).FirstOrDefaultAsync(x => x.HasTvDb && x.TvDbId.Equals(tvRequest.Id.ToString())); // the Id on the child is the tvdbid at this point
|
||||
if (anyTvDbMatches == null)
|
||||
{
|
||||
// So we do not have a TVDB Id, that really sucks.
|
||||
// Let's try and match on the title and year of the show
|
||||
var titleAndYearMatch = await tvContent.Include(x=> x.Episodes).FirstOrDefaultAsync(x =>
|
||||
x.Title.Equals(tvRequest.Title, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& x.ReleaseYear == tvRequest.ReleaseYear.Year.ToString());
|
||||
if (titleAndYearMatch != null)
|
||||
{
|
||||
// We have a match! Suprise Motherfucker
|
||||
return CheckExistingContent(tvRequest, titleAndYearMatch);
|
||||
}
|
||||
|
||||
// We do not have this
|
||||
return Success();
|
||||
}
|
||||
// looks like we have a match on the TVDbID
|
||||
return CheckExistingContent(tvRequest, anyTvDbMatches);
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
|
||||
private RuleResult CheckExistingContent(ChildRequests child, PlexServerContent content)
|
||||
{
|
||||
foreach (var season in child.SeasonRequests)
|
||||
{
|
||||
var currentSeasonRequest =
|
||||
content.Episodes.Where(x => x.SeasonNumber == season.SeasonNumber).ToList();
|
||||
if (!currentSeasonRequest.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var e in season.Episodes)
|
||||
{
|
||||
var hasEpisode = currentSeasonRequest.Any(x => x.EpisodeNumber == e.EpisodeNumber);
|
||||
if (hasEpisode)
|
||||
{
|
||||
return Fail($"We already have episodes requested from series {child.Title}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
57
src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs
Normal file
57
src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Request
|
||||
{
|
||||
public class ExistingTvRequestRule : BaseRequestRule, IRules<BaseRequest>
|
||||
{
|
||||
public ExistingTvRequestRule(ITvRequestRepository rv)
|
||||
{
|
||||
Tv = rv;
|
||||
}
|
||||
|
||||
private ITvRequestRepository Tv { get; }
|
||||
|
||||
/// <summary>
|
||||
/// We check if the request exists, if it does then we don't want to re-request it.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RuleResult> Execute(BaseRequest obj)
|
||||
{
|
||||
if (obj.RequestType == RequestType.TvShow)
|
||||
{
|
||||
var tv = (ChildRequests) obj;
|
||||
var tvRequests = Tv.GetChild();
|
||||
var currentRequest = await tvRequests.FirstOrDefaultAsync(x => x.ParentRequest.TvDbId == tv.Id); // the Id on the child is the tvdbid at this point
|
||||
if (currentRequest == null)
|
||||
{
|
||||
return Success();
|
||||
}
|
||||
foreach (var season in tv.SeasonRequests)
|
||||
{
|
||||
var currentSeasonRequest =
|
||||
currentRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
|
||||
if (currentSeasonRequest == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var e in season.Episodes)
|
||||
{
|
||||
var hasEpisode = currentSeasonRequest.Episodes.Any(x => x.EpisodeNumber == e.EpisodeNumber);
|
||||
if (hasEpisode)
|
||||
{
|
||||
return Fail($"We already have episodes requested from series {tv.Title}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,12 +7,12 @@ namespace Ombi.Core.Rule.Rules.Request
|
|||
{
|
||||
public class SonarrCacheRequestRule : BaseRequestRule, IRules<BaseRequest>
|
||||
{
|
||||
public SonarrCacheRequestRule(IOmbiContext ctx)
|
||||
public SonarrCacheRequestRule(IExternalContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
private readonly IOmbiContext _ctx;
|
||||
private readonly IExternalContext _ctx;
|
||||
|
||||
public Task<RuleResult> Execute(BaseRequest obj)
|
||||
{
|
||||
|
|
105
src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs
Normal file
105
src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
public static class AvailabilityRuleHelper
|
||||
{
|
||||
public static void CheckForUnairedEpisodes(SearchTvShowViewModel search)
|
||||
{
|
||||
if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
||||
{
|
||||
search.FullyAvailable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var airedButNotAvailable = search.SeasonRequests.Any(x =>
|
||||
x.Episodes.Any(c => !c.Available && c.AirDate <= DateTime.Now.Date && c.AirDate != DateTime.MinValue));
|
||||
if (!airedButNotAvailable)
|
||||
{
|
||||
var unairedEpisodes = search.SeasonRequests.Any(x =>
|
||||
x.Episodes.Any(c => !c.Available && c.AirDate > DateTime.Now.Date || c.AirDate != DateTime.MinValue));
|
||||
if (unairedEpisodes)
|
||||
{
|
||||
search.FullyAvailable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<PlexEpisode> allEpisodes, EpisodeRequests episode,
|
||||
SeasonRequests season, PlexServerContent item, bool useTheMovieDb, bool useTvDb, ILogger log)
|
||||
{
|
||||
PlexEpisode epExists = null;
|
||||
try
|
||||
{
|
||||
|
||||
if (useImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ImdbId == item.ImdbId);
|
||||
}
|
||||
|
||||
if (useTheMovieDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TheMovieDbId == item.TheMovieDbId);
|
||||
}
|
||||
|
||||
if (useTvDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.LogError(e, "Exception thrown when attempting to check if something is available");
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<EmbyEpisode> allEpisodes, EpisodeRequests episode,
|
||||
SeasonRequests season, EmbyContent item, bool useTheMovieDb, bool useTvDb)
|
||||
{
|
||||
EmbyEpisode epExists = null;
|
||||
if (useImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ImdbId == item.ImdbId);
|
||||
}
|
||||
|
||||
if (useTheMovieDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TheMovieDbId == item.TheMovieDbId);
|
||||
}
|
||||
|
||||
if (useTvDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,12 +11,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class CouchPotatoCacheRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public CouchPotatoCacheRule(IRepository<CouchPotatoCache> ctx)
|
||||
public CouchPotatoCacheRule(IExternalRepository<CouchPotatoCache> ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
private readonly IRepository<CouchPotatoCache> _ctx;
|
||||
private readonly IExternalRepository<CouchPotatoCache> _ctx;
|
||||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
@ -13,25 +13,39 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class EmbyAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public EmbyAvailabilityRule(IEmbyContentRepository repo)
|
||||
public EmbyAvailabilityRule(IEmbyContentRepository repo, ISettingsService<EmbySettings> s)
|
||||
{
|
||||
EmbyContentRepository = repo;
|
||||
EmbySettings = s;
|
||||
}
|
||||
|
||||
private IEmbyContentRepository EmbyContentRepository { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
EmbyContent item = null;
|
||||
var useImdb = false;
|
||||
var useTheMovieDb = false;
|
||||
var useTvDb = false;
|
||||
|
||||
if (obj.ImdbId.HasValue())
|
||||
{
|
||||
item = await EmbyContentRepository.GetByImdbId(obj.ImdbId);
|
||||
if (item != null)
|
||||
{
|
||||
useImdb = true;
|
||||
}
|
||||
}
|
||||
if (item == null)
|
||||
{
|
||||
if (obj.TheMovieDbId.HasValue())
|
||||
{
|
||||
item = await EmbyContentRepository.GetByTheMovieDbId(obj.TheMovieDbId);
|
||||
if (item != null)
|
||||
{
|
||||
useTheMovieDb = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
|
@ -39,6 +53,10 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
if (obj.TheTvDbId.HasValue())
|
||||
{
|
||||
item = await EmbyContentRepository.GetByTvDbId(obj.TheTvDbId);
|
||||
if (item != null)
|
||||
{
|
||||
useTvDb = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +64,16 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
if (item != null)
|
||||
{
|
||||
obj.Available = true;
|
||||
obj.EmbyUrl = item.Url;
|
||||
var s = await EmbySettings.GetSettingsAsync();
|
||||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||
{
|
||||
obj.EmbyUrl = $"{server.ServerHostname}#!/itemdetails.html?id={item.EmbyId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.EmbyUrl = $"https://app.emby.media/#!/itemdetails.html?id={item.EmbyId}";
|
||||
}
|
||||
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
|
@ -59,29 +86,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
EmbyEpisode epExists = null;
|
||||
|
||||
if (item.HasImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
||||
&& e.ImdbId == item.ImdbId);
|
||||
} if (item.HasTvDb && epExists == null)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
||||
&& e.Series.TvDbId == item.TvDbId);
|
||||
} if (item.HasTheMovieDb && epExists == null)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber
|
||||
&& e.TheMovieDbId == item.TheMovieDbId);
|
||||
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
|
@ -87,11 +88,11 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
}
|
||||
}
|
||||
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Available && e.AirDate > DateTime.MinValue)))
|
||||
{
|
||||
request.FullyAvailable = true;
|
||||
}
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available)))
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available && e.AirDate > DateTime.MinValue)))
|
||||
{
|
||||
request.PartlyAvailable = true;
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class LidarrAlbumCacheRule : SpecificRule, ISpecificRule<object>
|
||||
{
|
||||
public LidarrAlbumCacheRule(IRepository<LidarrAlbumCache> db)
|
||||
public LidarrAlbumCacheRule(IExternalRepository<LidarrAlbumCache> db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private readonly IRepository<LidarrAlbumCache> _db;
|
||||
private readonly IExternalRepository<LidarrAlbumCache> _db;
|
||||
|
||||
public Task<RuleResult> Execute(object objec)
|
||||
{
|
||||
|
|
|
@ -10,12 +10,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class LidarrArtistCacheRule : SpecificRule, ISpecificRule<object>
|
||||
{
|
||||
public LidarrArtistCacheRule(IRepository<LidarrArtistCache> db)
|
||||
public LidarrArtistCacheRule(IExternalRepository<LidarrArtistCache> db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private readonly IRepository<LidarrArtistCache> _db;
|
||||
private readonly IExternalRepository<LidarrArtistCache> _db;
|
||||
|
||||
public Task<RuleResult> Execute(object objec)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Helpers;
|
||||
|
@ -11,12 +11,14 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class PlexAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public PlexAvailabilityRule(IPlexContentRepository repo)
|
||||
public PlexAvailabilityRule(IPlexContentRepository repo, ILogger<PlexAvailabilityRule> log)
|
||||
{
|
||||
PlexContentRepository = repo;
|
||||
Log = log;
|
||||
}
|
||||
|
||||
private IPlexContentRepository PlexContentRepository { get; }
|
||||
private ILogger Log { get; }
|
||||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
|
@ -73,41 +75,17 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
PlexEpisode epExists = null;
|
||||
if (useImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ImdbId == item.ImdbId.ToString());
|
||||
}
|
||||
if (useTheMovieDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TheMovieDbId == item.TheMovieDbId.ToString());
|
||||
}
|
||||
if (useTvDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TvDbId == item.TvDbId.ToString());
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb, Log);
|
||||
}
|
||||
}
|
||||
|
||||
if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
||||
{
|
||||
search.FullyAvailable = true;
|
||||
}
|
||||
AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -9,12 +9,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class RadarrCacheRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public RadarrCacheRule(IRepository<RadarrCache> db)
|
||||
public RadarrCacheRule(IExternalRepository<RadarrCache> db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private readonly IRepository<RadarrCache> _db;
|
||||
private readonly IExternalRepository<RadarrCache> _db;
|
||||
|
||||
public Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
|
|
|
@ -34,12 +34,12 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class SonarrCacheSearchRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public SonarrCacheSearchRule(IOmbiContext ctx)
|
||||
public SonarrCacheSearchRule(IExternalContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
private readonly IOmbiContext _ctx;
|
||||
private readonly IExternalContext _ctx;
|
||||
|
||||
public Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
|
|
|
@ -10,12 +10,12 @@ namespace Ombi.Core.Rule.Rules
|
|||
{
|
||||
public class SonarrCacheRule
|
||||
{
|
||||
public SonarrCacheRule(IOmbiContext ctx)
|
||||
public SonarrCacheRule(IExternalContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
private readonly IOmbiContext _ctx;
|
||||
private readonly IExternalContext _ctx;
|
||||
|
||||
public async Task<RuleResult> Execute(BaseRequest obj)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -19,7 +20,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
public MovieSender(ISettingsService<RadarrSettings> radarrSettings, IRadarrApi api, ILogger<MovieSender> log,
|
||||
ISettingsService<DogNzbSettings> dogSettings, IDogNzbApi dogApi, ISettingsService<CouchPotatoSettings> cpSettings,
|
||||
ICouchPotatoApi cpApi, IRepository<UserQualityProfiles> userProfiles)
|
||||
ICouchPotatoApi cpApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> requestQueue, INotificationHelper notify)
|
||||
{
|
||||
RadarrSettings = radarrSettings;
|
||||
RadarrApi = api;
|
||||
|
@ -29,6 +30,8 @@ namespace Ombi.Core.Senders
|
|||
CouchPotatoSettings = cpSettings;
|
||||
CouchPotatoApi = cpApi;
|
||||
_userProfiles = userProfiles;
|
||||
_requestQueuRepository = requestQueue;
|
||||
_notificationHelper = notify;
|
||||
}
|
||||
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
|
@ -39,8 +42,12 @@ namespace Ombi.Core.Senders
|
|||
private ISettingsService<CouchPotatoSettings> CouchPotatoSettings { get; }
|
||||
private ICouchPotatoApi CouchPotatoApi { get; }
|
||||
private readonly IRepository<UserQualityProfiles> _userProfiles;
|
||||
private readonly IRepository<RequestQueue> _requestQueuRepository;
|
||||
private readonly INotificationHelper _notificationHelper;
|
||||
|
||||
public async Task<SenderResult> Send(MovieRequests model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cpSettings = await CouchPotatoSettings.GetSettingsAsync();
|
||||
//var watcherSettings = await WatcherSettings.GetSettingsAsync();
|
||||
|
@ -65,12 +72,32 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
return await SendToCp(model, cpSettings, cpSettings.DefaultProfileId);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Error when sending movie to DVR app, added to the request queue");
|
||||
|
||||
//if (watcherSettings.Enabled)
|
||||
//{
|
||||
// return SendToWatcher(model, watcherSettings);
|
||||
//}
|
||||
|
||||
// Check if already in request quee
|
||||
var existingQueue = await _requestQueuRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id);
|
||||
if (existingQueue != null)
|
||||
{
|
||||
existingQueue.RetryCount++;
|
||||
existingQueue.Error = e.Message;
|
||||
await _requestQueuRepository.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await _requestQueuRepository.Add(new RequestQueue
|
||||
{
|
||||
Dts = DateTime.UtcNow,
|
||||
Error = e.Message,
|
||||
RequestId = model.Id,
|
||||
Type = RequestType.Movie,
|
||||
RetryCount = 0
|
||||
});
|
||||
_notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
}
|
||||
|
||||
return new SenderResult
|
||||
{
|
||||
|
@ -101,13 +128,17 @@ namespace Ombi.Core.Senders
|
|||
var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);
|
||||
if (profiles != null)
|
||||
{
|
||||
if (profiles.SonarrRootPathAnime > 0)
|
||||
if (profiles.RadarrRootPath > 0)
|
||||
{
|
||||
rootFolderPath = await RadarrRootPath(profiles.SonarrRootPathAnime, settings);
|
||||
var tempPath = await RadarrRootPath(profiles.RadarrRootPath, settings);
|
||||
if (tempPath.HasValue())
|
||||
{
|
||||
rootFolderPath = tempPath;
|
||||
}
|
||||
if (profiles.SonarrQualityProfileAnime > 0)
|
||||
}
|
||||
if (profiles.RadarrQualityProfile > 0)
|
||||
{
|
||||
qualityToUse = profiles.SonarrQualityProfileAnime;
|
||||
qualityToUse = profiles.RadarrQualityProfile;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +181,7 @@ namespace Ombi.Core.Senders
|
|||
// Search for it
|
||||
if (!settings.AddOnly)
|
||||
{
|
||||
await RadarrApi.MovieSearch(new[] {existingMovie.id}, settings.ApiKey, settings.FullUri);
|
||||
await RadarrApi.MovieSearch(new[] { existingMovie.id }, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
return new SenderResult { Success = true, Sent = true };
|
||||
|
@ -163,7 +194,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
var paths = await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
var selectedPath = paths.FirstOrDefault(x => x.id == overrideId);
|
||||
return selectedPath.path;
|
||||
return selectedPath?.path ?? String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +1,41 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EnsureThat;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Serilog;
|
||||
using Ombi.Store.Repository;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
public class MusicSender : IMusicSender
|
||||
{
|
||||
public MusicSender(ISettingsService<LidarrSettings> lidarr, ILidarrApi lidarrApi)
|
||||
public MusicSender(ISettingsService<LidarrSettings> lidarr, ILidarrApi lidarrApi, ILogger<MusicSender> log,
|
||||
IRepository<RequestQueue> requestQueue, INotificationHelper notify)
|
||||
{
|
||||
_lidarrSettings = lidarr;
|
||||
_lidarrApi = lidarrApi;
|
||||
_log = log;
|
||||
_requestQueueRepository = requestQueue;
|
||||
_notificationHelper = notify;
|
||||
}
|
||||
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly ILogger _log;
|
||||
private readonly IRepository<RequestQueue> _requestQueueRepository;
|
||||
private readonly INotificationHelper _notificationHelper;
|
||||
|
||||
public async Task<SenderResult> Send(AlbumRequest model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await _lidarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
|
@ -33,6 +45,33 @@ namespace Ombi.Core.Senders
|
|||
|
||||
return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(e, "Exception thrown when sending a music to DVR app, added to the request queue");
|
||||
var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id);
|
||||
if (existingQueue != null)
|
||||
{
|
||||
existingQueue.RetryCount++;
|
||||
existingQueue.Error = e.Message;
|
||||
await _requestQueueRepository.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await _requestQueueRepository.Add(new RequestQueue
|
||||
{
|
||||
Dts = DateTime.UtcNow,
|
||||
Error = e.Message,
|
||||
RequestId = model.Id,
|
||||
Type = RequestType.Album,
|
||||
RetryCount = 0
|
||||
});
|
||||
_notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new SenderResult { Success = false, Sent = false, Message = "Something went wrong!" };
|
||||
}
|
||||
|
||||
private async Task<SenderResult> SendToLidarr(AlbumRequest model, LidarrSettings settings)
|
||||
{
|
||||
|
@ -49,6 +88,11 @@ namespace Ombi.Core.Senders
|
|||
|
||||
if (artist == null || artist.id <= 0)
|
||||
{
|
||||
EnsureArg.IsNotNullOrEmpty(model.ForeignArtistId, nameof(model.ForeignArtistId));
|
||||
EnsureArg.IsNotNullOrEmpty(model.ForeignAlbumId, nameof(model.ForeignAlbumId));
|
||||
EnsureArg.IsNotNullOrEmpty(model.ArtistName, nameof(model.ArtistName));
|
||||
EnsureArg.IsNotNullOrEmpty(rootFolderPath, nameof(rootFolderPath));
|
||||
|
||||
// Create artist
|
||||
var newArtist = new ArtistAdd
|
||||
{
|
||||
|
|
|
@ -16,16 +16,18 @@ using Ombi.Settings.Settings.Models.External;
|
|||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Remotion.Linq.Parsing.Structure.IntermediateModel;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
public class TvSender : ITvSender
|
||||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
public TvSender(ISonarrApi sonarrApi, ISonarrV3Api sonarrV3Api, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<DogNzbSettings> dog, IDogNzbApi dogApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISickRageApi srApi, IRepository<UserQualityProfiles> userProfiles)
|
||||
ISickRageApi srApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> requestQueue, INotificationHelper notify)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
SonarrV3Api = sonarrV3Api;
|
||||
Logger = log;
|
||||
SonarrSettings = sonarrSettings;
|
||||
DogNzbSettings = dog;
|
||||
|
@ -33,9 +35,12 @@ namespace Ombi.Core.Senders
|
|||
SickRageSettings = srSettings;
|
||||
SickRageApi = srApi;
|
||||
UserQualityProfiles = userProfiles;
|
||||
_requestQueueRepository = requestQueue;
|
||||
_notificationHelper = notify;
|
||||
}
|
||||
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISonarrV3Api SonarrV3Api { get; }
|
||||
private IDogNzbApi DogNzbApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private ILogger<TvSender> Logger { get; }
|
||||
|
@ -43,13 +48,17 @@ namespace Ombi.Core.Senders
|
|||
private ISettingsService<DogNzbSettings> DogNzbSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
private IRepository<UserQualityProfiles> UserQualityProfiles { get; }
|
||||
private readonly IRepository<RequestQueue> _requestQueueRepository;
|
||||
private readonly INotificationHelper _notificationHelper;
|
||||
|
||||
public async Task<SenderResult> Send(ChildRequests model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sonarr = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarr.Enabled)
|
||||
{
|
||||
var result = await SendToSonarr(model);
|
||||
var result = await SendToSonarr(model, sonarr);
|
||||
if (result != null)
|
||||
{
|
||||
return new SenderResult
|
||||
|
@ -98,6 +107,37 @@ namespace Ombi.Core.Senders
|
|||
Success = true
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Exception thrown when sending a movie to DVR app, added to the request queue");
|
||||
// Check if already in request queue
|
||||
var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id);
|
||||
if (existingQueue != null)
|
||||
{
|
||||
existingQueue.RetryCount++;
|
||||
existingQueue.Error = e.Message;
|
||||
await _requestQueueRepository.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await _requestQueueRepository.Add(new RequestQueue
|
||||
{
|
||||
Dts = DateTime.UtcNow,
|
||||
Error = e.Message,
|
||||
RequestId = model.Id,
|
||||
Type = RequestType.TvShow,
|
||||
RetryCount = 0
|
||||
});
|
||||
_notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue);
|
||||
}
|
||||
}
|
||||
|
||||
return new SenderResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Something went wrong!"
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DogNzbAddResult> SendToDogNzb(ChildRequests model, DogNzbSettings settings)
|
||||
{
|
||||
|
@ -111,13 +151,8 @@ namespace Ombi.Core.Senders
|
|||
/// <param name="s"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model)
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
|
||||
{
|
||||
var s = await SonarrSettings.GetSettingsAsync();
|
||||
if (!s.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(s.ApiKey))
|
||||
{
|
||||
return null;
|
||||
|
@ -175,6 +210,10 @@ namespace Ombi.Core.Senders
|
|||
qualityToUse = model.ParentRequest.QualityOverride.Value;
|
||||
}
|
||||
|
||||
// Are we using v3 sonarr?
|
||||
var sonarrV3 = s.V3;
|
||||
var languageProfileId = s.LanguageProfile;
|
||||
|
||||
try
|
||||
{
|
||||
// Does the series actually exist?
|
||||
|
@ -204,6 +243,11 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
};
|
||||
|
||||
if (sonarrV3)
|
||||
{
|
||||
newSeries.languageProfileId = languageProfileId;
|
||||
}
|
||||
|
||||
// Montitor the correct seasons,
|
||||
// If we have that season in the model then it's monitored!
|
||||
var seasonsToAdd = GetSeasonsToCreate(model);
|
||||
|
@ -271,10 +315,19 @@ namespace Ombi.Core.Senders
|
|||
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var sonarrEpisodeList = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber).ToList();
|
||||
var sonarrEpCount = sonarrEpisodeList.Count;
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
var ourEpisodes = season.Episodes.Select(x => x.EpisodeNumber).ToList();
|
||||
var unairedEpisodes = sonarrEpisodeList.Where(x => x.airDateUtc > DateTime.UtcNow).Select(x => x.episodeNumber).ToList();
|
||||
|
||||
//// Check if we have requested all the latest episodes, if we have then monitor
|
||||
//// NOTE, not sure if needed since ombi ui displays future episodes anyway...
|
||||
//ourEpisodes.AddRange(unairedEpisodes);
|
||||
//var distinctEpisodes = ourEpisodes.Distinct().ToList();
|
||||
//var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes);
|
||||
|
||||
var existingSeason =
|
||||
result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
|
@ -284,7 +337,7 @@ namespace Ombi.Core.Senders
|
|||
}
|
||||
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
if (sonarrEpCount == ourRequestCount /*|| !missingEpisodes.Any()*/)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
|
||||
|
@ -300,16 +353,16 @@ namespace Ombi.Core.Senders
|
|||
if (!existingSeason.monitored)
|
||||
{
|
||||
// We need to monitor it, problem being is all episodes will now be monitored
|
||||
// So we need to monior the series but unmonitor every episode
|
||||
// Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
|
||||
// So we need to monitor the series but unmonitor every episode
|
||||
// Except the episodes that are already monitored before we update the series (we do not want to unmonitored episodes that are monitored beforehand)
|
||||
existingSeason.monitored = true;
|
||||
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
||||
sea.monitored = true;
|
||||
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
|
||||
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
|
||||
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
||||
var epToUnmonitor = new List<Episode>();
|
||||
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
|
||||
var epToUnmonitored = new List<Episode>();
|
||||
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member
|
||||
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
||||
{
|
||||
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
|
||||
|
@ -318,10 +371,10 @@ namespace Ombi.Core.Senders
|
|||
// continue;
|
||||
//}
|
||||
ep.monitored = false;
|
||||
epToUnmonitor.Add(ep);
|
||||
epToUnmonitored.Add(ep);
|
||||
}
|
||||
|
||||
foreach (var epToUpdate in epToUnmonitor)
|
||||
foreach (var epToUpdate in epToUnmonitored)
|
||||
{
|
||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||
}
|
||||
|
@ -355,7 +408,7 @@ namespace Ombi.Core.Senders
|
|||
var sea = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
|
||||
monitored = false
|
||||
};
|
||||
seasonsToUpdate.Add(sea);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ using Ombi.Api.CouchPotato;
|
|||
using Ombi.Api.DogNzb;
|
||||
using Ombi.Api.FanartTv;
|
||||
using Ombi.Api.Github;
|
||||
using Ombi.Api.Gotify;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Mattermost;
|
||||
using Ombi.Api.Notifications;
|
||||
|
@ -53,6 +54,7 @@ using Ombi.Updater;
|
|||
using PlexContentCacher = Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Api.Telegram;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Demo;
|
||||
using Ombi.Core.Processor;
|
||||
using Ombi.Schedule.Jobs.Lidarr;
|
||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||
|
@ -92,6 +94,9 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IMusicSender, MusicSender>();
|
||||
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
|
||||
services.AddTransient<IVoteEngine, VoteEngine>();
|
||||
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
|
||||
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
|
||||
}
|
||||
public static void RegisterHttp(this IServiceCollection services)
|
||||
{
|
||||
|
@ -107,6 +112,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IPlexApi, PlexApi>();
|
||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||
services.AddTransient<ISonarrApi, SonarrApi>();
|
||||
services.AddTransient<ISonarrV3Api, SonarrV3Api>();
|
||||
services.AddTransient<ISlackApi, SlackApi>();
|
||||
services.AddTransient<ITvMazeApi, TvMazeApi>();
|
||||
services.AddTransient<ITraktApi, TraktApi>();
|
||||
|
@ -116,6 +122,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IOmbiService, OmbiService>();
|
||||
services.AddTransient<IFanartTvApi, FanartTvApi>();
|
||||
services.AddTransient<IPushoverApi, PushoverApi>();
|
||||
services.AddTransient<IGotifyApi, GotifyApi>();
|
||||
services.AddTransient<IMattermostApi, MattermostApi>();
|
||||
services.AddTransient<ICouchPotatoApi, CouchPotatoApi>();
|
||||
services.AddTransient<IDogNzbApi, DogNzbApi>();
|
||||
|
@ -128,23 +135,28 @@ namespace Ombi.DependencyInjection
|
|||
}
|
||||
|
||||
public static void RegisterStore(this IServiceCollection services) {
|
||||
services.AddEntityFrameworkSqlite().AddDbContext<OmbiContext>();
|
||||
services.AddDbContext<OmbiContext>();
|
||||
services.AddDbContext<SettingsContext>();
|
||||
services.AddDbContext<ExternalContext>();
|
||||
|
||||
services.AddScoped<IOmbiContext, OmbiContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
|
||||
services.AddTransient<ISettingsRepository, SettingsJsonRepository>();
|
||||
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
||||
services.AddTransient<IPlexContentRepository, PlexServerContentRepository>();
|
||||
services.AddTransient<IEmbyContentRepository, EmbyContentRepository>();
|
||||
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
services.AddScoped<ISettingsContext, SettingsContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
|
||||
services.AddScoped<IExternalContext, ExternalContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
|
||||
services.AddScoped<ISettingsRepository, SettingsJsonRepository>();
|
||||
services.AddScoped<ISettingsResolver, SettingsResolver>();
|
||||
services.AddScoped<IPlexContentRepository, PlexServerContentRepository>();
|
||||
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
|
||||
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
|
||||
services.AddTransient<ITvRequestRepository, TvRequestRepository>();
|
||||
services.AddTransient<IMovieRequestRepository, MovieRequestRepository>();
|
||||
services.AddTransient<IMusicRequestRepository, MusicRequestRepository>();
|
||||
services.AddTransient<IAuditRepository, AuditRepository>();
|
||||
services.AddTransient<IApplicationConfigRepository, ApplicationConfigRepository>();
|
||||
services.AddTransient<ITokenRepository, TokenRepository>();
|
||||
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>));
|
||||
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
|
||||
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
|
||||
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
|
||||
services.AddScoped<IMusicRequestRepository, MusicRequestRepository>();
|
||||
services.AddScoped<IAuditRepository, AuditRepository>();
|
||||
services.AddScoped<IApplicationConfigRepository, ApplicationConfigRepository>();
|
||||
services.AddScoped<ITokenRepository, TokenRepository>();
|
||||
services.AddScoped(typeof(ISettingsService<>), typeof(SettingsService<>));
|
||||
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||
services.AddScoped(typeof(IExternalRepository<>), typeof(ExternalRepository<>));
|
||||
}
|
||||
public static void RegisterServices(this IServiceCollection services)
|
||||
{
|
||||
|
@ -152,7 +164,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<INotificationService, NotificationService>();
|
||||
services.AddTransient<IEmailProvider, GenericEmailProvider>();
|
||||
services.AddTransient<INotificationHelper, NotificationHelper>();
|
||||
services.AddTransient<ICacheService, CacheService>();
|
||||
services.AddSingleton<ICacheService, CacheService>();
|
||||
|
||||
services.AddTransient<IDiscordNotification, DiscordNotification>();
|
||||
services.AddTransient<IEmailNotification, EmailNotification>();
|
||||
|
@ -161,6 +173,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ISlackNotification, SlackNotification>();
|
||||
services.AddTransient<IMattermostNotification, MattermostNotification>();
|
||||
services.AddTransient<IPushoverNotification, PushoverNotification>();
|
||||
services.AddTransient<IGotifyNotification, GotifyNotification>();
|
||||
services.AddTransient<ITelegramNotification, TelegramNotification>();
|
||||
services.AddTransient<IMobileNotification, MobileNotification>();
|
||||
services.AddTransient<IChangeLogProcessor, ChangeLogProcessor>();
|
||||
|
@ -194,6 +207,8 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ILidarrArtistSync, LidarrArtistSync>();
|
||||
services.AddTransient<ILidarrAvailabilityChecker, LidarrAvailabilityChecker>();
|
||||
services.AddTransient<IIssuesPurge, IssuesPurge>();
|
||||
services.AddTransient<IResendFailedRequests, ResendFailedRequests>();
|
||||
services.AddTransient<IMediaDatabaseRefresh, MediaDatabaseRefresh>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
19
src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj
Normal file
19
src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="nunit" Version="3.11.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
52
src/Ombi.Helpers.Tests/PlexHelperTests.cs
Normal file
52
src/Ombi.Helpers.Tests/PlexHelperTests.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Helpers.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PlexHelperTests
|
||||
{
|
||||
|
||||
[TestCaseSource(nameof(ProviderIdGuidData))]
|
||||
public string GetProviderIdFromPlexGuidTests(string guidInput, ProviderIdType type)
|
||||
{
|
||||
var result = PlexHelper.GetProviderIdFromPlexGuid(guidInput);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ProviderIdType.Imdb:
|
||||
Assert.That(result.ImdbId, Is.Not.Null);
|
||||
return result.ImdbId;
|
||||
case ProviderIdType.TvDb:
|
||||
Assert.That(result.TheTvDb, Is.Not.Null);
|
||||
return result.TheTvDb;
|
||||
case ProviderIdType.MovieDb:
|
||||
Assert.That(result.TheMovieDb, Is.Not.Null);
|
||||
return result.TheMovieDb;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> ProviderIdGuidData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new TestCaseData("com.plexapp.agents.thetvdb://269586/2/8?lang=en", ProviderIdType.TvDb).Returns("269586").SetName("Regular TvDb Id");
|
||||
yield return new TestCaseData("com.plexapp.agents.themoviedb://390043?lang=en", ProviderIdType.MovieDb).Returns("390043").SetName("Regular MovieDb Id");
|
||||
yield return new TestCaseData("com.plexapp.agents.imdb://tt2543164?lang=en", ProviderIdType.Imdb).Returns("tt2543164").SetName("Regular Imdb Id");
|
||||
yield return new TestCaseData("com.plexapp.agents.agent47://tt2543456?lang=en", ProviderIdType.Imdb).Returns("tt2543456").SetName("Unknown IMDB agent");
|
||||
yield return new TestCaseData("com.plexapp.agents.agent47://456822/1/1?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent");
|
||||
yield return new TestCaseData("com.plexapp.agents.agent47://456822/999/999?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent, large episode and season");
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProviderIdType
|
||||
{
|
||||
Imdb,
|
||||
TvDb,
|
||||
MovieDb
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,8 +28,6 @@ namespace Ombi.Helpers
|
|||
return result;
|
||||
}
|
||||
|
||||
using (await _mutex.LockAsync())
|
||||
{
|
||||
if (_memoryCache.TryGetValue(cacheKey, out result))
|
||||
{
|
||||
return result;
|
||||
|
@ -40,7 +38,6 @@ namespace Ombi.Helpers
|
|||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
|
|
11
src/Ombi.Helpers/DemoLists.cs
Normal file
11
src/Ombi.Helpers/DemoLists.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ombi.Config
|
||||
{
|
||||
public class DemoLists
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public int[] Movies { get; set; }
|
||||
public int[] TvShows { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
13
src/Ombi.Helpers/DemoSingleton.cs
Normal file
13
src/Ombi.Helpers/DemoSingleton.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ombi.Helpers
|
||||
{
|
||||
public class DemoSingleton
|
||||
{
|
||||
private static DemoSingleton instance;
|
||||
|
||||
private DemoSingleton() { }
|
||||
|
||||
public static DemoSingleton Instance => instance ?? (instance = new DemoSingleton());
|
||||
|
||||
public bool Demo { get; set; }
|
||||
}
|
||||
}
|
|
@ -7,11 +7,16 @@ namespace Ombi.Helpers
|
|||
{
|
||||
public class EmbyHelper
|
||||
{
|
||||
public static string GetEmbyMediaUrl(string mediaId)
|
||||
public static string GetEmbyMediaUrl(string mediaId, string customerServerUrl = null)
|
||||
{
|
||||
var url =
|
||||
$"http://app.emby.media/#!/itemdetails.html?id={mediaId}";
|
||||
return url;
|
||||
if (customerServerUrl.HasValue())
|
||||
{
|
||||
return $"{customerServerUrl}#!/itemdetails.html?id={mediaId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"https://app.emby.media/#!/itemdetails.html?id={mediaId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace Ombi.Helpers
|
|||
public static EventId PlexContentCacher => new EventId(2008);
|
||||
public static EventId SickRageCacher => new EventId(2009);
|
||||
public static EventId LidarrArtistCache => new EventId(2010);
|
||||
public static EventId MediaReferesh => new EventId(2011);
|
||||
|
||||
public static EventId MovieSender => new EventId(3000);
|
||||
|
||||
|
@ -31,6 +32,7 @@ namespace Ombi.Helpers
|
|||
public static EventId MattermostNotification => new EventId(4004);
|
||||
public static EventId PushoverNotification => new EventId(4005);
|
||||
public static EventId TelegramNotifcation => new EventId(4006);
|
||||
public static EventId GotifyNotification => new EventId(4007);
|
||||
|
||||
public static EventId TvSender => new EventId(5000);
|
||||
public static EventId SonarrSender => new EventId(5001);
|
||||
|
|
|
@ -10,5 +10,6 @@
|
|||
Slack = 5,
|
||||
Mattermost = 6,
|
||||
Mobile = 7,
|
||||
Gotify = 8,
|
||||
}
|
||||
}
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
|
||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -14,5 +14,7 @@
|
|||
public const string RequestMusic = nameof(RequestMusic);
|
||||
public const string Disabled = nameof(Disabled);
|
||||
public const string ReceivesNewsletter = nameof(ReceivesNewsletter);
|
||||
public const string ManageOwnRequests = nameof(ManageOwnRequests);
|
||||
public const string EditCustomPage = nameof(EditCustomPage);
|
||||
}
|
||||
}
|
|
@ -27,11 +27,14 @@
|
|||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ombi.Helpers
|
||||
{
|
||||
public class PlexHelper
|
||||
{
|
||||
private const string ImdbMatchExpression = "tt([0-9]{1,10})";
|
||||
private const string TvDbIdMatchExpression = "//[0-9]+/([0-9]{1,3})/([0-9]{1,3})";
|
||||
|
||||
public static ProviderId GetProviderIdFromPlexGuid(string guid)
|
||||
{
|
||||
|
@ -52,7 +55,7 @@ namespace Ombi.Helpers
|
|||
{
|
||||
TheTvDb = guidSplit[1]
|
||||
};
|
||||
}
|
||||
} else
|
||||
if (guid.Contains("themoviedb", CompareOptions.IgnoreCase))
|
||||
{
|
||||
return new ProviderId
|
||||
|
@ -60,6 +63,7 @@ namespace Ombi.Helpers
|
|||
TheMovieDb = guidSplit[1]
|
||||
};
|
||||
}
|
||||
else
|
||||
if (guid.Contains("imdb", CompareOptions.IgnoreCase))
|
||||
{
|
||||
return new ProviderId
|
||||
|
@ -67,6 +71,31 @@ namespace Ombi.Helpers
|
|||
ImdbId = guidSplit[1]
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var imdbRegex = new Regex(ImdbMatchExpression, RegexOptions.Compiled);
|
||||
var tvdbRegex = new Regex(TvDbIdMatchExpression, RegexOptions.Compiled);
|
||||
var imdbMatch = imdbRegex.IsMatch(guid);
|
||||
if (imdbMatch)
|
||||
{
|
||||
return new ProviderId
|
||||
{
|
||||
ImdbId = guidSplit[1]
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if it matches the TvDb pattern
|
||||
var tvdbMatch = tvdbRegex.IsMatch(guid);
|
||||
if (tvdbMatch)
|
||||
{
|
||||
return new ProviderId
|
||||
{
|
||||
TheTvDb = guidSplit[1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ProviderId();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
|
@ -76,6 +77,49 @@ namespace Ombi.Helpers
|
|||
return -1;
|
||||
}
|
||||
|
||||
public static string BuildEpisodeList(IEnumerable<int> orderedEpisodes)
|
||||
{
|
||||
var epSb = new StringBuilder();
|
||||
var previousEpisodes = new List<int>();
|
||||
var previousEpisode = -1;
|
||||
foreach (var ep in orderedEpisodes)
|
||||
{
|
||||
if (ep - 1 == previousEpisode)
|
||||
{
|
||||
// This is the next one
|
||||
previousEpisodes.Add(ep);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previousEpisodes.Count > 1)
|
||||
{
|
||||
// End it
|
||||
epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}, ");
|
||||
}
|
||||
else if (previousEpisodes.Count == 1)
|
||||
{
|
||||
epSb.Append($"{previousEpisodes.FirstOrDefault()}, ");
|
||||
}
|
||||
// New one
|
||||
previousEpisodes.Clear();
|
||||
previousEpisodes.Add(ep);
|
||||
}
|
||||
previousEpisode = ep;
|
||||
}
|
||||
|
||||
if (previousEpisodes.Count > 1)
|
||||
{
|
||||
// Got some left over
|
||||
epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}");
|
||||
}
|
||||
else if (previousEpisodes.Count == 1)
|
||||
{
|
||||
epSb.Append(previousEpisodes.FirstOrDefault());
|
||||
}
|
||||
|
||||
return epSb.ToString();
|
||||
}
|
||||
|
||||
public static string RemoveSpaces(this string str)
|
||||
{
|
||||
return str.Replace(" ", "");
|
||||
|
|
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