Merge pull request #4595 from Ombi-app/develop

dev to master
pull/4611/head
Jamie 2 years ago committed by GitHub
commit ee46bdf76f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -190,6 +190,7 @@ jobs:
if: contains(github.ref, 'develop')
with:
prerelease: true
generate_release_notes: true
body: ${{ needs.versioning.outputs.changelog }}
name: ${{ needs.versioning.outputs.tag }}
tag_name: ${{ needs.versioning.outputs.tag }}

@ -8,9 +8,6 @@ jobs:
issueCheck:
runs-on: ubuntu-latest
steps:
- name: Output version
run: |
echo "log: ${{ github.event.issue.body }}"
- if: startsWith(github.event.issue.body , '**Describe the bug**') == false
name: Close Issue

@ -1,376 +1,309 @@
## [4.10.2](https://github.com/Ombi-app/Ombi/compare/v4.10.1...v4.10.2) (2022-01-22)
## [4.16.10](https://github.com/Ombi-app/Ombi/compare/v4.16.9...v4.16.10) (2022-04-13)
## [4.10.1](https://github.com/Ombi-app/Ombi/compare/v4.10.0...v4.10.1) (2022-01-22)
## [4.16.9](https://github.com/Ombi-app/Ombi/compare/v4.16.8...v4.16.9) (2022-04-13)
## [4.3.3](https://github.com/Ombi-app/Ombi/compare/v4.3.2...v4.3.3) (2021-11-05)
# [4.10.0](https://github.com/Ombi-app/Ombi/compare/v4.9.2...v4.10.0) (2022-01-14)
### Features
### Bug Fixes
* **notifications:** :sparkles: Send new request email notifications to power users ([#4462](https://github.com/Ombi-app/Ombi/issues/4462)) ([10cc0c0](https://github.com/Ombi-app/Ombi/commit/10cc0c0951f13221179516f8ff5c44dbecc9a0fd))
* **plex-watchlist:** Only request the latest season when importing from the watchlist ([77a47ff](https://github.com/Ombi-app/Ombi/commit/77a47ff157c6c5feafe3f2a29a3fcba8df4fdfef))
## [4.9.2](https://github.com/Ombi-app/Ombi/compare/v4.9.1...v4.9.2) (2022-01-14)
## [4.16.8](https://github.com/Ombi-app/Ombi/compare/v4.16.7...v4.16.8) (2022-04-13)
### Bug Fixes
* :bug: Add UI for Emby recently added cronjob settings ([#4469](https://github.com/Ombi-app/Ombi/issues/4469)) ([7d47bbe](https://github.com/Ombi-app/Ombi/commit/7d47bbe92204855bf75d70b8fa548f9c3f3612bc))
* **sonarr:** :bug: Fixed an issue where we could attempt to add a series to sonarr before sonarr has got all the metadata [#4459](https://github.com/Ombi-app/Ombi/issues/4459) ([5c691dc](https://github.com/Ombi-app/Ombi/commit/5c691dc98437a4cd24560ff625414fe05dd22f89))
* **wizard:** :bug: Fixed the issue where the Application Url wasn't validated in the wizard ([33b8d11](https://github.com/Ombi-app/Ombi/commit/33b8d1111a1c6663d8c0bbd912be4660da7d013f)), closes [#4417](https://github.com/Ombi-app/Ombi/issues/4417)
* **availability:** Fixed an issue where we wouldn't mark a available 4k movie as available (when 4K request feature is disabled) ([b492699](https://github.com/Ombi-app/Ombi/commit/b49269961d4830a530e3054976a47f519524948b))
## [4.9.1](https://github.com/Ombi-app/Ombi/compare/v4.9.0...v4.9.1) (2022-01-14)
## [4.16.7](https://github.com/Ombi-app/Ombi/compare/v4.16.6...v4.16.7) (2022-04-12)
### Bug Fixes
* **discover:** 🌐 Localize episodes names in TV details ([#4467](https://github.com/Ombi-app/Ombi/issues/4467)) [skip ci] ([35806ea](https://github.com/Ombi-app/Ombi/commit/35806ea2d2c866d628cf08577026a02ab04e49d9))
* **email-notifications:** :bug: Fixed the issue where legacy requests were showing broken poster images [#4452](https://github.com/Ombi-app/Ombi/issues/4452) ([0ece2fd](https://github.com/Ombi-app/Ombi/commit/0ece2fd6e0eb01e0b7d4d2a01e1a276c7a9c5a51))
* **emby/jellyfin:** :bug: A more reliable Emby and Jellyfin sync [skip ci] ([ad677fa](https://github.com/Ombi-app/Ombi/commit/ad677fa02eb75633014e9c9791c21ed2d6a23229))
## [4.16.6](https://github.com/Ombi-app/Ombi/compare/v4.16.5...v4.16.6) (2022-04-11)
# [4.9.0](https://github.com/Ombi-app/Ombi/compare/v4.8.1...v4.9.0) (2022-01-05)
## [4.16.5](https://github.com/Ombi-app/Ombi/compare/v4.16.4...v4.16.5) (2022-04-08)
### Features
### Bug Fixes
* **customization:** :sparkles: Added possibility for custom favicons ([40af659](https://github.com/Ombi-app/Ombi/commit/40af6593b668d4712327c18f92f5b7b5a0a65e26))
* **watchlist:** actually fixed it this time... ([d962a32](https://github.com/Ombi-app/Ombi/commit/d962a3211eca29520662ddce962676e3aea17ec5))
## [4.8.1](https://github.com/Ombi-app/Ombi/compare/v4.8.0...v4.8.1) (2022-01-04)
## [4.16.4](https://github.com/Ombi-app/Ombi/compare/v4.16.3...v4.16.4) (2022-04-08)
# [4.8.0](https://github.com/Ombi-app/Ombi/compare/v4.7.11...v4.8.0) (2021-12-22)
## [4.16.3](https://github.com/Ombi-app/Ombi/compare/v4.16.2...v4.16.3) (2022-04-08)
### Bug Fixes
* **auto-delete:** :bug: We now also auto delete music requests, this was previously missing ([9fe1f8e](https://github.com/Ombi-app/Ombi/commit/9fe1f8e988aa31d36e7a685ae19f72d9c8414dc0))
* **plex-watchlist:** :bug: Fixed the issue where the watchlist didn't work for users logging in via OAuth ([6398f6a](https://github.com/Ombi-app/Ombi/commit/6398f6a4f7755281ebeac537e3ff623df5cfa0f3))
### Features
* **details:** :sparkles: Added the notify button back into the details pages for requests ([8b33cdc](https://github.com/Ombi-app/Ombi/commit/8b33cdccef83db8794414b247438214b00860fac))
## [4.16.2](https://github.com/Ombi-app/Ombi/compare/v4.16.1...v4.16.2) (2022-04-07)
### Bug Fixes
## [4.7.11](https://github.com/Ombi-app/Ombi/compare/v4.7.10...v4.7.11) (2021-12-17)
* **wizard:** Fixed an issue when using Plex OAuth it could fail setting up ([b743cf4](https://github.com/Ombi-app/Ombi/commit/b743cf4fafa7341ad1b163276f006d7ab0e9dcff))
### Bug Fixes
* **availability-rules:** :bug: Show the 'Requested' button when a show has all of the episodes marked as requested ([cb7ecf6](https://github.com/Ombi-app/Ombi/commit/cb7ecf684ac3ab204f329a28baecfd4f6cd408f7))
## [4.16.1](https://github.com/Ombi-app/Ombi/compare/v4.16.0...v4.16.1) (2022-04-07)
## [4.7.10](https://github.com/Ombi-app/Ombi/compare/v4.7.9...v4.7.10) (2021-12-16)
# [4.16.0](https://github.com/Ombi-app/Ombi/compare/v4.15.6...v4.16.0) (2022-04-07)
### Bug Fixes
* **discover:** :bug: Fixed an issue where monitored movies in radarr were not correctly represented on the search results ([75b15bc](https://github.com/Ombi-app/Ombi/commit/75b15bc7cba21f0a14a18c8e64fd52482f5c6325))
## [4.15.6](https://github.com/Ombi-app/Ombi/compare/v4.15.5...v4.15.6) (2022-04-07)
### Bug Fixes
## [4.7.9](https://github.com/Ombi-app/Ombi/compare/v4.7.8...v4.7.9) (2021-12-16)
* **radarr:** Fixed an issue where we couldn't sync radarr content [#4577](https://github.com/Ombi-app/Ombi/issues/4577) ([a5355a3](https://github.com/Ombi-app/Ombi/commit/a5355a3023e6900c4dd1b0da4722d7596c03907f))
### Bug Fixes
* **sonarr:** :bug: Fixed an issue where we were sometimes incorrectly setting the state of episodes that are already monitored in sonarr ([fd1acb8](https://github.com/Ombi-app/Ombi/commit/fd1acb88cbc5e73f91b7f81e6e28ee06d66b277e))
## [4.15.5](https://github.com/Ombi-app/Ombi/compare/v4.15.4...v4.15.5) (2022-04-06)
## [4.7.8](https://github.com/Ombi-app/Ombi/compare/v4.7.7...v4.7.8) (2021-12-11)
## [4.15.4](https://github.com/Ombi-app/Ombi/compare/v4.15.3...v4.15.4) (2022-03-29)
### Bug Fixes
* **notifications:** :bug: Fixed the DenyReason sometimes not appearing in the notification message [#4409](https://github.com/Ombi-app/Ombi/issues/4409) ([209e311](https://github.com/Ombi-app/Ombi/commit/209e31175c95f6ee8909d878d45bf8269a9842d9))
* **oauth:** :lock: Fixed the issue where some users running their browsers in a different language could not open the Plex OAuth window ([d5404ea](https://github.com/Ombi-app/Ombi/commit/d5404eaad7837010d6e4563cd8f7a1009231d362)), closes [#4408](https://github.com/Ombi-app/Ombi/issues/4408)
* **translations:** 🌐 New translations from Crowdin ([5cfb76c](https://github.com/Ombi-app/Ombi/commit/5cfb76cad7a25eed8b452bf9c01cef8c32804369))
## [4.15.3](https://github.com/Ombi-app/Ombi/compare/v4.15.2...v4.15.3) (2022-03-24)
## [4.7.7](https://github.com/Ombi-app/Ombi/compare/v4.7.6...v4.7.7) (2021-12-08)
## [4.15.2](https://github.com/Ombi-app/Ombi/compare/v4.15.1...v4.15.2) (2022-03-23)
### Bug Fixes
* **notifications:** 🐛 Do not notify user upon auto approval of a TV show ([#4432](https://github.com/Ombi-app/Ombi/issues/4432)) ([3ad3bdd](https://github.com/Ombi-app/Ombi/commit/3ad3bddd8313d607ee2a39a51a92e61a3673082c)), closes [#4431](https://github.com/Ombi-app/Ombi/issues/4431)
* **translations:** 🌐 New translations from Crowdin ([8943a97](https://github.com/Ombi-app/Ombi/commit/8943a978bf459eaeb496d50c61c4d1506c727366))
* **metadata:** improved the metadata job to also lookup the media in Plex to see if it has any more uptodate metadata ([83d1a15](https://github.com/Ombi-app/Ombi/commit/83d1a15cc9d0ee91be73bd91c4672cf1bcf2728a))
## [4.7.6](https://github.com/Ombi-app/Ombi/compare/v4.7.5...v4.7.6) (2021-12-02)
## [4.15.1](https://github.com/Ombi-app/Ombi/compare/v4.15.0...v4.15.1) (2022-03-18)
### Bug Fixes
* **user-management:** :bug: Fixed an issue where you couldn't 'unset' a users custom quality and root folders ([bae96af](https://github.com/Ombi-app/Ombi/commit/bae96af17f50a80ae3ade235a5ef68d5d2dc12ba))
* **mediaserver:** fixed an issue where we were not detecting available content correctly [#4542](https://github.com/Ombi-app/Ombi/issues/4542) ([9cdd6f4](https://github.com/Ombi-app/Ombi/commit/9cdd6f41cdab8825a984905c089611409c53c753))
## [4.7.5](https://github.com/Ombi-app/Ombi/compare/v4.7.4...v4.7.5) (2021-11-28)
# [4.15.0](https://github.com/Ombi-app/Ombi/compare/v4.14.4...v4.15.0) (2022-03-17)
### Bug Fixes
* **notifications:** fixed an error that could happen when Ombi sends out a issue notification ([7442dcf](https://github.com/Ombi-app/Ombi/commit/7442dcf59da5d2190cc3087b10402e85bcfcf83b))
* **translations:** 🌐 Fix incorrect text translation reference RequestedByOn ([#4420](https://github.com/Ombi-app/Ombi/issues/4420)) ([202d155](https://github.com/Ombi-app/Ombi/commit/202d155493c29a6ddd4c5507186bf376a28f4c1d))
* **translations:** 🌐 New translations from Crowdin ([473c172](https://github.com/Ombi-app/Ombi/commit/473c1724922515fe376e0b2058ac391807c923f2))
* **jellyfin:** :bug: Fixed an issue where Jellyfin content was showing the Play on Emby button ([18b167d](https://github.com/Ombi-app/Ombi/commit/18b167d16a3d682b5060ee36dedbbb069bef09de)), closes [#4542](https://github.com/Ombi-app/Ombi/issues/4542)
## [4.7.4](https://github.com/Ombi-app/Ombi/compare/v4.7.3...v4.7.4) (2021-11-25)
## [4.14.4](https://github.com/Ombi-app/Ombi/compare/v4.14.3...v4.14.4) (2022-03-10)
### Bug Fixes
* **availability-rules:** :bug: Fixed a small issue where some shows would not appear as Available even know they had no future unaired episodes listed ([914b096](https://github.com/Ombi-app/Ombi/commit/914b096781c9b73292a533a010a5dd05ecfd0aac))
* **emby:** :bug: Fixed an issue where we were not properly syncing episodes ([75529dd](https://github.com/Ombi-app/Ombi/commit/75529dd972c5102f3c5234a2acf6fe664a1bcfad))
* :bug: Fixed the Request On Behalf autocomplete not filtering correctly ([a8ba2f3](https://github.com/Ombi-app/Ombi/commit/a8ba2f3544a1c01c57f217c4036a277ab0e67a09)), closes [#4539](https://github.com/Ombi-app/Ombi/issues/4539)
* **translations:** 🌐 New translations from Crowdin [skip ci] ([356c742](https://github.com/Ombi-app/Ombi/commit/356c7424e0ce8c1c5063b04bc6ed9b809f214d65))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([6fcaecf](https://github.com/Ombi-app/Ombi/commit/6fcaecf80b766f2d43ac7082d74364238e1638b7))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([132f4d4](https://github.com/Ombi-app/Ombi/commit/132f4d4e609b7fb7e37f38ee2f395926e2911abe))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([f292006](https://github.com/Ombi-app/Ombi/commit/f292006a08894a8d0ba899c8c6e9fe863e558dda))
## [4.7.3](https://github.com/Ombi-app/Ombi/compare/v4.7.2...v4.7.3) (2021-11-23)
## [4.7.2](https://github.com/Ombi-app/Ombi/compare/v4.7.1...v4.7.2) (2021-11-22)
## [4.14.3](https://github.com/Ombi-app/Ombi/compare/v4.14.2...v4.14.3) (2022-03-06)
### Bug Fixes
* **request-list:** :bug: Fixed an issue where the bulk delete would not work for movie requests ([4b540fb](https://github.com/Ombi-app/Ombi/commit/4b540fb45bcc389664f0953159802288d005db9f))
* **availability:** :bug: Fixed an issue where with 4k content, we could repeat notifications ([f9ebc1c](https://github.com/Ombi-app/Ombi/commit/f9ebc1cc2e13c7cd335121cd86295b10eda529ba))
## [4.7.1](https://github.com/Ombi-app/Ombi/compare/v4.7.0...v4.7.1) (2021-11-22)
## [4.14.2](https://github.com/Ombi-app/Ombi/compare/v4.14.1...v4.14.2) (2022-03-05)
### Bug Fixes
* **emby:** :bug: Fixed an issue where we slightly broke the full sync ([332d934](https://github.com/Ombi-app/Ombi/commit/332d9344d002a5ffd5aeac516c7441dcdec52248))
* **Sonarr:** :bug: Fixed an issue where some seasons were not being monitored correctly in sonarr ([60cfd41](https://github.com/Ombi-app/Ombi/commit/60cfd41f68e9006555c1a419dcff1aaa24b3e09f)), closes [#4506](https://github.com/Ombi-app/Ombi/issues/4506)
# [4.7.0](https://github.com/Ombi-app/Ombi/compare/v4.6.5...v4.7.0) (2021-11-19)
## [4.14.1](https://github.com/Ombi-app/Ombi/compare/v4.14.0...v4.14.1) (2022-03-03)
### Features
* **emby:** :sparkles: Added a emby recently added sync! ([a0e1406](https://github.com/Ombi-app/Ombi/commit/a0e14068f4bc457f8a4a565de71707a8f16c803c))
# [4.14.0](https://github.com/Ombi-app/Ombi/compare/v4.13.2...v4.14.0) (2022-03-02)
## [4.6.5](https://github.com/Ombi-app/Ombi/compare/v4.6.4...v4.6.5) (2021-11-15)
## [4.13.2](https://github.com/Ombi-app/Ombi/compare/v4.13.1...v4.13.2) (2022-03-01)
### Bug Fixes
* **issues:** :bug: Added the issue category to the issue 'cards' [#4403](https://github.com/Ombi-app/Ombi/issues/4403) ([a3739f3](https://github.com/Ombi-app/Ombi/commit/a3739f375c49f48e34da12f0a74e4e068f12ab40))
* **issues:** :bug: Added the issues back to the details page for TV Shows ([0225000](https://github.com/Ombi-app/Ombi/commit/02250000c08a253e57d8a0a855c2d30b8a1e5baa))
* **issues:** :bug: Fixed an issue where you couldn't navigate to the details page from TV issues ([1a2825b](https://github.com/Ombi-app/Ombi/commit/1a2825bf3839b891b16e1dde4030afe53efe090e))
* **issues:** :bug: Fixed where we did not show the poster when an issue is raised for media we do not have a request for [#4402](https://github.com/Ombi-app/Ombi/issues/4402) ([15e37b5](https://github.com/Ombi-app/Ombi/commit/15e37b532a83097dbdf1a9fea3eead7d0e211898))
## [4.6.4](https://github.com/Ombi-app/Ombi/compare/v4.6.3...v4.6.4) (2021-11-12)
* **requests:** :bug: Fixed an issue where you couldn't approve movies from the request list ([1611ef9](https://github.com/Ombi-app/Ombi/commit/1611ef9198befbb7a4db50a4f0953e50f29a788f))
## [4.6.3](https://github.com/Ombi-app/Ombi/compare/v4.6.2...v4.6.3) (2021-11-11)
## [4.13.1](https://github.com/Ombi-app/Ombi/compare/v4.13.0...v4.13.1) (2022-03-01)
### Bug Fixes
* **discover:** :bug: Display TV + movies on actor page in user language ([#4395](https://github.com/Ombi-app/Ombi/issues/4395)) ([fe635c7](https://github.com/Ombi-app/Ombi/commit/fe635c7106bc487ff879bdc8a73bab16cb389b97))
* **permissions:** :bug: Improved the security around the role "Manage Own Requests" ([#4397](https://github.com/Ombi-app/Ombi/issues/4397)) ([334a32b](https://github.com/Ombi-app/Ombi/commit/334a32bca42f90198d9b720d2bdb710a583be47f)), closes [#4391](https://github.com/Ombi-app/Ombi/issues/4391)
* **search:** Fixed some cases where search wouldn't work correctly ([#4398](https://github.com/Ombi-app/Ombi/issues/4398)) ([4410790](https://github.com/Ombi-app/Ombi/commit/4410790bc096826bc11554098f846e3acb59589a))
* **details:** :bug: Fixed the missing Play on Media server button for 4k content [#4529](https://github.com/Ombi-app/Ombi/issues/4529) ([68600f3](https://github.com/Ombi-app/Ombi/commit/68600f3b45376e12dd2ef263d81ca4040c84cbca))
* **discover:** :bug: Fixed the issue where there was an option on the discover to request 4k shows (that's not supported currently) ([dcfd688](https://github.com/Ombi-app/Ombi/commit/dcfd688c8d2337e55fa9c6c33b7c3e80fc560cda))
* **requests:** :bug: Fixed the issue where we could no longer approve TV Requests from the requests list ([19fe4e3](https://github.com/Ombi-app/Ombi/commit/19fe4e342efe5578c26ab8ba7ee2f2e64bbc9418))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4526](https://github.com/Ombi-app/Ombi/issues/4526)) ([7e9f54f](https://github.com/Ombi-app/Ombi/commit/7e9f54fc80a09c938184e6be40ce5f49ce9673ef))
## [4.6.2](https://github.com/Ombi-app/Ombi/compare/v4.6.1...v4.6.2) (2021-11-10)
# [4.13.0](https://github.com/Ombi-app/Ombi/compare/v4.12.7...v4.13.0) (2022-02-25)
### Bug Fixes
* **discover:** TV shows now display on the Actor Pages ([#4388](https://github.com/Ombi-app/Ombi/issues/4388)) ([6b716e7](https://github.com/Ombi-app/Ombi/commit/6b716e722076e3d1e6bf2097c5263645d5ea9edf))
* **4k:** Hide 'Has 4K Request' column list if 4k feature is disabled ([#4521](https://github.com/Ombi-app/Ombi/issues/4521)) ([a9a6067](https://github.com/Ombi-app/Ombi/commit/a9a60678e74d22fa7ba34051a2645db86b600b4a))
* **issues:** Fix label ID in chatbox page ([#4520](https://github.com/Ombi-app/Ombi/issues/4520)) ([76882ad](https://github.com/Ombi-app/Ombi/commit/76882adf231f92e1cdd396239933c13467c112b3))
* **localisation:** Localize request types in notifications ([#4516](https://github.com/Ombi-app/Ombi/issues/4516)) ([e09435d](https://github.com/Ombi-app/Ombi/commit/e09435da455b12fc429f129372de31e0654da797))
* **notifications:** Remove generic admin email in favour of admins' email ([#4519](https://github.com/Ombi-app/Ombi/issues/4519)) ([b90fc5f](https://github.com/Ombi-app/Ombi/commit/b90fc5fea771a83e6cf576c71a307066efd59ea4))
* **tv:** Display TV show as requested if all episodes are requested ([#4518](https://github.com/Ombi-app/Ombi/issues/4518)) ([2ed8c48](https://github.com/Ombi-app/Ombi/commit/2ed8c48d128a69f0d144c5d332286dbf3b0bdf28))
### Features
## [4.6.1](https://github.com/Ombi-app/Ombi/compare/v4.6.0...v4.6.1) (2021-11-10)
* **email-notifications:** Add a link to Ombi details page in email notifications ([#4517](https://github.com/Ombi-app/Ombi/issues/4517)) ([a3e97b3](https://github.com/Ombi-app/Ombi/commit/a3e97b31e2298d95e7deebd71268095b8ed5e9dc))
* **media-details:** Add Trakt to social icons ([#4522](https://github.com/Ombi-app/Ombi/issues/4522)) ([d6ae79c](https://github.com/Ombi-app/Ombi/commit/d6ae79ce9eddbd5b7b888ab1b9f7e342d9d9ff9e))
### Bug Fixes
* :bug: Fixed the MySQL issue after .net 6 upgrade [#4393](https://github.com/Ombi-app/Ombi/issues/4393) ([fea7ff0](https://github.com/Ombi-app/Ombi/commit/fea7ff05139e9ff50c8097fa5389b4ef9ad21a15))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([c6acb45](https://github.com/Ombi-app/Ombi/commit/c6acb45f8d3f371c0b4024c4272849d0d0cc867f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([18c220a](https://github.com/Ombi-app/Ombi/commit/18c220a0cd0d19e45a07d0c319da2b9512778a8a))
## [4.12.7](https://github.com/Ombi-app/Ombi/compare/v4.12.6...v4.12.7) (2022-02-23)
# [4.6.0](https://github.com/Ombi-app/Ombi/compare/v4.4.0...v4.6.0) (2021-11-09)
## [4.12.6](https://github.com/Ombi-app/Ombi/compare/v4.12.5...v4.12.6) (2022-02-22)
### Features
### Bug Fixes
* :sparkles: Upgrade Ombi to .NET 6 ([#4390](https://github.com/Ombi-app/Ombi/issues/4390)) ([719eb7d](https://github.com/Ombi-app/Ombi/commit/719eb7dbe37b3a72d264e2f670067518eef70694)), closes [#4392](https://github.com/Ombi-app/Ombi/issues/4392)
* **emby/jellyfin:** :bug: Fixed another issue where we were not correctly displaying the correct status' for movies ([5c0556e](https://github.com/Ombi-app/Ombi/commit/5c0556e6f44b8997a611f3a4d8e9e4e05d08bd13))
* **mediaserver:** fixed some more issues in the media server sync and availability checks ([f3ea979](https://github.com/Ombi-app/Ombi/commit/f3ea979b8bd77842780ce8e6928b16237dd779cf))
# [4.4.0](https://github.com/Ombi-app/Ombi/compare/v4.3.3...v4.4.0) (2021-11-06)
## [4.12.5](https://github.com/Ombi-app/Ombi/compare/v4.12.4...v4.12.5) (2022-02-21)
### Bug Fixes
* **request-list:** :bug: Fixed an issue where the request options were not appearing for Music requests ([c0406a2](https://github.com/Ombi-app/Ombi/commit/c0406a2ddebafb03d98ed25cdf7d89dc9a600c7d))
* **emby:** :bug: Fixed the emby content sync [#4513](https://github.com/Ombi-app/Ombi/issues/4513) ([2927504](https://github.com/Ombi-app/Ombi/commit/2927504f0e0b4e7251e69b44e0e30c7ec9519980))
* **emby:** :bug: Fixed the emby content sync [#4513](https://github.com/Ombi-app/Ombi/issues/4513) ([bd441cb](https://github.com/Ombi-app/Ombi/commit/bd441cb54fd77d6befb03fae321dc36c29f0de2e))
### Features
* **mass-email:** :sparkles: Added the ability to configure the Mass Email, we can now send BCC and we are less likely to be rate limited when not using bcc [#4377](https://github.com/Ombi-app/Ombi/issues/4377) ([ca655ae](https://github.com/Ombi-app/Ombi/commit/ca655ae57042dec44106a2f2ef5ba2e6f1019ee4))
## [4.12.4](https://github.com/Ombi-app/Ombi/compare/v4.12.3...v4.12.4) (2022-02-17)
## [4.3.3](https://github.com/Ombi-app/Ombi/compare/v4.3.2...v4.3.3) (2021-11-05)
## [4.12.3](https://github.com/Ombi-app/Ombi/compare/v4.12.2...v4.12.3) (2022-02-16)
## [4.3.2](https://github.com/Ombi-app/Ombi/compare/v4.3.1...v4.3.2) (2021-11-02)
## [4.12.2](https://github.com/Ombi-app/Ombi/compare/v4.12.1...v4.12.2) (2022-02-16)
### Bug Fixes
* **translations:** 🌐 Localization - Ensuring all of the app including backend are localized [#4366](https://github.com/Ombi-app/Ombi/issues/4366) ([5e140ab](https://github.com/Ombi-app/Ombi/commit/5e140ab6183b887a7665f5e870eb0bd05d487ace))
* **requests:** :bug: Fixed the approve 4k option on the requests list not working as expected ([c0189da](https://github.com/Ombi-app/Ombi/commit/c0189dad478ea375beda61ba3bee3f029a39b8e5))
## [4.3.1](https://github.com/Ombi-app/Ombi/compare/v4.3.0...v4.3.1) (2021-10-27)
## [4.12.1](https://github.com/Ombi-app/Ombi/compare/v4.12.0...v4.12.1) (2022-02-16)
### Bug Fixes
* :bug: Hides no results message during search. ([#4375](https://github.com/Ombi-app/Ombi/issues/4375)) ([b819b0e](https://github.com/Ombi-app/Ombi/commit/b819b0e007e578bf3d8425f19591f87029c64d06))
* **requests:** :bug: Fixed the issue where Approving a 4K Request wouldn't send it to the correct 4K radarr instance ([87cb990](https://github.com/Ombi-app/Ombi/commit/87cb9903db30e1dead25ee8c5ea34305eb084a03)), closes [#4509](https://github.com/Ombi-app/Ombi/issues/4509)
# [4.3.0](https://github.com/Ombi-app/Ombi/compare/v4.2.13...v4.3.0) (2021-10-20)
### Bug Fixes
* **translations:** 🌐 New translations from Crowdin [skip ci] ([b0f3abb](https://github.com/Ombi-app/Ombi/commit/b0f3abb9ceebdbe5d6c20af98b7355df2999eb58))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([77d017b](https://github.com/Ombi-app/Ombi/commit/77d017b3d8ffd1714a2f6efecc8c900d56d062e4))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([f6e9784](https://github.com/Ombi-app/Ombi/commit/f6e9784367d3678d899ed79bef6caa52005b6661))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([601a877](https://github.com/Ombi-app/Ombi/commit/601a87762a2ad393ee5fa2fe52052ceeeefb1bef))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([a4a80ba](https://github.com/Ombi-app/Ombi/commit/a4a80ba4da49733a65e691003646c0f349bd4c5f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([2961319](https://github.com/Ombi-app/Ombi/commit/2961319f61e95b2871480152b86ddca3375576a1))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([fc8d108](https://github.com/Ombi-app/Ombi/commit/fc8d108b660d53f499538328bfc271b05ac47d2b))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([1e03651](https://github.com/Ombi-app/Ombi/commit/1e03651c3b0eb77e45f9f6c55d31ee672eacd51e))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([c0dd327](https://github.com/Ombi-app/Ombi/commit/c0dd327426514e305a88750d7c3deb21c194108f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([2156129](https://github.com/Ombi-app/Ombi/commit/2156129f175335746f204bb123035c070f518e96))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([aef0368](https://github.com/Ombi-app/Ombi/commit/aef0368de3aec306245bd1b16bc0de596a20d451))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([a38090b](https://github.com/Ombi-app/Ombi/commit/a38090b8dde17d1d150af0bca2830ea45d013a0e))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([c5f1d33](https://github.com/Ombi-app/Ombi/commit/c5f1d3355758a5c3648479d44e50397c7f6c1a9d))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([3846d56](https://github.com/Ombi-app/Ombi/commit/3846d56a6e561a1b1dc65c385151d90fdd6217ee))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([dafe9c1](https://github.com/Ombi-app/Ombi/commit/dafe9c1a19d84f00c13f0a51ba90927c24282926))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([edb418a](https://github.com/Ombi-app/Ombi/commit/edb418a6f05887c68a0c24c48decc691996f97e4))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([dadabf9](https://github.com/Ombi-app/Ombi/commit/dadabf93e1582a0c39321fd9bf3de3fb11e3f406))
# [4.12.0](https://github.com/Ombi-app/Ombi/compare/v4.11.8...v4.12.0) (2022-02-14)
### Features
* **request-limits:** :sparkles: Added the new request limit options into the user importer ([01d4f4d](https://github.com/Ombi-app/Ombi/commit/01d4f4d718fe85ac181dae52565fb1b427965b4f))
* **request-limits:** :sparkles: Added the new request limit options to the bulk edit ([03bc23a](https://github.com/Ombi-app/Ombi/commit/03bc23a74e4308aa6b4c6b25636edcdeb65c1f0e))
* **radarr:** 4K Requests and Radarr 4K support ([ba88848](https://github.com/Ombi-app/Ombi/commit/ba88848866b0a9dedb1e79b55c4d81a0fd453843))
## [4.2.13](https://github.com/Ombi-app/Ombi/compare/v4.2.12...v4.2.13) (2021-10-20)
## [4.11.8](https://github.com/Ombi-app/Ombi/compare/v4.11.7...v4.11.8) (2022-02-13)
### Bug Fixes
* **translations:** 🌐 New translations %two_letters_code% from Crowdin [skip ci] ([8fbd267](https://github.com/Ombi-app/Ombi/commit/8fbd267b516ddaa80fd16c091bae532b860fbf45))
* **settings:** :bug: Fixed an issue where we were not displaying the excluded keyworks correctly in the TheMovieDbSettings page ([d3b3316](https://github.com/Ombi-app/Ombi/commit/d3b3316cbac18356b2f6b0912a3deb2c183e6534))
## [4.2.12](https://github.com/Ombi-app/Ombi/compare/v4.2.11...v4.2.12) (2021-10-20)
## [4.11.7](https://github.com/Ombi-app/Ombi/compare/v4.11.6...v4.11.7) (2022-02-12)
### Bug Fixes
* **newsletter:** :bug: Fixed a few small bugs in the newsletter ([21dba4c](https://github.com/Ombi-app/Ombi/commit/21dba4c524b98b9f2b883d97e7e13329425a8762))
* **translations:** 🌐 New translations en.json from Crowdin [skip ci] ([52eda6a](https://github.com/Ombi-app/Ombi/commit/52eda6ab917a73842bc02b0d8e0c442e564ca8f0))
* **translations:** 🌐 New translations en.json from Crowdin [skip ci] ([1095d52](https://github.com/Ombi-app/Ombi/commit/1095d524962648a1e427f0bcd8105fa734dd5b60))
## [4.2.11](https://github.com/Ombi-app/Ombi/compare/v4.2.10...v4.2.11) (2021-10-18)
* **notifications:** :bug: This is a fix for some of the duplicate notification issues [#3825](https://github.com/Ombi-app/Ombi/issues/3825) ([22bb422](https://github.com/Ombi-app/Ombi/commit/22bb4226ead2d62e8c2c2c05be47d7da621402e2))
## [4.2.10](https://github.com/Ombi-app/Ombi/compare/v4.2.9...v4.2.10) (2021-10-15)
## [4.11.6](https://github.com/Ombi-app/Ombi/compare/v4.11.5...v4.11.6) (2022-02-10)
### Bug Fixes
* :bug: Really really fix it this time? ([543d36e](https://github.com/Ombi-app/Ombi/commit/543d36e5615341bc8378cac377b843a3dbc1ef99))
* **plex:** Fixed an issue where in a rare case we couldn't sync the data [#4502](https://github.com/Ombi-app/Ombi/issues/4502) ([191318d](https://github.com/Ombi-app/Ombi/commit/191318ddad5a8148422955bf928f1c49b890e3eb))
## [4.2.9](https://github.com/Ombi-app/Ombi/compare/v4.2.8...v4.2.9) (2021-10-15)
## [4.11.5](https://github.com/Ombi-app/Ombi/compare/v4.11.4...v4.11.5) (2022-02-05)
### Bug Fixes
* :fire: Really fix the base url issue this time ([9f36923](https://github.com/Ombi-app/Ombi/commit/9f36923c51bfabf9cb026f2da14f9947050af0d9))
* **sonarr:** Fixed where requesting all seasons would only mark the latest as monitored [#4496](https://github.com/Ombi-app/Ombi/issues/4496) ([cfb85c2](https://github.com/Ombi-app/Ombi/commit/cfb85c23d77626b9ec1d99a6cf76497c438d0338))
## [4.2.8](https://github.com/Ombi-app/Ombi/compare/v4.2.7...v4.2.8) (2021-10-15)
## [4.11.4](https://github.com/Ombi-app/Ombi/compare/v4.11.3...v4.11.4) (2022-02-05)
### Bug Fixes
* :adhesive_bandage: See if this fixes the proxy issue ([74d1aca](https://github.com/Ombi-app/Ombi/commit/74d1acae499707a7e21401f53eb2bb90c5bb9cfa))
* :bug: Fixed Ombi not writing the baseUrl correctly ([e9cc8b6](https://github.com/Ombi-app/Ombi/commit/e9cc8b6fe71d3e10c1a901e70227989b3362afe3))
* **media-sync:** Add sanity checks upon media server sync ([#4493](https://github.com/Ombi-app/Ombi/issues/4493)) ([9915234](https://github.com/Ombi-app/Ombi/commit/9915234d38d4701c527081ccc21b566980375331)), closes [#4472](https://github.com/Ombi-app/Ombi/issues/4472)
* **newsletter:** Fix newsletter not publishing double episodes ([#4495](https://github.com/Ombi-app/Ombi/issues/4495)) ([ddf63fb](https://github.com/Ombi-app/Ombi/commit/ddf63fbed0b9cbe458aec37318758c76b99b2de9))
## [4.2.7](https://github.com/Ombi-app/Ombi/compare/v4.2.6...v4.2.7) (2021-10-14)
## [4.11.3](https://github.com/Ombi-app/Ombi/compare/v4.11.2...v4.11.3) (2022-02-03)
### Bug Fixes
* :bug: Fixed the issue parsing TheMovieDB dates. They have broken something... ([6e397e0](https://github.com/Ombi-app/Ombi/commit/6e397e02e95f894a92e8bf02428efdcac1275b31))
## [4.2.6](https://github.com/Ombi-app/Ombi/compare/v4.2.5...v4.2.6) (2021-10-14)
### Performance Improvements
* :zap: Use ngxs store for the whole customization section of the app ([97b493d](https://github.com/Ombi-app/Ombi/commit/97b493d869feee59d360b484a6c59388a2aead1f))
## [4.2.5](https://github.com/Ombi-app/Ombi/compare/v4.2.4...v4.2.5) (2021-10-14)
* **API:** Fixed an issue where the API key couldn't delete a request [#4489](https://github.com/Ombi-app/Ombi/issues/4489) ([8e42dbf](https://github.com/Ombi-app/Ombi/commit/8e42dbf8f78caa51ca891bf3d702c6b0ac401f9c))

@ -106,14 +106,21 @@ Here are some of the features Ombi has:
<sub><b>Anojh Thayaparan</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sephrat">
<img src="https://avatars.githubusercontent.com/u/34862846?v=4" width="50;" alt="sephrat"/>
<br />
<sub><b>Sephrat</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Magikarplvl4">
<img src="https://avatars.githubusercontent.com/u/2944704?v=4" width="50;" alt="Magikarplvl4"/>
<br />
<sub><b>Magikarp Lvl 4</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/MrTopCat">
<img src="https://avatars.githubusercontent.com/u/774415?v=4" width="50;" alt="MrTopCat"/>
@ -135,13 +142,6 @@ Here are some of the features Ombi has:
<sub><b>Matt Jeanes</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sephrat">
<img src="https://avatars.githubusercontent.com/u/34862846?v=4" width="50;" alt="sephrat"/>
<br />
<sub><b>Sephrat</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/bernarden">
<img src="https://avatars.githubusercontent.com/u/12289537?v=4" width="50;" alt="bernarden"/>
@ -243,6 +243,13 @@ Here are some of the features Ombi has:
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/fservida">
<img src="https://avatars.githubusercontent.com/u/501958?v=4" width="50;" alt="fservida"/>
<br />
<sub><b>Francesco Servida</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Patricol">
<img src="https://avatars.githubusercontent.com/u/13428020?v=4" width="50;" alt="Patricol"/>
@ -277,15 +284,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Aptalca</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/mhann">
<img src="https://avatars.githubusercontent.com/u/17162399?v=4" width="50;" alt="mhann"/>
<br />
<sub><b>Mhann</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/ombi-bot">
<img src="https://avatars.githubusercontent.com/u/51722903?v=4" width="50;" alt="ombi-bot"/>
@ -320,15 +327,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>D34DC3N73R</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/pooley182">
<img src="https://avatars.githubusercontent.com/u/5040011?v=4" width="50;" alt="pooley182"/>
<br />
<sub><b>David Pooley</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/Fredrik81">
<img src="https://avatars.githubusercontent.com/u/21292774?v=4" width="50;" alt="Fredrik81"/>
@ -363,15 +370,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Marius Schiffer</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Qstick">
<img src="https://avatars.githubusercontent.com/u/376117?v=4" width="50;" alt="Qstick"/>
<br />
<sub><b>Qstick</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/Vbgf">
<img src="https://avatars.githubusercontent.com/u/5571734?v=4" width="50;" alt="Vbgf"/>
@ -406,15 +413,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Alexander Standke</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Alasano">
<img src="https://avatars.githubusercontent.com/u/14372930?v=4" width="50;" alt="Alasano"/>
<br />
<sub><b>Aljosa Asanovic</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/Ashyni">
<img src="https://avatars.githubusercontent.com/u/18462848?v=4" width="50;" alt="Ashyni"/>
@ -449,15 +456,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Christopher Demicoli</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Codehhh">
<img src="https://avatars.githubusercontent.com/u/12055335?v=4" width="50;" alt="Codehhh"/>
<br />
<sub><b>Codehhh</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/danopia">
<img src="https://avatars.githubusercontent.com/u/40628?v=4" width="50;" alt="danopia"/>
@ -492,15 +499,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Eli</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Fish2">
<img src="https://avatars.githubusercontent.com/u/2311734?v=4" width="50;" alt="Fish2"/>
<br />
<sub><b>Fish2</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/hariesramdhani">
<img src="https://avatars.githubusercontent.com/u/24251244?v=4" width="50;" alt="hariesramdhani"/>
@ -535,15 +542,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Joe Groocock</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/errorhandler">
<img src="https://avatars.githubusercontent.com/u/17112958?v=4" width="50;" alt="errorhandler"/>
<br />
<sub><b>Joe Harvey</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/jonbloom">
<img src="https://avatars.githubusercontent.com/u/492819?v=4" width="50;" alt="jonbloom"/>
@ -578,15 +585,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Lightkeeper</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/devbymadde">
<img src="https://avatars.githubusercontent.com/u/6094593?v=4" width="50;" alt="devbymadde"/>
<br />
<sub><b>Madeleine Schönemann</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/mattmattmatt">
<img src="https://avatars.githubusercontent.com/u/927830?v=4" width="50;" alt="mattmattmatt"/>
@ -594,6 +601,13 @@ Here are some of the features Ombi has:
<sub><b>Matt</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/LMaxence">
<img src="https://avatars.githubusercontent.com/u/29194680?v=4" width="50;" alt="LMaxence"/>
<br />
<sub><b>Maxence Lecanu</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/AliMickey">
<img src="https://avatars.githubusercontent.com/u/60691199?v=4" width="50;" alt="AliMickey"/>
@ -608,6 +622,14 @@ Here are some of the features Ombi has:
<sub><b>Nathan Miller</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/cqxmzz">
<img src="https://avatars.githubusercontent.com/u/3071863?v=4" width="50;" alt="cqxmzz"/>
<br />
<sub><b>Qiming Chen</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/randallbruder">
<img src="https://avatars.githubusercontent.com/u/6447487?v=4" width="50;" alt="randallbruder"/>
@ -628,8 +650,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Samuel Bartík</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/seancallinan">
<img src="https://avatars.githubusercontent.com/u/1139665?v=4" width="50;" alt="seancallinan"/>
@ -650,7 +671,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Thomas Van Tilburg</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Tim-Trott">
<img src="https://avatars.githubusercontent.com/u/8249434?v=4" width="50;" alt="Tim-Trott"/>
@ -671,8 +693,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Torkil</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/bybeet">
<img src="https://avatars.githubusercontent.com/u/1662279?v=4" width="50;" alt="bybeet"/>
@ -693,7 +714,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Tim OBrien</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/x-limitless-x">
<img src="https://avatars.githubusercontent.com/u/17127926?v=4" width="50;" alt="x-limitless-x"/>
@ -714,8 +736,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Michael DiStaula</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/baikunz">
<img src="https://avatars.githubusercontent.com/u/984911?v=4" width="50;" alt="baikunz"/>
@ -736,7 +757,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Maartenheebink</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/masterhuck">
<img src="https://avatars.githubusercontent.com/u/4671442?v=4" width="50;" alt="masterhuck"/>
@ -757,8 +779,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Tdorsey</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/thegame3202">
<img src="https://avatars.githubusercontent.com/u/22148848?v=4" width="50;" alt="thegame3202"/>

@ -1,10 +1,10 @@
commit_message: "fix(translations): 🌐 New translations from Crowdin [skip ci]"
append_commit_message: false
pull_request_title: "🌐 Translations Update"
pull_request_labels:
- translations
files:
- source: /src/Ombi/wwwroot/translations/en.json
translation: /src/Ombi/wwwroot/translations/%two_letters_code%.json
- source: /src/Ombi.I18n/Resources/Texts.resx
translation: /src/Ombi.I18n/Resources/Texts.%two_letters_code%.resx

@ -929,7 +929,7 @@
<e p="EmbyConfiguration.cs" t="Include" />
<e p="EmbyConnectUser.cs" t="Include" />
<e p="EmbyItemContainer.cs" t="Include" />
<e p="EmbyMediaType.cs" t="Include" />
<e p="MediaType.cs" t="Include" />
<e p="EmbyPolicy.cs" t="Include" />
<e p="EmbySystemInfo.cs" t="Include" />
<e p="EmbyUser.cs" t="Include" />
@ -1876,7 +1876,7 @@
<e p="PlexAvailabilityChecker.cs" t="Include" />
<e p="PlexContentSync.cs" t="Include" />
<e p="PlexEpisodeSync.cs" t="Include" />
<e p="PlexMediaType.cs" t="Include" />
<e p="MediaType.cs" t="Include" />
<e p="PlexRecentlyAddedSync.cs" t="Include" />
<e p="PlexUserImporter.cs" t="Include" />
</e>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -170,7 +170,7 @@ namespace Ombi.Api.Emby
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("Fields", includeOverview ? "ProviderIds,MediaStreams,Overview" : "ProviderIds,MediaStreams ");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
request.AddQueryString("sortBy", "DateCreated");
@ -221,7 +221,7 @@ namespace Ombi.Api.Emby
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview,MediaStreams" : "ProviderIds,MediaStreams");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
if (!string.IsNullOrEmpty(parentIdFilder))

@ -1,10 +0,0 @@
namespace Ombi.Api.Emby.Models
{
public enum EmbyMediaType
{
Movie = 0,
Series = 1,
Music = 2,
Episode = 3
}
}

@ -2,35 +2,6 @@ namespace Ombi.Api.Emby.Models.Movie
{
public class EmbyMediastream
{
public string Codec { get; set; }
public string Language { get; set; }
public string TimeBase { get; set; }
public string CodecTimeBase { get; set; }
public string NalLengthSize { get; set; }
public bool IsInterlaced { get; set; }
public bool IsAVC { get; set; }
public int BitRate { get; set; }
public int BitDepth { get; set; }
public int RefFrames { get; set; }
public bool IsDefault { get; set; }
public bool IsForced { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public float AverageFrameRate { get; set; }
public float RealFrameRate { get; set; }
public string Profile { get; set; }
public string Type { get; set; }
public string AspectRatio { get; set; }
public int Index { get; set; }
public bool IsExternal { get; set; }
public bool IsTextSubtitleStream { get; set; }
public bool SupportsExternalStream { get; set; }
public string PixelFormat { get; set; }
public int Level { get; set; }
public bool IsAnamorphic { get; set; }
public string DisplayTitle { get; set; }
public string ChannelLayout { get; set; }
public int Channels { get; set; }
public int SampleRate { get; set; }
}
}

@ -1,12 +1,10 @@
namespace Ombi.Api.Emby.Models.Movie
using Ombi.Api.MediaServer.Models;
namespace Ombi.Api.Emby.Models.Movie
{
public class EmbyProviderids
public class EmbyProviderids: BaseProviderids
{
public string Tmdb { get; set; }
public string Imdb { get; set; }
public string TmdbCollection { get; set; }
public string Tvdb { get; set; }
public string Zap2It { get; set; }
public string TvRage { get; set; }
}

@ -30,5 +30,6 @@ namespace Ombi.Api.Emby.Models.Movie
public int CriticRating { get; set; }
public string Overview { get; set; }
public EmbyProviderids ProviderIds { get; set; }
public EmbyMediastream[] MediaStreams { get; set; }
}
}

@ -6,12 +6,13 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
<ProjectReference Include="..\Ombi.Api.MediaServer\Ombi.Api.MediaServer.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
</ItemGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -157,7 +157,7 @@ namespace Ombi.Api.Jellyfin
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview,ParentId" : "ProviderIds,ParentId");
request.AddQueryString("Fields", includeOverview ? "ProviderIds,MediaStreams Overview,ParentId" : "ProviderIds,ParentId,MediaStreams");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
if(!string.IsNullOrEmpty(parentIdFilder))

@ -1,10 +0,0 @@
namespace Ombi.Api.Jellyfin.Models
{
public enum JellyfinMediaType
{
Movie = 0,
Series = 1,
Music = 2,
Episode = 3
}
}

@ -2,35 +2,6 @@ namespace Ombi.Api.Jellyfin.Models.Movie
{
public class JellyfinMediastream
{
public string Codec { get; set; }
public string Language { get; set; }
public string TimeBase { get; set; }
public string CodecTimeBase { get; set; }
public string NalLengthSize { get; set; }
public bool IsInterlaced { get; set; }
public bool IsAVC { get; set; }
public int BitRate { get; set; }
public int BitDepth { get; set; }
public int RefFrames { get; set; }
public bool IsDefault { get; set; }
public bool IsForced { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public float AverageFrameRate { get; set; }
public float RealFrameRate { get; set; }
public string Profile { get; set; }
public string Type { get; set; }
public string AspectRatio { get; set; }
public int Index { get; set; }
public bool IsExternal { get; set; }
public bool IsTextSubtitleStream { get; set; }
public bool SupportsExternalStream { get; set; }
public string PixelFormat { get; set; }
public int Level { get; set; }
public bool IsAnamorphic { get; set; }
public string DisplayTitle { get; set; }
public string ChannelLayout { get; set; }
public int Channels { get; set; }
public int SampleRate { get; set; }
}
}

@ -1,12 +1,10 @@
namespace Ombi.Api.Jellyfin.Models.Movie
using Ombi.Api.MediaServer.Models;
namespace Ombi.Api.Jellyfin.Models.Movie
{
public class JellyfinProviderids
public class JellyfinProviderids: BaseProviderids
{
public string Tmdb { get; set; }
public string Imdb { get; set; }
public string TmdbCollection { get; set; }
public string Tvdb { get; set; }
public string Zap2It { get; set; }
public string TvRage { get; set; }
}

@ -30,5 +30,6 @@ namespace Ombi.Api.Jellyfin.Models.Movie
public int CriticRating { get; set; }
public string Overview { get; set; }
public JellyfinProviderids ProviderIds { get; set; }
public JellyfinMediastream[] MediaStreams { get; set; }
}
}

@ -6,13 +6,14 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
<ProjectReference Include="..\Ombi.Api.MediaServer\Ombi.Api.MediaServer.csproj" />
</ItemGroup>
</Project>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -0,0 +1,11 @@
namespace Ombi.Api.MediaServer.Models
{
public class BaseProviderids
{
public string Tmdb { get; set; }
public string Imdb { get; set; }
public string Tvdb { get; set; }
public bool Any() =>
!string.IsNullOrEmpty(Imdb) || !string.IsNullOrEmpty(Tmdb) || !string.IsNullOrEmpty(Tvdb);
}
}

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
</ItemGroup>
</Project>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Ombi.Api.Plex.Models;
using Ombi.Api.Plex.Models.Friends;
@ -16,9 +17,9 @@ namespace Ombi.Api.Plex
Task<PlexServer> GetServer(string authToken);
Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost);
Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId);
Task<PlexMetadata> GetEpisodeMetaData(string authToken, string host, int ratingKey);
Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId);
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey);
Task<PlexMetadata> GetEpisodeMetaData(string authToken, string host, string ratingKey);
Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, string itemId);
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, string ratingKey);
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
Task<PlexFriends> GetUsers(string authToken);
Task<PlexAccount> GetAccount(string authToken);
@ -26,5 +27,7 @@ namespace Ombi.Api.Plex
Task<OAuthContainer> GetPin(int pinId);
Task<Uri> GetOAuthUrl(string code, string applicationUrl);
Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs);
Task<PlexWatchlistContainer> GetWatchlist(string plexToken, CancellationToken cancellationToken);
Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken);
}
}

@ -4,7 +4,7 @@ namespace Ombi.Api.Plex.Models
{
public class Metadata
{
public int ratingKey { get; set; }
public string ratingKey { get; set; }
public string key { get; set; }
public string studio { get; set; }
public string type { get; set; }
@ -12,26 +12,18 @@ namespace Ombi.Api.Plex.Models
public string contentRating { get; set; }
public string summary { get; set; }
public int index { get; set; }
public float rating { get; set; }
//public int viewCount { get; set; }
//public int lastViewedAt { get; set; }
public int year { get; set; }
public string thumb { get; set; }
public string art { get; set; }
public string banner { get; set; }
public string theme { get; set; }
//public string duration { get; set; }
//public string originallyAvailableAt { get; set; }
public int leafCount { get; set; }
public int viewedLeafCount { get; set; }
public int childCount { get; set; }
//public long addedAt { get; set; }
//public int updatedAt { get; set; }
public Genre[] Genre { get; set; }
//public Role[] Role { get; set; }
public string primaryExtraKey { get; set; }
public int parentRatingKey { get; set; }
public int grandparentRatingKey { get; set; }
public string parentRatingKey { get; set; }
public string grandparentRatingKey { get; set; }
public string guid { get; set; }
public int librarySectionID { get; set; }
public string librarySectionKey { get; set; }
@ -47,12 +39,10 @@ namespace Ombi.Api.Plex.Models
public string chapterSource { get; set; }
public Medium[] Media { get; set; }
public List<PlexGuids> Guid { get; set; } = new List<PlexGuids>();
// public Director[] Director { get; set; }
// public Writer[] Writer { get; set; }
}
public class PlexGuids
{
public string Id { get; set; }
}
}
}

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Ombi.Api.Plex.Models
{
public class PlexWatchlist
{
public string librarySectionID { get; set; }
public string librarySectionTitle { get; set; }
public int offset { get; set; }
public int totalSize { get; set; }
public int size { get; set; }
public List<Metadata> Metadata { get; set; } = new List<Metadata>();
}
}

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Ombi.Api.Plex.Models
{
public class PlexWatchlistContainer
{
public PlexWatchlist MediaContainer { get; set; }
}
}

@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace Ombi.Api.Plex.Models
{
public class PlexWatchlistMetadataContainer
{
public PlexWatchlistMetadata MediaContainer { get; set; }
}
public class PlexWatchlistMetadata
{
public int offset { get; set; }
public int totalSize { get; set; }
public string identifier { get; set; }
public int size { get; set; }
public WatchlistMetadata[] Metadata { get; set; }
}
public class WatchlistMetadata
{
public string guid { get; set; }
public string key { get; set; }
public string primaryExtraKey { get; set; }
public string ratingKey { get; set; }
public string type { get; set; }
public string slug { get; set; }
public string title { get; set; }
public List<PlexGuids> Guid { get; set; } = new List<PlexGuids>();
}
}

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -1,5 +1,6 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ombi.Api.Plex.Models;
@ -66,6 +67,7 @@ namespace Ombi.Api.Plex
private const string FriendsUri = "https://plex.tv/pms/friends/all";
private const string GetAccountUri = "https://plex.tv/users/account.json";
private const string ServerUri = "https://plex.tv/pms/servers.xml";
private const string WatchlistUri = "https://metadata.provider.plex.tv/";
/// <summary>
/// Sign into the Plex API
@ -145,21 +147,21 @@ namespace Ombi.Api.Plex
/// <param name="authToken"></param>
/// <param name="plexFullHost"></param>
/// <param name="ratingKey"></param>
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, string ratingKey)
{
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request);
}
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, string itemId)
{
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request);
}
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, string ratingKey)
{
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
await AddHeaders(request, authToken);
@ -288,6 +290,26 @@ namespace Ombi.Api.Plex
}
}
public async Task<PlexWatchlistContainer> GetWatchlist(string plexToken, CancellationToken cancellationToken)
{
var request = new Request("library/sections/watchlist/all", WatchlistUri, HttpMethod.Get);
await AddHeaders(request, plexToken);
var result = await Api.Request<PlexWatchlistContainer>(request, cancellationToken);
return result;
}
public async Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken)
{
var request = new Request($"library/metadata/{ratingKey}", WatchlistUri, HttpMethod.Get);
await AddHeaders(request, plexToken);
var result = await Api.Request<PlexWatchlistMetadataContainer>(request, cancellationToken);
return result;
}
/// <summary>
/// Adds the required headers and also the authorization header

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -44,7 +44,10 @@ namespace Ombi.Api.Radarr.Models
public int id { get; set; }
}
public class MovieQuality
{
public V3.Quality quality { get; set; }
}
public class Moviefile
{
public int movieId { get; set; }
@ -54,7 +57,7 @@ namespace Ombi.Api.Radarr.Models
public DateTime dateAdded { get; set; }
public string sceneName { get; set; }
public int indexerFlags { get; set; }
public V3.Quality quality { get; set; }
public MovieQuality quality { get; set; }
public Mediainfo mediaInfo { get; set; }
public string originalFilePath { get; set; }
public bool qualityCutoffNotMet { get; set; }
@ -74,13 +77,10 @@ namespace Ombi.Api.Radarr.Models
public class Mediainfo
{
public string audioAdditionalFeatures { get; set; }
public int audioBitrate { get; set; }
public float audioChannels { get; set; }
public string audioCodec { get; set; }
public string audioLanguages { get; set; }
public int audioStreamCount { get; set; }
public int videoBitDepth { get; set; }
public int videoBitrate { get; set; }
public string videoCodec { get; set; }
public float videoFps { get; set; }
public string resolution { get; set; }
@ -119,4 +119,4 @@ namespace Ombi.Api.Radarr.Models
public int id { get; set; }
public string name { get; set; }
}
}
}

@ -25,5 +25,4 @@
public int resolution { get; set; }
public string modifier { get; set; }
}
}

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -8,7 +8,7 @@
<PackageVersion></PackageVersion>
<AssemblyName>Ombi.Api.Service</AssemblyName>
<RootNamespace>Ombi.Api.Service</RootNamespace>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -8,10 +8,8 @@ namespace Ombi.Api.TvMaze
public interface ITvMazeApi
{
Task<IEnumerable<TvMazeEpisodes>> EpisodeLookup(int showId);
Task<List<TvMazeSeasons>> GetSeasons(int id);
Task<List<TvMazeSearch>> Search(string searchTerm);
Task<TvMazeShow> ShowLookup(int showId);
Task<TvMazeShow> ShowLookupByTheTvDbId(int theTvDbId);
Task<FullSearch> GetTvFullInformation(int id);
}
}

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -64,27 +64,5 @@ namespace Ombi.Api.TvMaze
return null;
}
}
public async Task<List<TvMazeSeasons>> GetSeasons(int id)
{
var request = new Request($"shows/{id}/seasons", Uri, HttpMethod.Get);
request.AddContentHeader("Content-Type", "application/json");
return await Api.Request<List<TvMazeSeasons>>(request);
}
public async Task<FullSearch> GetTvFullInformation(int id)
{
var request = new Request($"shows/{id}", Uri, HttpMethod.Get);
request.AddQueryString("embed[]", "cast");
request.AddQueryString("embed[]", "crew");
request.AddQueryString("embed[]", "episodes");
request.AddContentHeader("Content-Type", "application/json");
return await Api.Request<FullSearch>(request);
}
}
}

@ -6,7 +6,7 @@
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup>

@ -10,6 +10,7 @@ using Ombi.Core.Authentication;
using Ombi.Core.Engine.V2;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository.Requests;
using Ombi.Core.Helpers;
namespace Ombi.Core.Tests.Engine
{
@ -25,7 +26,7 @@ namespace Ombi.Core.Tests.Engine
{
MovieRepo = new Mock<IMovieRequestRepository>();
TvRepo = new Mock<ITvRequestRepository>();
var principle = new Mock<IPrincipal>();
var principle = new Mock<ICurrentUser>();
var identity = new Mock<IIdentity>();
identity.Setup(x => x.Name).Returns("UnitTest");
principle.Setup(x => x.Identity).Returns(identity.Object);

@ -1,135 +1,520 @@
//using System.Collections.Generic;
//using System.Linq;
//using System.Threading.Tasks;
//using Moq;
//using NUnit.Framework;
//using Ombi.Core.Engine;
//using Ombi.Core.Models.Requests;
//using Ombi.Store.Entities.Requests;
//using Ombi.Store.Repository;
//using Assert = Xunit.Assert;
//namespace Ombi.Core.Tests.Engine
//{
// [TestFixture]
// public class MovieRequestEngineTests
// {
// public MovieRequestEngineTests()
// {
// RequestService = new Mock<IMovieRequestRepository>();
// var requestService = new RequestService(null, RequestService.Object);
// Engine = new MovieRequestEngine(null, requestService, null, null, null, null, null, null);
// }
// private MovieRequestEngine Engine { get; }
// private Mock<IMovieRequestRepository> RequestService { get; }
// [Test]
// public async Task GetNewRequests_Should_ReturnEmpty_WhenThereAreNoNewRequests()
// {
// var requests = new List<MovieRequests>
// {
// new MovieRequests { Available = true },
// new MovieRequests { Approved = true },
// };
// var r = DbHelper.GetQueryable(requests[0], requests[1]);
// RequestService.Setup(x => x.Get()).Returns(r);
// var result = await Engine.GetNewRequests();
// Assert.False(result.Any());
// }
// [Test]
// public async Task GetNewRequests_Should_ReturnOnlyNewRequests_WhenThereAreMultipleRequests()
// {
// var requests = new List<MovieRequests>
// {
// new MovieRequests { Available = true },
// new MovieRequests { Approved = true },
// new MovieRequests(),
// };
// RequestService.Setup(x => x.Get()).Returns(requests.AsQueryable);
// var result = await Engine.GetNewRequests();
// Assert.Single(result);
// Assert.All(result, x =>
// {
// Assert.False(x.Available);
// Assert.False(x.Approved);
// });
// }
// [Test]
// public async Task GetApprovedRequests_Should_ReturnEmpty_WhenThereAreNoApprovedRequests()
// {
// var requests = new List<MovieRequests>
// {
// new MovieRequests { Available = true },
// };
// RequestService.Setup(x => x.Get()).Returns(requests.AsQueryable);
// var result = await Engine.GetApprovedRequests();
// Assert.False(result.Any());
// }
// [Test]
// public async Task GetApprovedRequests_Should_ReturnOnlyApprovedRequests_WhenThereAreMultipleRequests()
// {
// var requests = new List<MovieRequests>
// {
// new MovieRequests { Available = true },
// new MovieRequests { Approved = true },
// new MovieRequests(),
// };
// RequestService.Setup(x => x.Get()).Returns(requests.AsQueryable);
// var result = await Engine.GetApprovedRequests();
// Assert.Single(result);
// Assert.All(result, x =>
// {
// Assert.False(x.Available);
// Assert.True(x.Approved);
// });
// }
// [Test]
// public async Task GetAvailableRequests_Should_ReturnEmpty_WhenThereAreNoAvailableRequests()
// {
// var requests = new List<MovieRequests>
// {
// new MovieRequests { Approved = true },
// };
// RequestService.Setup(x => x.Get()).Returns(requests.AsQueryable);
// var result = await Engine.GetAvailableRequests();
// Assert.False(result.Any());
// }
// [Test]
// public async Task GetAvailableRequests_Should_ReturnOnlyAvailableRequests_WhenThereAreMultipleRequests()
// {
// var requests = new List<MovieRequests>
// {
// new MovieRequests { Available = true },
// new MovieRequests { Approved = true },
// new MovieRequests(),
// };
// RequestService.Setup(x => x.Get()).Returns(requests.AsQueryable);
// var result = await Engine.GetAvailableRequests();
// Assert.Single(result);
// Assert.All(result, x =>
// {
// Assert.True(x.Available);
// Assert.False(x.Approved);
// });
// }
// }
//}
using MockQueryable.Moq;
using Moq;
using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
using Ombi.Test.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Tests.Engine
{
[TestFixture]
public class MovieRequestEngineTests
{
private MovieRequestEngine _subject;
private Mock<IMovieRequestRepository> _repoMock;
private AutoMocker _mocker;
[SetUp]
public void Setup()
{
_mocker = new AutoMocker();
var userManager = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { NormalizedUserName = "TEST", Id = "a" } });
userManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(true);
var principle = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
identity.Setup(x => x.Name).Returns("Test");
principle.Setup(x => x.Identity).Returns(identity.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identity.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { NormalizedUserName = "TEST", Id = "a" });
_repoMock = new Mock<IMovieRequestRepository>();
var requestServiceMock = new Mock<IRequestServiceMain>();
requestServiceMock.Setup(x => x.MovieRequestService).Returns(_repoMock.Object);
_mocker.Use(principle.Object);
_mocker.Use(currentUser.Object);
_mocker.Use(userManager.Object);
_mocker.Use(requestServiceMock);
_subject = _mocker.CreateInstance<MovieRequestEngine>();
var list = DbHelper.GetQueryableMockDbSet(new RequestSubscription());
_mocker.Setup<IRepository<RequestSubscription>, IQueryable<RequestSubscription>>(x => x.GetAll()).Returns(new List<RequestSubscription>().AsQueryable().BuildMock().Object);
}
[Test]
public async Task GetRequestByStatus_PendingRequests_Non4K()
{
var movies = RegularRequestData();
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", Models.Requests.RequestStatus.PendingApproval);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(4));
}
[Test]
public async Task GetRequestByStatus_PendingRequests_4K()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.MinValue
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.PendingApproval);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(4));
}
[Test]
public async Task GetRequestByStatus_PendingRequests_Both4K_And_Regular()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved = false,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.MinValue
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.PendingApproval);
Assert.That(result.Total, Is.EqualTo(2));
Assert.That(result.Collection.First().Id, Is.EqualTo(1));
Assert.That(result.Collection.ToArray()[1].Id, Is.EqualTo(4));
}
[Test]
public async Task GetRequestByStatus_ProcessingRequests_Non4K()
{
var movies = RegularRequestData();
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", Models.Requests.RequestStatus.ProcessingRequest);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(1));
}
[Test]
public async Task GetRequestByStatus_ProcessingRequests_4K()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.MinValue
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.ProcessingRequest);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(1));
}
[Test]
public async Task GetRequestByStatus_ProcessingRequests_Both4K_And_Regular()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved = false,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Approved = true,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.Now
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.ProcessingRequest);
Assert.That(result.Total, Is.EqualTo(2));
Assert.That(result.Collection.First().Id, Is.EqualTo(1));
Assert.That(result.Collection.ToArray()[1].Id, Is.EqualTo(4));
}
[Test]
public async Task GetRequestByStatus_AvailableRequests_Non4K()
{
List<MovieRequests> movies = RegularRequestData();
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", Models.Requests.RequestStatus.Available);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(2));
}
[Test]
public async Task GetRequestByStatus_AvailableRequests_4K()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.MinValue
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.Available);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(2));
}
[Test]
public async Task GetRequestByStatus_AvailableRequests_Both4K_And_Regular()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Available = true,
Approved = false,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Approved = true,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.Now
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.Available);
Assert.That(result.Total, Is.EqualTo(2));
Assert.That(result.Collection.First().Id, Is.EqualTo(1));
Assert.That(result.Collection.ToArray()[1].Id, Is.EqualTo(2));
}
[Test]
public async Task GetRequestByStatus_DeniedRequests_Non4K()
{
List<MovieRequests> movies = RegularRequestData();
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", Models.Requests.RequestStatus.Denied);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(3));
}
[Test]
public async Task GetRequestByStatus_DeniedRequests_4K()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.MinValue
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.Denied);
Assert.That(result.Total, Is.EqualTo(1));
Assert.That(result.Collection.First().Id, Is.EqualTo(3));
}
[Test]
public async Task GetRequestByStatus_DeniedRequests_Both4K_And_Regular()
{
var movies = new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Available = true,
Approved = false,
Approved4K = true,
Has4KRequest = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 2,
Approved4K = false,
Available4K = true,
Denied = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 3,
Denied4K = true,
Has4KRequest = true,
RequestedDate = DateTime.MinValue
},
new MovieRequests
{
Id = 4,
Has4KRequest = true,
Approved4K = false,
Approved = true,
Available4K = false,
Denied4K = false,
RequestedDate = DateTime.Now
}
};
_repoMock.Setup(x => x.GetWithUser()).Returns(movies.AsQueryable());
var result = await _subject.GetRequestsByStatus(10, 0, "id", "asc", RequestStatus.Denied);
Assert.That(result.Total, Is.EqualTo(2));
Assert.That(result.Collection.First().Id, Is.EqualTo(2));
Assert.That(result.Collection.ToArray()[1].Id, Is.EqualTo(3));
}
private static List<MovieRequests> RegularRequestData()
{
return new List<MovieRequests>
{
new MovieRequests
{
Id= 1,
Approved = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 2,
Approved = false,
Available = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 3,
Denied = true,
RequestedDate = DateTime.Now
},
new MovieRequests
{
Id = 4,
Approved = false,
RequestedDate = DateTime.Now
}
};
}
}
}

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
@ -36,6 +37,13 @@ namespace Ombi.Core.Tests.Engine
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identityMock.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "Test", NormalizedUserName = "TEST", Id = "a" });
_mocker.Use(currentUser.Object);
_mocker.Use(principleMock.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
@ -429,6 +437,7 @@ namespace Ombi.Core.Tests.Engine
Id = "id1"
};
var lastWeek = new DateTime(2020, 09, 05).AddMonths(-1).AddDays(-1);
var today = new DateTime(2020, 09, 15, 13, 0, 0);
var log = new List<RequestLog>
{
new RequestLog
@ -441,7 +450,7 @@ namespace Ombi.Core.Tests.Engine
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
var result = await _subject.GetRemainingMovieRequests(user, today);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
@ -474,7 +483,7 @@ namespace Ombi.Core.Tests.Engine
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
var result = await _subject.GetRemainingMovieRequests(user, today);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
@ -494,7 +503,7 @@ namespace Ombi.Core.Tests.Engine
MovieRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.UtcNow;
var today = new DateTime(2022,2,2,13,0,0);
var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
var log = new List<RequestLog>
{
@ -514,7 +523,7 @@ namespace Ombi.Core.Tests.Engine
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
var result = await _subject.GetRemainingMovieRequests(user, today);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
@ -36,7 +37,12 @@ namespace Ombi.Core.Tests.Engine
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identityMock.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "Test", NormalizedUserName = "TEST", Id = "a" });
_mocker.Use(principleMock.Object);
_mocker.Use(currentUser.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}
@ -494,7 +500,7 @@ namespace Ombi.Core.Tests.Engine
MusicRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.UtcNow;
var today = new DateTime(2022, 2, 2, 13, 0, 0);
var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
var log = new List<RequestLog>
{
@ -514,7 +520,7 @@ namespace Ombi.Core.Tests.Engine
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
var result = await _subject.GetRemainingMusicRequests(user, today);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)

@ -4,6 +4,7 @@ using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
@ -33,7 +34,12 @@ namespace Ombi.Core.Tests.Engine
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
var currentUser = new Mock<ICurrentUser>();
currentUser.Setup(x => x.Identity).Returns(identityMock.Object);
currentUser.Setup(x => x.Username).Returns("Test");
currentUser.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "Test", NormalizedUserName = "TEST", Id = "a" });
_mocker.Use(principleMock.Object);
_mocker.Use(currentUser.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}

@ -7,8 +7,10 @@ using Moq;
using NUnit.Framework;
using Ombi.Api.TheMovieDb;
using Ombi.Core.Engine;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Services;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Settings.Settings.Models;
@ -32,7 +34,7 @@ namespace Ombi.Core.Tests.Engine.V2
var requestService = new Mock<IRequestServiceMain>();
_movieRequestRepository = new Mock<IMovieRequestRepository>();
requestService.Setup(x => x.MovieRequestService).Returns(_movieRequestRepository.Object);
var user = new Mock<IPrincipal>();
var user = new Mock<ICurrentUser>();
var notificationHelper = new Mock<INotificationHelper>();
var rules = new Mock<IRuleEvaluator>();
var movieSender = new Mock<IMovieSender>();
@ -43,8 +45,9 @@ namespace Ombi.Core.Tests.Engine.V2
var ombiSettings = new Mock<ISettingsService<OmbiSettings>>();
var requestSubs = new Mock<IRepository<RequestSubscription>>();
var mediaCache = new Mock<IMediaCacheService>();
var featureService = new Mock<IFeatureService>();
_engine = new MovieRequestEngine(movieApi.Object, requestService.Object, user.Object, notificationHelper.Object, rules.Object, movieSender.Object,
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object);
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object);
}
[Test]

@ -23,6 +23,7 @@ using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Test.Common;
using Artist = Hqub.MusicBrainz.API.Entities.Artist;
using Ombi.Core.Helpers;
namespace Ombi.Core.Tests.Engine.V2
{
@ -45,7 +46,7 @@ namespace Ombi.Core.Tests.Engine.V2
.ForEach(b => F.Behaviors.Remove(b));
F.Behaviors.Add(new OmitOnRecursionBehavior());
var principle = new Mock<IPrincipal>();
var principle = new Mock<ICurrentUser>();
var requestService = new Mock<IRequestServiceMain>();
var ruleEval = new Mock<IRuleEvaluator>();
var um = MockHelper.MockUserManager(new List<OmbiUser>());

@ -9,6 +9,7 @@ using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings;
@ -32,8 +33,9 @@ namespace Ombi.Core.Tests.Engine
TvRequestEngine = new Mock<ITvRequestEngine>();
MovieRequestEngine = new Mock<IMovieRequestEngine>();
MovieRequestEngine = new Mock<IMovieRequestEngine>();
User = new Mock<IPrincipal>();
User.Setup(x => x.Identity.Name).Returns("abc");
User = new Mock<ICurrentUser>();
User.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "abc", NormalizedUserName = "ABC", Id = "abc" });
UserManager = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserName = "abc", NormalizedUserName = "ABC" } });
Rule = new Mock<IRuleEvaluator>();
Engine = new VoteEngine(VoteRepository.Object, User.Object, UserManager.Object, Rule.Object, VoteSettings.Object, MusicRequestEngine.Object,
@ -48,7 +50,7 @@ namespace Ombi.Core.Tests.Engine
public Fixture F { get; set; }
public VoteEngine Engine { get; set; }
public Mock<IPrincipal> User { get; set; }
public Mock<ICurrentUser> User { get; set; }
public Mock<OmbiUserManager> UserManager { get; set; }
public Mock<IRuleEvaluator> Rule { get; set; }
public Mock<IRepository<Votes>> VoteRepository { get; set; }
@ -83,7 +85,7 @@ namespace Ombi.Core.Tests.Engine
Assert.That(result.Result, Is.True);
VoteRepository.Verify(x => x.Add(It.Is<Votes>(c => c.UserId == "abc" && c.VoteType == type)), Times.Once);
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1, false), Times.Never);
}
public static IEnumerable<TestCaseData> VoteData
{
@ -129,7 +131,7 @@ namespace Ombi.Core.Tests.Engine
Assert.That(result.Result, Is.False);
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1, false), Times.Never);
}
public static IEnumerable<TestCaseData> AttemptedTwiceData
{
@ -175,7 +177,7 @@ namespace Ombi.Core.Tests.Engine
Assert.That(result.Result, Is.True);
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Once);
VoteRepository.Verify(x => x.Add(It.Is<Votes>(v => v.VoteType == type)), Times.Once);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1, false), Times.Never);
}
public static IEnumerable<TestCaseData> VoteConvertData
{

@ -10,6 +10,8 @@ using Ombi.Test.Common;
using System.Collections.Generic;
using Ombi.Store.Entities;
using System;
using Ombi.Core.Services;
using Ombi.Core.Helpers;
namespace Ombi.Core.Tests.Rule.Request
{
@ -26,23 +28,26 @@ namespace Ombi.Core.Tests.Rule.Request
public void Setup()
{
PrincipalMock = new Mock<IPrincipal>();
PrincipalMock.Setup(x => x.Identity.Name).Returns("abc");
FeatureService = new Mock<IFeatureService>();
PrincipalMock = new Mock<ICurrentUser>();
PrincipalMock.Setup(x => x.Username).Returns("abc");
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "abc", NormalizedUserName = "ABC", Id = "a" });
UserManager = MockHelper.MockUserManager(_users);
Rule = new AutoApproveRule(PrincipalMock.Object, UserManager.Object);
Rule = new AutoApproveRule(PrincipalMock.Object, UserManager.Object, FeatureService.Object);
}
private AutoApproveRule Rule { get; set; }
private Mock<IPrincipal> PrincipalMock { get; set; }
private Mock<ICurrentUser> PrincipalMock { get; set; }
private Mock<OmbiUserManager> UserManager { get; set; }
private Mock<IFeatureService> FeatureService { get; set; }
[Test]
public async Task Should_ReturnSuccess_WhenAdminAndRequestMovie()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Admin)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
Assert.True(result.Success);
@ -64,7 +69,7 @@ namespace Ombi.Core.Tests.Rule.Request
public async Task Should_ReturnSuccess_WhenAutoApproveMovieAndRequestMovie()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
Assert.True(result.Success);
@ -96,7 +101,8 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenSystemUserAndRequestTV()
{
PrincipalMock.Setup(x => x.Identity.Name).Returns("sys");
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "sys", NormalizedUserName = "SYS", Id = "a" });
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveTv)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
@ -137,5 +143,17 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(result.Success);
Assert.False(request.Approved);
}
[Test]
public async Task Should_ReturnFail_When4kRequestAndFeatureNotEnabled()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(false);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true };
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.False(request.Approved);
Assert.False(request.Approved4K);
}
}
}

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Rule.Rules;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Helpers;
@ -26,8 +27,9 @@ namespace Ombi.Core.Tests.Rule.Request
public void Setup()
{
PrincipalMock = new Mock<IPrincipal>();
PrincipalMock.Setup(x => x.Identity.Name).Returns("abc");
PrincipalMock = new Mock<ICurrentUser>();
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "abc", NormalizedUserName = "ABC", Id = "a" });
UserManager = MockHelper.MockUserManager(_users);
Rule = new CanRequestRule(PrincipalMock.Object, UserManager.Object);
@ -35,14 +37,49 @@ namespace Ombi.Core.Tests.Rule.Request
private CanRequestRule Rule { get; set; }
private Mock<IPrincipal> PrincipalMock { get; set; }
private Mock<ICurrentUser> PrincipalMock { get; set; }
private Mock<OmbiUserManager> UserManager { get; set; }
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithMovieRole()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
Assert.True(result.Success);
}
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovie4KWithMovieRole()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Has4KRequest = true };
var result = await Rule.Execute(request);
Assert.True(result.Success);
}
[Test]
public async Task Should_ReturnFailure_WhenRequestingMovie4KWithMovieRole()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(false);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true };
var result = await Rule.Execute(request);
Assert.False(result.Success);
Assert.False(string.IsNullOrEmpty(result.Message));
}
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovie4KWithAutoApprove()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(false);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Has4KRequest = true };
var result = await Rule.Execute(request);
Assert.True(result.Success);
@ -52,7 +89,7 @@ namespace Ombi.Core.Tests.Rule.Request
public async Task Should_ReturnFail_WhenRequestingMovieWithoutMovieRole()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
Assert.False(result.Success);
@ -72,7 +109,8 @@ namespace Ombi.Core.Tests.Rule.Request
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithSystemRole()
{
PrincipalMock.Setup(x => x.Identity.Name).Returns("sys");
PrincipalMock.Setup(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "sys", NormalizedUserName = "SYS", Id = "a" });
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Admin)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);

@ -9,7 +9,9 @@ using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Rule.Rules;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Core.Services;
using Ombi.Helpers;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository.Requests;
@ -24,12 +26,14 @@ namespace Ombi.Core.Tests.Rule.Request
public void Setup()
{
ContextMock = new Mock<IMovieRequestRepository>();
Rule = new ExistingMovieRequestRule(ContextMock.Object);
FeatureService = new Mock<IFeatureService>();
Rule = new ExistingMovieRequestRule(ContextMock.Object, FeatureService.Object);
}
private ExistingMovieRequestRule Rule { get; set; }
private Mock<IMovieRequestRepository> ContextMock { get; set; }
private Mock<IFeatureService> FeatureService { get; set; }
[Test]
public async Task ExistingRequestRule_Movie_Has_Been_Requested_With_TheMovieDBId()
@ -96,5 +100,82 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.That(result.Success, Is.True);
Assert.That(result.Message, Is.Null.Or.Empty);
}
[Test]
public async Task ExistingRequestRule_Movie_HasAlready4K_Request()
{
ContextMock.Setup(x => x.GetAll()).Returns(new List<MovieRequests>
{
new MovieRequests
{
TheMovieDbId = 2,
ImdbId = "2",
RequestType = RequestType.Movie,
Is4kRequest = true
}
}.AsQueryable().BuildMock().Object);
var o = new MovieRequests
{
TheMovieDbId = 2,
ImdbId = "1",
Has4KRequest = true
};
var result = await Rule.Execute(o);
Assert.That(result.Success, Is.False);
Assert.That(result.Message, Is.Not.Empty);
}
[Test]
public async Task ExistingRequestRule_Movie_4K_Request()
{
FeatureService.Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
ContextMock.Setup(x => x.GetAll()).Returns(new List<MovieRequests>
{
new MovieRequests
{
TheMovieDbId = 2,
ImdbId = "2",
RequestType = RequestType.Movie,
Is4kRequest = false
}
}.AsQueryable().BuildMock().Object);
var o = new MovieRequests
{
TheMovieDbId = 2,
ImdbId = "1",
Is4kRequest = true
};
var result = await Rule.Execute(o);
Assert.That(result.Success, Is.True);
Assert.That(result.Message, Is.Null.Or.Empty);
}
[Test]
public async Task ExistingRequestRule_Movie_4K_Request_FeatureNotEnabled()
{
FeatureService.Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(false);
ContextMock.Setup(x => x.GetAll()).Returns(new List<MovieRequests>
{
new MovieRequests
{
TheMovieDbId = 2,
ImdbId = "2",
RequestType = RequestType.Movie,
Is4kRequest = false
}
}.AsQueryable().BuildMock().Object);
var o = new MovieRequests
{
TheMovieDbId = 2,
ImdbId = "1",
Is4kRequest = true
};
var result = await Rule.Execute(o);
Assert.That(result.Success, Is.False);
Assert.That(result.Message, Is.Not.Null);
}
}
}

@ -1,6 +1,7 @@
using MockQueryable.Moq;
using Moq;
using NUnit.Framework;
using Ombi.Core.Engine;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
@ -176,17 +177,58 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.That(result.Success, Is.True);
}
[Test]
public async Task RequestMovie_IsSuccessful()
{
SetupMockData();
var req = new MovieRequests
{
RequestType = RequestType.Movie,
TheMovieDbId = 123,
Id = 1,
};
var result = await Rule.Execute(req);
Assert.That(result.Success, Is.True);
}
[Test]
public async Task RequestMovie_IsAlreadyAvailable()
{
var content = new List<PlexServerContent> {
new PlexServerContent
{
TheMovieDbId = 123.ToString(),
}
};
PlexContentRepo.Setup(x => x.GetAll()).Returns(content.AsQueryable().BuildMock().Object);
var req = new MovieRequests
{
RequestType = RequestType.Movie,
TheMovieDbId = 123,
Id = 1,
};
var result = await Rule.Execute(req);
Assert.That(result.Success, Is.False);
Assert.That(result.ErrorCode, Is.EqualTo(ErrorCode.AlreadyRequested));
}
private void SetupMockData()
{
var childRequests = new List<PlexServerContent>
{
new PlexServerContent
{
Type = PlexMediaTypeEntity.Show,
Type = MediaType.Series,
TheMovieDbId = "1",
Title = "Test",
ReleaseYear = "2001",
Episodes = new List<PlexEpisode>
Episodes = new List<IMediaServerEpisode>
{
new PlexEpisode
{

@ -1,11 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Rules.Search;
using Ombi.Core.Services;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
@ -18,21 +21,23 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup()
{
ContextMock = new Mock<IEmbyContentRepository>();
SettingsMock = new Mock<ISettingsService<EmbySettings>>();
Rule = new EmbyAvailabilityRule(ContextMock.Object, SettingsMock.Object);
LoggerMock = new Mock<ILogger<EmbyAvailabilityRule>>();
FeatureMock = new Mock<IFeatureService>();
Rule = new EmbyAvailabilityRule(ContextMock.Object, LoggerMock.Object, FeatureMock.Object);
}
private EmbyAvailabilityRule Rule { get; set; }
private Mock<IEmbyContentRepository> ContextMock { get; set; }
private Mock<ISettingsService<EmbySettings>> SettingsMock { get; set; }
private Mock<ILogger<EmbyAvailabilityRule>> LoggerMock { get; set; }
private Mock<IFeatureService> FeatureMock { get; set; }
[Test]
public async Task Movie_ShouldBe_Available_WhenFoundInEmby()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings());
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
{
ProviderId = "123"
TheMovieDbId = "123",
Quality = "1"
});
var search = new SearchMovieViewModel()
{
@ -45,24 +50,13 @@ namespace Ombi.Core.Tests.Rule.Search
}
[Test]
public async Task Movie_Has_Custom_Url_When_Specified_In_Settings()
public async Task Movie_ShouldBe_Available_WhenFoundInEmby_4K()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings
{
Enable = true,
Servers = new List<EmbyServers>
{
new EmbyServers
{
ServerHostname = "http://test.com/",
ServerId = "8"
}
}
});
FeatureMock.Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
{
ProviderId = "123",
EmbyId = 1.ToString(),
TheMovieDbId = "123",
Has4K = true
});
var search = new SearchMovieViewModel()
{
@ -71,28 +65,19 @@ namespace Ombi.Core.Tests.Rule.Search
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/web/index.html#!/item?id=1&serverId=8"));
Assert.True(search.Available4K);
Assert.False(search.Available);
}
[Test]
public async Task Movie_Uses_Default_Url_When()
public async Task Movie_ShouldBe_Available_WhenFoundInEmby_Both()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new EmbySettings
{
Enable = true,
Servers = new List<EmbyServers>
{
new EmbyServers
{
ServerHostname = string.Empty,
ServerId = "8"
}
}
});
FeatureMock.Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
{
ProviderId = "123",
EmbyId = 1.ToString()
TheMovieDbId = "123",
Has4K = true,
Quality = "1"
});
var search = new SearchMovieViewModel()
{
@ -101,7 +86,8 @@ namespace Ombi.Core.Tests.Rule.Search
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/web/index.html#!/item?id=1&serverId=8"));
Assert.True(search.Available4K);
Assert.True(search.Available);
}
[Test]

@ -30,13 +30,14 @@ namespace Ombi.Core.Tests.Rule.Search
[Test]
public async Task ShouldBe_Requested_WhenExisitngMovie()
public async Task ShouldBe_Requested_WhenExistingMovie()
{
var list = new MovieRequests
{
TheMovieDbId = 123,
Approved = true,
RequestType = RequestType.Movie
RequestType = RequestType.Movie,
RequestedDate = System.DateTime.Now,
};
MovieMock.Setup(x => x.GetRequestAsync(123)).ReturnsAsync(list);

@ -1,11 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Rules.Search;
using Ombi.Core.Services;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
@ -18,21 +21,23 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup()
{
ContextMock = new Mock<IJellyfinContentRepository>();
SettingsMock = new Mock<ISettingsService<JellyfinSettings>>();
Rule = new JellyfinAvailabilityRule(ContextMock.Object, SettingsMock.Object);
LoggerMock = new Mock<ILogger<JellyfinAvailabilityRule>>();
FeatureMock = new Mock<IFeatureService>();
Rule = new JellyfinAvailabilityRule(ContextMock.Object, LoggerMock.Object, FeatureMock.Object);
}
private JellyfinAvailabilityRule Rule { get; set; }
private Mock<IJellyfinContentRepository> ContextMock { get; set; }
private Mock<ISettingsService<JellyfinSettings>> SettingsMock { get; set; }
private Mock<ILogger<JellyfinAvailabilityRule>> LoggerMock { get; set; }
private Mock<IFeatureService> FeatureMock { get; set; }
[Test]
public async Task Movie_ShouldBe_Available_WhenFoundInJellyfin()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings());
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
{
ProviderId = "123"
TheMovieDbId = "123",
Quality = "1080"
});
var search = new SearchMovieViewModel()
{
@ -45,24 +50,34 @@ namespace Ombi.Core.Tests.Rule.Search
}
[Test]
public async Task Movie_Has_Custom_Url_When_Specified_In_Settings()
public async Task Movie_ShouldBe_Available_WhenFoundInJellyfin_4K()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings
FeatureMock.Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
{
Enable = true,
Servers = new List<JellyfinServers>
{
new JellyfinServers
{
ServerHostname = "http://test.com/",
ServerId = "8"
}
}
TheMovieDbId = "123",
Has4K = true
});
var search = new SearchMovieViewModel()
{
TheMovieDbId = "123",
};
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.False(search.Available);
Assert.True(search.Available4K);
}
[Test]
public async Task Movie_ShouldBe_Available_WhenFoundInJellyfin_Both()
{
FeatureMock.Setup(x => x.FeatureEnabled(FeatureNames.Movie4KRequests)).ReturnsAsync(true);
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
{
ProviderId = "123",
JellyfinId = 1.ToString(),
TheMovieDbId = "123",
Has4K = true,
Quality = "1"
});
var search = new SearchMovieViewModel()
{
@ -71,29 +86,16 @@ namespace Ombi.Core.Tests.Rule.Search
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.JellyfinUrl, Is.EqualTo("http://test.com/web/index.html#!/details?id=1&serverId=8"));
Assert.True(search.Available);
Assert.True(search.Available4K);
}
[Test]
public async Task Movie_Uses_Default_Url_When()
{
SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings
{
Enable = true,
Servers = new List<JellyfinServers>
{
new JellyfinServers
{
Ip = "8080",
Port = 9090,
ServerHostname = string.Empty,
ServerId = "8"
}
}
});
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new JellyfinContent
{
ProviderId = "123",
TheMovieDbId = "123",
JellyfinId = 1.ToString()
});
var search = new SearchMovieViewModel()

@ -28,25 +28,67 @@ namespace Ombi.Core.Tests.Rule.Search
{
var list = new List<RadarrCache>(){new RadarrCache
{
TheMovieDbId = 123
TheMovieDbId = 123,
HasRegular = true
}}.AsQueryable();
ContextMock.Setup(x => x.GetAll()).Returns(list);
var request = new SearchMovieViewModel { Id = 123 };
var result =await Rule.Execute(request);
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Approved);
}
[Test]
public async Task Should_ReturnAvailabl_WhenMovieIsInRadarr_4K()
{
var list = new List<RadarrCache>(){new RadarrCache
{
TheMovieDbId = 123,
Has4K = true,
HasFile = true
}}.AsQueryable();
ContextMock.Setup(x => x.GetAll()).Returns(list);
var request = new SearchMovieViewModel { Id = 123 };
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.False(request.Available);
Assert.True(request.Available4K);
}
[Test]
public async Task Should_ReturnAvailable_WhenMovieIsInRadarr_Both()
{
var list = new List<RadarrCache>(){new RadarrCache
{
TheMovieDbId = 123,
Has4K = true,
HasRegular = true,
HasFile = true
}}.AsQueryable();
ContextMock.Setup(x => x.GetAll()).Returns(list);
var request = new SearchMovieViewModel { Id = 123 };
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Available);
Assert.True(request.Available4K);
}
[Test]
public async Task Should_ReturnNotApproved_WhenMovieIsNotInRadarr()
{
var list = DbHelper.GetQueryableMockDbSet(new RadarrCache
{
TheMovieDbId = 000012
TheMovieDbId = 000012,
});
ContextMock.Setup(x => x.GetAll()).Returns(list);

@ -116,16 +116,25 @@ namespace Ombi.Core.Authentication
public async Task<OmbiUser> GetOmbiUserFromPlexToken(string plexToken)
{
var plexAccount = await _plexApi.GetAccount(plexToken);
// Check for a ombi user
if (plexAccount?.user != null)
if (plexAccount?.user == null)
{
return null;
}
var potentialOmbiUser = await Users.FirstOrDefaultAsync(x =>
x.ProviderUserId == plexAccount.user.id);
// Update ombi user with the token
if (potentialOmbiUser != null)
{
var potentialOmbiUser = await Users.FirstOrDefaultAsync(x =>
x.ProviderUserId == plexAccount.user.id);
return potentialOmbiUser;
potentialOmbiUser.MediaServerToken = plexAccount.user.authentication_token;
await UpdateAsync(potentialOmbiUser);
}
return null;
return potentialOmbiUser;
}
@ -142,6 +151,10 @@ namespace Ombi.Core.Authentication
var result = await _plexApi.SignIn(new UserRequest { password = password, login = login });
if (result.user?.authentication_token != null)
{
// Update ombi user with the token
user.MediaServerToken = result.user?.authentication_token;
await UpdateAsync(user);
return true;
}
return false;

@ -26,7 +26,7 @@ namespace Ombi.Core.Engine
private Dictionary<int, MovieRequests> _dbMovies;
private Dictionary<int, TvRequests> _dbTv;
protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService,
protected BaseMediaEngine(ICurrentUser identity, IRequestServiceMain requestService,
IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub) : base(identity, um, rules)
{
RequestService = requestService;

@ -11,6 +11,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Config;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
@ -24,7 +25,7 @@ namespace Ombi.Core.Engine.Demo
{
public class DemoMovieSearchEngine : MovieSearchEngine, IDemoMovieSearchEngine
{
public DemoMovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
public DemoMovieSearchEngine(ICurrentUser 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)

@ -4,6 +4,7 @@ using Ombi.Api.Trakt;
using Ombi.Api.TvMaze;
using Ombi.Config;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
@ -24,7 +25,7 @@ namespace Ombi.Core.Engine.Demo
public class DemoTvSearchEngine : TvSearchEngine, IDemoTvSearchEngine
{
public DemoTvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
public DemoTvSearchEngine(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists, IImageService imageService,
ISettingsService<CustomizationSettings> custom)

@ -1,42 +1,35 @@
using System;
using Ombi.Core.Rule;
using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Entities;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Helpers;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine.Interfaces
{
public abstract class BaseEngine
{
protected BaseEngine(IPrincipal user, OmbiUserManager um, IRuleEvaluator rules)
protected BaseEngine(ICurrentUser user, OmbiUserManager um, IRuleEvaluator rules)
{
UserPrinciple = user;
CurrentUser = user;
Rules = rules;
UserManager = um;
}
protected IPrincipal UserPrinciple { get; }
protected ICurrentUser CurrentUser { get; }
protected IRuleEvaluator Rules { get; }
protected OmbiUserManager UserManager { get; }
protected string Username => UserPrinciple.Identity.Name;
protected OmbiUserManager UserManager { get; }
protected string Username => CurrentUser.Username;
protected Task<OmbiUser> GetUser() => CurrentUser.GetUser();
private OmbiUser _user;
protected async Task<OmbiUser> GetUser()
{
if(!Username.HasValue())
{
return null;
}
var username = Username.ToUpper();
return _user ??= await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}
/// <summary>
/// Only used for background tasks
/// </summary>
public void SetUser(OmbiUser user) => CurrentUser.SetUser(user);
protected async Task<string> UserAlias()
{
@ -45,9 +38,14 @@ namespace Ombi.Core.Engine.Interfaces
protected async Task<bool> IsInRole(string roleName)
{
return await UserManager.IsInRoleAsync(await GetUser(), roleName);
if (Username.Equals("API", StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
var user = await GetUser();
return await UserManager.IsInRoleAsync(user, roleName);
}
public async Task<IEnumerable<RuleResult>> RunRequestRules(BaseRequest model)
{
var ruleResults = await Rules.StartRequestRules(model);

@ -18,9 +18,9 @@ namespace Ombi.Core.Engine.Interfaces
Task RemoveAllMovieRequests();
Task<MovieRequests> GetRequest(int requestId);
Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
Task<RequestEngineResult> ApproveMovieById(int requestId);
Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason);
Task<RequestEngineResult> ApproveMovie(MovieRequests request, bool is4K);
Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K);
Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K);
Task<RequestsViewModel<MovieRequests>> GetRequests(int count, int position, string sortProperty, string sortOrder);
Task<RequestsViewModel<MovieRequests>> GetUnavailableRequests(int count, int position, string sortProperty,

@ -19,11 +19,12 @@ namespace Ombi.Core.Engine.Interfaces
Task<IEnumerable<T>> GetRequests();
Task<bool> UserHasRequest(string userId);
Task<RequestEngineResult> MarkUnavailable(int modelId);
Task<RequestEngineResult> MarkAvailable(int modelId);
Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K);
Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K);
Task<int> GetTotal();
Task UnSubscribeRequest(int requestId, RequestType type);
Task SubscribeToRequest(int requestId, RequestType type);
Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken);
Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken);
void SetUser(OmbiUser user);
}
}

@ -22,15 +22,18 @@ using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using Ombi.Core.Models;
using System.Threading;
using Ombi.Core.Services;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine
{
public class MovieRequestEngine : BaseMediaEngine, IMovieRequestEngine
{
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService)
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
IFeatureService featureService)
: base(user, requestService, r, manager, cache, ombiSettings, sub)
{
MovieApi = movieApi;
@ -39,6 +42,7 @@ namespace Ombi.Core.Engine
Logger = log;
_requestLog = rl;
_mediaCacheService = mediaCacheService;
_featureService = featureService;
}
private IMovieDbApi MovieApi { get; }
@ -47,6 +51,7 @@ namespace Ombi.Core.Engine
private ILogger<MovieRequestEngine> Logger { get; }
private readonly IRepository<RequestLog> _requestLog;
private readonly IMediaCacheService _mediaCacheService;
private readonly IFeatureService _featureService;
/// <summary>
/// Requests the movie.
@ -72,7 +77,8 @@ namespace Ombi.Core.Engine
var userDetails = await GetUser();
var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
if (canRequestOnBehalf && !isAdmin)
{
return new RequestEngineResult
@ -93,27 +99,54 @@ namespace Ombi.Core.Engine
};
}
var requestModel = new MovieRequests
var is4kFeatureEnabled = await _featureService.FeatureEnabled(FeatureNames.Movie4KRequests);
var is4kRequest = is4kFeatureEnabled && model.Is4kRequest;
MovieRequests requestModel;
bool isExisting = false;
// Do we already have a request? 4k or non 4k
var existingRequest = await MovieRepository.GetRequestAsync(movieInfo.Id);
if (existingRequest != null && is4kFeatureEnabled)
{
TheMovieDbId = movieInfo.Id,
RequestType = RequestType.Movie,
Overview = movieInfo.Overview,
ImdbId = movieInfo.ImdbId,
PosterPath = PosterPathHelper.FixPosterPath(movieInfo.PosterPath),
Title = movieInfo.Title,
ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate)
? DateTime.Parse(movieInfo.ReleaseDate)
: DateTime.MinValue,
Status = movieInfo.Status,
RequestedDate = DateTime.UtcNow,
Approved = false,
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
Background = movieInfo.BackdropPath,
LangCode = model.LanguageCode,
RequestedByAlias = model.RequestedByAlias,
RootPathOverride = model.RootFolderOverride.GetValueOrDefault(),
QualityOverride = model.QualityPathOverride.GetValueOrDefault()
};
if (model.Is4kRequest)
{
existingRequest.Is4kRequest = true;
existingRequest.RequestedDate4k = DateTime.Now;
}
else
{
existingRequest.RequestedDate = DateTime.Now;
}
isExisting = true;
requestModel = existingRequest;
}
else
{
requestModel = new MovieRequests
{
TheMovieDbId = movieInfo.Id,
RequestType = RequestType.Movie,
Overview = movieInfo.Overview,
ImdbId = movieInfo.ImdbId,
PosterPath = PosterPathHelper.FixPosterPath(movieInfo.PosterPath),
Title = movieInfo.Title,
ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate)
? DateTime.Parse(movieInfo.ReleaseDate)
: DateTime.MinValue,
Status = movieInfo.Status,
RequestedDate = model.Is4kRequest ? DateTime.MinValue : DateTime.Now,
Approved = false,
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
Background = movieInfo.BackdropPath,
LangCode = model.LanguageCode,
RequestedByAlias = model.RequestedByAlias,
RootPathOverride = model.RootFolderOverride.GetValueOrDefault(),
QualityOverride = model.QualityPathOverride.GetValueOrDefault(),
RequestedDate4k = model.Is4kRequest ? DateTime.Now : DateTime.MinValue,
Is4kRequest = model.Is4kRequest,
Source = model.Source
};
}
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
requestModel.DigitalReleaseDate = usDates?.ReleaseDate
@ -132,10 +165,10 @@ namespace Ombi.Core.Engine
if (requestModel.Approved) // The rules have auto approved this
{
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf, isExisting, is4kRequest);
if (requestEngineResult.Result)
{
var result = await ApproveMovie(requestModel);
var result = await ApproveMovie(requestModel, model.Is4kRequest);
if (result.IsError)
{
Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message);
@ -153,7 +186,7 @@ namespace Ombi.Core.Engine
// If there are no providers then it's successful but movie has not been sent
}
return await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
return await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf, isExisting, is4kRequest);
}
@ -218,7 +251,7 @@ namespace Ombi.Core.Engine
var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count)
.ToListAsync();
await CheckForSubscription(shouldHide, requests);
await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -262,7 +295,7 @@ namespace Ombi.Core.Engine
var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList();
await CheckForSubscription(shouldHide, requests);
await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -290,21 +323,44 @@ namespace Ombi.Core.Engine
switch (status)
{
case RequestStatus.PendingApproval:
allRequests = allRequests.Where(x => !x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value));
allRequests = allRequests.Where(x =>
(x.RequestedDate != DateTime.MinValue && !x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value))
||
(x.Has4KRequest && !x.Approved4K && !x.Available4K && (!x.Denied4K.HasValue || !x.Denied4K.Value))
);
break;
case RequestStatus.ProcessingRequest:
allRequests = allRequests.Where(x => x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value));
allRequests = allRequests.Where(x =>
(x.RequestedDate != DateTime.MinValue && x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value))
||
(x.Has4KRequest && x.Approved4K && !x.Available && (!x.Denied.HasValue || !x.Denied.Value))
);
break;
case RequestStatus.Available:
allRequests = allRequests.Where(x => x.Available);
allRequests = allRequests.Where(x => x.Available || x.Available4K);
break;
case RequestStatus.Denied:
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available);
allRequests = allRequests.Where(x =>
(x.Denied.HasValue && x.Denied.Value && !x.Available)
||
(x.Has4KRequest && x.Denied4K.HasValue && x.Denied4K.Value && !x.Available4K)
);
break;
default:
break;
}
var requests = allRequests.ToList();
var total = requests.Count;
if (total == 0)
{
return new RequestsViewModel<MovieRequests>
{
Collection = Enumerable.Empty<MovieRequests>(),
Total = total
};
}
var prop = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(sortProperty, true);
if (sortProperty.Contains('.'))
@ -317,14 +373,14 @@ namespace Ombi.Core.Engine
//var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true);
}
// TODO fix this so we execute this on the server
var requests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
requests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
? allRequests.ToList().OrderBy(x => prop.GetValue(x)).ToList()
: allRequests.ToList().OrderByDescending(x => prop.GetValue(x)).ToList();
var total = requests.Count();
// TODO fix this so we execute this on the server
requests = requests.Skip(position).Take(count).ToList();
await CheckForSubscription(shouldHide, requests);
await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -367,7 +423,7 @@ namespace Ombi.Core.Engine
var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList();
await CheckForSubscription(shouldHide, requests);
await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -449,7 +505,7 @@ namespace Ombi.Core.Engine
allRequests = await MovieRepository.GetWithUser().ToListAsync();
}
await CheckForSubscription(shouldHide, allRequests);
await CheckForSubscription(shouldHide.UserId, allRequests);
return allRequests;
}
@ -457,21 +513,21 @@ namespace Ombi.Core.Engine
public async Task<MovieRequests> GetRequest(int requestId)
{
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
await CheckForSubscription(new HideResult(), new List<MovieRequests> { request });
await CheckForSubscription((await GetUser()).Id, new List<MovieRequests> { request });
return request;
}
private async Task CheckForSubscription(HideResult shouldHide, List<MovieRequests> movieRequests)
private async Task CheckForSubscription(string UserId, List<MovieRequests> movieRequests)
{
var requestIds = movieRequests.Select(x => x.Id);
var sub = await _subscriptionRepository.GetAll().Where(s =>
s.UserId == shouldHide.UserId && requestIds.Contains(s.RequestId) && s.RequestType == RequestType.Movie)
s.UserId == UserId && requestIds.Contains(s.RequestId) && s.RequestType == RequestType.Movie)
.ToListAsync();
foreach (var x in movieRequests)
{
x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath);
if (shouldHide.UserId == x.RequestedUserId)
if (UserId == x.RequestedUserId)
{
x.ShowSubscribe = false;
}
@ -503,18 +559,18 @@ namespace Ombi.Core.Engine
}
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
await CheckForSubscription(shouldHide, results);
await CheckForSubscription(shouldHide.UserId, results);
return results;
}
public async Task<RequestEngineResult> ApproveMovieById(int requestId)
public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K)
{
var request = await MovieRepository.Find(requestId);
return await ApproveMovie(request);
return await ApproveMovie(request, is4K);
}
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason)
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@ -525,8 +581,16 @@ namespace Ombi.Core.Engine
};
}
request.Denied = true;
request.DeniedReason = denyReason;
if (is4K)
{
request.Denied4K = true;
request.DeniedReason4K = denyReason;
}
else
{
request.Denied = true;
request.DeniedReason = denyReason;
}
await MovieRepository.Update(request);
await _mediaCacheService.Purge();
@ -540,7 +604,7 @@ namespace Ombi.Core.Engine
};
}
public async Task<RequestEngineResult> ApproveMovie(MovieRequests request)
public async Task<RequestEngineResult> ApproveMovie(MovieRequests request, bool is4K)
{
if (request == null)
{
@ -550,9 +614,18 @@ namespace Ombi.Core.Engine
};
}
request.MarkedAsApproved = DateTime.Now;
request.Approved = true;
request.Denied = false;
if (is4K)
{
request.MarkedAsApproved4K = DateTime.Now;
request.Approved4K = true;
request.Denied4K = false;
}
else
{
request.MarkedAsApproved = DateTime.Now;
request.Approved = true;
request.Denied = false;
}
await MovieRepository.Update(request);
var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification, string.Empty);
@ -562,7 +635,7 @@ namespace Ombi.Core.Engine
}
await _mediaCacheService.Purge();
return await ProcessSendingMovie(request);
return await ProcessSendingMovie(request, is4K);
}
public async Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken)
@ -590,11 +663,11 @@ namespace Ombi.Core.Engine
return new RequestEngineResult { Result = true, Message = $"The collection {collections.name} has been successfully added!", RequestId = results.FirstOrDefault().RequestId };
}
private async Task<RequestEngineResult> ProcessSendingMovie(MovieRequests request)
private async Task<RequestEngineResult> ProcessSendingMovie(MovieRequests request, bool is4K)
{
if (request.Approved)
if (is4K ? request.Approved4K : request.Approved)
{
var result = await Sender.Send(request);
var result = await Sender.Send(request, is4K);
if (result.Success && result.Sent)
{
return new RequestEngineResult
@ -662,7 +735,7 @@ namespace Ombi.Core.Engine
var result = await CheckCanManageRequest(request);
if (result.IsError)
return result;
await MovieRepository.Delete(request);
await _mediaCacheService.Purge();
return new RequestEngineResult
@ -683,7 +756,7 @@ namespace Ombi.Core.Engine
return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId);
}
public async Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken)
public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken)
{
var request = await MovieRepository.Find(requestId);
if (request == null)
@ -695,10 +768,10 @@ namespace Ombi.Core.Engine
};
}
return await ProcessSendingMovie(request);
return await ProcessSendingMovie(request, is4K);
}
public async Task<RequestEngineResult> MarkUnavailable(int modelId)
public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@ -709,7 +782,14 @@ namespace Ombi.Core.Engine
};
}
request.Available = false;
if (is4K)
{
request.Available4K = false;
}
else
{
request.Available = false;
}
await MovieRepository.Update(request);
await _mediaCacheService.Purge();
@ -720,7 +800,7 @@ namespace Ombi.Core.Engine
};
}
public async Task<RequestEngineResult> MarkAvailable(int modelId)
public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@ -730,9 +810,16 @@ namespace Ombi.Core.Engine
ErrorMessage = "Request does not exist"
};
}
request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
if (!is4K)
{
request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
}
else
{
request.Available4K = true;
request.MarkedAsAvailable4K = DateTime.Now;
}
await NotificationHelper.Notify(request, NotificationType.RequestAvailable);
await MovieRepository.Update(request);
await _mediaCacheService.Purge();
@ -744,9 +831,20 @@ namespace Ombi.Core.Engine
};
}
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf)
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf, bool isExisting, bool is4k)
{
await MovieRepository.Add(model);
if (is4k)
{
model.Has4KRequest = true;
}
if (!isExisting)
{
await MovieRepository.Add(model);
}
else
{
await MovieRepository.Update(model);
}
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf);
if (result.Success)

@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
@ -22,7 +23,7 @@ namespace Ombi.Core.Engine
{
public class MovieSearchEngine : BaseMediaEngine, IMovieEngine
{
public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
public MovieSearchEngine(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
ILogger<MovieSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub)
: base(identity, service, r, um, mem, s, sub)
{
@ -46,7 +47,7 @@ namespace Ombi.Core.Engine
{
langCode = await DefaultLanguageCode(langCode);
var movieInfo = await Cache.GetOrAddAsync(nameof(LookupImdbInformation) + langCode + theMovieDbId,
() => MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode),
() => MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode),
DateTimeOffset.Now.AddHours(12));
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
@ -81,11 +82,11 @@ namespace Ombi.Core.Engine
{
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();
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();
@ -120,7 +121,7 @@ namespace Ombi.Core.Engine
/// <returns></returns>
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
{
var result = await Cache.GetOrAddAsync(CacheKeys.PopularMovies, async () =>
{
var langCode = await DefaultLanguageCode(null);
@ -201,7 +202,7 @@ namespace Ombi.Core.Engine
protected async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false)
{
if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty())
if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty() && viewMovie.Id > 0)
{
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
viewMovie.Id = showInfo.Id; // TheMovieDbId
@ -217,7 +218,7 @@ namespace Ombi.Core.Engine
// This requires the rules to be run first to populate the RequestId property
await CheckForSubscription(viewMovie);
return viewMovie;
}

@ -24,12 +24,13 @@ using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using System.ComponentModel;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine
{
public class MusicRequestEngine : BaseMediaEngine, IMusicRequestEngine
{
public MusicRequestEngine(IRequestServiceMain requestService, IPrincipal user,
public MusicRequestEngine(IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator r, ILogger<MusicRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, ILidarrApi lidarr,

@ -27,7 +27,7 @@ namespace Ombi.Core.Engine
{
public class MusicSearchEngine : BaseMediaEngine, IMusicSearchEngine
{
public MusicSearchEngine(IPrincipal identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper,
public MusicSearchEngine(ICurrentUser identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper,
ILogger<MusicSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
ISettingsService<LidarrSettings> lidarrSettings)
: base(identity, service, r, um, mem, s, sub)

@ -30,26 +30,26 @@ namespace Ombi.Core.Engine
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to)
{
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie && x.AddedAt > from && x.AddedAt < to);
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
var plexMovies = _plex.GetAll().Where(x => x.Type == MediaType.Movie && x.AddedAt > from && x.AddedAt < to);
var embyMovies = _emby.GetAll().Where(x => x.Type == MediaType.Movie && x.AddedAt > from && x.AddedAt < to);
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == MediaType.Movie && x.AddedAt > from && x.AddedAt < to);
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies).Take(30);
}
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies()
{
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie);
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie);
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie);
var plexMovies = _plex.GetAll().Where(x => x.Type == MediaType.Movie);
var embyMovies = _emby.GetAll().Where(x => x.Type == MediaType.Movie);
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == MediaType.Movie);
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies);
}
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason)
{
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show && x.AddedAt > from && x.AddedAt < to);
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series && x.AddedAt > from && x.AddedAt < to);
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series && x.AddedAt > from && x.AddedAt < to);
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == MediaType.Series && x.AddedAt > from && x.AddedAt < to);
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series && x.AddedAt > from && x.AddedAt < to);
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series && x.AddedAt > from && x.AddedAt < to);
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason).Take(30);
}
@ -57,9 +57,9 @@ namespace Ombi.Core.Engine
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(bool groupBySeason)
{
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show);
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series);
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series);
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == MediaType.Series);
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason);
}
@ -76,7 +76,7 @@ namespace Ombi.Core.Engine
{
continue;
}
if (p.Type == PlexMediaTypeEntity.Movie)
if (p.Type == MediaType.Movie)
{
recentlyAddedLog.Add(new RecentlyAddedLog
{
@ -114,7 +114,7 @@ namespace Ombi.Core.Engine
{
continue;
}
if (e.Type == EmbyMediaType.Movie)
if (e.Type == MediaType.Movie)
{
recentlyAddedLog.Add(new RecentlyAddedLog
{
@ -152,7 +152,7 @@ namespace Ombi.Core.Engine
{
continue;
}
if (e.Type == JellyfinMediaType.Movie)
if (e.Type == MediaType.Movie)
{
recentlyAddedLog.Add(new RecentlyAddedLog
{

@ -32,7 +32,7 @@ namespace Ombi.Core.Engine
{
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
{
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService) : base(user, requestService, rule, manager, cache, settings, sub)
@ -188,7 +188,7 @@ namespace Ombi.Core.Engine
(await tvBuilder
.GetShowInfo(tv.TheMovieDbId, tv.languageCode))
.CreateTvList(tv)
.CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id);
.CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id, tv.Source);
await tvBuilder.BuildEpisodes(tv);
@ -793,7 +793,7 @@ namespace Ombi.Core.Engine
return await TvRepository.GetChild().AnyAsync(x => x.RequestedUserId == userId);
}
public async Task<RequestEngineResult> MarkUnavailable(int modelId)
public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K)
{
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null)
@ -821,7 +821,7 @@ namespace Ombi.Core.Engine
};
}
public async Task<RequestEngineResult> MarkAvailable(int modelId)
public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K)
{
ChildRequests request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null)
@ -918,7 +918,7 @@ namespace Ombi.Core.Engine
return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf);
}
public async Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken)
public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken)
{
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId, cancellationToken);
if (request == null)

@ -23,6 +23,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using System.Threading;
using TraktSharp.Entities;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine
{
@ -32,7 +33,7 @@ namespace Ombi.Core.Engine
private readonly IImageService _imageService;
private readonly IMovieDbApi _theMovieDbApi;
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
public TvSearchEngine(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ISettingsService<CustomizationSettings> customizationSettings,
ICacheService memCache, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IImageService imageService,
IMovieDbApi theMovieDbApi)

@ -5,6 +5,7 @@ using System.Security.Principal;
using System.Threading.Tasks;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Search.V2;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities;
@ -17,7 +18,7 @@ namespace Ombi.Core.Engine.V2
{
public DateTime DaysAgo => DateTime.Now.AddDays(-90);
public DateTime DaysAhead => DateTime.Now.AddDays(90);
public CalendarEngine(IPrincipal user, OmbiUserManager um, IRuleEvaluator rules, IMovieRequestRepository movieRepo,
public CalendarEngine(ICurrentUser user, OmbiUserManager um, IRuleEvaluator rules, IMovieRequestRepository movieRepo,
ITvRequestRepository tvRequestRepo) : base(user, um, rules)
{
_movieRepo = movieRepo;

@ -4,10 +4,15 @@ using System.Threading.Tasks;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Models.Search.V2;
// Due to conflicting Genre models in
// Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models
using Genre = Ombi.TheMovieDbApi.Models.Genre;
namespace Ombi.Core.Engine.V2
{
public interface IMultiSearchEngine
{
Task<List<MultiSearchResult>> MultiSearch(string searchTerm, MultiSearchFilter filter, CancellationToken cancellationToken);
Task<IEnumerable<Genre>> GetGenres(string media, CancellationToken requestAborted);
}
}

@ -33,7 +33,7 @@ namespace Ombi.Core.Engine.V2
public async Task<IEnumerable<IssuesSummaryModel>> GetIssues(int position, int take, IssueStatus status, CancellationToken token)
{
var issues = await _issues.GetAll().Where(x => x.Status == status && x.ProviderId != null).Skip(position).Take(take).OrderBy(x => x.Title).ToListAsync(token);
var grouped = issues.GroupBy(x => x.Title, (key, g) => new { Title = key, Issues = g });
var grouped = issues.GroupBy(x => new { x.Title, x.ProviderId }, (key, g) => new { key = key, Issues = g });
var model = new List<IssuesSummaryModel>();
@ -42,8 +42,8 @@ namespace Ombi.Core.Engine.V2
model.Add(new IssuesSummaryModel
{
Count = group.Issues.Count(),
Title = group.Title,
ProviderId = group.Issues.FirstOrDefault()?.ProviderId
Title = group.key.Title,
ProviderId = group.key.ProviderId
});
}

@ -5,6 +5,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Models.Search.V2;
@ -28,7 +29,7 @@ namespace Ombi.Core.Engine.V2
{
public class MovieSearchEngineV2 : BaseMediaEngine, IMovieEngineV2
{
public MovieSearchEngineV2(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
public MovieSearchEngineV2(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
ILogger<MovieSearchEngineV2> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
ISettingsService<CustomizationSettings> customizationSettings, IMovieRequestEngine movieRequestEngine, IHttpClientFactory httpClientFactory)
: base(identity, service, r, um, mem, s, sub)
@ -398,6 +399,8 @@ namespace Ombi.Core.Engine.V2
mapped.Available = viewMovie.Available;
mapped.Approved = viewMovie.Approved;
mapped.Denied = viewMovie.Denied;
mapped.DeniedReason = viewMovie.DeniedReason;
mapped.RequestId = viewMovie.RequestId;
mapped.Requested = viewMovie.Requested;
mapped.PlexUrl = viewMovie.PlexUrl;
@ -406,6 +409,13 @@ namespace Ombi.Core.Engine.V2
mapped.Subscribed = viewMovie.Subscribed;
mapped.ShowSubscribe = viewMovie.ShowSubscribe;
mapped.DigitalReleaseDate = viewMovie.DigitalReleaseDate;
mapped.RequestedDate4k = viewMovie.RequestedDate4k;
mapped.Approved4K = viewMovie.Approved4K;
mapped.Available4K = viewMovie.Available4K;
mapped.Denied4K = viewMovie.Denied4K;
mapped.DeniedReason4K = viewMovie.DeniedReason4K;
mapped.Has4KRequest = viewMovie.Has4KRequest;
return mapped;
}

@ -7,6 +7,7 @@ using Ombi.Api.MusicBrainz;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Authentication;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search.V2;
using Ombi.Core.Rule.Interfaces;
@ -17,11 +18,15 @@ using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
// Due to conflicting Genre models in
// Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models
using Genre = Ombi.TheMovieDbApi.Models.Genre;
namespace Ombi.Core.Engine.V2
{
public class MultiSearchEngine : BaseMediaEngine, IMultiSearchEngine
{
public MultiSearchEngine(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules,
public MultiSearchEngine(ICurrentUser identity, IRequestServiceMain requestService, IRuleEvaluator rules,
OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub,
IMovieDbApi movieDbApi, ISettingsService<LidarrSettings> lidarrSettings, IMusicBrainzApi musicApi)
: base(identity, requestService, rules, um, cache, ombiSettings, sub)
@ -113,5 +118,11 @@ namespace Ombi.Core.Engine.V2
return model;
}
public async Task<IEnumerable<Genre>> GetGenres(string media, CancellationToken cancellationToken)
{
var lang = await DefaultLanguageCode(null);
return await _movieDbApi.GetGenres(media, cancellationToken, lang);
}
}
}

@ -11,6 +11,7 @@ using Ombi.Api.Lidarr.Models;
using Ombi.Api.MusicBrainz;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search.V2.Music;
using Ombi.Core.Rule.Interfaces;
@ -31,7 +32,7 @@ namespace Ombi.Core.Engine.V2
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
private readonly ILidarrApi _lidarrApi;
public MusicSearchEngineV2(IPrincipal identity, IRequestServiceMain requestService, IRuleEvaluator rules,
public MusicSearchEngineV2(ICurrentUser identity, IRequestServiceMain requestService, IRuleEvaluator rules,
OmbiUserManager um, ICacheService cache, ISettingsService<OmbiSettings> ombiSettings,
IRepository<RequestSubscription> sub, IMusicBrainzApi musicBrainzApi, ISettingsService<LidarrSettings> lidarrSettings,
ILidarrApi lidarrApi)

@ -25,6 +25,7 @@ using Ombi.Api.TheMovieDb.Models;
using System.Diagnostics;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models.UI;
using Ombi.Core.Helpers;
namespace Ombi.Core.Engine.V2
{
@ -37,7 +38,7 @@ namespace Ombi.Core.Engine.V2
private readonly ISettingsService<CustomizationSettings> _customization;
private readonly ITvRequestEngine _requestEngine;
public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
public TvSearchEngineV2(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService<OmbiSettings> s,
IRepository<RequestSubscription> sub, IMovieDbApi movieApi, ISettingsService<CustomizationSettings> customization, ITvRequestEngine requestEngine)
: base(identity, service, r, um, memCache, s, sub)
@ -307,6 +308,8 @@ namespace Ombi.Core.Engine.V2
item.PartlyAvailable = oldModel.PartlyAvailable;
item.Requested = oldModel.Requested;
item.Available = oldModel.Available;
item.Denied = oldModel.Denied;
item.DeniedReason = oldModel.DeniedReason;
item.Approved = oldModel.Approved;
item.SeasonRequests = oldModel.SeasonRequests;
item.RequestId = oldModel.RequestId;

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Core.Models.UI;
using Ombi.Core.Rule.Interfaces;
@ -20,7 +21,7 @@ namespace Ombi.Core.Engine
{
public class VoteEngine : BaseEngine, IVoteEngine
{
public VoteEngine(IRepository<Votes> votes, IPrincipal user, OmbiUserManager um, IRuleEvaluator r, ISettingsService<VoteSettings> voteSettings,
public VoteEngine(IRepository<Votes> votes, ICurrentUser user, OmbiUserManager um, IRuleEvaluator r, ISettingsService<VoteSettings> voteSettings,
IMusicRequestEngine musicRequestEngine, ITvRequestEngine tvRequestEngine, IMovieRequestEngine movieRequestEngine) : base(user, um, r)
{
_voteRepository = votes;
@ -193,7 +194,7 @@ namespace Ombi.Core.Engine
case RequestType.Movie:
if (totalVotes >= voteSettings.MovieVoteMax)
{
result = await _movieRequestEngine.ApproveMovieById(requestId);
result = await _movieRequestEngine.ApproveMovieById(requestId, false);
}
break;
case RequestType.Album:

@ -0,0 +1,47 @@
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Helpers;
using Ombi.Store.Entities;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Helpers
{
public class CurrentUser : ICurrentUser
{
private readonly IPrincipal _principle;
private readonly OmbiUserManager _userManager;
private OmbiUser _user;
public IIdentity Identity { get; set; }
public CurrentUser(IPrincipal principle, OmbiUserManager userManager)
{
_principle = principle;
_userManager = userManager;
Identity = _principle?.Identity ?? null;
}
public void SetUser(OmbiUser user)
{
_user = user;
}
public string Username => Identity?.Name ?? _user?.UserName;
public async Task<OmbiUser> GetUser()
{
if (!Username.HasValue() && _user == null)
{
return null;
}
if (_user != null)
{
return _user;
}
var username = Username.ToUpper();
return _user ??= await _userManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}
}
}

@ -0,0 +1,15 @@
using Ombi.Store.Entities;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Helpers
{
public interface ICurrentUser
{
string Username { get; }
Task<OmbiUser> GetUser();
void SetUser(OmbiUser user);
IIdentity Identity { get; set; }
}
}

@ -53,7 +53,7 @@ namespace Ombi.Core.Helpers
return this;
}
public TvShowRequestBuilderV2 CreateChild(TvRequestViewModelV2 model, string userId)
public TvShowRequestBuilderV2 CreateChild(TvRequestViewModelV2 model, string userId, RequestSource source)
{
var animationGenre = TheMovieDbRecord.genres?.Any(s => s.name.Equals("Animation", StringComparison.InvariantCultureIgnoreCase)) ?? false;
var animeKeyword = TheMovieDbRecord.Keywords?.KeywordsValue?.Any(s => s.Name.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ?? false;
@ -68,7 +68,8 @@ namespace Ombi.Core.Helpers
Title = TheMovieDbRecord.name,
ReleaseYear = FirstAir,
RequestedByAlias = model.RequestedByAlias,
SeriesType = animationGenre && animeKeyword ? SeriesType.Anime : SeriesType.Standard
SeriesType = animationGenre && animeKeyword ? SeriesType.Anime : SeriesType.Standard,
Source = source
};
return this;

@ -1,31 +1,5 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2018 Jamie Rees
// File: MovieRequestViewModel.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using Newtonsoft.Json;
using Newtonsoft.Json;
using Ombi.Store.Entities.Requests;
namespace Ombi.Core.Models.Requests
{
@ -34,10 +8,18 @@ namespace Ombi.Core.Models.Requests
public int TheMovieDbId { get; set; }
public string LanguageCode { get; set; } = "en";
public bool Is4kRequest { get; set; }
/// <summary>
/// This is only set from a HTTP Header
/// </summary>
[JsonIgnore]
public string RequestedByAlias { get; set; }
/// <summary>
/// Only set via list imports
/// </summary>
[JsonIgnore]
public RequestSource Source { get; set; } = RequestSource.Ombi;
}
}

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Ombi.Store.Entities.Requests;
namespace Ombi.Core.Models.Requests
{
@ -7,5 +8,6 @@ namespace Ombi.Core.Models.Requests
{
public int TheMovieDbId { get; set; }
public string languageCode { get; set; } = "en";
public RequestSource Source { get; set; } = RequestSource.Ombi;
}
}

@ -28,5 +28,14 @@ namespace Ombi.Core.Models.Search
public override RequestType Type => RequestType.Movie;
public ReleaseDatesDto ReleaseDates { get; set; }
public DateTime? DigitalReleaseDate { get; set; }
public bool Has4KRequest { get; set; }
public bool Approved4K { get; set; }
public DateTime MarkedAsApproved4K { get; set; }
public DateTime RequestedDate4k { get; set; }
public bool Available4K { get; set; }
public DateTime? MarkedAsAvailable4K { get; set; }
public bool? Denied4K { get; set; }
public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; }
}
}

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using Ombi.Store.Entities;
namespace Ombi.Core.Models.Search
@ -32,6 +33,7 @@ namespace Ombi.Core.Models.Search
public string TheMovieDbId { get; set; }
[NotMapped]
[Obsolete("Use request service instead")]
public bool Subscribed { get; set; }
[NotMapped]
public bool ShowSubscribe { get; set; }

@ -41,6 +41,15 @@ namespace Ombi.Core.Models.Search.V2
public Recommendations Recommendations { get; set; }
public ExternalIds ExternalIds { get; set; }
public Keywords Keywords { get; set; }
public bool Has4KRequest { get; set; }
public bool Approved4K { get; set; }
public DateTime MarkedAsApproved4K { get; set; }
public DateTime RequestedDate4k { get; set; }
public bool Available4K { get; set; }
public DateTime? MarkedAsAvailable4K { get; set; }
public bool? Denied4K { get; set; }
public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; }
}
public class Keywords
{

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save