mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-22 06:13:22 -07:00
Merge branch 'Ombi-app:develop' into add-postgres-provider
This commit is contained in:
commit
b2ffef63f8
58 changed files with 7597 additions and 10777 deletions
650
CHANGELOG.md
650
CHANGELOG.md
|
@ -1,3 +1,40 @@
|
||||||
|
## [4.43.14](https://github.com/Ombi-app/Ombi/compare/v4.43.13...v4.43.14) (2024-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* ⚡ Improve render performance on the discover, movie and tv pages ([#5084](https://github.com/Ombi-app/Ombi/issues/5084)) ([71c86a8](https://github.com/Ombi-app/Ombi/commit/71c86a8db9e63bf0ab779f9a8b5d62a42c246392))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.43.13](https://github.com/Ombi-app/Ombi/compare/v4.43.12...v4.43.13) (2024-03-05)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.43.12](https://github.com/Ombi-app/Ombi/compare/v4.43.11...v4.43.12) (2024-03-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities ([#5040](https://github.com/Ombi-app/Ombi/issues/5040)) [skip ci] ([955a742](https://github.com/Ombi-app/Ombi/commit/955a742fae1d0a3983c59cf77eb1a2d222f18b48))
|
||||||
|
* src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities ([#5072](https://github.com/Ombi-app/Ombi/issues/5072)) [skip ci] ([af6a986](https://github.com/Ombi-app/Ombi/commit/af6a9867719deb7b651a6a78352a8ce0df7a0cf0))
|
||||||
|
* src/Ombi/Ombi.csproj to reduce vulnerabilities ([#5066](https://github.com/Ombi-app/Ombi/issues/5066)) [skip ci] ([71df058](https://github.com/Ombi-app/Ombi/commit/71df05886512b8589f193a5cda0166c694438fc0))
|
||||||
|
* upgrade @fortawesome/fontawesome-free from 6.4.2 to 6.5.0 ([#5053](https://github.com/Ombi-app/Ombi/issues/5053)) [skip ci] ([5017e38](https://github.com/Ombi-app/Ombi/commit/5017e38f87e32821adb744935fffcb2d76927e2c))
|
||||||
|
* upgrade @types/jquery from 3.5.27 to 3.5.28 ([#5049](https://github.com/Ombi-app/Ombi/issues/5049)) [skip ci] ([2c8fe80](https://github.com/Ombi-app/Ombi/commit/2c8fe8087aea227e7425e82392ad9ccb3f8261b4))
|
||||||
|
* upgrade moment from 2.29.4 to 2.30.1 ([#5075](https://github.com/Ombi-app/Ombi/issues/5075)) [skip ci] ([460fa39](https://github.com/Ombi-app/Ombi/commit/460fa39bb95c73bafcd65fcc394fecca04d3ac49))
|
||||||
|
* upgrade multiple dependencies with Snyk ([#5073](https://github.com/Ombi-app/Ombi/issues/5073)) [skip ci] ([a27b459](https://github.com/Ombi-app/Ombi/commit/a27b4592471c58fca9ad5193b979171fa7c5e66d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.43.11](https://github.com/Ombi-app/Ombi/compare/v4.43.10...v4.43.11) (2024-01-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **emby:** Add more logging on the PlaySync to check for Tv Shows without a valid TMDB ([08eb13b](https://github.com/Ombi-app/Ombi/commit/08eb13b788582d576a0e1befdb8e84ef7ff0d2f3))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.43.10](https://github.com/Ombi-app/Ombi/compare/v4.43.9...v4.43.10) (2023-11-20)
|
## [4.43.10](https://github.com/Ombi-app/Ombi/compare/v4.43.9...v4.43.10) (2023-11-20)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1844,616 +1881,3 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.12](https://github.com/Ombi-app/Ombi/compare/v4.35.10...v4.35.12) (2023-03-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **sonarr:** :bug: Improved the error handling in the sonarr settings page in the UI ([fcd78fe](https://github.com/Ombi-app/Ombi/commit/fcd78fee619d10ec7d78e8c8ec6c3ac4b0a361a1)), closes [#4877](https://github.com/Ombi-app/Ombi/issues/4877)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.9](https://github.com/Ombi-app/Ombi/compare/v4.35.8...v4.35.9) (2023-02-24)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.8](https://github.com/Ombi-app/Ombi/compare/v4.35.7...v4.35.8) (2023-02-17)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex-oauth:** 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user [#4835](https://github.com/Ombi-app/Ombi/issues/4835) ([4098da3](https://github.com/Ombi-app/Ombi/commit/4098da305aaea9dae9a552644268a4fed7204cfe))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.7](https://github.com/Ombi-app/Ombi/compare/v4.35.6...v4.35.7) (2023-02-10)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **wizard:** :bug: Stop access to the wizard when you have already setup ombi ([#4866](https://github.com/Ombi-app/Ombi/issues/4866)) ([353de98](https://github.com/Ombi-app/Ombi/commit/353de981a462e1753288d225ec4644a44a62d2bc))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.6](https://github.com/Ombi-app/Ombi/compare/v4.35.5...v4.35.6) (2023-01-31)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fixed the issue where the login page is still present after logging in with oauth ([aca4ee3](https://github.com/Ombi-app/Ombi/commit/aca4ee37915a28200e5233be3dc711ccc4a5aee9))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.5](https://github.com/Ombi-app/Ombi/compare/v4.35.4...v4.35.5) (2023-01-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **radarr-settings:** 🐛 Fixed a typo ([4a50a00](https://github.com/Ombi-app/Ombi/commit/4a50a00d4729d99f4359874b9af4dbc58a0c220b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.4](https://github.com/Ombi-app/Ombi/compare/v4.35.3...v4.35.4) (2023-01-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **discover:** :bug: Fixed the default poster not taking into account the base url in some scenarios [#4845](https://github.com/Ombi-app/Ombi/issues/4845) ([8eda250](https://github.com/Ombi-app/Ombi/commit/8eda250367953183daec03ccb5cdf9fe94275b27))
|
|
||||||
* **Hide music from navbar and request list when not enabled:** :bug: ([5123a76](https://github.com/Ombi-app/Ombi/commit/5123a76954e9f81d58c05e31afc7a29aec19cb7a))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.3](https://github.com/Ombi-app/Ombi/compare/v4.35.2...v4.35.3) (2023-01-13)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **#4847:** Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page ([#4848](https://github.com/Ombi-app/Ombi/issues/4848)) ([f229d88](https://github.com/Ombi-app/Ombi/commit/f229d88bd744bc5253b5d3db69ae5ef22d014230))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.2](https://github.com/Ombi-app/Ombi/compare/v4.35.1...v4.35.2) (2023-01-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **database:** Just some tweaks, shouldn't notice any difference, maybe a less error in the log ([67fb992](https://github.com/Ombi-app/Ombi/commit/67fb9921c0c025025286eb6c0a9d09fd01b18465))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.1](https://github.com/Ombi-app/Ombi/compare/v4.35.0...v4.35.1) (2023-01-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex-watchlist:** Index out of bounds error ([8cd556e](https://github.com/Ombi-app/Ombi/commit/8cd556e268931596b9c1d1ae0ce533bfaaf330f4))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.35.0](https://github.com/Ombi-app/Ombi/compare/v4.34.1...v4.35.0) (2023-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add the option for header authentication to create users ([#4841](https://github.com/Ombi-app/Ombi/issues/4841)) ([e6c9ce5](https://github.com/Ombi-app/Ombi/commit/e6c9ce5ad0056608ecda8273fb8124ed292e2942))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.34.1](https://github.com/Ombi-app/Ombi/compare/v4.34.0...v4.34.1) (2023-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex-watchlist:** Lookup the ID from different sources when Plex doesn't contain the metadata ([#4843](https://github.com/Ombi-app/Ombi/issues/4843)) ([a2cc23b](https://github.com/Ombi-app/Ombi/commit/a2cc23b351c4a568c44e6c855f94db9f71ad084a))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.34.0](https://github.com/Ombi-app/Ombi/compare/v4.33.1...v4.34.0) (2023-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Radarr tags ([#4815](https://github.com/Ombi-app/Ombi/issues/4815)) ([6fa5064](https://github.com/Ombi-app/Ombi/commit/6fa506491fe867cdeef9df79991ae49319d71c3d))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.33.1](https://github.com/Ombi-app/Ombi/compare/v4.33.0...v4.33.1) (2022-12-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex:** Added the watchlist request whole show back into the settings ([10701c4](https://github.com/Ombi-app/Ombi/commit/10701c4a0b6190eebb75c5d8b18224f3d0bc8502))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.33.0](https://github.com/Ombi-app/Ombi/compare/v4.32.3...v4.33.0) (2022-12-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Angular 15 and Dependency upgrades ([#4818](https://github.com/Ombi-app/Ombi/issues/4818)) ([4816acf](https://github.com/Ombi-app/Ombi/commit/4816acf6f94443d23ebef6091d4cfcbca580f9ca))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.32.3](https://github.com/Ombi-app/Ombi/compare/v4.32.2...v4.32.3) (2022-11-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **sonarr:** V4 actually works this time around ([f62e70f](https://github.com/Ombi-app/Ombi/commit/f62e70fc493c7971da5e4508ce10522f5df0bbf7))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.32.2](https://github.com/Ombi-app/Ombi/compare/v4.32.1...v4.32.2) (2022-11-23)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **sonarr:** :bug: Sonarr V4 should work now ([#4810](https://github.com/Ombi-app/Ombi/issues/4810)) ([37655af](https://github.com/Ombi-app/Ombi/commit/37655aff9d3d133b42f5664bc9445d6571e966d6))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.32.1](https://github.com/Ombi-app/Ombi/compare/v4.32.0...v4.32.1) (2022-11-21)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex:** :bug: Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework ([187b18d](https://github.com/Ombi-app/Ombi/commit/187b18d5c01f6a13831e4a410b5d7c349e27d847))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.32.0](https://github.com/Ombi-app/Ombi/compare/v4.31.0...v4.32.0) (2022-11-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4801](https://github.com/Ombi-app/Ombi/issues/4801)) ([4692003](https://github.com/Ombi-app/Ombi/commit/46920032baed04675b2ffbe1700afdc0740a4ac4))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **plex:** Rework the Plex Settings page ([#4805](https://github.com/Ombi-app/Ombi/issues/4805)) ([1b8c47f](https://github.com/Ombi-app/Ombi/commit/1b8c47f3163f618851d4904732cb07015e1e93ff))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.31.0](https://github.com/Ombi-app/Ombi/compare/v4.30.0...v4.31.0) (2022-11-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **sonarr:** Added the ability to add default tags when sending to Sonarr ([#4803](https://github.com/Ombi-app/Ombi/issues/4803)) ([ecfbb8e](https://github.com/Ombi-app/Ombi/commit/ecfbb8eda91e1a90239dcf8be847afcc2394a78e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.30.0](https://github.com/Ombi-app/Ombi/compare/v4.29.3...v4.30.0) (2022-11-17)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **sonarr:** :sparkles: Add the username to a Sonarr tag when sent to Sonarr ([#4802](https://github.com/Ombi-app/Ombi/issues/4802)) ([1d5fabd](https://github.com/Ombi-app/Ombi/commit/1d5fabd317e3ce8f6dd31f06d15dc81277f39dbd))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.29.3](https://github.com/Ombi-app/Ombi/compare/v4.29.2...v4.29.3) (2022-11-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **notifications:** Fixed the Partially TV notifications going to the admin [#4797](https://github.com/Ombi-app/Ombi/issues/4797) ([#4799](https://github.com/Ombi-app/Ombi/issues/4799)) ([bcb3e7f](https://github.com/Ombi-app/Ombi/commit/bcb3e7f00380a4c4278f59dc55febf43e6d05d47))
|
|
||||||
* Only log error messages from Microsoft ([#4787](https://github.com/Ombi-app/Ombi/issues/4787)) ([c614e0c](https://github.com/Ombi-app/Ombi/commit/c614e0ca5fe5023cbe7ced326145273cd75be85d))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.29.2](https://github.com/Ombi-app/Ombi/compare/v4.29.1...v4.29.2) (2022-10-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex:** Fixed an issue where sometimes the availability checker would throw an exception when checking episodes ([17ba202](https://github.com/Ombi-app/Ombi/commit/17ba2020ee0950c2c0e0e03fdb7835b579da75a9))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.29.1](https://github.com/Ombi-app/Ombi/compare/v4.29.0...v4.29.1) (2022-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Consistently reset loading flag when requesting movies on discover page. ([#4777](https://github.com/Ombi-app/Ombi/issues/4777)) ([a40ab5c](https://github.com/Ombi-app/Ombi/commit/a40ab5cddf769d4147696eca50c1610b466ab99b))
|
|
||||||
* **sonarr:** :bug: Fixed an issue where the language list didn't correctly load for power users in the advanced options [#4782](https://github.com/Ombi-app/Ombi/issues/4782) ([2173670](https://github.com/Ombi-app/Ombi/commit/217367047d1568070dd507e54ad3fd2c68f05b88))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.29.0](https://github.com/Ombi-app/Ombi/compare/v4.28.1...v4.29.0) (2022-10-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Partially Available prevents further TV requests ([#4768](https://github.com/Ombi-app/Ombi/issues/4768)) ([#4779](https://github.com/Ombi-app/Ombi/issues/4779)) ([031e2b9](https://github.com/Ombi-app/Ombi/commit/031e2b9283b239827cabaca4e35f69f2f93a4d7b))
|
|
||||||
* Unable to Delete Jellyfin Server ([#4705](https://github.com/Ombi-app/Ombi/issues/4705)) ([#4780](https://github.com/Ombi-app/Ombi/issues/4780)) ([76a0d0d](https://github.com/Ombi-app/Ombi/commit/76a0d0d26893bd480fea4735f77522ac6261a425))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Provide a flag for missing users on Plex Server ([#4688](https://github.com/Ombi-app/Ombi/issues/4688)) ([#4778](https://github.com/Ombi-app/Ombi/issues/4778)) ([b4a14c2](https://github.com/Ombi-app/Ombi/commit/b4a14c2d28218409390e517b226130e3e84efee1))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.28.1](https://github.com/Ombi-app/Ombi/compare/v4.28.0...v4.28.1) (2022-10-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex:** :bug: Fixed not being able to enable watchlist requests in the Plex settings ([3e5158e](https://github.com/Ombi-app/Ombi/commit/3e5158ef9cda58ea2dd3be143f07aa5433691d79))
|
|
||||||
* Reworked the version check ([#4719](https://github.com/Ombi-app/Ombi/issues/4719)) ([#4781](https://github.com/Ombi-app/Ombi/issues/4781)) ([55855c5](https://github.com/Ombi-app/Ombi/commit/55855c5adda3cd1c51b7fbd0c19b469fc813f98e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.28.0](https://github.com/Ombi-app/Ombi/compare/v4.27.8...v4.28.0) (2022-10-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **plex:** ✨ Added the ability to configure the watchlist to request the whole TV show rather than latest season ([#4774](https://github.com/Ombi-app/Ombi/issues/4774)) ([fa65712](https://github.com/Ombi-app/Ombi/commit/fa65712bd570fe8d5d21b8ca0abe182b84960017))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.8](https://github.com/Ombi-app/Ombi/compare/v4.27.7...v4.27.8) (2022-10-07)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.7](https://github.com/Ombi-app/Ombi/compare/v4.27.6...v4.27.7) (2022-10-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fixes default image for recently requested items. ([#4767](https://github.com/Ombi-app/Ombi/issues/4767)) ([2e6f35f](https://github.com/Ombi-app/Ombi/commit/2e6f35f89abb3dd3685ec8289f8620c7ef7072cd))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.6](https://github.com/Ombi-app/Ombi/compare/v4.27.5...v4.27.6) (2022-10-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **notifications:** Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid ([fc14780](https://github.com/Ombi-app/Ombi/commit/fc14780bd354483119ddcbb55a8c382e1890a783))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.5](https://github.com/Ombi-app/Ombi/compare/v4.27.4...v4.27.5) (2022-09-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **importer:** 🐛 Allow you to only import Plex Admins without the Plex Users ([8c9ad9b](https://github.com/Ombi-app/Ombi/commit/8c9ad9b414fdc6c88bdb911d6057ae5d38783b98))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.4](https://github.com/Ombi-app/Ombi/compare/v4.27.3...v4.27.4) (2022-09-30)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.3](https://github.com/Ombi-app/Ombi/compare/v4.27.2...v4.27.3) (2022-09-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **availability:** 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area ([28e2480](https://github.com/Ombi-app/Ombi/commit/28e248046ad56390595f84172bbd5f5961325b4d))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.2](https://github.com/Ombi-app/Ombi/compare/v4.27.1...v4.27.2) (2022-09-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **sonarr:** :bug: Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 ([#4764](https://github.com/Ombi-app/Ombi/issues/4764)) ([2cddec7](https://github.com/Ombi-app/Ombi/commit/2cddec759004b6490f686ff74cb092238e3dc946))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.27.1](https://github.com/Ombi-app/Ombi/compare/v4.27.0...v4.27.1) (2022-09-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex:** stop the plex sync from deleting episodes when we can't find the plex key ([66b05e5](https://github.com/Ombi-app/Ombi/commit/66b05e5a85dbfe1fec5f9366e80987f2cfa1f4fe))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.27.0](https://github.com/Ombi-app/Ombi/compare/v4.26.0...v4.27.0) (2022-09-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Recently requested improvements ([#4755](https://github.com/Ombi-app/Ombi/issues/4755)) ([ff04d87](https://github.com/Ombi-app/Ombi/commit/ff04d875343604c77c391bf55d0968977e480281))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.26.0](https://github.com/Ombi-app/Ombi/compare/v4.25.1...v4.26.0) (2022-09-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **notifications:** Add more curly variables for partially available notification ([66aa101](https://github.com/Ombi-app/Ombi/commit/66aa101019c4c4b34e186db9d303049d02b9c781))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.25.1](https://github.com/Ombi-app/Ombi/compare/v4.25.0...v4.25.1) (2022-09-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **webhook:** Remove added trailing slash from webhook URL [#4710](https://github.com/Ombi-app/Ombi/issues/4710) ([369eb33](https://github.com/Ombi-app/Ombi/commit/369eb339171671101be219486e2aab27a20f3d74))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.25.0](https://github.com/Ombi-app/Ombi/compare/v4.24.0...v4.25.0) (2022-08-23)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* fixed stats controller ([#4742](https://github.com/Ombi-app/Ombi/issues/4742)) ([47ea64b](https://github.com/Ombi-app/Ombi/commit/47ea64b5a401770f1943b575ca40f84d515e96b3))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Watchlist history errors([#4741](https://github.com/Ombi-app/Ombi/issues/4741)) ([c222f1a](https://github.com/Ombi-app/Ombi/commit/c222f1a945e944ef34e68cad2b61f40e57cab823))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.24.0](https://github.com/Ombi-app/Ombi/compare/v4.23.2...v4.24.0) (2022-08-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* add crew on movie page ([#4722](https://github.com/Ombi-app/Ombi/issues/4722)) ([1d53261](https://github.com/Ombi-app/Ombi/commit/1d532613823804b25984bd1d223d081a54ad143d))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.23.2](https://github.com/Ombi-app/Ombi/compare/v4.23.1...v4.23.2) (2022-08-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix conflicting property name for Swagger ([#4733](https://github.com/Ombi-app/Ombi/issues/4733)) ([d661f32](https://github.com/Ombi-app/Ombi/commit/d661f32e8a9e105faab6380b4b7b642896b98163))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.23.1](https://github.com/Ombi-app/Ombi/compare/v4.23.0...v4.23.1) (2022-08-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Localize recently requested on discover page ([#4729](https://github.com/Ombi-app/Ombi/issues/4729)) ([bf65c76](https://github.com/Ombi-app/Ombi/commit/bf65c76ff9ce38f65a9e5feb872734e8d8e35eb6))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.23.0](https://github.com/Ombi-app/Ombi/compare/v4.22.5...v4.23.0) (2022-08-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Log Microsoft warnings to log file ([#4723](https://github.com/Ombi-app/Ombi/issues/4723)) ([26ac75f](https://github.com/Ombi-app/Ombi/commit/26ac75f0c223c2a91e3471797ae46ede3fde89cc))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* ✨ Recently Requested on Discover Page ([#4387](https://github.com/Ombi-app/Ombi/issues/4387)) ([44d38fb](https://github.com/Ombi-app/Ombi/commit/44d38fbaae521dbb467b61c7471b2384015ac52e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.22.4](https://github.com/Ombi-app/Ombi/compare/v4.22.3...v4.22.4) (2022-08-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: Fixed missing externals ([#4712](https://github.com/Ombi-app/Ombi/issues/4712)) ([fcc1eaa](https://github.com/Ombi-app/Ombi/commit/fcc1eaaa377683dcdc81d62a2a688fb0c4490c7b))
|
|
||||||
* fixed trakt image not loading when base url present ([#4711](https://github.com/Ombi-app/Ombi/issues/4711)) ([f102dcf](https://github.com/Ombi-app/Ombi/commit/f102dcf751c2eb62ebfe30f9f8e4b2ad863c3b0d))
|
|
||||||
* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4713](https://github.com/Ombi-app/Ombi/issues/4713)) ([ff142b0](https://github.com/Ombi-app/Ombi/commit/ff142b09abbb2f9540387284222552e6e12639fe))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.22.3](https://github.com/Ombi-app/Ombi/compare/v4.22.2...v4.22.3) (2022-07-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Override Sonarr V3 Profiles endpoint ([#4678](https://github.com/Ombi-app/Ombi/issues/4678)) ([875da95](https://github.com/Ombi-app/Ombi/commit/875da959f353119b05138d68ee6d32a49e14b91e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.22.2](https://github.com/Ombi-app/Ombi/compare/v4.22.1...v4.22.2) (2022-07-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* fixed an issue where I broke images for some users ([81ddc85](https://github.com/Ombi-app/Ombi/commit/81ddc8553b9094c3f6843b036daebb2eb9262e00))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.22.1](https://github.com/Ombi-app/Ombi/compare/v4.22.0...v4.22.1) (2022-07-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **discover:** :bug: Created new Image component to handle 429's from TMDB ([#4698](https://github.com/Ombi-app/Ombi/issues/4698)) and fixed [#4635](https://github.com/Ombi-app/Ombi/issues/4635) ([#4699](https://github.com/Ombi-app/Ombi/issues/4699)) ([f22d3da](https://github.com/Ombi-app/Ombi/commit/f22d3da765799365455b919027f7563e52b347c3))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.22.0](https://github.com/Ombi-app/Ombi/compare/v4.21.2...v4.22.0) (2022-07-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **discover:** ✨ Added infinite scroll on advanced search results ([898bc89](https://github.com/Ombi-app/Ombi/commit/898bc89fa78245c1f3de9481f6c724f087a16e39))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.21.2](https://github.com/Ombi-app/Ombi/compare/v4.21.1...v4.21.2) (2022-07-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Landing and Login page improvements ([#4690](https://github.com/Ombi-app/Ombi/issues/4690)) ([6d423b5](https://github.com/Ombi-app/Ombi/commit/6d423b5447c52c5e59d8d2bd92a23b47468eb736))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.21.1](https://github.com/Ombi-app/Ombi/compare/v4.21.0...v4.21.1) (2022-07-11)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **images:** Retry images with a backoff when we get a Too Many requests from TheMovieDb [#4685](https://github.com/Ombi-app/Ombi/issues/4685) ([3f1f35d](https://github.com/Ombi-app/Ombi/commit/3f1f35df3164db6739691cdda8f925c296239791))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.21.0](https://github.com/Ombi-app/Ombi/compare/v4.20.4...v4.21.0) (2022-06-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Upgrade to Angular14 ([#4668](https://github.com/Ombi-app/Ombi/issues/4668)) ([b9d55a4](https://github.com/Ombi-app/Ombi/commit/b9d55a469b412558cbf67c1e25db7fdda5964cd8))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* stop populating obsolete subscribe fields ([#4625](https://github.com/Ombi-app/Ombi/issues/4625)) ([9a73463](https://github.com/Ombi-app/Ombi/commit/9a734637665f671b17c2bb440d93b35a891c142b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.20.4](https://github.com/Ombi-app/Ombi/compare/v4.20.3...v4.20.4) (2022-06-15)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* fixed build ([f877921](https://github.com/Ombi-app/Ombi/commit/f8779219146051ea74f8b6408658ff7975afb88b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.20.3](https://github.com/Ombi-app/Ombi/compare/v4.20.2...v4.20.3) (2022-06-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex:** 🐛 Fixed an issue with the Plex Sync ([ab1a11a](https://github.com/Ombi-app/Ombi/commit/ab1a11af78efbe9d37bd55aa80a640796c138a98))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.20.2](https://github.com/Ombi-app/Ombi/compare/v4.20.1...v4.20.2) (2022-06-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: Fixed the Request on Behalf of having blanks ([#4667](https://github.com/Ombi-app/Ombi/issues/4667)) ([7dd9b1c](https://github.com/Ombi-app/Ombi/commit/7dd9b1cac07f571dd35b362544e4fe0226c4b817))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.20.1](https://github.com/Ombi-app/Ombi/compare/v4.20.0...v4.20.1) (2022-05-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* added media type tag to media type text ([#4638](https://github.com/Ombi-app/Ombi/issues/4638)) ([fe501d3](https://github.com/Ombi-app/Ombi/commit/fe501d34a0c36ac9f000b107eca49dbc6694d006))
|
|
||||||
* **API:** Fix pagination in some edge cases ([#4649](https://github.com/Ombi-app/Ombi/issues/4649)) ([a70bf8f](https://github.com/Ombi-app/Ombi/commit/a70bf8f46c76d74c9dfdf908c53bd9955ca0a35d))
|
|
||||||
* **discover:** Carousel touch not working when scrolling page and recommendations and similar movie navigation ([#4633](https://github.com/Ombi-app/Ombi/issues/4633)) ([d5ef1d5](https://github.com/Ombi-app/Ombi/commit/d5ef1d53e5f77d19dba8b8059c80b538a3e14f2a))
|
|
||||||
* Improve Swagger documentation ([#4652](https://github.com/Ombi-app/Ombi/issues/4652)) ([181892b](https://github.com/Ombi-app/Ombi/commit/181892bcfe88e6d76febf49ef57745d04552d08e))
|
|
||||||
* Missing Poster broken link fix ([#4637](https://github.com/Ombi-app/Ombi/issues/4637)) ([4070f0d](https://github.com/Ombi-app/Ombi/commit/4070f0d093b1c92487a1c80cabad8283a9650f51))
|
|
||||||
* **sickrage:** Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present ([#4648](https://github.com/Ombi-app/Ombi/issues/4648)) ([6d16442](https://github.com/Ombi-app/Ombi/commit/6d16442d4d714920367df065a3ced42b729f4233))
|
|
||||||
* **sync:** Emby+Jellyfin - sync multi-episode files of 3+ episodes ([bd8fd89](https://github.com/Ombi-app/Ombi/commit/bd8fd890554c9d85d6da4d2cee813e82ce698e52))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.20.0](https://github.com/Ombi-app/Ombi/compare/v4.19.1...v4.20.0) (2022-04-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **discover:** Show more relevant shows in upcoming TV ([8357819](https://github.com/Ombi-app/Ombi/commit/8357819b53b8c675c0b246d7006b5a778bdba33f))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.19.1](https://github.com/Ombi-app/Ombi/compare/v4.19.0...v4.19.1) (2022-04-27)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.19.0](https://github.com/Ombi-app/Ombi/compare/v4.18.0...v4.19.0) (2022-04-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **sync:** Detect reidentified movies in Emby and Jellyfin ([5938077](https://github.com/Ombi-app/Ombi/commit/5938077d82a5357f79c07b218b3986557a5816e8))
|
|
||||||
* **sync:** Detect reidentified series in Emby and Jellyfin ([9096e91](https://github.com/Ombi-app/Ombi/commit/9096e91d55d268819bce22831f8a8b27f2a1776b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.18.0](https://github.com/Ombi-app/Ombi/compare/v4.17.0...v4.18.0) (2022-04-26)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **discover:** Fix cache mix up ([03d9422](https://github.com/Ombi-app/Ombi/commit/03d94220c7eaafb50c6c80a6ed1150794b873ac3))
|
|
||||||
* **discover:** Fix new trending feature detection ([6794b88](https://github.com/Ombi-app/Ombi/commit/6794b887f6544fb41528bdd9728b7824b65e47ee))
|
|
||||||
* **settings:** Allow toggling features when there are more than one ([a373359](https://github.com/Ombi-app/Ombi/commit/a373359ae8e6bad42b558a6e01a8ff2840d3bbaa))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **discover:** Add new trending source experimental feature ([1a0823c](https://github.com/Ombi-app/Ombi/commit/1a0823ca80559417c67323aaeaa1ef5243e98031))
|
|
||||||
* **discover:** Default trending source to new logic ([4f12939](https://github.com/Ombi-app/Ombi/commit/4f12939e22020a67a5ee75e2907923faea136e8d))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.17.0](https://github.com/Ombi-app/Ombi/compare/v4.16.17...v4.17.0) (2022-04-25)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.16.17](https://github.com/Ombi-app/Ombi/compare/v4.16.16...v4.16.17) (2022-04-25)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.16.16](https://github.com/Ombi-app/Ombi/compare/v4.16.15...v4.16.16) (2022-04-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **4616:** :bug: fixed mandatory fields ([d8f2260](https://github.com/Ombi-app/Ombi/commit/d8f2260c7ae3ed48386743b7adbd06e284487034))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.16.15](https://github.com/Ombi-app/Ombi/compare/v4.16.14...v4.16.15) (2022-04-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **discover:** Add original language filter ([ef7ec86](https://github.com/Ombi-app/Ombi/commit/ef7ec861d8aede2a4817752c990617f583805391))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.16.14](https://github.com/Ombi-app/Ombi/compare/v4.16.13...v4.16.14) (2022-04-19)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.16.13](https://github.com/Ombi-app/Ombi/compare/v4.16.12...v4.16.13) (2022-04-19)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.10](https://github.com/Ombi-app/Ombi/compare/v4.35.9...v4.35.10) (2023-02-25)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.9](https://github.com/Ombi-app/Ombi/compare/v4.35.8...v4.35.9) (2023-02-24)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.22.5](https://github.com/Ombi-app/Ombi/compare/v4.22.4...v4.22.5) (2022-08-05)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.35.8](https://github.com/Ombi-app/Ombi/compare/v4.35.7...v4.35.8) (2023-02-17)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **plex-oauth:** 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user [#4835](https://github.com/Ombi-app/Ombi/issues/4835) ([4098da3](https://github.com/Ombi-app/Ombi/commit/4098da305aaea9dae9a552644268a4fed7204cfe))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -118,39 +118,39 @@
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="NewAlbums" xml:space="preserve">
|
<data name="NewAlbums" xml:space="preserve">
|
||||||
<value>New Albums</value>
|
<value>Novos álbuns</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewMovies" xml:space="preserve">
|
<data name="NewMovies" xml:space="preserve">
|
||||||
<value>New Movies</value>
|
<value>Novos filmes</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewTV" xml:space="preserve">
|
<data name="NewTV" xml:space="preserve">
|
||||||
<value>New TV</value>
|
<value>Novas séries</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="GenresLabel" xml:space="preserve">
|
<data name="GenresLabel" xml:space="preserve">
|
||||||
<value>Gêneros:</value>
|
<value>Gêneros:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AlbumTypeLabel" xml:space="preserve">
|
<data name="AlbumTypeLabel" xml:space="preserve">
|
||||||
<value>Type:</value>
|
<value>Tipo:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SeasonLabel" xml:space="preserve">
|
<data name="SeasonLabel" xml:space="preserve">
|
||||||
<value>Temporada:</value>
|
<value>Temporadas:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EpisodesLabel" xml:space="preserve">
|
<data name="EpisodesLabel" xml:space="preserve">
|
||||||
<value>Episodes:</value>
|
<value>Episódios:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PoweredBy" xml:space="preserve">
|
<data name="PoweredBy" xml:space="preserve">
|
||||||
<value>Powered by</value>
|
<value>Distribuído por</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Unsubscribe" xml:space="preserve">
|
<data name="Unsubscribe" xml:space="preserve">
|
||||||
<value>Unsubscribe</value>
|
<value>Cancelar subscrição</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Album" xml:space="preserve">
|
<data name="Album" xml:space="preserve">
|
||||||
<value>Album</value>
|
<value>Álbum</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Movie" xml:space="preserve">
|
<data name="Movie" xml:space="preserve">
|
||||||
<value>Movie</value>
|
<value>Filmes</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TvShow" xml:space="preserve">
|
<data name="TvShow" xml:space="preserve">
|
||||||
<value>TV Show</value>
|
<value>Séries</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -174,13 +174,18 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
}
|
}
|
||||||
if (parent.TheMovieDbId.IsNullOrEmpty())
|
if (parent.TheMovieDbId.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"Episode {episode.Name} is not linked to a TMDB series. Skipping.");
|
_logger.LogWarning($"Episode {episode.Name} for Tv Show {parent.Title} Doesn't have a valid TheMovieDbId. Skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!int.TryParse(parent.TheMovieDbId, out var parentMovieDb))
|
||||||
|
{
|
||||||
|
_logger.LogWarning($"Episode {episode.Name} for Tv Show {parent.Title} Doesn't have a valid TheMovieDbId. Skipping.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await AddToContent(content, new UserPlayedEpisode()
|
await AddToContent(content, new UserPlayedEpisode()
|
||||||
{
|
{
|
||||||
TheMovieDbId = int.Parse(parent.TheMovieDbId),
|
TheMovieDbId = parentMovieDb,
|
||||||
SeasonNumber = episode.ParentIndexNumber,
|
SeasonNumber = episode.ParentIndexNumber,
|
||||||
EpisodeNumber = episode.IndexNumber,
|
EpisodeNumber = episode.IndexNumber,
|
||||||
UserId = user.Id
|
UserId = user.Id
|
||||||
|
@ -196,7 +201,7 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
|
|
||||||
await AddToContent(content, new UserPlayedEpisode()
|
await AddToContent(content, new UserPlayedEpisode()
|
||||||
{
|
{
|
||||||
TheMovieDbId = int.Parse(parent.TheMovieDbId),
|
TheMovieDbId = parentMovieDb,
|
||||||
SeasonNumber = episode.ParentIndexNumber,
|
SeasonNumber = episode.ParentIndexNumber,
|
||||||
EpisodeNumber = episodeNumber,
|
EpisodeNumber = episodeNumber,
|
||||||
UserId = user.Id
|
UserId = user.Id
|
||||||
|
|
18
src/Ombi/ClientApp/.prettierrc.js
Normal file
18
src/Ombi/ClientApp/.prettierrc.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module.exports = {
|
||||||
|
...require('@exclaimer/prettier-config'),
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.yaml', '*.yml'],
|
||||||
|
options: {
|
||||||
|
tabWidth: 2,
|
||||||
|
singleQuote: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: 'index.html',
|
||||||
|
options: {
|
||||||
|
parser: 'html',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -82,22 +82,22 @@
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "ombi:build"
|
"buildTarget": "ombi:build"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"browserTarget": "ombi:build:production"
|
"buildTarget": "ombi:build:production"
|
||||||
},
|
},
|
||||||
"hmr": {
|
"hmr": {
|
||||||
"hmr": true,
|
"hmr": true,
|
||||||
"browserTarget": "ombi:build:hmr"
|
"buildTarget": "ombi:build:hmr"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "ombi:build"
|
"buildTarget": "ombi:build"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
|
|
|
@ -1,70 +1,71 @@
|
||||||
{
|
{
|
||||||
"name": "ombi",
|
"name": "ombi",
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve --port 3578 --configuration hmr",
|
"start": "ng serve --port 3578 --configuration hmr",
|
||||||
"build": "node --max_old_space_size=6144 node_modules/@angular/cli/bin/ng build -c production",
|
"build": "node --max_old_space_size=6144 node_modules/@angular/cli/bin/ng build -c production",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"docs:json": "compodoc -p ./tsconfig.json -e json -d .",
|
"docs:json": "compodoc -p ./tsconfig.json -e json -d .",
|
||||||
"storybook": "start-storybook -p 6006",
|
"storybook": "start-storybook -p 6006",
|
||||||
"chromatic": "chromatic --exit-zero-on-changes",
|
"chromatic": "chromatic --exit-zero-on-changes",
|
||||||
"storybookbuild": "yarn build-storybook"
|
"storybookbuild": "yarn build-storybook"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^15.2.10",
|
"@angular/animations": "^17.1.3",
|
||||||
"@angular/cdk": "^14.2.7",
|
"@angular/cdk": "16.2.14",
|
||||||
"@angular/common": "^15.2.10",
|
"@angular/common": "^17.1.3",
|
||||||
"@angular/compiler": "^15.2.10",
|
"@angular/compiler": "^17.1.3",
|
||||||
"@angular/core": "^15.2.10",
|
"@angular/core": "^17.1.3",
|
||||||
"@angular/forms": "^15.2.10",
|
"@angular/forms": "^17.1.3",
|
||||||
"@angular/material": "^14.2.7",
|
"@angular/material": "^14.2.7",
|
||||||
"@angular/platform-browser": "^15.2.10",
|
"@angular/platform-browser": "^17.1.3",
|
||||||
"@angular/platform-browser-dynamic": "^15.2.10",
|
"@angular/platform-browser-dynamic": "^17.1.3",
|
||||||
"@angular/platform-server": "^15.2.10",
|
"@angular/platform-server": "^17.1.3",
|
||||||
"@angular/router": "^15.2.10",
|
"@angular/router": "^17.1.3",
|
||||||
"@angularclass/hmr": "^3.0.0",
|
"@angularclass/hmr": "^3.0.0",
|
||||||
"@auth0/angular-jwt": "^5.0.2",
|
"@auth0/angular-jwt": "^5.0.2",
|
||||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||||
"@microsoft/signalr": "^6.0.23",
|
"@microsoft/signalr": "^6.0.23",
|
||||||
"@ngx-translate/core": "^14.0.0",
|
"@ngx-translate/core": "^14.0.0",
|
||||||
"@ngx-translate/http-loader": "^7.0.0",
|
"@ngx-translate/http-loader": "^7.0.0",
|
||||||
"@ngxs/devtools-plugin": "3.8.1",
|
"@ngxs/devtools-plugin": "3.8.1",
|
||||||
"@ngxs/store": "3.8.1",
|
"@ngxs/store": "3.8.1",
|
||||||
"@types/jquery": "^3.5.23",
|
"@types/jquery": "^3.5.23",
|
||||||
"@yellowspot/ng-truncate": "^2.0.0",
|
"@yellowspot/ng-truncate": "^2.0.0",
|
||||||
"angularx-qrcode": "^15.0.0",
|
"angularx-qrcode": "^16.0.0",
|
||||||
"bootstrap": "^4.2.1",
|
"bootstrap": "^4.2.1",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
"jquery": "3.7.1",
|
"date-fns": "3.3.1",
|
||||||
"lodash": "^4.17.21",
|
"jquery": "3.7.1",
|
||||||
"moment": "^2.29.1",
|
"lodash": "^4.17.21",
|
||||||
"ng2-cookies": "^1.0.12",
|
"lodash-es": "^4.17.21",
|
||||||
"ngx-clipboard": "^12.1.0",
|
"ng2-cookies": "^1.0.12",
|
||||||
"ngx-infinite-scroll": "^9.0.0",
|
"ngx-clipboard": "^16.0.0",
|
||||||
"ngx-moment": "^3.0.1",
|
"ngx-date-fns": "^11.0.0",
|
||||||
"ngx-order-pipe": "^2.2.0",
|
"ngx-infinite-scroll": "^17.0.0",
|
||||||
"popper.js": "^1.14.3",
|
"popper.js": "^1.14.3",
|
||||||
"primeicons": "^6.0.1",
|
"primeicons": "^6.0.1",
|
||||||
"primeng": "^15.4.1",
|
"primeng": "^17.6.0",
|
||||||
"rxjs": "^7.5.4",
|
"rxjs": "^7.5.4",
|
||||||
"ts-md5": "^1.2.7",
|
"ts-md5": "^1.2.7",
|
||||||
"zone.js": "~0.13.2"
|
"zone.js": "0.14.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^15.0.2",
|
"@angular-devkit/build-angular": "^17.1.3",
|
||||||
"@angular/cli": "^15.0.2",
|
"@angular/cli": "^17.1.3",
|
||||||
"@angular/compiler-cli": "^15.0.4",
|
"@angular/compiler-cli": "^17.1.3",
|
||||||
"@babel/core": "^7.18.9",
|
"@babel/core": "^7.18.9",
|
||||||
"@compodoc/compodoc": "^1.1.19",
|
"@compodoc/compodoc": "^1.1.19",
|
||||||
"@storybook/angular": "^6.5.9",
|
"@storybook/angular": "7.6.14",
|
||||||
"chromatic": "^6.7.1",
|
"@types/node": "^20.11.17",
|
||||||
"typescript": "~4.8.4"
|
"chromatic": "^6.7.1",
|
||||||
},
|
"typescript": "5.2.2"
|
||||||
"optionalDependencies": {
|
},
|
||||||
"protractor": "~5.4.0",
|
"optionalDependencies": {
|
||||||
"ts-node": "~5.0.1",
|
"protractor": "~5.4.0",
|
||||||
"tslint": "^5.12.0"
|
"ts-node": "~5.0.1",
|
||||||
}
|
"tslint": "^5.12.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { CanActivate } from "@angular/router";
|
|
||||||
import { AuthService } from "./auth.service";
|
import { AuthService } from "./auth.service";
|
||||||
import { StorageService } from "../shared/storage/storage-service";
|
import { StorageService } from "../shared/storage/storage-service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard {
|
||||||
|
|
||||||
constructor(private auth: AuthService, private router: Router,
|
constructor(private auth: AuthService, private router: Router,
|
||||||
private store: StorageService) { }
|
private store: StorageService) { }
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<p id="detailed-request-requestedby-{{request.mediaId}}">{{'MediaDetails.RequestedBy' | translate}} {{request.username}}</p>
|
<p id="detailed-request-requestedby-{{request.mediaId}}">{{'MediaDetails.RequestedBy' | translate}} {{request.username}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<p id="detailed-request-date-{{request.mediaId}}">{{'MediaDetails.OnDate' | translate}} {{request.requestDate | amFromUtc | amLocal | amUserLocale | amDateFormat: 'l LT'}}</p>
|
<p id="detailed-request-date-{{request.mediaId}}">{{'MediaDetails.OnDate' | translate}} {{request.requestDate | ombiDate: 'Ppp'}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<p id="detailed-request-status-{{request.mediaId}}">{{'MediaDetails.Status' | translate}} <span class="badge badge-{{getClass(request)}}">{{getStatus(request) | translate}}</span></p>
|
<p id="detailed-request-status-{{request.mediaId}}">{{'MediaDetails.Status' | translate}} <span class="badge badge-{{getClass(request)}}">{{getStatus(request) | translate}}</span></p>
|
||||||
|
|
|
@ -1,43 +1,42 @@
|
||||||
import { OmbiCommonModules } from "../modules";
|
import { OmbiCommonModules } from '../modules';
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ImageService } from "../../services";
|
import { ImageService } from '../../services';
|
||||||
import { fadeInOutAnimation } from "app/animations/fadeinout";
|
import { fadeInOutAnimation } from 'app/animations/fadeinout';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
selector: 'ombi-image-background',
|
selector: 'ombi-image-background',
|
||||||
templateUrl: './image-background.component.html',
|
templateUrl: './image-background.component.html',
|
||||||
styleUrls: ['./image-background.component.scss'],
|
styleUrls: ['./image-background.component.scss'],
|
||||||
imports: [...OmbiCommonModules, BrowserAnimationsModule],
|
imports: [...OmbiCommonModules, BrowserAnimationsModule],
|
||||||
providers: [ ImageService ],
|
providers: [ImageService],
|
||||||
animations: [ fadeInOutAnimation ],
|
animations: [fadeInOutAnimation],
|
||||||
})
|
})
|
||||||
export class ImageBackgroundComponent implements OnInit, OnDestroy {
|
export class ImageBackgroundComponent implements OnInit, OnDestroy {
|
||||||
|
public background: any;
|
||||||
|
public name: string;
|
||||||
|
private timer: any;
|
||||||
|
|
||||||
public background: any;
|
constructor(private images: ImageService, private sanitizer: DomSanitizer) {}
|
||||||
public name: string;
|
|
||||||
private timer: NodeJS.Timeout;
|
|
||||||
|
|
||||||
constructor(private images: ImageService, private sanitizer: DomSanitizer) { }
|
public ngOnDestroy(): void {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnInit(): void {
|
||||||
clearTimeout(this.timer);
|
this.cycleBackground();
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
this.timer = setInterval(() => {
|
||||||
this.cycleBackground();
|
this.cycleBackground();
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
|
||||||
this.timer = setInterval(() => {
|
private cycleBackground() {
|
||||||
this.cycleBackground();
|
this.images.getRandomBackgroundWithInfo().subscribe((x) => {
|
||||||
}, 30000);
|
this.background = this.sanitizer.bypassSecurityTrustStyle('url(' + x.url + ')');
|
||||||
}
|
this.name = x.name;
|
||||||
|
});
|
||||||
private cycleBackground() {
|
}
|
||||||
this.images.getRandomBackgroundWithInfo().subscribe((x) => {
|
}
|
||||||
this.background = this.sanitizer.bypassSecurityTrustStyle("url(" + x.url + ")");
|
|
||||||
this.name = x.name;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from '@angular/common';
|
||||||
import { MomentModule } from "ngx-moment";
|
|
||||||
|
|
||||||
export const OmbiCommonModules = [ CommonModule, MomentModule ];
|
export const OmbiCommonModules = [CommonModule];
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
$primary-colour: #df691a;
|
|
||||||
$primary-colour-outline: #ff761b;
|
|
||||||
$bg-colour: #333333;
|
|
||||||
$bg-colour-disabled: #252424;
|
|
|
@ -6,7 +6,6 @@ import { CustomPageService, NotificationService } from "../services";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./custompage.component.html",
|
templateUrl: "./custompage.component.html",
|
||||||
styleUrls: ["./custompage.component.scss"],
|
|
||||||
})
|
})
|
||||||
export class CustomPageComponent implements OnInit {
|
export class CustomPageComponent implements OnInit {
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
<p-skeleton id="cardLoading{{result.id}}" *ngIf="!fullyLoaded" width="100%" height="315px"></p-skeleton>
|
|
||||||
|
|
||||||
<div id="result{{result.id}}" *ngIf="fullyLoaded" class="ombi-card dark-card c" [style.display]="hide ? 'none' : 'block'">
|
<div id="result{{result.id}}" *ngIf="fullyLoaded" class="ombi-card dark-card c" [style.display]="hide ? 'none' : 'block'">
|
||||||
<div class="card-top-info">
|
<div class="card-top-info">
|
||||||
|
@ -43,8 +42,5 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,43 +1,51 @@
|
||||||
<div class="small-middle-container">
|
<div class="small-middle-container">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{'Discovery.Genres' | translate}}</h2>
|
@defer (on viewport; prefetch on idle) {
|
||||||
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
|
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{ 'Discovery.Genres' | translate }}</h2>
|
||||||
</div>
|
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
|
||||||
<div class="section">
|
}
|
||||||
<h2>{{'Discovery.RecentlyRequestedTab' | translate}}</h2>
|
@placeholder {
|
||||||
<div>
|
<p-skeleton width="100%" height="2rem"></p-skeleton>
|
||||||
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="section">
|
||||||
|
<h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
|
||||||
|
<div>
|
||||||
|
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section" [hidden]="!showSeasonal">
|
|
||||||
<h2>{{'Discovery.SeasonalTab' | translate}}</h2>
|
|
||||||
<div>
|
|
||||||
<carousel-list [id]="'seasonal'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Seasonal" (movieCount)="setSeasonalMovieCount($event)"></carousel-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
<div class="section" [hidden]="!showSeasonal">
|
||||||
<h2>{{'Discovery.PopularTab' | translate}}</h2>
|
<h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
|
||||||
<div>
|
<div>
|
||||||
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list>
|
<carousel-list
|
||||||
</div>
|
[id]="'seasonal'"
|
||||||
|
[isAdmin]="isAdmin"
|
||||||
|
[discoverType]="DiscoverType.Seasonal"
|
||||||
|
(movieCount)="setSeasonalMovieCount($event)"
|
||||||
|
></carousel-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div class="section">
|
||||||
|
<h2>{{ 'Discovery.PopularTab' | translate }}</h2>
|
||||||
|
<div>
|
||||||
|
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>{{'Discovery.TrendingTab' | translate}}</h2>
|
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2>
|
||||||
<div >
|
<div>
|
||||||
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list>
|
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<h2>{{'Discovery.UpcomingTab' | translate}}</h2>
|
|
||||||
<div>
|
|
||||||
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2>
|
||||||
|
<div>
|
||||||
|
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -3,6 +3,7 @@
|
||||||
<p-carousel #carousel [value]="requests" [numVisible]="3" [numScroll]="1"
|
<p-carousel #carousel [value]="requests" [numVisible]="3" [numScroll]="1"
|
||||||
[responsiveOptions]="responsiveOptions" [page]="0">
|
[responsiveOptions]="responsiveOptions" [page]="0">
|
||||||
<ng-template let-result pTemplate="item">
|
<ng-template let-result pTemplate="item">
|
||||||
|
@defer (on viewport; prefetch on idle) {
|
||||||
<ombi-detailed-card
|
<ombi-detailed-card
|
||||||
[request]="result"
|
[request]="result"
|
||||||
[isAdmin]="isAdmin"
|
[isAdmin]="isAdmin"
|
||||||
|
@ -10,6 +11,10 @@
|
||||||
(onApprove)="approve(result)"
|
(onApprove)="approve(result)"
|
||||||
(onDeny)="deny(result)">
|
(onDeny)="deny(result)">
|
||||||
</ombi-detailed-card>
|
</ombi-detailed-card>
|
||||||
|
}
|
||||||
|
@placeholder {
|
||||||
|
<p-skeleton width="100%" height="315px"></p-skeleton>
|
||||||
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</p-carousel>
|
</p-carousel>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,28 +1,42 @@
|
||||||
<mat-card class="issue-card" *ngIf="!deleted">
|
<mat-card class="issue-card" *ngIf="!deleted">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>{{issue.subject}}</mat-card-title>
|
<mat-card-title>{{ issue.subject }}</mat-card-title>
|
||||||
<mat-card-subtitle>{{'Issues.UserOnDate' | translate: { user: issue.userReported?.userName, date: issue.createdDate | amLocal | amUserLocale | amDateFormat: 'LL' } }}</mat-card-subtitle>
|
<mat-card-subtitle>{{
|
||||||
<mat-card-subtitle>{{issue.issueCategory.value}}</mat-card-subtitle>
|
'Issues.UserOnDate' | translate: { user: issue.userReported?.userName, date: issue.createdDate | ombiDate: 'PP' }
|
||||||
</mat-card-header>
|
}}</mat-card-subtitle>
|
||||||
<mat-card-content>
|
<mat-card-subtitle>{{ issue.issueCategory.value }}</mat-card-subtitle>
|
||||||
<p>
|
</mat-card-header>
|
||||||
{{issue.description}}
|
<mat-card-content>
|
||||||
</p>
|
<p>
|
||||||
</mat-card-content>
|
{{ issue.description }}
|
||||||
<mat-card-actions>
|
</p>
|
||||||
<button mat-raised-button (click)="openChat(issue)" color="accent"><i class="far fa-comments"></i> {{'Issues.Chat' | translate }}</button>
|
</mat-card-content>
|
||||||
<div *ngIf="isAdmin && settings;then content else empty"></div>
|
<mat-card-actions>
|
||||||
<ng-template #content>
|
<button mat-raised-button (click)="openChat(issue)" color="accent"><i class="far fa-comments"></i> {{ 'Issues.Chat' | translate }}</button>
|
||||||
<button mat-raised-button color="accent"
|
<div *ngIf="isAdmin && settings; then content; else empty"></div>
|
||||||
*ngIf="issue.status === IssueStatus.Pending && settings.enableInProgress"
|
<ng-template #content>
|
||||||
(click)="inProgress(issue)">{{'Issues.MarkInProgress' | translate }}</button>
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
color="accent"
|
||||||
|
*ngIf="issue.status === IssueStatus.Pending && settings.enableInProgress"
|
||||||
|
(click)="inProgress(issue)"
|
||||||
|
>
|
||||||
|
{{ 'Issues.MarkInProgress' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button mat-raised-button color="accent"
|
<button
|
||||||
*ngIf="issue.status === IssueStatus.Pending && !settings.enableInProgress || issue.status == IssueStatus.InProgress"
|
mat-raised-button
|
||||||
(click)="resolve(issue)">{{'Issues.MarkResolved' | translate}}</button>
|
color="accent"
|
||||||
|
*ngIf="(issue.status === IssueStatus.Pending && !settings.enableInProgress) || issue.status == IssueStatus.InProgress"
|
||||||
|
(click)="resolve(issue)"
|
||||||
|
>
|
||||||
|
{{ 'Issues.MarkResolved' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button mat-raised-button color="warn" (click)="delete(issue)"><i class="far fa-times-circle"></i> {{'Issues.Delete' | translate}}</button></ng-template>
|
<button mat-raised-button color="warn" (click)="delete(issue)">
|
||||||
<ng-template #empty></ng-template>
|
<i class="far fa-times-circle"></i> {{ 'Issues.Delete' | translate }}
|
||||||
</mat-card-actions>
|
</button></ng-template
|
||||||
|
>
|
||||||
|
<ng-template #empty></ng-template>
|
||||||
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
|
|
|
@ -1,95 +1,124 @@
|
||||||
<div *ngIf="issue">
|
<div *ngIf="issue">
|
||||||
<div class="row issue-details">
|
<div class="row issue-details">
|
||||||
<div class="myBg backdrop" [style.background-image]="backgroundPath"></div>
|
<div class="myBg backdrop" [style.background-image]="backgroundPath"></div>
|
||||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.6) 100%)"></div>
|
||||||
<h1>{{issue.title}} </h1>
|
<h1>{{ issue.title }}</h1>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<img class="img-responsive poster" src="{{posterPath}}" alt="poster">
|
<img class="img-responsive poster" src="{{ posterPath }}" alt="poster" />
|
||||||
|
|
||||||
<div class="issue-status">
|
<div class="issue-status">
|
||||||
<span *ngIf="issue.status === IssueStatus.Pending" id="pendingLabel" class="label label-warning">{{IssueStatus[issue.status]}}</span>
|
<span *ngIf="issue.status === IssueStatus.Pending" id="pendingLabel" class="label label-warning">{{
|
||||||
<span *ngIf="issue.status === IssueStatus.InProgress" id="inprogressLabel" class="label label-info">{{IssueStatus[issue.status]}}</span>
|
IssueStatus[issue.status]
|
||||||
<span *ngIf="issue.status === IssueStatus.Resolved" id="resolvedLabel" class="label label-success">{{IssueStatus[issue.status]}}</span>
|
}}</span>
|
||||||
</div>
|
<span *ngIf="issue.status === IssueStatus.InProgress" id="inprogressLabel" class="label label-info">{{
|
||||||
<span class="label label-success">{{issue.issueCategory.value}}</span>
|
IssueStatus[issue.status]
|
||||||
<br>
|
}}</span>
|
||||||
<span class="reported-by">
|
<span *ngIf="issue.status === IssueStatus.Resolved" id="resolvedLabel" class="label label-success">{{
|
||||||
<h3 *ngIf="issue.userReported?.alias">{{'Issues.ReportedBy' | translate}}:</h3>
|
IssueStatus[issue.status]
|
||||||
<h3 *ngIf="!issue.userReported?.alias">{{'Issues.ReportedBy' | translate}}:</h3>
|
}}</span>
|
||||||
</span>
|
</div>
|
||||||
<span class="reported-user">
|
<span class="label label-success">{{ issue.issueCategory.value }}</span>
|
||||||
<h3 *ngIf="issue.userReported?.alias">{{issue.userReported.alias}}</h3>
|
<br />
|
||||||
<h3 *ngIf="!issue.userReported?.alias">{{issue.userReported.userName}}</h3>
|
<span class="reported-by">
|
||||||
</span>
|
<h3 *ngIf="issue.userReported?.alias">{{ 'Issues.ReportedBy' | translate }}:</h3>
|
||||||
<br>
|
<h3 *ngIf="!issue.userReported?.alias">{{ 'Issues.ReportedBy' | translate }}:</h3>
|
||||||
<span class="subject-category"><h3 *ngIf="issue.subject">{{'Issues.Subject' | translate}}:</h3></span>
|
</span>
|
||||||
<span class="subject"><h3 *ngIf="issue.subject">{{issue.subject}}</h3></span>
|
<span class="reported-user">
|
||||||
<br>
|
<h3 *ngIf="issue.userReported?.alias">{{ issue.userReported.alias }}</h3>
|
||||||
<div class="form-group">
|
<h3 *ngIf="!issue.userReported?.alias">{{ issue.userReported.userName }}</h3>
|
||||||
<label for="description" class="control-label" [translate]="'Issues.Description'"></label>
|
</span>
|
||||||
<div>
|
<br />
|
||||||
<textarea class="form-control-custom form-control" disabled="disabled" [(ngModel)]="issue.description" rows="5" type="text"></textarea>
|
<span class="subject-category"
|
||||||
</div>
|
><h3 *ngIf="issue.subject">{{ 'Issues.Subject' | translate }}:</h3></span
|
||||||
</div>
|
>
|
||||||
|
<span class="subject"
|
||||||
|
><h3 *ngIf="issue.subject">{{ issue.subject }}</h3></span
|
||||||
|
>
|
||||||
|
<br />
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="description" class="control-label" [translate]="'Issues.Description'"></label>
|
||||||
|
<div>
|
||||||
|
<textarea
|
||||||
|
class="form-control-custom form-control"
|
||||||
|
disabled="disabled"
|
||||||
|
[(ngModel)]="issue.description"
|
||||||
|
rows="5"
|
||||||
|
type="text"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row chat-window col-xs-7 col-md-5" id="chat_window_1" style="margin-left: 10px">
|
||||||
|
<div class="col-xs-12 col-md-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading top-bar">
|
||||||
|
<div class="col-md-8 col-xs-8">
|
||||||
|
<h3 class="panel-title"><span class="glyphicon glyphicon-comment"></span> {{ 'Issues.Comments' | translate }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="comments" class="panel-body msg_container_base">
|
||||||
|
<div *ngIf="comments.length <= 0" class="row msg_container base_receive">
|
||||||
|
<div class="col-md-10 col-xs-10">
|
||||||
|
<div class="messages msg_sent">
|
||||||
|
<p [translate]="'Issues.NoComments'"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div
|
||||||
|
*ngFor="let comment of comments"
|
||||||
|
class="row msg_container"
|
||||||
|
[ngClass]="{ base_sent: comment.adminComment, base_receive: !comment.adminComment }"
|
||||||
|
>
|
||||||
|
<div class="col-md-10 col-xs-10">
|
||||||
|
<div class="messages msg_sent">
|
||||||
|
<i
|
||||||
|
*ngIf="isAdmin"
|
||||||
|
style="float: right"
|
||||||
|
class="fas fa-times"
|
||||||
|
aria-hidden="true"
|
||||||
|
(click)="deleteComment(comment.id)"
|
||||||
|
></i>
|
||||||
|
<p>{{ comment.comment }}</p>
|
||||||
|
<time>{{ comment.username }} • {{ comment.date | ombiDate: 'Ppp' }}</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
id="btn-input"
|
||||||
|
type="text"
|
||||||
|
class="form-control input-sm chat_input"
|
||||||
|
[(ngModel)]="newComment.comment"
|
||||||
|
[attr.placeholder]="'Issues.WriteMessagePlaceholder' | translate"
|
||||||
|
/>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
id="btn-chat"
|
||||||
|
(click)="addComment()"
|
||||||
|
[translate]="'Issues.SendMessageButton'"
|
||||||
|
></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
<div class="row chat-window col-xs-7 col-md-5" id="chat_window_1" style="margin-left:10px;">
|
<div *ngIf="isAdmin && settings">
|
||||||
<div class="col-xs-12 col-md-12">
|
<div *ngIf="issue.status === IssueStatus.Pending && settings.enableInProgress">
|
||||||
<div class="panel panel-default">
|
<button class="btn btn-primary btn-sm bottom-btn" (click)="inProgress()">{{ 'Issues.MarkInProgress' | translate }}</button>
|
||||||
<div class="panel-heading top-bar">
|
</div>
|
||||||
<div class="col-md-8 col-xs-8">
|
<div *ngIf="(issue.status === IssueStatus.Pending && !settings.enableInProgress) || issue.status == IssueStatus.InProgress">
|
||||||
<h3 class="panel-title">
|
<button class="btn btn-primary btn-sm bottom-btn" (click)="resolve()" [translate]="'Issues.MarkResolved'"></button>
|
||||||
<span class="glyphicon glyphicon-comment"></span> {{'Issues.Comments' | translate}}
|
</div>
|
||||||
</h3>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="comments" class="panel-body msg_container_base">
|
|
||||||
<div *ngIf="comments.length <= 0" class="row msg_container base_receive">
|
|
||||||
<div class="col-md-10 col-xs-10">
|
|
||||||
|
|
||||||
<div class="messages msg_sent">
|
|
||||||
<p [translate]="'Issues.NoComments'"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngFor="let comment of comments" class="row msg_container" [ngClass]="{'base_sent': comment.adminComment, 'base_receive': !comment.adminComment}">
|
|
||||||
<div class="col-md-10 col-xs-10">
|
|
||||||
|
|
||||||
<div class="messages msg_sent"> <i *ngIf="isAdmin" style="float:right;" class="fas fa-times" aria-hidden="true" (click)="deleteComment(comment.id)"></i>
|
|
||||||
<p>{{comment.comment}}</p>
|
|
||||||
<time>{{comment.username}} • {{comment.date | amFromUtc | amLocal | amUserLocale | amDateFormat: 'l LT'}}</time>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<div class="input-group">
|
|
||||||
<input id="btn-input" type="text" class="form-control input-sm chat_input" [(ngModel)]="newComment.comment" [attr.placeholder]="'Issues.WriteMessagePlaceholder' | translate" />
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-primary btn-sm" id="btn-chat" (click)="addComment()" [translate]="'Issues.SendMessageButton'"></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div *ngIf="isAdmin && settings">
|
|
||||||
<div *ngIf="issue.status === IssueStatus.Pending && settings.enableInProgress">
|
|
||||||
<button class="btn btn-primary btn-sm bottom-btn" (click)="inProgress()">{{'Issues.MarkInProgress' | translate }}</button>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="issue.status === IssueStatus.Pending && !settings.enableInProgress || issue.status == IssueStatus.InProgress">
|
|
||||||
<button class="btn btn-primary btn-sm bottom-btn" (click)="resolve()" [translate]="'Issues.MarkResolved'"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
|
@ -1,47 +1,34 @@
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
// import { NbChatModule, NbThemeModule } from '@nebular/theme';
|
// import { NbChatModule, NbThemeModule } from '@nebular/theme';
|
||||||
|
|
||||||
import { OrderModule } from "ngx-order-pipe";
|
import { AuthGuard } from '../auth/auth.guard';
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
import { SharedModule } from "../shared/shared.module";
|
import { IssueDetailsComponent } from './issueDetails.component';
|
||||||
|
import { IssuesComponent } from './issues.component';
|
||||||
|
import { IssuesTableComponent } from './issuestable.component';
|
||||||
|
import { IssuesDetailsComponent } from './components/details/details.component';
|
||||||
|
|
||||||
import { IssueDetailsComponent } from "./issueDetails.component";
|
import { PipeModule } from '../pipes/pipe.module';
|
||||||
import { IssuesComponent } from "./issues.component";
|
|
||||||
import { IssuesTableComponent } from "./issuestable.component";
|
|
||||||
import { IssuesDetailsComponent } from "./components/details/details.component";
|
|
||||||
|
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import * as fromComponents from './components';
|
||||||
|
|
||||||
import * as fromComponents from "./components";
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", component: IssuesComponent, canActivate: [AuthGuard] },
|
{ path: '', component: IssuesComponent, canActivate: [AuthGuard] },
|
||||||
{ path: ":providerId", component: IssuesDetailsComponent, canActivate: [AuthGuard] },
|
{ path: ':providerId', component: IssuesDetailsComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
OrderModule,
|
PipeModule,
|
||||||
PipeModule,
|
SharedModule,
|
||||||
SharedModule,
|
// NbChatModule,
|
||||||
// NbChatModule,
|
],
|
||||||
],
|
declarations: [IssuesComponent, IssueDetailsComponent, IssuesTableComponent, ...fromComponents.components],
|
||||||
declarations: [
|
exports: [RouterModule],
|
||||||
IssuesComponent,
|
providers: [...fromComponents.providers],
|
||||||
IssueDetailsComponent,
|
|
||||||
IssuesTableComponent,
|
|
||||||
...fromComponents.components
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
RouterModule,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
...fromComponents.providers
|
|
||||||
],
|
|
||||||
|
|
||||||
})
|
})
|
||||||
export class IssuesModule { }
|
export class IssuesModule {}
|
||||||
|
|
|
@ -1,300 +1,433 @@
|
||||||
<div *ngIf="!movie" class="justify-content-md-center top-spacing loading-spinner">
|
<div *ngIf="!movie" class="justify-content-md-center top-spacing loading-spinner">
|
||||||
<mat-spinner [color]="'accent'"></mat-spinner>
|
<mat-spinner [color]="'accent'"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="movie" class="main-content-container">
|
<div *ngIf="movie" class="main-content-container">
|
||||||
|
<top-banner
|
||||||
|
[background]="movie.background"
|
||||||
|
[available]="movie.available"
|
||||||
|
[title]="movie.title"
|
||||||
|
[releaseDate]="movie.releaseDate"
|
||||||
|
[tagline]="movie.tagline"
|
||||||
|
></top-banner>
|
||||||
|
<div class="social-icons-container">
|
||||||
|
<social-icons
|
||||||
|
[homepage]="movie.homepage"
|
||||||
|
[theMoviedbId]="movie.id"
|
||||||
|
[hasTrailer]="movie.videos?.results?.length > 0"
|
||||||
|
[imdbId]="movie.imdbId"
|
||||||
|
[twitter]="movie.externalIds?.twitterId"
|
||||||
|
[facebook]="movie.externalIds?.facebookId"
|
||||||
|
[instagram]="movie.externalIds?.instagramId"
|
||||||
|
[available]="movie.available"
|
||||||
|
[isAdmin]="isAdmin"
|
||||||
|
[canShowAdvanced]="showAdvanced && movieRequest"
|
||||||
|
[type]="requestType"
|
||||||
|
[has4KRequest]="movie.has4KRequest"
|
||||||
|
(openTrailer)="openDialog()"
|
||||||
|
(onAdvancedOptions)="openAdvancedOptions()"
|
||||||
|
(onReProcessRequest)="reProcessRequest(false)"
|
||||||
|
(onReProcess4KRequest)="reProcessRequest(true)"
|
||||||
|
>
|
||||||
|
</social-icons>
|
||||||
|
</div>
|
||||||
|
<section id="info-wrapper">
|
||||||
|
<div class="small-middle-container">
|
||||||
|
<div class="row justify-content-center justify-content-sm-start header-container">
|
||||||
|
<div class="details-poster-container">
|
||||||
|
<media-poster [posterPath]="movie.posterPath"></media-poster>
|
||||||
|
</div>
|
||||||
|
|
||||||
<top-banner [background]="movie.background" [available]="movie.available" [title]="movie.title"
|
<!--Next to poster-->
|
||||||
[releaseDate]="movie.releaseDate" [tagline]="movie.tagline"></top-banner>
|
<div class="details-button-container">
|
||||||
<div class="social-icons-container">
|
<div class="col-12 media-row">
|
||||||
|
<span *ngIf="movie.available || movie.available4K">
|
||||||
|
<a
|
||||||
|
id="viewOnPlexButton"
|
||||||
|
*ngIf="movie.plexUrl"
|
||||||
|
href="{{ movie.plexUrl }}"
|
||||||
|
mat-raised-button
|
||||||
|
target="_blank"
|
||||||
|
class="btn-spacing viewon-btn plex"
|
||||||
|
>
|
||||||
|
{{ 'Search.ViewOnPlex' | translate }}
|
||||||
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
id="viewOnEmbyButton"
|
||||||
|
*ngIf="movie.embyUrl"
|
||||||
|
href="{{ movie.embyUrl }}"
|
||||||
|
mat-raised-button
|
||||||
|
target="_blank"
|
||||||
|
class="btn-spacing viewon-btn emby"
|
||||||
|
>
|
||||||
|
{{ 'Search.ViewOnEmby' | translate }}
|
||||||
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
id="viewOnJellyfinButton"
|
||||||
|
*ngIf="movie.jellyfinUrl"
|
||||||
|
href="{{ movie.jellyfinUrl }}"
|
||||||
|
mat-raised-button
|
||||||
|
target="_blank"
|
||||||
|
class="btn-spacing viewon-btn jellyfin"
|
||||||
|
>
|
||||||
|
{{ 'Search.ViewOnJellyfin' | translate }}
|
||||||
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<!-- Regular Movie Status -->
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-green btn-spacing"
|
||||||
|
id="availableBtn"
|
||||||
|
*ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"
|
||||||
|
>
|
||||||
|
{{ 'Common.Available' | translate }}
|
||||||
|
</button>
|
||||||
|
<span *ngIf="!movie.available">
|
||||||
|
<span *ngIf="movie.requested || movie.approved; then requestedBtn; else notRequestedBtn"></span>
|
||||||
|
<ng-template #requestedBtn>
|
||||||
|
<button
|
||||||
|
id="requestedBtn"
|
||||||
|
mat-raised-button
|
||||||
|
*ngIf="!hasRequest || (hasRequest && movieRequest && !movieRequest.denied)"
|
||||||
|
class="btn-spacing"
|
||||||
|
color="warn"
|
||||||
|
[disabled]
|
||||||
|
>
|
||||||
|
<i class="fas fa-check"></i>
|
||||||
|
{{ 'Common.Requested' | translate }}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #notRequestedBtn>
|
||||||
|
<button
|
||||||
|
*ngIf="!movie.requested"
|
||||||
|
id="requestBtn"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="primary"
|
||||||
|
(click)="request(false)"
|
||||||
|
>
|
||||||
|
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
||||||
|
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
||||||
|
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
||||||
|
{{ 'Common.Request' | translate }}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
</span>
|
||||||
|
|
||||||
<social-icons [homepage]="movie.homepage" [theMoviedbId]="movie.id"
|
<!-- 4k Status -->
|
||||||
[hasTrailer]="movie.videos?.results?.length > 0" [imdbId]="movie.imdbId"
|
<span *ngIf="is4KEnabled">
|
||||||
[twitter]="movie.externalIds?.twitterId" [facebook]="movie.externalIds?.facebookId"
|
<span *permission="roleName4k">
|
||||||
[instagram]="movie.externalIds?.instagramId" [available]="movie.available" [isAdmin]="isAdmin"
|
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn4k" *ngIf="movie.available4K">
|
||||||
[canShowAdvanced]="showAdvanced && movieRequest" [type]="requestType" [has4KRequest]="movie.has4KRequest"
|
{{ 'Common.Available4K' | translate }}
|
||||||
(openTrailer)="openDialog()" (onAdvancedOptions)="openAdvancedOptions()"
|
</button>
|
||||||
(onReProcessRequest)="reProcessRequest(false)" (onReProcess4KRequest)="reProcessRequest(true)">
|
|
||||||
</social-icons>
|
|
||||||
|
|
||||||
</div>
|
<span *ngIf="!movie.available4K">
|
||||||
<section id="info-wrapper">
|
<span *ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K; else notRequestedBtn4K"></span>
|
||||||
<div class="small-middle-container">
|
<ng-template #requestedBtn4K>
|
||||||
|
<button
|
||||||
|
id="requestedBtn4K"
|
||||||
|
mat-raised-button
|
||||||
|
*ngIf="movieRequest && !movieRequest.denied4K"
|
||||||
|
class="btn-spacing"
|
||||||
|
color="warn"
|
||||||
|
[disabled]
|
||||||
|
>
|
||||||
|
<i class="fas fa-check"></i>
|
||||||
|
{{ 'Common.Requested4K' | translate }}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #notRequestedBtn4K>
|
||||||
|
<button
|
||||||
|
*ngIf="!movie.has4KRequest"
|
||||||
|
id="requestBtn4k"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="primary"
|
||||||
|
(click)="request(true)"
|
||||||
|
>
|
||||||
|
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
||||||
|
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
||||||
|
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
||||||
|
{{ 'Common.Request4K' | translate }}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="row justify-content-center justify-content-sm-start header-container">
|
<span *ngIf="movieRequest?.showSubscribe">
|
||||||
<div class="details-poster-container">
|
<button *ngIf="!movieRequest?.subscribed" (click)="notify()" id="notifyBtn" mat-raised-button class="btn-spacing">
|
||||||
<media-poster [posterPath]=movie.posterPath></media-poster>
|
<i class="fas fa-bell"></i> {{ 'Requests.Notify' | translate }}
|
||||||
</div>
|
</button>
|
||||||
|
<button *ngIf="movieRequest?.subscribed" (click)="unNotify()" id="unnotifyBtn" mat-raised-button class="btn-spacing">
|
||||||
|
<i class="fas fa-bell-slash"></i> {{ 'Requests.RemoveNotification' | translate }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
|
||||||
<!--Next to poster-->
|
<span *ngIf="isAdmin && hasRequest">
|
||||||
<div class="details-button-container">
|
<button
|
||||||
<div class="col-12 media-row">
|
id="approveBtn"
|
||||||
<span *ngIf="movie.available || movie.available4K">
|
*ngIf="!movie.approved && movie.requested"
|
||||||
<a id="viewOnPlexButton" *ngIf="movie.plexUrl" href="{{movie.plexUrl}}" mat-raised-button
|
(click)="approve(false)"
|
||||||
target="_blank" class="btn-spacing viewon-btn plex">
|
mat-raised-button
|
||||||
{{'Search.ViewOnPlex' | translate}}
|
class="btn-spacing"
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
color="accent"
|
||||||
</a>
|
>
|
||||||
<a id="viewOnEmbyButton" *ngIf="movie.embyUrl" href="{{movie.embyUrl}}" mat-raised-button
|
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||||
target="_blank" class="btn-spacing viewon-btn emby">
|
</button>
|
||||||
{{'Search.ViewOnEmby' | translate}}
|
<button
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
id="markAvailableBtn"
|
||||||
</a>
|
*ngIf="!movie.available && movie.requested"
|
||||||
<a id="viewOnJellyfinButton" *ngIf="movie.jellyfinUrl" href="{{movie.jellyfinUrl}}"
|
(click)="markAvailable(false)"
|
||||||
mat-raised-button target="_blank" class="btn-spacing viewon-btn jellyfin">
|
mat-raised-button
|
||||||
{{'Search.ViewOnJellyfin' | translate}}
|
class="btn-spacing"
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
color="accent"
|
||||||
</a>
|
>
|
||||||
</span>
|
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||||
<!-- Regular Movie Status -->
|
</button>
|
||||||
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn"
|
<button
|
||||||
*ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{
|
id="markUnavailableBtn"
|
||||||
'Common.Available' | translate }}</button>
|
*ngIf="movie.available && movie.requested"
|
||||||
<span *ngIf="!movie.available">
|
(click)="markUnavailable(false)"
|
||||||
<span
|
mat-raised-button
|
||||||
*ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
|
class="btn-spacing"
|
||||||
<ng-template #requestedBtn>
|
color="accent"
|
||||||
<button id="requestedBtn" mat-raised-button
|
>
|
||||||
*ngIf="!hasRequest || hasRequest && movieRequest && !movieRequest.denied"
|
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
||||||
class="btn-spacing" color="warn" [disabled]>
|
</button>
|
||||||
<i class="fas fa-check"></i>
|
|
||||||
{{ 'Common.Requested' | translate }}
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template #notRequestedBtn>
|
|
||||||
<button *ngIf="!movie.requested" id="requestBtn" mat-raised-button class="btn-spacing"
|
|
||||||
color="primary" (click)="request(false)">
|
|
||||||
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
|
||||||
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
|
||||||
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
|
||||||
{{'Common.Request' | translate }}
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- 4k Status -->
|
<!-- 4k -->
|
||||||
<span *ngIf="is4KEnabled">
|
<span *ngIf="is4KEnabled">
|
||||||
<span *permission="roleName4k">
|
<span *permission="roleName4k">
|
||||||
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn4k"
|
<button
|
||||||
*ngIf="movie.available4K"> {{
|
id="approve4kBtn"
|
||||||
'Common.Available4K' | translate }}
|
*ngIf="!movie.approved4K && movie.has4KRequest"
|
||||||
</button>
|
(click)="approve(true)"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="accent"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Common.Approve4K' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="markAvailable4kBtn"
|
||||||
|
*ngIf="!movie.available4K && movie.has4KRequest"
|
||||||
|
(click)="markAvailable(true)"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="accent"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable4K' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="markUnavailable4kBtn"
|
||||||
|
*ngIf="movie.available4K"
|
||||||
|
(click)="markUnavailable(true)"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="accent"
|
||||||
|
>
|
||||||
|
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable4K' | translate }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
<span *ngIf="!movie.available4K">
|
<button
|
||||||
<span
|
id="denyBtn"
|
||||||
*ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K else notRequestedBtn4K"></span>
|
*ngIf="!movieRequest.denied && movie.requested"
|
||||||
<ng-template #requestedBtn4K>
|
mat-raised-button
|
||||||
<button id="requestedBtn4K" mat-raised-button
|
class="btn-spacing"
|
||||||
*ngIf="movieRequest && !movieRequest.denied4K" class="btn-spacing"
|
color="warn"
|
||||||
color="warn" [disabled]>
|
(click)="deny(false)"
|
||||||
<i class="fas fa-check"></i>
|
>
|
||||||
{{ 'Common.Requested4K' | translate }}
|
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
|
||||||
<ng-template #notRequestedBtn4K>
|
|
||||||
<button *ngIf="!movie.has4KRequest" id="requestBtn4k" mat-raised-button
|
|
||||||
class="btn-spacing" color="primary" (click)="request(true)">
|
|
||||||
<i *ngIf="movie.requestProcessing"
|
|
||||||
class="fas fa-circle-notch fa-spin fa-fw"></i>
|
|
||||||
<i *ngIf="!movie.requestProcessing && !movie.processed"
|
|
||||||
class="fas fa-plus"></i>
|
|
||||||
<i *ngIf="movie.processed && !movie.requestProcessing"
|
|
||||||
class="fas fa-check"></i>
|
|
||||||
{{'Common.Request4K' | translate }}
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span *ngIf="movieRequest?.showSubscribe">
|
<button
|
||||||
<button *ngIf="!movieRequest?.subscribed" (click)="notify()" id="notifyBtn"
|
id="deniedButton"
|
||||||
mat-raised-button class="btn-spacing"> <i class="fas fa-bell"></i>
|
*ngIf="movieRequest && movieRequest.denied"
|
||||||
{{ 'Requests.Notify' | translate }}</button>
|
[matTooltip]="movieRequest.deniedReason"
|
||||||
<button *ngIf="movieRequest?.subscribed" (click)="unNotify()" id="unnotifyBtn"
|
mat-raised-button
|
||||||
mat-raised-button class="btn-spacing"> <i class="fas fa-bell-slash"></i>
|
class="btn-spacing"
|
||||||
{{ 'Requests.RemoveNotification' | translate }}</button>
|
color="warn"
|
||||||
</span>
|
>
|
||||||
|
<i class="fas fa-times"></i> {{ 'MediaDetails.Denied' | translate }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
|
||||||
<span *ngIf="isAdmin && hasRequest">
|
<button id="reportIssueBtn" mat-raised-button class="btn-spacing" color="danger" (click)="issue()" *ngIf="issuesEnabled">
|
||||||
<button id="approveBtn" *ngIf="!movie.approved && movie.requested" (click)="approve(false)"
|
<i class="fas fa-exclamation"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||||
mat-raised-button class="btn-spacing" color="accent">
|
</button>
|
||||||
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
<button
|
||||||
</button>
|
id="viewCollectionBtn"
|
||||||
<button id="markAvailableBtn" *ngIf="!movie.available && movie.requested"
|
*ngIf="movie.belongsToCollection"
|
||||||
(click)="markAvailable(false)" mat-raised-button class="btn-spacing" color="accent">
|
[routerLink]="'/discover/collection/' + movie.belongsToCollection.id"
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
mat-raised-button
|
||||||
</button>
|
class="btn-spacing"
|
||||||
<button id="markUnavailableBtn" *ngIf="movie.available && movie.requested"
|
>
|
||||||
(click)="markUnavailable(false)" mat-raised-button class="btn-spacing" color="accent">
|
<i class="fas fa-list"></i> {{ 'MediaDetails.ViewCollection' | translate }}
|
||||||
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
</button>
|
||||||
</button>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 4k -->
|
<div class="row">
|
||||||
<span *ngIf="is4KEnabled">
|
<div class="col-12 col-md-2">
|
||||||
<span *permission="roleName4k">
|
<mat-card class="mat-elevation-z8">
|
||||||
<button id="approve4kBtn" *ngIf="!movie.approved4K && movie.has4KRequest"
|
<mat-card-content>
|
||||||
(click)="approve(true)" mat-raised-button class="btn-spacing" color="accent">
|
<movie-information-panel
|
||||||
<i class="fas fa-plus"></i> {{ 'Common.Approve4K' | translate }}
|
[movie]="movie"
|
||||||
</button>
|
[request]="movieRequest"
|
||||||
<button id="markAvailable4kBtn" *ngIf="!movie.available4K && movie.has4KRequest"
|
[advancedOptions]="showAdvanced"
|
||||||
(click)="markAvailable(true)" mat-raised-button class="btn-spacing"
|
></movie-information-panel>
|
||||||
color="accent">
|
</mat-card-content>
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable4K' | translate }}
|
</mat-card>
|
||||||
</button>
|
</div>
|
||||||
<button id="markUnavailable4kBtn" *ngIf="movie.available4K"
|
|
||||||
(click)="markUnavailable(true)" mat-raised-button class="btn-spacing"
|
|
||||||
color="accent">
|
|
||||||
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable4K' | translate }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<button id="denyBtn" *ngIf="!movieRequest.denied && movie.requested" mat-raised-button
|
<div class="col-12 col-md-10">
|
||||||
class="btn-spacing" color="warn" (click)="deny(false)">
|
<div class="row">
|
||||||
<i class="fas fa-times"></i> {{'Requests.Deny' | translate }}
|
<div class="col-12">
|
||||||
</button>
|
<mat-card class="mat-elevation-z8 spacing-below">
|
||||||
|
<mat-card-content>
|
||||||
|
@defer (on viewport; prefetch on idle) {
|
||||||
|
{{ movie.overview }}
|
||||||
|
}
|
||||||
|
@placeholder {
|
||||||
|
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton>
|
||||||
|
}
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="deniedButton" *ngIf="movieRequest && movieRequest.denied"
|
<div class="row">
|
||||||
[matTooltip]="movieRequest.deniedReason" mat-raised-button class="btn-spacing"
|
<div class="col-12">
|
||||||
color="warn">
|
@defer (on viewport; prefetch on idle) {
|
||||||
<i class="fas fa-times"></i> {{'MediaDetails.Denied' | translate }}
|
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
|
||||||
</button>
|
}
|
||||||
</span>
|
@placeholder {
|
||||||
|
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="reportIssueBtn" mat-raised-button class="btn-spacing" color="danger"
|
<div class="row">
|
||||||
(click)="issue()" *ngIf="issuesEnabled">
|
<div class="col-12">
|
||||||
<i class="fas fa-exclamation"></i> {{'Requests.ReportIssue' | translate }}
|
@defer (on viewport; prefetch on idle) {
|
||||||
</button>
|
<cast-carousel [cast]="movie.credits.crew"></cast-carousel>
|
||||||
<button id="viewCollectionBtn" *ngIf="movie.belongsToCollection"
|
}
|
||||||
[routerLink]="'/discover/collection/' + movie.belongsToCollection.id" mat-raised-button
|
@placeholder {
|
||||||
class="btn-spacing">
|
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton>
|
||||||
<i class="fas fa-list"></i> {{'MediaDetails.ViewCollection' | translate}}
|
}
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<!-- <div class="row card-spacer" *ngIf="movie.videos?.results?.length > 0">
|
||||||
<div class="col-12 col-md-2">
|
|
||||||
<mat-card class="mat-elevation-z8">
|
|
||||||
<mat-card-content>
|
|
||||||
<movie-information-panel [movie]="movie" [request]="movieRequest"
|
|
||||||
[advancedOptions]="showAdvanced"></movie-information-panel>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-10">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<mat-card class=" mat-elevation-z8 spacing-below">
|
|
||||||
<mat-card-content>
|
|
||||||
{{movie.overview}}
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<crew-carousel [crew]="movie.credits.crew"></crew-carousel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="row card-spacer" *ngIf="movie.videos?.results?.length > 0">
|
|
||||||
|
|
||||||
<div class="col-md-6" *ngFor="let video of movie.videos?.results">
|
<div class="col-md-6" *ngFor="let video of movie.videos?.results">
|
||||||
<iframe width="100%" height="315px" [src]="'https://www.youtube.com/embed/' + video.key | safe" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
<iframe width="100%" height="315px" [src]="'https://www.youtube.com/embed/' + video.key | safe" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div class="row" *ngIf="movie.videos?.results?.length > 0">
|
<div class="row" *ngIf="movie.videos?.results?.length > 0">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<mat-card class="mat-elevation-z8">
|
<mat-card class="mat-elevation-z8">
|
||||||
<mat-card-header>{{'MediaDetails.Trailers' | translate}}</mat-card-header>
|
@defer (on viewport; prefetch on idle) {
|
||||||
<mat-card-content>
|
<mat-card-header>{{ 'MediaDetails.Trailers' | translate }}</mat-card-header>
|
||||||
<p-carousel class="no-indicator" [numVisible]="2" [numScroll]="10" [page]="0"
|
<mat-card-content>
|
||||||
[value]="movie.videos?.results">
|
<p-carousel class="no-indicator" [numVisible]="2" [numScroll]="10" [page]="0" [value]="movie.videos?.results">
|
||||||
<ng-template let-result pTemplate="item">
|
<ng-template let-result pTemplate="item">
|
||||||
<iframe width="98%" height="315px"
|
<iframe
|
||||||
[src]="'https://www.youtube.com/embed/' + result.key | safe"
|
width="98%"
|
||||||
frameborder="0"
|
height="315px"
|
||||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
[src]="'https://www.youtube.com/embed/' + result.key | safe"
|
||||||
allowfullscreen></iframe>
|
frameborder="0"
|
||||||
</ng-template>
|
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
||||||
</p-carousel>
|
allowfullscreen
|
||||||
</mat-card-content>
|
></iframe>
|
||||||
</mat-card>
|
</ng-template>
|
||||||
</div>
|
</p-carousel>
|
||||||
</div>
|
</mat-card-content>
|
||||||
|
}
|
||||||
|
@placeholder {
|
||||||
|
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton>
|
||||||
|
}
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="issuesPanel">
|
<div class="issuesPanel">
|
||||||
<issues-panel [providerId]="movie.imdbId" [isAdmin]="isAdmin"></issues-panel>
|
<issues-panel [providerId]="movie.imdbId" [isAdmin]="isAdmin"></issues-panel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-accordion class=" mat-elevation-z8 spacing-below ">
|
@defer (on viewport; prefetch on idle) {
|
||||||
<mat-expansion-panel *ngIf="movie.recommendations?.results?.length> 0">
|
<mat-accordion class="mat-elevation-z8 spacing-below">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel *ngIf="movie.recommendations?.results?.length > 0">
|
||||||
<mat-panel-title>
|
<mat-expansion-panel-header>
|
||||||
{{'MediaDetails.RecommendationsTitle' | translate}}
|
<mat-panel-title>
|
||||||
</mat-panel-title>
|
{{ 'MediaDetails.RecommendationsTitle' | translate }}
|
||||||
</mat-expansion-panel-header>
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<div class="row card-spacer ">
|
<div class="row card-spacer">
|
||||||
|
<div class="col-md-2" *ngFor="let r of movie.recommendations?.results">
|
||||||
|
<div class="sidebar affixable affix-top preview-poster">
|
||||||
|
<div class="poster">
|
||||||
|
<a [routerLink]="'/details/movie/' + r.id">
|
||||||
|
<ombi-image
|
||||||
|
class="real grow"
|
||||||
|
matTooltip="{{ r.title }}"
|
||||||
|
src="https://image.tmdb.org/t/p/w300/{{ r.poster_path }}"
|
||||||
|
alt="Poster"
|
||||||
|
style="display: block"
|
||||||
|
>
|
||||||
|
</ombi-image>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
<mat-expansion-panel *ngIf="movie.similar?.results?.length > 0">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>
|
||||||
|
{{ 'MediaDetails.SimilarTitle' | translate }}
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<div class="col-md-2" *ngFor="let r of movie.recommendations?.results">
|
<div class="row card-spacer">
|
||||||
<div class="sidebar affixable affix-top preview-poster">
|
<div class="col-md-2" *ngFor="let r of movie.similar.results">
|
||||||
<div class="poster">
|
<div class="sidebar affixable affix-top preview-poster">
|
||||||
<a [routerLink]="'/details/movie/'+r.id">
|
<div class="poster">
|
||||||
<ombi-image class="real grow" matTooltip="{{r.title}}"
|
<a [routerLink]="'/details/movie/' + r.id">
|
||||||
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}"
|
<ombi-image
|
||||||
alt="Poster" style="display: block;"> </ombi-image>
|
class="real grow"
|
||||||
</a>
|
matTooltip="{{ r.title }}"
|
||||||
</div>
|
src="https://image.tmdb.org/t/p/w300/{{ r.poster_path }}"
|
||||||
</div>
|
alt="Poster"
|
||||||
</div>
|
style="display: block"
|
||||||
</div>
|
></ombi-image>
|
||||||
</mat-expansion-panel>
|
</a>
|
||||||
<mat-expansion-panel *ngIf="movie.similar?.results?.length > 0">
|
</div>
|
||||||
<mat-expansion-panel-header>
|
</div>
|
||||||
<mat-panel-title>
|
</div>
|
||||||
{{'MediaDetails.SimilarTitle' | translate}}
|
</div>
|
||||||
</mat-panel-title>
|
</mat-expansion-panel>
|
||||||
</mat-expansion-panel-header>
|
</mat-accordion>
|
||||||
|
}
|
||||||
<div class="row card-spacer">
|
@placeholder {
|
||||||
|
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton>
|
||||||
<div class="col-md-2" *ngFor="let r of movie.similar.results">
|
}
|
||||||
<div class="sidebar affixable affix-top preview-poster">
|
</div>
|
||||||
<div class="poster ">
|
</div>
|
||||||
<a [routerLink]="'/details/movie/'+r.id">
|
</div>
|
||||||
<ombi-image class="real grow" matTooltip="{{r.title}}"
|
</div>
|
||||||
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}"
|
</div>
|
||||||
alt="Poster" style="display: block;"></ombi-image>
|
<div class="bottom-page-gap"></div>
|
||||||
</a>
|
</section>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</mat-expansion-panel>
|
|
||||||
</mat-accordion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-page-gap">
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,336 +1,376 @@
|
||||||
import { Component, OnInit, ViewEncapsulation } from "@angular/core";
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from "../../../services";
|
import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from '../../../services';
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { ICrewViewModel, ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";
|
import { ICrewViewModel, ISearchMovieResultV2 } from '../../../interfaces/ISearchMovieResultV2';
|
||||||
import { MatDialog } from "@angular/material/dialog";
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
|
import { YoutubeTrailerComponent } from '../shared/youtube-trailer.component';
|
||||||
import { AuthService } from "../../../auth/auth.service";
|
import { AuthService } from '../../../auth/auth.service';
|
||||||
import { IMovieRequests, RequestType, IAdvancedData } from "../../../interfaces";
|
import { IMovieRequests, RequestType, IAdvancedData } from '../../../interfaces';
|
||||||
import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component";
|
import { DenyDialogComponent } from '../shared/deny-dialog/deny-dialog.component';
|
||||||
import { NewIssueComponent } from "../shared/new-issue/new-issue.component";
|
import { NewIssueComponent } from '../shared/new-issue/new-issue.component';
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component";
|
import { MovieAdvancedOptionsComponent } from './panels/movie-advanced-options/movie-advanced-options.component';
|
||||||
import { RequestServiceV2 } from "../../../services/requestV2.service";
|
import { RequestServiceV2 } from '../../../services/requestV2.service';
|
||||||
import { firstValueFrom, forkJoin } from "rxjs";
|
import { firstValueFrom, forkJoin } from 'rxjs';
|
||||||
import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component";
|
import { AdminRequestDialogComponent } from '../../../shared/admin-request-dialog/admin-request-dialog.component';
|
||||||
import { FeaturesFacade } from "../../../state/features/features.facade";
|
import { FeaturesFacade } from '../../../state/features/features.facade';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./movie-details.component.html",
|
templateUrl: './movie-details.component.html',
|
||||||
styleUrls: ["../../media-details.component.scss"],
|
styleUrls: ['../../media-details.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class MovieDetailsComponent implements OnInit{
|
export class MovieDetailsComponent implements OnInit {
|
||||||
public movie: ISearchMovieResultV2;
|
public movie: ISearchMovieResultV2;
|
||||||
public hasRequest: boolean;
|
public hasRequest: boolean;
|
||||||
public movieRequest: IMovieRequests;
|
public movieRequest: IMovieRequests;
|
||||||
public isAdmin: boolean;
|
public isAdmin: boolean;
|
||||||
public advancedOptions: IAdvancedData;
|
public advancedOptions: IAdvancedData;
|
||||||
public showAdvanced: boolean; // Set on the UI
|
public showAdvanced: boolean; // Set on the UI
|
||||||
public issuesEnabled: boolean;
|
public issuesEnabled: boolean;
|
||||||
public roleName4k = "Request4KMovie";
|
public roleName4k = 'Request4KMovie';
|
||||||
public is4KEnabled = false;
|
public is4KEnabled = false;
|
||||||
public requestType = RequestType.movie;
|
public requestType = RequestType.movie;
|
||||||
private theMovidDbId: number;
|
private theMovidDbId: number;
|
||||||
private imdbId: string;
|
private imdbId: string;
|
||||||
private snapMovieId: string;
|
private snapMovieId: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private searchService: SearchV2Service,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private sanitizer: DomSanitizer,
|
||||||
|
private imageService: ImageService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private requestService: RequestService,
|
||||||
|
private requestService2: RequestServiceV2,
|
||||||
|
private radarrService: RadarrService,
|
||||||
|
public messageService: MessageService,
|
||||||
|
private auth: AuthService,
|
||||||
|
private settingsState: SettingsStateService,
|
||||||
|
private translate: TranslateService,
|
||||||
|
private featureFacade: FeaturesFacade,
|
||||||
|
) {
|
||||||
|
this.snapMovieId = this.route.snapshot.params.movieDbId;
|
||||||
|
this.route.params.subscribe(async (params: any) => {
|
||||||
|
if (typeof params.movieDbId === 'string' || params.movieDbId instanceof String) {
|
||||||
|
if (params.movieDbId.startsWith('tt')) {
|
||||||
|
this.imdbId = params.movieDbId;
|
||||||
|
// Check if we user navigated to another movie and if so reload the component
|
||||||
|
if (this.imdbId !== this.snapMovieId) {
|
||||||
|
this.reloadComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.theMovidDbId = params.movieDbId;
|
||||||
|
// Check if we user navigated to another movie and if so reload the component
|
||||||
|
if (params.movieDbId !== this.snapMovieId) {
|
||||||
|
this.reloadComponent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private router: Router,
|
reloadComponent() {
|
||||||
private sanitizer: DomSanitizer, private imageService: ImageService,
|
let currentUrl = this.router.url;
|
||||||
public dialog: MatDialog, private requestService: RequestService,
|
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
|
||||||
private requestService2: RequestServiceV2, private radarrService: RadarrService,
|
this.router.onSameUrlNavigation = 'reload';
|
||||||
public messageService: MessageService, private auth: AuthService, private settingsState: SettingsStateService,
|
this.router.navigate([currentUrl]);
|
||||||
private translate: TranslateService, private featureFacade: FeaturesFacade) {
|
}
|
||||||
this.snapMovieId = this.route.snapshot.params.movieDbId;
|
|
||||||
this.route.params.subscribe(async (params: any) => {
|
|
||||||
if (typeof params.movieDbId === 'string' || params.movieDbId instanceof String) {
|
|
||||||
if (params.movieDbId.startsWith("tt")) {
|
|
||||||
this.imdbId = params.movieDbId;
|
|
||||||
// Check if we user navigated to another movie and if so reload the component
|
|
||||||
if (this.imdbId !== this.snapMovieId) {
|
|
||||||
this.reloadComponent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.theMovidDbId = params.movieDbId;
|
|
||||||
// Check if we user navigated to another movie and if so reload the component
|
|
||||||
if (params.movieDbId !== this.snapMovieId) {
|
|
||||||
this.reloadComponent()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadComponent() {
|
async ngOnInit() {
|
||||||
let currentUrl = this.router.url;
|
this.is4KEnabled = this.featureFacade.is4kEnabled();
|
||||||
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
|
this.issuesEnabled = this.settingsState.getIssue();
|
||||||
this.router.onSameUrlNavigation = 'reload';
|
this.isAdmin = this.auth.hasRole('admin') || this.auth.hasRole('poweruser');
|
||||||
this.router.navigate([currentUrl]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
if (this.isAdmin) {
|
||||||
this.is4KEnabled = this.featureFacade.is4kEnabled();
|
this.showAdvanced = await firstValueFrom(this.radarrService.isRadarrEnabled());
|
||||||
this.issuesEnabled = this.settingsState.getIssue();
|
}
|
||||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
|
||||||
|
|
||||||
if (this.isAdmin) {
|
if (this.imdbId) {
|
||||||
this.showAdvanced = await firstValueFrom(this.radarrService.isRadarrEnabled());
|
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async (x) => {
|
||||||
}
|
this.movie = x;
|
||||||
|
this.checkPoster();
|
||||||
|
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
|
||||||
|
if (this.movie.requestId > 0) {
|
||||||
|
// Load up this request
|
||||||
|
this.hasRequest = true;
|
||||||
|
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
||||||
|
}
|
||||||
|
this.loadBanner();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async (x) => {
|
||||||
|
this.movie = x;
|
||||||
|
this.checkPoster();
|
||||||
|
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
|
||||||
|
if (this.movie.requestId > 0) {
|
||||||
|
// Load up this request
|
||||||
|
this.hasRequest = true;
|
||||||
|
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
||||||
|
this.loadAdvancedInfo();
|
||||||
|
}
|
||||||
|
this.loadBanner();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.imdbId) {
|
public async request(is4K: boolean, userId?: string) {
|
||||||
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => {
|
if (!this.is4KEnabled) {
|
||||||
this.movie = x;
|
is4K = false;
|
||||||
this.checkPoster();
|
}
|
||||||
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
|
if (this.isAdmin) {
|
||||||
if (this.movie.requestId > 0) {
|
const dialog = this.dialog.open(AdminRequestDialogComponent, {
|
||||||
// Load up this request
|
width: '700px',
|
||||||
this.hasRequest = true;
|
data: { type: RequestType.movie, id: this.movie.id, is4K: is4K },
|
||||||
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
panelClass: 'modal-panel',
|
||||||
}
|
});
|
||||||
this.loadBanner();
|
dialog.afterClosed().subscribe(async (result) => {
|
||||||
});
|
if (result) {
|
||||||
} else {
|
const requestResult = await firstValueFrom(
|
||||||
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => {
|
this.requestService.requestMovie({
|
||||||
this.movie = x;
|
theMovieDbId: this.theMovidDbId,
|
||||||
this.checkPoster();
|
languageCode: this.translate.currentLang,
|
||||||
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
|
qualityPathOverride: result.radarrPathId,
|
||||||
if (this.movie.requestId > 0) {
|
requestOnBehalf: result.username?.id,
|
||||||
// Load up this request
|
rootFolderOverride: result.radarrFolderId,
|
||||||
this.hasRequest = true;
|
is4KRequest: is4K,
|
||||||
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
}),
|
||||||
this.loadAdvancedInfo();
|
);
|
||||||
}
|
if (requestResult.result) {
|
||||||
this.loadBanner();
|
if (is4K) {
|
||||||
});
|
this.movie.has4KRequest = true;
|
||||||
}
|
} else {
|
||||||
}
|
this.movie.requested = true;
|
||||||
|
}
|
||||||
|
this.movie.requestId = requestResult.requestId;
|
||||||
|
this.messageService.send(this.translate.instant('Requests.RequestAddedSuccessfully', { title: this.movie.title }), 'Ok');
|
||||||
|
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
||||||
|
} else {
|
||||||
|
this.messageService.sendRequestEngineResultError(requestResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const result = await firstValueFrom(
|
||||||
|
this.requestService.requestMovie({
|
||||||
|
theMovieDbId: this.theMovidDbId,
|
||||||
|
languageCode: this.translate.currentLang,
|
||||||
|
requestOnBehalf: userId,
|
||||||
|
qualityPathOverride: undefined,
|
||||||
|
rootFolderOverride: undefined,
|
||||||
|
is4KRequest: is4K,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (result.result) {
|
||||||
|
if (is4K) {
|
||||||
|
this.movie.has4KRequest = true;
|
||||||
|
} else {
|
||||||
|
this.movie.requested = true;
|
||||||
|
}
|
||||||
|
this.movie.requestId = result.requestId;
|
||||||
|
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
||||||
|
this.messageService.send(this.translate.instant('Requests.RequestAddedSuccessfully', { title: this.movie.title }), 'Ok');
|
||||||
|
} else {
|
||||||
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async request(is4K: boolean, userId?: string) {
|
public openDialog() {
|
||||||
if (!this.is4KEnabled) {
|
this.dialog.open(YoutubeTrailerComponent, {
|
||||||
is4K = false;
|
width: '560px',
|
||||||
}
|
data: this.movie.videos.results[0].key,
|
||||||
if (this.isAdmin) {
|
});
|
||||||
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id, is4K: is4K }, panelClass: 'modal-panel' });
|
}
|
||||||
dialog.afterClosed().subscribe(async (result) => {
|
|
||||||
if (result) {
|
|
||||||
const requestResult = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId,
|
|
||||||
languageCode: this.translate.currentLang,
|
|
||||||
qualityPathOverride: result.radarrPathId,
|
|
||||||
requestOnBehalf: result.username?.id,
|
|
||||||
rootFolderOverride: result.radarrFolderId,
|
|
||||||
is4KRequest: is4K }));
|
|
||||||
if (requestResult.result) {
|
|
||||||
if (is4K) {
|
|
||||||
this.movie.has4KRequest = true;
|
|
||||||
} else {
|
|
||||||
this.movie.requested = true;
|
|
||||||
}
|
|
||||||
this.movie.requestId = requestResult.requestId;
|
|
||||||
this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.movie.title }), "Ok");
|
|
||||||
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
|
||||||
} else {
|
|
||||||
this.messageService.sendRequestEngineResultError(requestResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const result = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: this.translate.currentLang, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined, is4KRequest: is4K }));
|
|
||||||
if (result.result) {
|
|
||||||
if (is4K) {
|
|
||||||
this.movie.has4KRequest = true;
|
|
||||||
} else {
|
|
||||||
this.movie.requested = true;
|
|
||||||
}
|
|
||||||
this.movie.requestId = result.requestId;
|
|
||||||
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
|
|
||||||
this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.movie.title }), "Ok");
|
|
||||||
} else {
|
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public openDialog() {
|
public async deny() {
|
||||||
this.dialog.open(YoutubeTrailerComponent, {
|
const dialogRef = this.dialog.open(DenyDialogComponent, {
|
||||||
width: '560px',
|
width: '250px',
|
||||||
data: this.movie.videos.results[0].key
|
data: { requestId: this.movieRequest.id, requestType: RequestType.movie },
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
public async deny() {
|
dialogRef.afterClosed().subscribe((result) => {
|
||||||
const dialogRef = this.dialog.open(DenyDialogComponent, {
|
this.movieRequest.denied = result.denied;
|
||||||
width: '250px',
|
this.movieRequest.deniedReason = result.reason;
|
||||||
data: { requestId: this.movieRequest.id, requestType: RequestType.movie }
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
public async issue() {
|
||||||
this.movieRequest.denied = result.denied;
|
let provider = this.movie.id.toString();
|
||||||
this.movieRequest.deniedReason = result.reason;
|
if (this.movie.imdbId) {
|
||||||
});
|
provider = this.movie.imdbId;
|
||||||
}
|
}
|
||||||
|
const dialogRef = this.dialog.open(NewIssueComponent, {
|
||||||
|
width: '500px',
|
||||||
|
data: {
|
||||||
|
requestId: this.movieRequest ? this.movieRequest.id : null,
|
||||||
|
requestType: RequestType.movie,
|
||||||
|
providerId: provider,
|
||||||
|
title: this.movie.title,
|
||||||
|
posterPath: this.movie.posterPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async issue() {
|
public async approve(is4K: boolean) {
|
||||||
let provider = this.movie.id.toString();
|
const result = await firstValueFrom(this.requestService.approveMovie({ id: this.movieRequest.id, is4K }));
|
||||||
if (this.movie.imdbId) {
|
if (result.result) {
|
||||||
provider = this.movie.imdbId;
|
if (is4K) {
|
||||||
}
|
this.movie.approved4K = true;
|
||||||
const dialogRef = this.dialog.open(NewIssueComponent, {
|
} else {
|
||||||
width: '500px',
|
this.movie.approved = true;
|
||||||
data: { requestId: this.movieRequest ? this.movieRequest.id : null, requestType: RequestType.movie, providerId: provider, title: this.movie.title, posterPath: this.movie.posterPath }
|
}
|
||||||
});
|
this.messageService.send(this.translate.instant('Requests.SuccessfullyApproved'), 'Ok');
|
||||||
}
|
} else {
|
||||||
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async approve(is4K: boolean) {
|
public async markAvailable(is4K: boolean) {
|
||||||
const result = await firstValueFrom(this.requestService.approveMovie({ id: this.movieRequest.id, is4K }));
|
const result = await firstValueFrom(this.requestService.markMovieAvailable({ id: this.movieRequest.id, is4K }));
|
||||||
if (result.result) {
|
if (result.result) {
|
||||||
if (is4K) {
|
if (is4K) {
|
||||||
this.movie.approved4K = true;
|
this.movie.available4K = true;
|
||||||
} else {
|
} else {
|
||||||
this.movie.approved = true;
|
this.movie.available = true;
|
||||||
}
|
}
|
||||||
this.messageService.send(this.translate.instant("Requests.SuccessfullyApproved"), "Ok");
|
this.messageService.send(this.translate.instant('Requests.NowAvailable'), 'Ok');
|
||||||
} else {
|
} else {
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async markAvailable(is4K: boolean) {
|
public async markUnavailable(is4K: boolean) {
|
||||||
const result = await firstValueFrom(this.requestService.markMovieAvailable({ id: this.movieRequest.id, is4K }))
|
const result = await firstValueFrom(this.requestService.markMovieUnavailable({ id: this.movieRequest.id, is4K }));
|
||||||
if (result.result) {
|
if (result.result) {
|
||||||
if (is4K) {
|
if (is4K) {
|
||||||
this.movie.available4K = true;
|
this.movie.available4K = false;
|
||||||
} else {
|
} else {
|
||||||
this.movie.available = true;
|
this.movie.available = false;
|
||||||
}
|
}
|
||||||
this.messageService.send(this.translate.instant("Requests.NowAvailable"), "Ok");
|
this.messageService.send(this.translate.instant('Requests.NowUnavailable'), 'Ok');
|
||||||
} else {
|
} else {
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setAdvancedOptions(data: IAdvancedData) {
|
||||||
|
this.advancedOptions = data;
|
||||||
|
if (data.rootFolderId) {
|
||||||
|
this.movieRequest.qualityOverrideTitle = data.profiles.filter((x) => x.id == data.profileId)[0].name;
|
||||||
|
}
|
||||||
|
if (data.profileId) {
|
||||||
|
this.movieRequest.rootPathOverrideTitle = data.rootFolders.filter((x) => x.id == data.rootFolderId)[0].path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async markUnavailable(is4K: boolean) {
|
public async openAdvancedOptions() {
|
||||||
const result = await firstValueFrom(this.requestService.markMovieUnavailable({ id: this.movieRequest.id, is4K }));
|
const dialog = this.dialog.open(MovieAdvancedOptionsComponent, {
|
||||||
if (result.result) {
|
width: '700px',
|
||||||
if (is4K) {
|
data: <IAdvancedData>{ movieRequest: this.movieRequest },
|
||||||
this.movie.available4K = false;
|
panelClass: 'modal-panel',
|
||||||
} else {
|
});
|
||||||
this.movie.available = false;
|
await dialog.afterClosed().subscribe(async (result) => {
|
||||||
}
|
if (result) {
|
||||||
this.messageService.send(this.translate.instant("Requests.NowUnavailable"), "Ok");
|
result.rootFolder = result.rootFolders.filter((f) => f.id === +result.rootFolderId)[0];
|
||||||
} else {
|
result.profile = result.profiles.filter((f) => f.id === +result.profileId)[0];
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
await this.requestService2
|
||||||
}
|
.updateMovieAdvancedOptions({
|
||||||
}
|
qualityOverride: result.profileId,
|
||||||
|
rootPathOverride: result.rootFolderId,
|
||||||
|
languageProfile: 0,
|
||||||
|
requestId: this.movieRequest.id,
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
this.setAdvancedOptions(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public setAdvancedOptions(data: IAdvancedData) {
|
public reProcessRequest(is4K: boolean) {
|
||||||
this.advancedOptions = data;
|
this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie, is4K).subscribe((result) => {
|
||||||
if (data.rootFolderId) {
|
if (result.result) {
|
||||||
this.movieRequest.qualityOverrideTitle = data.profiles.filter(x => x.id == data.profileId)[0].name;
|
this.messageService.send(result.message ? result.message : this.translate.instant('Requests.SuccessfullyReprocessed'), 'Ok');
|
||||||
}
|
} else {
|
||||||
if (data.profileId) {
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
this.movieRequest.rootPathOverrideTitle = data.rootFolders.filter(x => x.id == data.rootFolderId)[0].path;
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openAdvancedOptions() {
|
public notify() {
|
||||||
const dialog = this.dialog.open(MovieAdvancedOptionsComponent, { width: "700px", data: <IAdvancedData>{ movieRequest: this.movieRequest }, panelClass: 'modal-panel' })
|
this.requestService.subscribeToMovie(this.movieRequest.id).subscribe((result) => {
|
||||||
await dialog.afterClosed().subscribe(async result => {
|
if (result) {
|
||||||
if (result) {
|
this.movie.subscribed = true;
|
||||||
result.rootFolder = result.rootFolders.filter(f => f.id === +result.rootFolderId)[0];
|
this.messageService.send(this.translate.instant('Requests.SuccessfulNotify', { title: this.movie.title }), 'Ok');
|
||||||
result.profile = result.profiles.filter(f => f.id === +result.profileId)[0];
|
} else {
|
||||||
await this.requestService2.updateMovieAdvancedOptions({ qualityOverride: result.profileId, rootPathOverride: result.rootFolderId, languageProfile: 0, requestId: this.movieRequest.id }).toPromise();
|
this.messageService.send(this.translate.instant('Requests.CouldntNotify', { title: this.movie.title }), 'Ok');
|
||||||
this.setAdvancedOptions(result);
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public reProcessRequest(is4K: boolean) {
|
public unNotify() {
|
||||||
this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie, is4K).subscribe(result => {
|
this.requestService.unSubscribeToMovie(this.movieRequest.id).subscribe((result) => {
|
||||||
if (result.result) {
|
if (result) {
|
||||||
this.messageService.send(result.message ? result.message : this.translate.instant("Requests.SuccessfullyReprocessed"), "Ok");
|
this.movie.subscribed = false;
|
||||||
} else {
|
this.messageService.send(this.translate.instant('Requests.SuccessfulUnNotify', { title: this.movie.title }), 'Ok');
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
} else {
|
||||||
}
|
this.messageService.send(this.translate.instant('Requests.CouldntNotify', { title: this.movie.title }), 'Ok');
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public notify() {
|
private loadBanner() {
|
||||||
this.requestService.subscribeToMovie(this.movieRequest.id).subscribe(result => {
|
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe((x) => {
|
||||||
if (result) {
|
if (!this.movie.backdropPath) {
|
||||||
this.movie.subscribed = true;
|
this.movie.background = this.sanitizer.bypassSecurityTrustStyle('url(' + x + ')');
|
||||||
this.messageService.send(this.translate.instant("Requests.SuccessfulNotify", {title: this.movie.title}), "Ok");
|
} else {
|
||||||
} else {
|
this.movie.background = this.sanitizer.bypassSecurityTrustStyle(
|
||||||
this.messageService.send(this.translate.instant("Requests.CouldntNotify", {title: this.movie.title}), "Ok");
|
'url(https://image.tmdb.org/t/p/original/' + this.movie.backdropPath + ')',
|
||||||
}
|
);
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
private checkPoster() {
|
||||||
|
if (this.movie.posterPath == null) {
|
||||||
|
this.movie.posterPath = '../../../images/default_movie_poster.png';
|
||||||
|
} else {
|
||||||
|
this.movie.posterPath = 'https://image.tmdb.org/t/p/w300/' + this.movie.posterPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private loadAdvancedInfo() {
|
||||||
|
const profile = this.radarrService.getQualityProfilesFromSettings();
|
||||||
|
const folders = this.radarrService.getRootFoldersFromSettings();
|
||||||
|
|
||||||
public unNotify() {
|
forkJoin([profile, folders]).subscribe((x) => {
|
||||||
this.requestService.unSubscribeToMovie(this.movieRequest.id).subscribe(result => {
|
const radarrProfiles = x[0] ?? [];
|
||||||
if (result) {
|
const radarrRootFolders = x[1] ?? [];
|
||||||
this.movie.subscribed = false;
|
|
||||||
this.messageService.send(this.translate.instant("Requests.SuccessfulUnNotify", {title: this.movie.title}), "Ok");
|
|
||||||
} else {
|
|
||||||
this.messageService.send(this.translate.instant("Requests.CouldntNotify", {title: this.movie.title}), "Ok");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadBanner() {
|
const profile = radarrProfiles.filter((p) => {
|
||||||
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => {
|
return p.id === this.movieRequest.qualityOverride;
|
||||||
if (!this.movie.backdropPath) {
|
});
|
||||||
this.movie.background = this.sanitizer.bypassSecurityTrustStyle
|
if (profile.length > 0) {
|
||||||
("url(" + x + ")");
|
this.movieRequest.qualityOverrideTitle = profile[0].name;
|
||||||
} else {
|
}
|
||||||
this.movie.background = this.sanitizer.bypassSecurityTrustStyle
|
|
||||||
("url(https://image.tmdb.org/t/p/original/" + this.movie.backdropPath + ")");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private checkPoster() {
|
|
||||||
if (this.movie.posterPath == null) {
|
|
||||||
this.movie.posterPath = "../../../images/default_movie_poster.png";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.movie.posterPath = "https://image.tmdb.org/t/p/w300/" + this.movie.posterPath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
private loadAdvancedInfo() {
|
|
||||||
const profile = this.radarrService.getQualityProfilesFromSettings();
|
|
||||||
const folders = this.radarrService.getRootFoldersFromSettings();
|
|
||||||
|
|
||||||
forkJoin([profile, folders]).subscribe(x => {
|
const path = radarrRootFolders.filter((folder) => {
|
||||||
const radarrProfiles = x[0];
|
return folder.id === this.movieRequest.rootPathOverride;
|
||||||
const radarrRootFolders = x[1];
|
});
|
||||||
|
if (path.length > 0) {
|
||||||
|
this.movieRequest.rootPathOverrideTitle = path[0].path;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const profile = radarrProfiles.filter((p) => {
|
private orderCrew(crew: ICrewViewModel[]): ICrewViewModel[] {
|
||||||
return p.id === this.movieRequest.qualityOverride;
|
return crew.sort((a, b) => {
|
||||||
});
|
if (a.job === 'Director') {
|
||||||
if (profile.length > 0) {
|
return -1;
|
||||||
this.movieRequest.qualityOverrideTitle = profile[0].name;
|
} else if (b.job === 'Director') {
|
||||||
}
|
return 1;
|
||||||
|
} else {
|
||||||
const path = radarrRootFolders.filter((folder) => {
|
return 0;
|
||||||
return folder.id === this.movieRequest.rootPathOverride;
|
}
|
||||||
});
|
});
|
||||||
if (path.length > 0) {
|
}
|
||||||
this.movieRequest.rootPathOverrideTitle = path[0].path;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private orderCrew(crew: ICrewViewModel[]): ICrewViewModel[] {
|
|
||||||
return crew.sort((a, b) => {
|
|
||||||
if (a.job === "Director") {
|
|
||||||
return -1;
|
|
||||||
} else if (b.job === "Director") {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,137 +1,138 @@
|
||||||
<div *ngIf="movie" class="left-panel-details">
|
<div *ngIf="movie" class="left-panel-details">
|
||||||
<div class="rating medium-font">
|
<div class="rating medium-font">
|
||||||
<span *ngIf="movie.voteAverage"
|
<span *ngIf="movie.voteAverage" matTooltip="{{ 'MediaDetails.Votes' | translate }} {{ movie.voteCount | thousandShort: 1 }}">
|
||||||
matTooltip="{{'MediaDetails.Votes' | translate }} {{movie.voteCount | thousandShort: 1}}">
|
<img class="rating-small" src="{{ baseUrl }}/images/tmdb-logo.svg" /> {{ movie.voteAverage | number: '1.0-1' }}/10
|
||||||
<img class="rating-small" src="{{baseUrl}}/images/tmdb-logo.svg"> {{movie.voteAverage | number:'1.0-1'}}/10
|
</span>
|
||||||
</span>
|
<span *ngIf="ratings?.critics_rating && ratings?.critics_score">
|
||||||
<span *ngIf="ratings?.critics_rating && ratings?.critics_score">
|
<img
|
||||||
<img class="rating-small"
|
class="rating-small"
|
||||||
src="{{baseUrl}}/images/{{ratings.critics_rating === 'Rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg'}}">
|
src="{{ baseUrl }}/images/{{ ratings.critics_rating === 'Rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg' }}"
|
||||||
{{ratings.critics_score}}%
|
/>
|
||||||
</span>
|
{{ ratings.critics_score }}%
|
||||||
<span *ngIf="ratings?.audience_rating && ratings?.audience_score">
|
</span>
|
||||||
<img class="rating-small"
|
<span *ngIf="ratings?.audience_rating && ratings?.audience_score">
|
||||||
src="{{baseUrl}}/images/{{ratings.audience_rating === 'Upright' ? 'rotten-audience-fresh.svg' : 'rotten-audience-rotten.svg'}}">
|
<img
|
||||||
{{ratings.audience_score}}%
|
class="rating-small"
|
||||||
</span>
|
src="{{ baseUrl }}/images/{{ ratings.audience_rating === 'Upright' ? 'rotten-audience-fresh.svg' : 'rotten-audience-rotten.svg' }}"
|
||||||
</div>
|
/>
|
||||||
<div *ngIf="streams?.length > 0" class="streaming-on">
|
{{ ratings.audience_score }}%
|
||||||
<hr>
|
</span>
|
||||||
<span class="label">{{'MediaDetails.StreamingOn' | translate }}</span>
|
</div>
|
||||||
<div>
|
<div *ngIf="streams?.length > 0" class="streaming-on">
|
||||||
<span *ngFor="let stream of streams">
|
<hr />
|
||||||
<img class="stream-small" [matTooltip]="stream.streamingProvider" src="https://image.tmdb.org/t/p/original{{stream.logo}}">
|
<span class="label">{{ 'MediaDetails.StreamingOn' | translate }}</span>
|
||||||
</span>
|
<div>
|
||||||
</div>
|
<span *ngFor="let stream of streams">
|
||||||
</div>
|
<img class="stream-small" [matTooltip]="stream.streamingProvider" src="https://image.tmdb.org/t/p/original{{ stream.logo }}" />
|
||||||
<hr>
|
</span>
|
||||||
<div>
|
</div>
|
||||||
<span class="label">{{'MediaDetails.Status' | translate }}</span>
|
</div>
|
||||||
<span id="status"> {{ this.movie.status | translateStatus }}</span>
|
<hr />
|
||||||
</div>
|
<div>
|
||||||
<div>
|
<span class="label">{{ 'MediaDetails.Status' | translate }}</span>
|
||||||
<span class="label">{{'MediaDetails.Availability' | translate }}</span>
|
<span id="status"> {{ this.movie.status | translateStatus }}</span>
|
||||||
<span *ngIf="movie.available || movie.available4K"> {{'Common.Available' | translate}}</span>
|
</div>
|
||||||
<span *ngIf="!movie.available && !movie.available4K"> {{'Common.NotAvailable' | translate}}</span>
|
<div>
|
||||||
</div>
|
<span class="label">{{ 'MediaDetails.Availability' | translate }}</span>
|
||||||
<div *ngIf="(!movie.available && movie.requested) || (!movie.available4K && movie.has4KRequest)">
|
<span *ngIf="movie.available || movie.available4K"> {{ 'Common.Available' | translate }}</span>
|
||||||
<span class="label">{{'MediaDetails.RequestStatus' | translate }}</span>
|
<span *ngIf="!movie.available && !movie.available4K"> {{ 'Common.NotAvailable' | translate }}</span>
|
||||||
<div>{{getStatus(movie) | translate}}</div>
|
</div>
|
||||||
</div>
|
<div *ngIf="(!movie.available && movie.requested) || (!movie.available4K && movie.has4KRequest)">
|
||||||
|
<span class="label">{{ 'MediaDetails.RequestStatus' | translate }}</span>
|
||||||
|
<div>{{ getStatus(movie) | translate }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="request">
|
<div *ngIf="request">
|
||||||
<span class="label">{{'MediaDetails.RequestedBy' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.RequestedBy' | translate }}</span>
|
||||||
<span id="requestedByInfo"> {{request.requestedUser.userAlias}}</span>
|
<span id="requestedByInfo"> {{ request.requestedUser.userAlias }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="request">
|
<div *ngIf="request">
|
||||||
<span class="label">{{'MediaDetails.RequestDate' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.RequestDate' | translate }}</span>
|
||||||
<span *ngIf="request.requestedDate < request.requestedDate4k"> {{request.requestedDate4k | amUserLocale | amDateFormat: 'LL'}}</span>
|
<span *ngIf="request.requestedDate < request.requestedDate4k"> {{ request.requestedDate4k | ombiDate: 'PP' }}</span>
|
||||||
<span *ngIf="request.requestedDate > request.requestedDate4k"> {{request.requestedDate | amUserLocale | amDateFormat: 'LL'}}</span>
|
<span *ngIf="request.requestedDate > request.requestedDate4k"> {{ request.requestedDate | ombiDate: 'PP' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="request && request.source !== RequestSource.Ombi">
|
<div *ngIf="request && request.source !== RequestSource.Ombi">
|
||||||
<span class="label">{{'MediaDetails.RequestSource' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.RequestSource' | translate }}</span>
|
||||||
{{RequestSource[request.source]}}
|
{{ RequestSource[request.source] }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="request && ( request.deniedReason || request.deniedReason4K )">
|
<div *ngIf="request && (request.deniedReason || request.deniedReason4K)">
|
||||||
<span class="label">{{'MediaDetails.DeniedReason' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.DeniedReason' | translate }}</span>
|
||||||
<div *ngIf="request.deniedReason">
|
<div *ngIf="request.deniedReason">
|
||||||
<span id="deniedReasonInfo">{{request.deniedReason}}</span>
|
<span id="deniedReasonInfo">{{ request.deniedReason }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="request.deniedReason4K">
|
<div *ngIf="request.deniedReason4K">
|
||||||
<span id="deniedReasonInfo4K">{{request.deniedReason4K}}</span>
|
<span id="deniedReasonInfo4K">{{ request.deniedReason4K }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="movie.quality">
|
||||||
|
<span class="label">{{ 'MediaDetails.Quality' | translate }}</span>
|
||||||
|
<div>{{ movie.quality | quality }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="movie.quality">
|
<div *ngIf="movie.available4K">
|
||||||
<span class="label">{{'MediaDetails.Quality' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.Quality' | translate }} </span>
|
||||||
<div>{{movie.quality | quality}}</div>
|
<span>{{ '4K' | quality }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="movie.available4K">
|
<div *ngIf="advancedOptions && request && request.rootPathOverrideTitle">
|
||||||
<span class="label">{{'MediaDetails.Quality' | translate }} </span>
|
<span class="label">{{ 'MediaDetails.RootFolderOverride' | translate }}</span>
|
||||||
<span>{{"4K" | quality}}</span>
|
<div>{{ request.rootPathOverrideTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="advancedOptions && request && request.qualityOverrideTitle">
|
||||||
|
<span class="label">{{ 'MediaDetails.QualityOverride' | translate }}</span>
|
||||||
|
<div>{{ request.qualityOverrideTitle }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="advancedOptions && request && request.rootPathOverrideTitle">
|
<hr />
|
||||||
<span class="label">{{'MediaDetails.RootFolderOverride' | translate }}</span>
|
|
||||||
<div>{{request.rootPathOverrideTitle}}</div>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="advancedOptions && request && request.qualityOverrideTitle">
|
|
||||||
<span class="label">{{'MediaDetails.QualityOverride' | translate }}</span>
|
|
||||||
<div>{{request.qualityOverrideTitle}}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<span class="label">{{ 'MediaDetails.TheatricalRelease' | translate }}</span>
|
||||||
|
{{ movie.releaseDate | ombiDate: 'PP' }}
|
||||||
|
|
||||||
|
<div *ngIf="movie.digitalReleaseDate">
|
||||||
|
<span class="label">{{ 'MediaDetails.DigitalRelease' | translate }}</span>
|
||||||
|
{{ movie.digitalReleaseDate | ombiDate: 'PP' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div *ngIf="movie.voteCount">
|
||||||
|
<span class="label">{{ 'MediaDetails.Votes' | translate }}</span>
|
||||||
|
{{ movie.voteCount | thousandShort: 1 }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="label">{{ 'MediaDetails.Runtime' | translate }}</span>
|
||||||
|
{{ 'MediaDetails.Minutes' | translate: { runtime: movie.runtime } }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="movie.revenue">
|
||||||
|
<span class="label">{{ 'MediaDetails.Revenue' | translate }}</span>
|
||||||
|
{{ movie.revenue | currency: 'USD' }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="movie.budget">
|
||||||
|
<span class="label">{{ 'MediaDetails.Budget' | translate }}</span>
|
||||||
|
{{ movie.budget | currency: 'USD' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="label">{{'MediaDetails.TheatricalRelease' | translate }}</span>
|
<hr />
|
||||||
{{movie.releaseDate | amUserLocale | amDateFormat: 'LL': 'mediumDate'}}
|
<div class="genre-button-container" *ngIf="movie.genres">
|
||||||
|
<span class="label">{{ 'MediaDetails.GenresLabel' | translate }}</span>
|
||||||
|
<div>
|
||||||
|
<mat-chip-list>
|
||||||
|
<mat-chip selected *ngFor="let genre of movie.genres">
|
||||||
|
{{ genre.name }}
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="movie.digitalReleaseDate">
|
<hr />
|
||||||
<span class="label">{{'MediaDetails.DigitalRelease' | translate }}</span>
|
<div class="keyword-button-container" *ngIf="movie?.keywords?.keywordsValue?.length > 0">
|
||||||
{{movie.digitalReleaseDate | amUserLocale | amDateFormat: 'LL': 'mediumDate'}}
|
<span class="label">{{ 'MediaDetails.Keywords' | translate }}</span>
|
||||||
</div>
|
<mat-chip-list>
|
||||||
|
<mat-chip selected *ngFor="let keyword of movie.keywords.keywordsValue">
|
||||||
<div *ngIf="movie.voteCount">
|
{{ keyword.name }}
|
||||||
<span class="label">{{'MediaDetails.Votes' | translate }}</span>
|
</mat-chip>
|
||||||
{{movie.voteCount | thousandShort: 1}}
|
</mat-chip-list>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<span class="label">{{'MediaDetails.Runtime' | translate }}</span>
|
|
||||||
{{'MediaDetails.Minutes' | translate:{runtime: movie.runtime} }}
|
|
||||||
</div>
|
|
||||||
<div *ngIf="movie.revenue">
|
|
||||||
<span class="label">{{'MediaDetails.Revenue' | translate }}</span>
|
|
||||||
{{movie.revenue | currency: 'USD'}}
|
|
||||||
</div>
|
|
||||||
<div *ngIf="movie.budget">
|
|
||||||
<span class="label">{{'MediaDetails.Budget' | translate }}</span>
|
|
||||||
{{movie.budget | currency: 'USD'}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div class="genre-button-container" *ngIf="movie.genres">
|
|
||||||
<span class="label">{{'MediaDetails.GenresLabel' | translate }}</span>
|
|
||||||
<div>
|
|
||||||
<mat-chip-list>
|
|
||||||
<mat-chip selected *ngFor="let genre of movie.genres">
|
|
||||||
{{genre.name}}
|
|
||||||
</mat-chip>
|
|
||||||
</mat-chip-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div class="keyword-button-container" *ngIf="movie?.keywords?.keywordsValue?.length > 0">
|
|
||||||
<span class="label">{{'MediaDetails.Keywords' | translate }}</span>
|
|
||||||
<mat-chip-list>
|
|
||||||
<mat-chip selected *ngFor="let keyword of movie.keywords.keywordsValue">
|
|
||||||
{{keyword.name}}
|
|
||||||
</mat-chip>
|
|
||||||
</mat-chip-list>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
|
<section id="summary-wrapper">
|
||||||
|
<div class="full-screenshot enabled" [style.background-image]="background"></div>
|
||||||
|
<div class="full-screenshot enabled overlay"></div>
|
||||||
|
|
||||||
<section id="summary-wrapper">
|
<div class="container summary">
|
||||||
<div class="full-screenshot enabled" [style.background-image]="background"></div>
|
<div class="container title-top-banner">
|
||||||
<div class="full-screenshot enabled overlay"></div>
|
<div class="row">
|
||||||
|
<div class="mobile-top-text">
|
||||||
|
<h1 id="mediaTitle" class="large-text">
|
||||||
|
{{ title }}
|
||||||
|
<span *ngIf="releaseDateFormat" class="grey-text"> ({{ releaseDate | ombiDate: 'P' }})</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<div class="container summary">
|
<h2 class="tagline">{{ tagline }}</h2>
|
||||||
<div class="container title-top-banner">
|
</div>
|
||||||
<div class="row">
|
</div>
|
||||||
<div
|
</div>
|
||||||
class="mobile-top-text">
|
</div>
|
||||||
<h1 id="mediaTitle" class="large-text">{{title}}
|
</section>
|
||||||
<span *ngIf="releaseDateFormat" class="grey-text">
|
|
||||||
({{releaseDate | amLocal | amDateFormat: 'YYYY'}})</span>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<h2 class="tagline">{{tagline}}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
|
@ -1,82 +1,85 @@
|
||||||
<div class="left-panel-details">
|
<div class="left-panel-details">
|
||||||
<div>
|
<div>
|
||||||
<div class="rating medium-font">
|
<div class="rating medium-font">
|
||||||
<span *ngIf="tv.rating">
|
<span *ngIf="tv.rating">
|
||||||
<img class="rating-small" src="{{baseUrl}}/images/tmdb-logo.svg"> {{tv.rating * 10 | number : '1.2-2'}}%
|
<img class="rating-small" src="{{ baseUrl }}/images/tmdb-logo.svg" /> {{ tv.rating * 10 | number: '1.2-2' }}%
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="ratings?.score && ratings?.class">
|
<span *ngIf="ratings?.score && ratings?.class">
|
||||||
<img class="rating-small" src="{{baseUrl}}/images/{{ratings.class === 'rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg'}}"> {{ratings.score}}%
|
<img class="rating-small" src="{{ baseUrl }}/images/{{ ratings.class === 'rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg' }}" />
|
||||||
</span>
|
{{ ratings.score }}%
|
||||||
</div>
|
</span>
|
||||||
<div *ngIf="streams?.length > 0" id="streamingContainer" class="streaming-on-container">
|
</div>
|
||||||
<hr>
|
<div *ngIf="streams?.length > 0" id="streamingContainer" class="streaming-on-container">
|
||||||
<div class="streaming-on-content">
|
<hr />
|
||||||
<span class="label">{{'MediaDetails.StreamingOn' | translate }}</span>
|
<div class="streaming-on-content">
|
||||||
<div>
|
<span class="label">{{ 'MediaDetails.StreamingOn' | translate }}</span>
|
||||||
<span *ngFor="let stream of sortBy('order')">
|
<div>
|
||||||
<img class="stream-small" id="stream{{stream.streamingProvider}}" [matTooltip]="stream.streamingProvider" src="https://image.tmdb.org/t/p/original{{stream.logo}}">
|
<span *ngFor="let stream of sortBy('order')">
|
||||||
</span>
|
<img
|
||||||
</div>
|
class="stream-small"
|
||||||
</div>
|
id="stream{{ stream.streamingProvider }}"
|
||||||
</div>
|
[matTooltip]="stream.streamingProvider"
|
||||||
<hr>
|
src="https://image.tmdb.org/t/p/original{{ stream.logo }}"
|
||||||
<div *ngIf="tv.status">
|
/>
|
||||||
<span class="label">{{'MediaDetails.Status' | translate }}</span>
|
</span>
|
||||||
<span id="status"> {{ tv.status | translateStatus }}</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="label">{{'MediaDetails.FirstAired' | translate }}</span>
|
</div>
|
||||||
{{tv.firstAired | amLocal | amUserLocale | amDateFormat: 'LL' }}
|
<hr />
|
||||||
</div>
|
<div *ngIf="tv.status">
|
||||||
|
<span class="label">{{ 'MediaDetails.Status' | translate }}</span>
|
||||||
|
<span id="status"> {{ tv.status | translateStatus }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="label">{{ 'MediaDetails.FirstAired' | translate }}</span>
|
||||||
|
{{ tv.firstAired | ombiDate: 'PP' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="seasonCount">
|
<div *ngIf="seasonCount">
|
||||||
<span class="label">{{'MediaDetails.Seasons' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.Seasons' | translate }}</span>
|
||||||
{{seasonCount}}
|
{{ seasonCount }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="totalEpisodes">
|
<div *ngIf="totalEpisodes">
|
||||||
<span class="label">{{'MediaDetails.Episodes' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.Episodes' | translate }}</span>
|
||||||
{{totalEpisodes}}
|
{{ totalEpisodes }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="advancedOptions && request?.rootPathOverrideTitle">
|
<div *ngIf="advancedOptions && request?.rootPathOverrideTitle">
|
||||||
<span class="label">{{'MediaDetails.RootFolderOverride' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.RootFolderOverride' | translate }}</span>
|
||||||
<div>{{request.rootPathOverrideTitle}}</div>
|
<div>{{ request.rootPathOverrideTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="advancedOptions && request?.qualityOverrideTitle">
|
<div *ngIf="advancedOptions && request?.qualityOverrideTitle">
|
||||||
<span class="label">{{'MediaDetails.QualityOverride' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.QualityOverride' | translate }}</span>
|
||||||
<div>{{request.qualityOverrideTitle}}</div>
|
<div>{{ request.qualityOverrideTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span class="label">{{'MediaDetails.Runtime' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.Runtime' | translate }}</span>
|
||||||
{{'MediaDetails.Minutes' | translate:{ runtime: tv.runtime} }}
|
{{ 'MediaDetails.Minutes' | translate: { runtime: tv.runtime } }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="tv.network">
|
<div *ngIf="tv.network">
|
||||||
<span class="label">{{'MediaDetails.Network' | translate }}</span>
|
<span class="label">{{ 'MediaDetails.Network' | translate }}</span>
|
||||||
{{tv.network.name}}
|
{{ tv.network.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="genre-button-container" *ngIf="tv.genres">
|
||||||
|
<span class="label">{{ 'MediaDetails.GenresLabel' | translate }}</span>
|
||||||
|
<div>
|
||||||
|
<mat-chip-list>
|
||||||
|
<mat-chip selected *ngFor="let genre of tv.genres">
|
||||||
|
{{ genre.name }}
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="genre-button-container" *ngIf="tv.genres">
|
<hr />
|
||||||
<span class="label">{{'MediaDetails.GenresLabel' | translate }}</span>
|
<div class="keyword-button-container" *ngIf="tv?.keywords?.keywordsValue?.length > 0">
|
||||||
<div>
|
<span class="label">{{ 'MediaDetails.Keywords' | translate }}</span>
|
||||||
<mat-chip-list>
|
<mat-chip-list>
|
||||||
<mat-chip selected *ngFor="let genre of tv.genres">
|
<mat-chip selected *ngFor="let keyword of tv.keywords.keywordsValue">
|
||||||
{{genre.name}}
|
{{ keyword.name }}
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<div class="keyword-button-container" *ngIf="tv?.keywords?.keywordsValue?.length > 0">
|
|
||||||
<span class="label">{{'MediaDetails.Keywords' | translate }}</span>
|
|
||||||
<mat-chip-list>
|
|
||||||
<mat-chip selected *ngFor="let keyword of tv.keywords.keywordsValue">
|
|
||||||
{{keyword.name}}
|
|
||||||
</mat-chip>
|
|
||||||
</mat-chip-list>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,69 +1,73 @@
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab *ngFor="let season of tv.seasonRequests">
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<div attr.data-test="classStatus{{ season.seasonNumber }}" class="{{ getStatusClass(season) }} top-right">
|
||||||
|
<span>{{ 'Requests.Season' | translate }} {{ season.seasonNumber }}</span>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<mat-tab-group>
|
<mat-card *ngIf="season.overview" class="mat-elevation-z8">
|
||||||
<mat-tab *ngFor="let season of tv.seasonRequests">
|
<mat-card-content>
|
||||||
|
<p>{{ season.overview }}</p>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
<ng-template mat-tab-label>
|
<table mat-table [dataSource]="season.episodes" class="mat-elevation-z8">
|
||||||
<div attr.data-test="classStatus{{season.seasonNumber}}" class="{{getStatusClass(season)}} top-right">
|
<ng-container matColumnDef="select">
|
||||||
<span>{{ 'Requests.Season' | translate }} {{season.seasonNumber}}</span>
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
</div>
|
<mat-checkbox
|
||||||
</ng-template>
|
attr.data-test="masterCheckbox{{ season.seasonNumber }}"
|
||||||
|
*ngIf="isSeasonCheckable(season)"
|
||||||
|
(change)="$event ? masterToggle(season.episodes) : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected(season.episodes)"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected(season.episodes)"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<mat-checkbox
|
||||||
|
attr.data-test="episodeCheckbox{{ season.seasonNumber }}{{ row.episodeNumber }}"
|
||||||
|
*ngIf="!row.available && !row.requested && !row.approved"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<mat-card *ngIf="season.overview" class="mat-elevation-z8">
|
<ng-container matColumnDef="number">
|
||||||
<mat-card-content>
|
<th mat-header-cell *matHeaderCellDef>#</th>
|
||||||
<p>{{season.overview}}</p>
|
<td mat-cell *matCellDef="let element">{{ element.episodeNumber }}</td>
|
||||||
</mat-card-content>
|
</ng-container>
|
||||||
</mat-card>
|
|
||||||
|
|
||||||
|
<ng-container matColumnDef="title">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.GridTitle' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.title }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<table mat-table [dataSource]="season.episodes" class="mat-elevation-z8">
|
<ng-container matColumnDef="airDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.AirDate' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.airDate | ombiDate: 'P' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="status">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.GridStatus' | translate }}</th>
|
||||||
<mat-checkbox attr.data-test="masterCheckbox{{season.seasonNumber}}" *ngIf="isSeasonCheckable(season)" (change)="$event ? masterToggle(season.episodes) : null"
|
<td mat-cell *matCellDef="let ep">
|
||||||
[checked]="selection.hasValue() && isAllSelected(season.episodes)"
|
<div
|
||||||
[indeterminate]="selection.hasValue() && !isAllSelected(season.episodes)">
|
attr.data-test="episodeStatus{{ season.seasonNumber }}{{ ep.episodeNumber }}"
|
||||||
</mat-checkbox>
|
class="{{ getEpisodeStatusClass(ep) }} top-right"
|
||||||
</th>
|
>
|
||||||
<td mat-cell *matCellDef="let row">
|
<span>{{ ep.requestStatus | translate }}</span>
|
||||||
<mat-checkbox attr.data-test="episodeCheckbox{{season.seasonNumber}}{{row.episodeNumber}}" *ngIf="!row.available && !row.requested && !row.approved" (click)="$event.stopPropagation()"
|
</div>
|
||||||
(change)="$event ? selection.toggle(row) : null"
|
</td>
|
||||||
[checked]="selection.isSelected(row)">
|
</ng-container>
|
||||||
</mat-checkbox>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="number">
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
<th mat-header-cell *matHeaderCellDef> # </th>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns" (click)="selection.toggle(row)"></tr>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.episodeNumber}} </td>
|
</table>
|
||||||
</ng-container>
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
<ng-container matColumnDef="title">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'Requests.GridTitle' | translate }} </th>
|
|
||||||
<td mat-cell *matCellDef="let element"> {{element.title}} </td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="airDate">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'Requests.AirDate' | translate }} </th>
|
|
||||||
<td mat-cell *matCellDef="let element"> {{element.airDate | amLocal | amUserLocale | amDateFormat: 'L' }}</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="status">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'Requests.GridStatus' | translate }} </th>
|
|
||||||
<td mat-cell *matCellDef="let ep">
|
|
||||||
<div attr.data-test="episodeStatus{{season.seasonNumber}}{{ep.episodeNumber}}" class="{{getEpisodeStatusClass(ep)}} top-right">
|
|
||||||
<span>{{ep.requestStatus | translate}}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
|
|
||||||
(click)="selection.toggle(row)">
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</mat-tab>
|
|
||||||
|
|
||||||
</mat-tab-group>
|
|
||||||
|
|
||||||
<!-- <div *ngIf="isAdmin">
|
<!-- <div *ngIf="isAdmin">
|
||||||
<button *ngIf="!request.approved" mat-raised-button color="accent" (click)="approve(request);"> {{ 'Common.Approve' | translate }}</button>
|
<button *ngIf="!request.approved" mat-raised-button color="accent" (click)="approve(request);"> {{ 'Common.Approve' | translate }}</button>
|
||||||
|
@ -73,16 +77,15 @@
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
<button *ngIf="!tv.fullyAvailable && requestable" mat-fab color="accent" id="addFabBtn" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
|
<button *ngIf="!tv.fullyAvailable && requestable" mat-fab color="accent" id="addFabBtn" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
|
||||||
<i class="fas fa-plus fa-lg"></i>
|
<i class="fas fa-plus fa-lg"></i>
|
||||||
<mat-menu #aboveMenu="matMenu" yPosition="above">
|
<mat-menu #aboveMenu="matMenu" yPosition="above">
|
||||||
|
<button id="requestAll" mat-menu-item (click)="requestAllSeasons()">{{ 'Search.TvShows.AllSeasons' | translate }}</button>
|
||||||
|
|
||||||
<button id="requestAll" mat-menu-item (click)="requestAllSeasons()">{{'Search.TvShows.AllSeasons' | translate }}</button>
|
<button id="requestFirst" mat-menu-item (click)="requestFirstSeason()">{{ 'Search.TvShows.FirstSeason' | translate }}</button>
|
||||||
|
|
||||||
<button id="requestFirst" mat-menu-item (click)="requestFirstSeason()">{{ 'Search.TvShows.FirstSeason' | translate }}</button>
|
<button id="requestLatest" mat-menu-item (click)="requestLatestSeason()">{{ 'Search.TvShows.LatestSeason' | translate }}</button>
|
||||||
|
|
||||||
<button id="requestLatest" mat-menu-item (click)="requestLatestSeason()">{{ 'Search.TvShows.LatestSeason' | translate }}</button>
|
<button id="requestSelected" mat-menu-item (click)="submitRequests()">{{ 'Common.Request' | translate }}</button>
|
||||||
|
</mat-menu>
|
||||||
<button id="requestSelected" mat-menu-item (click)="submitRequests()">{{ 'Common.Request' | translate }}</button>
|
</button>
|
||||||
</mat-menu>
|
|
||||||
|
|
|
@ -1,66 +1,69 @@
|
||||||
<mat-accordion class="mat-elevation-z8">
|
<mat-accordion class="mat-elevation-z8">
|
||||||
<mat-expansion-panel *ngFor="let request of tvRequest">
|
<mat-expansion-panel *ngFor="let request of tvRequest">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
<div> {{ request.requestStatus | translate }}</div>
|
<div>{{ request.requestStatus | translate }}</div>
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
<mat-panel-description>
|
<mat-panel-description>
|
||||||
{{'MediaDetails.RequestedByOn' | translate: {
|
{{
|
||||||
user: request.requestedUser.userAlias,
|
'MediaDetails.RequestedByOn'
|
||||||
date: request.requestedDate | amLocal | amUserLocale | amDateFormat: 'LL' } }}
|
| translate
|
||||||
<span *ngIf="request.denied"> - {{request.deniedReason}}</span>
|
: {
|
||||||
<span *ngIf="request.source !== RequestSource.Ombi"> {{'MediaDetails.RequestSource' | translate }} {{RequestSource[request.source]}}</span>
|
user: request.requestedUser.userAlias,
|
||||||
</mat-panel-description>
|
date: request.requestedDate | ombiDate: 'PP'
|
||||||
</mat-expansion-panel-header>
|
}
|
||||||
|
}}
|
||||||
|
<span *ngIf="request.denied"> - {{ request.deniedReason }}</span>
|
||||||
|
<span *ngIf="request.source !== RequestSource.Ombi"
|
||||||
|
> {{ 'MediaDetails.RequestSource' | translate }} {{ RequestSource[request.source] }}</span
|
||||||
|
>
|
||||||
|
</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab *ngFor="let season of request.seasonRequests" label="{{ 'Requests.Season' | translate }} {{ season.seasonNumber }}">
|
||||||
|
<table mat-table [dataSource]="season.episodes" class="mat-elevation-z8">
|
||||||
|
<ng-container matColumnDef="number">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>#</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.episodeNumber }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<mat-tab-group>
|
<ng-container matColumnDef="title">
|
||||||
<mat-tab *ngFor="let season of request.seasonRequests" label="{{ 'Requests.Season' | translate }} {{season.seasonNumber}}">
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.GridTitle' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.title }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<table mat-table [dataSource]="season.episodes" class="mat-elevation-z8">
|
<ng-container matColumnDef="airDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.AirDate' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.airDate | ombiDate: 'P' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.GridStatus' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let ep">
|
||||||
|
<span> {{ request.requestStatus | translate }} </span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="number">
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
<th mat-header-cell *matHeaderCellDef> # </th>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.episodeNumber}} </td>
|
</table>
|
||||||
</ng-container>
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
<ng-container matColumnDef="title">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'Requests.GridTitle' | translate }} </th>
|
|
||||||
<td mat-cell *matCellDef="let element"> {{element.title}} </td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="airDate">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'Requests.AirDate' | translate }} </th>
|
|
||||||
<td mat-cell *matCellDef="let element"> {{element.airDate | amLocal | amUserLocale | amDateFormat: 'L' }}</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="status">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'Requests.GridStatus' | translate }} </th>
|
|
||||||
<td mat-cell *matCellDef="let ep">
|
|
||||||
<span> {{ request.requestStatus | translate }} </span>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
|
||||||
</table>
|
|
||||||
</mat-tab>
|
|
||||||
|
|
||||||
</mat-tab-group>
|
|
||||||
|
|
||||||
<div *ngIf="isAdmin">
|
|
||||||
<button *ngIf="!request.approved" mat-raised-button color="accent" (click)="approve(request);"> {{ 'Common.Approve' | translate }}</button>
|
|
||||||
<button *ngIf="!request.available" mat-raised-button color="warn" (click)="changeAvailability(request, true);">{{ 'Requests.MarkAvailable' | translate }}</button>
|
|
||||||
<button *ngIf="request.available" mat-raised-button color="warn" (click)="changeAvailability(request, false);">{{ 'Requests.MarkUnavailable' | translate }}</button>
|
|
||||||
<button *ngIf="!request.denied" mat-raised-button color="danger" (click)="deny(request);">{{ 'Requests.Deny' | translate }}</button>
|
|
||||||
<button mat-raised-button color="accent" (click)="reProcessRequest(request);">{{ 'MediaDetails.ReProcessRequest' | translate }}</button>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="isAdmin || manageOwnRequests">
|
|
||||||
<button mat-raised-button color="danger" (click)="delete(request);">{{ 'Requests.RequestPanel.Delete' | translate }}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</mat-expansion-panel>
|
|
||||||
|
|
||||||
|
<div *ngIf="isAdmin">
|
||||||
|
<button *ngIf="!request.approved" mat-raised-button color="accent" (click)="approve(request)">{{ 'Common.Approve' | translate }}</button>
|
||||||
|
<button *ngIf="!request.available" mat-raised-button color="warn" (click)="changeAvailability(request, true)">
|
||||||
|
{{ 'Requests.MarkAvailable' | translate }}
|
||||||
|
</button>
|
||||||
|
<button *ngIf="request.available" mat-raised-button color="warn" (click)="changeAvailability(request, false)">
|
||||||
|
{{ 'Requests.MarkUnavailable' | translate }}
|
||||||
|
</button>
|
||||||
|
<button *ngIf="!request.denied" mat-raised-button color="danger" (click)="deny(request)">{{ 'Requests.Deny' | translate }}</button>
|
||||||
|
<button mat-raised-button color="accent" (click)="reProcessRequest(request)">{{ 'MediaDetails.ReProcessRequest' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isAdmin || manageOwnRequests">
|
||||||
|
<button mat-raised-button color="danger" (click)="delete(request)">{{ 'Requests.RequestPanel.Delete' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
|
|
|
@ -1,171 +1,238 @@
|
||||||
<div *ngIf="!tv" class="justify-content-md-center top-spacing loading-spinner">
|
<div *ngIf="!tv" class="justify-content-md-center top-spacing loading-spinner">
|
||||||
<mat-spinner [color]="'accent'"></mat-spinner>
|
<mat-spinner [color]="'accent'"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="tv" class="main-content-container">
|
<div *ngIf="tv" class="main-content-container">
|
||||||
<div *ngIf="tv.id === 0; else main">
|
<div *ngIf="tv.id === 0; else main">
|
||||||
<div class="small-middle-container no-info">
|
<div class="small-middle-container no-info">
|
||||||
<h1><i class="far fa-frown-o" aria-hidden="true"></i></h1>
|
<h1><i class="far fa-frown-o" aria-hidden="true"></i></h1>
|
||||||
<h3> {{ 'MediaDetails.NotEnoughInfo' | translate }}</h3>
|
<h3>{{ 'MediaDetails.NotEnoughInfo' | translate }}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ng-template #main>
|
||||||
|
<div>
|
||||||
|
<top-banner
|
||||||
|
[background]="tv.background"
|
||||||
|
[available]="tv.available"
|
||||||
|
[title]="tv.title"
|
||||||
|
[releaseDate]="tv.firstAired"
|
||||||
|
[tagline]="tv.tagline"
|
||||||
|
></top-banner>
|
||||||
|
<div class="social-icons-container">
|
||||||
|
<social-icons
|
||||||
|
[homepage]="tv.homepage"
|
||||||
|
[theMoviedbId]="tv.id"
|
||||||
|
[hasTrailer]="tv.trailer"
|
||||||
|
[twitter]="tv.externalIds?.twitterId"
|
||||||
|
[facebook]="tv.externalIds?.facebookId"
|
||||||
|
[instagram]="tv.externalIds?.instagramId"
|
||||||
|
(openTrailer)="openDialog()"
|
||||||
|
[imdbId]="tv.imdbId"
|
||||||
|
[isAdmin]="isAdmin"
|
||||||
|
[canShowAdvanced]="showAdvanced && showRequest"
|
||||||
|
[type]="requestType"
|
||||||
|
(onAdvancedOptions)="openAdvancedOptions()"
|
||||||
|
>
|
||||||
|
</social-icons>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template #main>
|
<section id="info-wrapper">
|
||||||
|
<div class="small-middle-container">
|
||||||
|
<div class="row justify-content-center justify-content-sm-start header-container">
|
||||||
|
<div class="details-poster-container">
|
||||||
|
<media-poster [posterPath]="tv.images.original"></media-poster>
|
||||||
|
</div>
|
||||||
|
<!--Next to poster-->
|
||||||
|
<div class="details-button-container">
|
||||||
|
<div class="col-12 media-row">
|
||||||
|
<ng-container *ngIf="tv.fullyAvailable || tv.partlyAvailable">
|
||||||
|
<a
|
||||||
|
id="viewOnPlexButton"
|
||||||
|
*ngIf="tv.plexUrl"
|
||||||
|
href="{{ tv.plexUrl }}"
|
||||||
|
mat-raised-button
|
||||||
|
target="_blank"
|
||||||
|
class="btn-spacing viewon-btn plex"
|
||||||
|
>
|
||||||
|
{{ 'Search.ViewOnPlex' | translate }}
|
||||||
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
id="viewOnEmbyButton"
|
||||||
|
*ngIf="tv.embyUrl"
|
||||||
|
href="{{ tv.embyUrl }}"
|
||||||
|
mat-raised-button
|
||||||
|
target="_blank"
|
||||||
|
class="btn-spacing viewon-btn emby"
|
||||||
|
>
|
||||||
|
{{ 'Search.ViewOnEmby' | translate }}
|
||||||
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
id="viewOnJellyfinButton"
|
||||||
|
*ngIf="tv.jellyfinUrl"
|
||||||
|
href="{{ tv.jellyfinUrl }}"
|
||||||
|
mat-raised-button
|
||||||
|
target="_blank"
|
||||||
|
class="btn-spacing viewon-btn jellyfin"
|
||||||
|
>
|
||||||
|
{{ 'Search.ViewOnJellyfin' | translate }}
|
||||||
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
|
</a>
|
||||||
|
</ng-container>
|
||||||
|
<button
|
||||||
|
*ngIf="(!tv.fullyAvailable || (tv.fullyAvailable && tv.partlyAvailable)) && !allEpisodesRequestedOrAvailable()"
|
||||||
|
mat-raised-button
|
||||||
|
id="requestBtn"
|
||||||
|
class="btn-spacing"
|
||||||
|
color="primary"
|
||||||
|
(click)="request()"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Common.Request' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<div>
|
<button
|
||||||
|
*ngIf="!tv.denied && allEpisodesRequestedOrAvailable()"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="warn"
|
||||||
|
[disabled]
|
||||||
|
>
|
||||||
|
<i class="fas fa-check"></i>
|
||||||
|
{{ 'Common.Requested' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<top-banner [background]="tv.background" [available]="tv.available" [title]="tv.title"
|
<button
|
||||||
[releaseDate]="tv.firstAired" [tagline]="tv.tagline"></top-banner>
|
*ngIf="tv.fullyAvailable && !tv.partlyAvailable"
|
||||||
<div class="social-icons-container">
|
id="availableBtn"
|
||||||
<social-icons
|
mat-raised-button
|
||||||
[homepage]="tv.homepage"
|
class="btn-spacing"
|
||||||
[theMoviedbId]="tv.id"
|
color="accent"
|
||||||
[hasTrailer]="tv.trailer"
|
[disabled]
|
||||||
[twitter]="tv.externalIds?.twitterId"
|
>
|
||||||
[facebook]="tv.externalIds?.facebookId"
|
<i class="fas fa-check"></i> {{ 'Common.Available' | translate }}
|
||||||
[instagram]="tv.externalIds?.instagramId"
|
</button>
|
||||||
(openTrailer)="openDialog()"
|
|
||||||
[imdbId]="tv.imdbId"
|
|
||||||
[isAdmin]="isAdmin"
|
|
||||||
[canShowAdvanced]="showAdvanced && showRequest"
|
|
||||||
[type]="requestType"
|
|
||||||
(onAdvancedOptions)="openAdvancedOptions()"
|
|
||||||
>
|
|
||||||
</social-icons>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section id="info-wrapper">
|
<button
|
||||||
<div class="small-middle-container">
|
*ngIf="tv.partlyAvailable && !tv.fullyAvailable"
|
||||||
<div class="row justify-content-center justify-content-sm-start header-container">
|
id="partiallyAvailableBtn"
|
||||||
<div class="details-poster-container">
|
mat-raised-button
|
||||||
<media-poster [posterPath]=tv.images.original></media-poster>
|
class="btn-spacing"
|
||||||
</div>
|
color="accent"
|
||||||
<!--Next to poster-->
|
[disabled]
|
||||||
<div class="details-button-container">
|
>
|
||||||
<div class="col-12 media-row">
|
<i class="fas fa-check"></i> {{ 'Common.PartiallyAvailable' | translate }}
|
||||||
<ng-container *ngIf="tv.fullyAvailable || tv.partlyAvailable">
|
</button>
|
||||||
<a id="viewOnPlexButton" *ngIf="tv.plexUrl" href="{{tv.plexUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn plex">
|
|
||||||
{{'Search.ViewOnPlex' | translate}}
|
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
|
||||||
</a>
|
|
||||||
<a id="viewOnEmbyButton" *ngIf="tv.embyUrl" href="{{tv.embyUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn emby">
|
|
||||||
{{'Search.ViewOnEmby' | translate}}
|
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
|
||||||
</a>
|
|
||||||
<a id="viewOnJellyfinButton" *ngIf="tv.jellyfinUrl" href="{{tv.jellyfinUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn jellyfin">
|
|
||||||
{{'Search.ViewOnJellyfin' | translate}}
|
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
|
||||||
</a>
|
|
||||||
</ng-container>
|
|
||||||
<button *ngIf="(!tv.fullyAvailable || (tv.fullyAvailable && tv.partlyAvailable)) && !allEpisodesRequestedOrAvailable()" mat-raised-button id="requestBtn" class="btn-spacing" color="primary"
|
|
||||||
(click)="request()"><i class="fas fa-plus"></i>
|
|
||||||
{{ 'Common.Request' | translate }}</button>
|
|
||||||
|
|
||||||
<button *ngIf="!tv.denied && allEpisodesRequestedOrAvailable()" mat-raised-button class="btn-spacing" color="warn" [disabled]>
|
<!-- There are unaired episodes-->
|
||||||
<i class="fas fa-check"></i>
|
<button
|
||||||
{{ 'Common.Requested' | translate }}</button>
|
*ngIf="tv.partlyAvailable && tv.fullyAvailable"
|
||||||
|
id="partiallyAvailableBtn"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="accent"
|
||||||
|
[disabled]
|
||||||
|
>
|
||||||
|
<i class="fas fa-check"></i> {{ 'Common.PartiallyAvailable' | translate }}
|
||||||
|
</button>
|
||||||
|
<!-- end unaired episodes-->
|
||||||
|
|
||||||
<button *ngIf="tv.fullyAvailable && !tv.partlyAvailable" id="availableBtn" mat-raised-button class="btn-spacing" color="accent"
|
<button
|
||||||
[disabled]>
|
id="deniedButton"
|
||||||
<i class="fas fa-check"></i> {{'Common.Available' | translate }}</button>
|
*ngIf="tv.denied"
|
||||||
|
[matTooltip]="tv.deniedReason"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
color="warn"
|
||||||
|
>
|
||||||
|
<i class="fas fa-times"></i> {{ 'Common.Denied' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button *ngIf="tv.partlyAvailable && !tv.fullyAvailable" id="partiallyAvailableBtn" mat-raised-button
|
<button
|
||||||
class="btn-spacing" color="accent" [disabled]>
|
mat-raised-button
|
||||||
<i class="fas fa-check"></i> {{'Common.PartiallyAvailable' | translate }}</button>
|
class="btn-spacing"
|
||||||
|
color="danger"
|
||||||
|
id="reportIssueBtn"
|
||||||
|
*ngIf="issuesEnabled"
|
||||||
|
(click)="issue()"
|
||||||
|
>
|
||||||
|
<i class="fas fa-exclamation"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- There are unaired episodes-->
|
<div class="row">
|
||||||
<button *ngIf="tv.partlyAvailable && tv.fullyAvailable" id="partiallyAvailableBtn" mat-raised-button
|
<div class="col-12 col-md-2">
|
||||||
class="btn-spacing" color="accent" [disabled]>
|
<mat-card class="mat-elevation-z8 spacing-below">
|
||||||
<i class="fas fa-check"></i> {{'Common.PartiallyAvailable' | translate }}</button>
|
<mat-card-content>
|
||||||
<!-- end unaired episodes-->
|
<tv-information-panel [tv]="tv" [request]="showRequest" [advancedOptions]="showAdvanced"></tv-information-panel>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-10">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-card class="mat-elevation-z8 spacing-below">
|
||||||
|
<mat-card-content>
|
||||||
|
{{ tv.overview }}
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<cast-carousel [cast]="tv.cast"></cast-carousel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="deniedButton" *ngIf="tv.denied" [matTooltip]="tv.deniedReason" mat-raised-button class="btn-spacing" color="warn">
|
<div class="row">
|
||||||
<i class="fas fa-times"></i> {{'Common.Denied' | translate }}
|
<div class="col-12 col-md-2">
|
||||||
</button>
|
<!--Just some space yo-->
|
||||||
|
</div>
|
||||||
|
|
||||||
<button mat-raised-button class="btn-spacing" color="danger" id="reportIssueBtn" *ngIf="issuesEnabled" (click)="issue()">
|
<div class="col-12 col-md-10">
|
||||||
<i class="fas fa-exclamation"></i> {{
|
@defer (on viewport; prefetch on idle) {
|
||||||
'Requests.ReportIssue' | translate }}</button>
|
<tv-request-grid id="requests-grid" [tvRequest]="tvRequest" [isAdmin]="isAdmin" [tv]="tv"></tv-request-grid>
|
||||||
|
} @placeholder {
|
||||||
|
<p-skeleton height="2rem" styleClass="mb-2"></p-skeleton>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div class="col-12 col-md-2">
|
||||||
</div>
|
<!--Just some space yo-->
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 col-md-10">
|
||||||
|
<div class="issuesPanel">
|
||||||
|
<issues-panel [providerId]="tv.id" [isAdmin]="isAdmin"></issues-panel>
|
||||||
|
</div>
|
||||||
|
<mat-accordion id="requests-panel">
|
||||||
|
<mat-expansion-panel>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>
|
||||||
|
{{ 'Requests.Title' | translate }}
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<div class="row">
|
@defer (on viewport; prefetch on idle) {
|
||||||
<div class="col-12 col-md-2">
|
<tv-requests-panel
|
||||||
<mat-card class="mat-elevation-z8 spacing-below">
|
[tvRequest]="tvRequest"
|
||||||
<mat-card-content>
|
[isAdmin]="isAdmin"
|
||||||
<tv-information-panel [tv]="tv" [request]="showRequest"
|
[manageOwnRequests]="manageOwnRequests"
|
||||||
[advancedOptions]="showAdvanced"></tv-information-panel>
|
></tv-requests-panel>
|
||||||
</mat-card-content>
|
} @placeholder {
|
||||||
</mat-card>
|
<div class="loading-spinner" style="left: 50%;position: absolute;">
|
||||||
</div>
|
<mat-spinner [color]="'accent'"></mat-spinner>
|
||||||
<div class="col-12 col-md-10">
|
</div>
|
||||||
<div class="row">
|
}
|
||||||
<div class="col-12">
|
</mat-expansion-panel>
|
||||||
<mat-card class="mat-elevation-z8 spacing-below">
|
</mat-accordion>
|
||||||
<mat-card-content>
|
</div>
|
||||||
{{tv.overview}}
|
</div>
|
||||||
</mat-card-content>
|
</div>
|
||||||
</mat-card>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<cast-carousel [cast]="tv.cast"></cast-carousel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="bottom-page-gap"></div>
|
||||||
<div class="row">
|
</section>
|
||||||
<div class="col-12 col-md-2">
|
</div>
|
||||||
|
</ng-template>
|
||||||
<!--Just some space yo-->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-10">
|
|
||||||
<tv-request-grid id="requests-grid" [tvRequest]="tvRequest" [isAdmin]="isAdmin" [tv]="tv"></tv-request-grid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-md-2">
|
|
||||||
|
|
||||||
<!--Just some space yo-->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-10">
|
|
||||||
<div class="issuesPanel">
|
|
||||||
<issues-panel [providerId]="tv.id" [isAdmin]="isAdmin"></issues-panel>
|
|
||||||
</div>
|
|
||||||
<mat-accordion id="requests-panel">
|
|
||||||
<mat-expansion-panel>
|
|
||||||
<mat-expansion-panel-header>
|
|
||||||
<mat-panel-title>
|
|
||||||
{{'Requests.Title' | translate}}
|
|
||||||
</mat-panel-title>
|
|
||||||
</mat-expansion-panel-header>
|
|
||||||
<tv-requests-panel [tvRequest]="tvRequest" [isAdmin]="isAdmin" [manageOwnRequests]="manageOwnRequests"></tv-requests-panel>
|
|
||||||
</mat-expansion-panel>
|
|
||||||
|
|
||||||
</mat-accordion>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="bottom-page-gap">
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -362,3 +362,8 @@
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
height: 100px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { AuthGuard } from "../auth/auth.guard";
|
||||||
import { ArtistDetailsComponent } from "./components/artist/artist-details.component";
|
import { ArtistDetailsComponent } from "./components/artist/artist-details.component";
|
||||||
import { ReactiveFormsModule } from "@angular/forms";
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
import { ImageComponent } from "app/components";
|
import { ImageComponent } from "app/components";
|
||||||
|
import { SkeletonModule } from "primeng/skeleton";
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
|
@ -29,6 +30,7 @@ const routes: Routes = [
|
||||||
PipeModule,
|
PipeModule,
|
||||||
CarouselModule,
|
CarouselModule,
|
||||||
ImageComponent,
|
ImageComponent,
|
||||||
|
SkeletonModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
...fromComponents.components
|
...fromComponents.components
|
||||||
|
|
17
src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts
Normal file
17
src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Pipe, PipeTransform } from "@angular/core";
|
||||||
|
import { FormatPipe } from 'ngx-date-fns';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: "ombiDate",
|
||||||
|
})
|
||||||
|
export class OmbiDatePipe implements PipeTransform {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private FormatPipe: FormatPipe,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public transform(value: string, format: string ) {
|
||||||
|
const date = new Date(value);
|
||||||
|
return this.FormatPipe.transform(date, format);
|
||||||
|
}
|
||||||
|
}
|
11
src/Ombi/ClientApp/src/app/pipes/OrderPipe.ts
Normal file
11
src/Ombi/ClientApp/src/app/pipes/OrderPipe.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { orderBy as _orderBy } from 'lodash';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'orderBy',
|
||||||
|
})
|
||||||
|
export class OrderPipe<T> implements PipeTransform {
|
||||||
|
transform(data: T[], orderBy: string, direction: 'asc' | 'desc' = 'asc'): T[] {
|
||||||
|
return _orderBy(data, orderBy, direction);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
})
|
})
|
||||||
export class ThousandShortPipe implements PipeTransform {
|
export class ThousandShortPipe implements PipeTransform {
|
||||||
transform(input: any, args?: any): any {
|
transform(input: any, args?: any): any {
|
||||||
var exp, rounded,
|
var exp,
|
||||||
suffixes = ['k', 'M', 'G', 'T', 'P', 'E'];
|
suffixes = ['k', 'M', 'G', 'T', 'P', 'E'];
|
||||||
|
|
||||||
if (Number.isNaN(input)) {
|
if (Number.isNaN(input)) {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { Pipe, PipeTransform } from "@angular/core";
|
|
||||||
import * as moment from "moment";
|
|
||||||
|
|
||||||
const momentConstructor = moment;
|
|
||||||
|
|
||||||
@Pipe({ name: "amUserLocale" })
|
|
||||||
export class UserLocalePipe implements PipeTransform {
|
|
||||||
transform(value: moment.MomentInput): moment.Moment {
|
|
||||||
const locale = navigator.language;
|
|
||||||
return momentConstructor(value).locale(locale);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +1,24 @@
|
||||||
import { ModuleWithProviders, NgModule } from "@angular/core";
|
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||||
import { HumanizePipe } from "./HumanizePipe";
|
import { HumanizePipe } from './HumanizePipe';
|
||||||
import { TranslateStatusPipe } from "./TranslateStatus";
|
import { TranslateStatusPipe } from './TranslateStatus';
|
||||||
import { ThousandShortPipe } from "./ThousandShortPipe";
|
import { ThousandShortPipe } from './ThousandShortPipe';
|
||||||
import { SafePipe } from "./SafePipe";
|
import { SafePipe } from './SafePipe';
|
||||||
import { QualityPipe } from "./QualityPipe";
|
import { QualityPipe } from './QualityPipe';
|
||||||
import { UserLocalePipe } from "./UserLocalePipe";
|
import { OrderPipe } from './OrderPipe';
|
||||||
|
import { OmbiDatePipe } from './OmbiDatePipe';
|
||||||
|
import { FormatPipeModule, FormatPipe } from 'ngx-date-fns';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [],
|
imports: [FormatPipeModule],
|
||||||
declarations: [HumanizePipe, ThousandShortPipe, SafePipe, QualityPipe, UserLocalePipe, TranslateStatusPipe ],
|
declarations: [HumanizePipe, ThousandShortPipe, SafePipe, QualityPipe, TranslateStatusPipe, OrderPipe, OmbiDatePipe],
|
||||||
exports: [HumanizePipe, ThousandShortPipe, SafePipe, QualityPipe, UserLocalePipe, TranslateStatusPipe ],
|
exports: [HumanizePipe, ThousandShortPipe, SafePipe, QualityPipe, TranslateStatusPipe, OrderPipe, OmbiDatePipe],
|
||||||
|
providers: [FormatPipe],
|
||||||
})
|
})
|
||||||
export class PipeModule {
|
export class PipeModule {
|
||||||
|
public static forRoot(): ModuleWithProviders<PipeModule> {
|
||||||
public static forRoot(): ModuleWithProviders<PipeModule> {
|
return {
|
||||||
return {
|
ngModule: PipeModule,
|
||||||
ngModule: PipeModule,
|
providers: [],
|
||||||
providers: [],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,128 @@
|
||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
<grid-spinner [loading]="isLoadingResults"></grid-spinner>
|
<grid-spinner [loading]="isLoadingResults"></grid-spinner>
|
||||||
|
|
||||||
|
<!-- <div class="row"> -->
|
||||||
|
<div class="row justify-content-md-center top-spacing">
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="switchFilter(RequestFilter.All)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.AllRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="switchFilter(RequestFilter.Pending)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.PendingRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="switchFilter(RequestFilter.Processing)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.ProcessingRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="switchFilter(RequestFilter.Available)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.AvailableRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="switchFilter(RequestFilter.Denied)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.DeniedRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <div class="row"> -->
|
<div class="row">
|
||||||
<div class="row justify-content-md-center top-spacing">
|
<div class="col-md-2 offset-md-10">
|
||||||
<div class="btn-group" role="group">
|
<mat-form-field>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.AllRequests' | translate}}</button>
|
<mat-select placeholder="{{ 'Requests.RequestsToDisplay' | translate }}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.PendingRequests' | translate}}</button>
|
<mat-option value="10">10</mat-option>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
<mat-option value="15">15</mat-option>
|
||||||
class="grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
<mat-option value="30">30</mat-option>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
<mat-option value="100">100</mat-option>
|
||||||
class="grow">{{'Requests.AvailableRequests' | translate}}</button>
|
</mat-select>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.DeniedRequests' | translate}}</button>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
|
<table
|
||||||
|
mat-table
|
||||||
|
[dataSource]="dataSource"
|
||||||
|
class="requests table"
|
||||||
|
matSort
|
||||||
|
[matSortActive]="defaultSort"
|
||||||
|
matSortDisableClear
|
||||||
|
[matSortDirection]="defaultOrder"
|
||||||
|
>
|
||||||
|
<ng-container matColumnDef="artistName">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.ArtistName' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.artistName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="title">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.AlbumName' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.title }} ({{ element.releaseDate | ombiDate: 'P' }})</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<div class="row">
|
<ng-container matColumnDef="requestedUser.requestedBy">
|
||||||
<div class="col-md-2 offset-md-10">
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.RequestedBy' | translate }}</th>
|
||||||
<mat-form-field>
|
<td mat-cell *matCellDef="let element">{{ element.requestedUser?.userAlias }}</td>
|
||||||
<mat-select placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
</ng-container>
|
||||||
<mat-option value="10">10</mat-option>
|
|
||||||
<mat-option value="15">15</mat-option>
|
|
||||||
<mat-option value="30">30</mat-option>
|
|
||||||
<mat-option value="100">100</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- </div> -->
|
|
||||||
<table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">
|
|
||||||
|
|
||||||
|
<ng-container matColumnDef="requestedDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.RequestDate' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.requestedDate | ombiDate: 'PP' }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="requestStatus">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.RequestStatus' | translate }}</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.requestStatus | translate }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="artistName">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.ArtistName' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.artistName}} </td>
|
<td mat-cell *matCellDef="let element">
|
||||||
</ng-container>
|
<a mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{
|
||||||
<ng-container matColumnDef="title">
|
'Requests.Details' | translate
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.AlbumName' | translate}} </th>
|
}}</a>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.title}} ({{element.releaseDate | amLocal | amDateFormat: 'YYYY'}}) </td>
|
<button
|
||||||
</ng-container>
|
mat-raised-button
|
||||||
|
color="warn"
|
||||||
|
(click)="openOptions(element)"
|
||||||
|
*ngIf="isAdmin || (manageOwnRequests && element.requestedUser?.userName == userName)"
|
||||||
|
>
|
||||||
|
{{ 'Requests.Options' | translate }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedUser.requestedBy">
|
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestedUser?.userAlias}} </td>
|
</table>
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedDate">
|
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestDate' | translate}} </th>
|
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestedDate | amLocal | amUserLocale | amDateFormat: 'LL'}} </td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="requestStatus">
|
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestStatus' | translate}} </th>
|
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestStatus | translate}} </td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
|
||||||
<td mat-cell *matCellDef="let element">
|
|
||||||
<a mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</a>
|
|
||||||
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName )"> {{ 'Requests.Options' | translate}}</button>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,123 +1,198 @@
|
||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
<grid-spinner [loading]="isLoadingResults"></grid-spinner>
|
<grid-spinner [loading]="isLoadingResults"></grid-spinner>
|
||||||
|
|
||||||
|
<!-- <div class="row"> -->
|
||||||
|
<div class="row justify-content-md-center top-spacing">
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterAll"
|
||||||
|
(click)="switchFilter(RequestFilter.All)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.AllRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterPending"
|
||||||
|
(click)="switchFilter(RequestFilter.Pending)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.PendingRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterProcessing"
|
||||||
|
(click)="switchFilter(RequestFilter.Processing)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.ProcessingRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterAvailable"
|
||||||
|
(click)="switchFilter(RequestFilter.Available)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.AvailableRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterDenied"
|
||||||
|
(click)="switchFilter(RequestFilter.Denied)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.DeniedRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <div class="row"> -->
|
<div class="row">
|
||||||
<div class="row justify-content-md-center top-spacing">
|
<div class="col-md-2 offset-md-10">
|
||||||
<div class="btn-group" role="group">
|
<mat-form-field>
|
||||||
<button type="button" id="filterAll" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.AllRequests' | translate}}</button>
|
<mat-select
|
||||||
<button type="button" id="filterPending" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.PendingRequests' | translate}}</button>
|
id="requestsToDisplayDropdown"
|
||||||
<button type="button" id="filterProcessing" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
placeholder="{{ 'Requests.RequestsToDisplay' | translate }}"
|
||||||
class="grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
[(value)]="gridCount"
|
||||||
<button type="button" id="filterAvailable" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
(selectionChange)="ngAfterViewInit()"
|
||||||
class="grow">{{'Requests.AvailableRequests' | translate}}</button>
|
>
|
||||||
<button type="button" id="filterDenied" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.DeniedRequests' | translate}}</button>
|
<mat-option value="10">10</mat-option>
|
||||||
</div>
|
<mat-option value="15">15</mat-option>
|
||||||
</div>
|
<mat-option value="30">30</mat-option>
|
||||||
|
<mat-option value="100">100</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
|
<table
|
||||||
|
mat-table
|
||||||
|
[dataSource]="dataSource"
|
||||||
|
class="requests table"
|
||||||
|
matSort
|
||||||
|
[matSortActive]="defaultSort"
|
||||||
|
matSortDisableClear
|
||||||
|
[matSortDirection]="defaultOrder"
|
||||||
|
>
|
||||||
|
<ng-container matColumnDef="select" *ngIf="isAdmin">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
<mat-checkbox
|
||||||
|
id="adminMasterCheckbox"
|
||||||
|
(change)="$event ? masterToggle() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<mat-checkbox
|
||||||
|
id="adminMasterCheckbox{{ row.id }}"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<div class="row">
|
<ng-container matColumnDef="title">
|
||||||
<div class="col-md-2 offset-md-10">
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.RequestsTitle' | translate }}</th>
|
||||||
<mat-form-field>
|
<td mat-cell id="title{{ element.id }}" *matCellDef="let element">{{ element.title }} ({{ element.releaseDate | ombiDate: 'P' }})</td>
|
||||||
<mat-select id="requestsToDisplayDropdown" placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
</ng-container>
|
||||||
<mat-option value="10">10</mat-option>
|
|
||||||
<mat-option value="15">15</mat-option>
|
|
||||||
<mat-option value="30">30</mat-option>
|
|
||||||
<mat-option value="100">100</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- </div> -->
|
|
||||||
<table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">
|
|
||||||
|
|
||||||
<ng-container matColumnDef="select" *ngIf="isAdmin">
|
<ng-container matColumnDef="requestedUser.requestedBy">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.RequestedBy' | translate }}</th>
|
||||||
<mat-checkbox id="adminMasterCheckbox" (change)="$event ? masterToggle() : null"
|
<td mat-cell id="requestedBy{{ element.id }}" *matCellDef="let element">
|
||||||
[checked]="selection.hasValue() && isAllSelected()"
|
{{ element.requestedByAlias ? element.requestedByAlias : element.requestedUser?.userAlias }}
|
||||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
</td>
|
||||||
</mat-checkbox>
|
</ng-container>
|
||||||
</th>
|
|
||||||
<td mat-cell *matCellDef="let row">
|
|
||||||
<mat-checkbox id="adminMasterCheckbox{{row.id}}" (click)="$event.stopPropagation()"
|
|
||||||
(change)="$event ? selection.toggle(row) : null"
|
|
||||||
[checked]="selection.isSelected(row)">
|
|
||||||
</mat-checkbox>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="title">
|
<ng-container matColumnDef="requestedDate">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestsTitle' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.RequestDate' | translate }}</th>
|
||||||
<td mat-cell id="title{{element.id}}" *matCellDef="let element"> {{element.title}} ({{element.releaseDate | amLocal | amDateFormat: 'YYYY'}}) </td>
|
<td mat-cell id="requestedDate{{ element.id }}" *matCellDef="let element">{{ getRequestDate(element) | ombiDate: 'PP' }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedUser.requestedBy">
|
<ng-container matColumnDef="status">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.Status' | translate }}</th>
|
||||||
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedByAlias ? element.requestedByAlias : element.requestedUser?.userAlias}} </td>
|
<td mat-cell id="status{{ element.id }}" *matCellDef="let element">{{ element.status | translateStatus }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="has4kRequest">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.Has4KRequest' | translate }}</th>
|
||||||
|
<td mat-cell id="has4kRequest{{ element.id }}" *matCellDef="let element">
|
||||||
|
<i *ngIf="element.has4KRequest" class="fas fa-check"></i>
|
||||||
|
<i *ngIf="!element.has4KRequest" class="fas fa-times"></i>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedDate">
|
<ng-container matColumnDef="requestStatus">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestDate' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.RequestStatus' | translate }}</th>
|
||||||
<td mat-cell id="requestedDate{{element.id}}" *matCellDef="let element"> {{getRequestDate(element) | amLocal | amUserLocale | amDateFormat: 'LL'}} </td>
|
<td mat-cell id="requestedStatus{{ element.id }}" *matCellDef="let element">{{ element.requestStatus | translate }}</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="status">
|
<ng-container matColumnDef="watchedByRequestedUser">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.Status' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef disableClear matTooltip="{{ 'Requests.WatchedTooltip' | translate }}">
|
||||||
<td mat-cell id="status{{element.id}}" *matCellDef="let element"> {{element.status |translateStatus }} </td>
|
{{ 'Requests.Watched' | translate }}
|
||||||
</ng-container>
|
</th>
|
||||||
|
<td mat-cell id="watchedByRequestedUser{{ element.id }}" *matCellDef="let element">
|
||||||
|
<mat-checkbox
|
||||||
|
[checked]="element.watchedByRequestedUser"
|
||||||
|
disabled="true"
|
||||||
|
matTooltip="{{ 'Requests.WatchedByUsersCount' | translate: { count: element.playedByUsersCount } }}"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="has4kRequest">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.Has4KRequest' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
<td mat-cell id="has4kRequest{{element.id}}" *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<i *ngIf="element.has4KRequest" class="fas fa-check"></i>
|
<a id="detailsButton{{ element.id }}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{
|
||||||
<i *ngIf="!element.has4KRequest" class="fas fa-times"></i>
|
'Requests.Details' | translate
|
||||||
</td>
|
}}</a>
|
||||||
</ng-container>
|
<a
|
||||||
|
id="optionsButton{{ element.id }}"
|
||||||
|
mat-raised-button
|
||||||
|
color="warn"
|
||||||
|
(click)="openOptions(element)"
|
||||||
|
*ngIf="isAdmin || (manageOwnRequests && element.requestedUser?.userName == userName)"
|
||||||
|
>
|
||||||
|
{{ 'Requests.Options' | translate }}</a
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestStatus">
|
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestStatus' | translate}} </th>
|
|
||||||
<td mat-cell id="requestedStatus{{element.id}}" *matCellDef="let element"> {{element.requestStatus | translate}} </td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="watchedByRequestedUser">
|
|
||||||
<th
|
|
||||||
mat-header-cell
|
|
||||||
*matHeaderCellDef
|
|
||||||
disableClear
|
|
||||||
matTooltip="{{ 'Requests.WatchedTooltip' | translate}}">
|
|
||||||
{{ 'Requests.Watched' | translate}}
|
|
||||||
</th>
|
|
||||||
<td mat-cell id="watchedByRequestedUser{{element.id}}" *matCellDef="let element">
|
|
||||||
<mat-checkbox
|
|
||||||
[checked]="element.watchedByRequestedUser"
|
|
||||||
disabled="true"
|
|
||||||
matTooltip="{{'Requests.WatchedByUsersCount' | translate: {count: element.playedByUsersCount} }}">
|
|
||||||
</mat-checkbox>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
|
||||||
<td mat-cell *matCellDef="let element">
|
|
||||||
<a id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</a>
|
|
||||||
<a id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName ) "> {{ 'Requests.Options' | translate}}</a>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="bulkFab" *ngIf="selection.hasValue() && isAdmin" mat-fab color="accent" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
|
<button id="bulkFab" *ngIf="selection.hasValue() && isAdmin" mat-fab color="accent" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
|
||||||
<i class="fas fa-bars"></i></button>
|
<i class="fas fa-bars"></i>
|
||||||
<mat-menu #aboveMenu="matMenu" yPosition="above">
|
</button>
|
||||||
<button id="approveFabButton" mat-menu-item (click)="bulkApprove()">{{'Requests.RequestPanel.Approve' | translate}}</button>
|
<mat-menu #aboveMenu="matMenu" yPosition="above">
|
||||||
<button *ngIf="is4kEnabled" id="approve4kFabButton" mat-menu-item (click)="bulkApprove4K()">{{'Requests.RequestPanel.Approve4K' | translate}}</button>
|
<button id="approveFabButton" mat-menu-item (click)="bulkApprove()">{{ 'Requests.RequestPanel.Approve' | translate }}</button>
|
||||||
<button id="denyFabButton" mat-menu-item (click)="bulkDeny()">{{'Requests.RequestPanel.Deny' | translate}}</button>
|
<button *ngIf="is4kEnabled" id="approve4kFabButton" mat-menu-item (click)="bulkApprove4K()">
|
||||||
<button *ngIf="is4kEnabled" id="deny4kFabButton" mat-menu-item (click)="bulkDeny4K()">{{'Requests.RequestPanel.Deny4K' | translate}}</button>
|
{{ 'Requests.RequestPanel.Approve4K' | translate }}
|
||||||
<button id="deleteFabButton" mat-menu-item (click)="bulkDelete()">{{'Requests.RequestPanel.Delete' | translate}}</button>
|
</button>
|
||||||
</mat-menu>
|
<button id="denyFabButton" mat-menu-item (click)="bulkDeny()">{{ 'Requests.RequestPanel.Deny' | translate }}</button>
|
||||||
|
<button *ngIf="is4kEnabled" id="deny4kFabButton" mat-menu-item (click)="bulkDeny4K()">{{ 'Requests.RequestPanel.Deny4K' | translate }}</button>
|
||||||
|
<button id="deleteFabButton" mat-menu-item (click)="bulkDelete()">{{ 'Requests.RequestPanel.Delete' | translate }}</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
|
@ -1,89 +1,152 @@
|
||||||
<div class="mat-elevation-z8">
|
<div class="mat-elevation-z8">
|
||||||
|
<grid-spinner [loading]="isLoadingResults"></grid-spinner>
|
||||||
|
|
||||||
<grid-spinner [loading]="isLoadingResults"></grid-spinner>
|
<div class="row justify-content-md-center top-spacing">
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterAll"
|
||||||
|
(click)="switchFilter(RequestFilter.All)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.AllRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterPending"
|
||||||
|
(click)="switchFilter(RequestFilter.Pending)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.PendingRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterProcessing"
|
||||||
|
(click)="switchFilter(RequestFilter.Processing)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.ProcessingRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterAvailable"
|
||||||
|
(click)="switchFilter(RequestFilter.Available)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.AvailableRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="filterDenied"
|
||||||
|
(click)="switchFilter(RequestFilter.Denied)"
|
||||||
|
[attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'"
|
||||||
|
[ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'"
|
||||||
|
mat-raised-button
|
||||||
|
class="grow"
|
||||||
|
>
|
||||||
|
{{ 'Requests.DeniedRequests' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2 offset-md-10">
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select
|
||||||
|
id="requestsToDisplayDropdown"
|
||||||
|
placeholder="{{ 'Requests.RequestsToDisplay' | translate }}"
|
||||||
|
[(value)]="gridCount"
|
||||||
|
(selectionChange)="ngAfterViewInit()"
|
||||||
|
>
|
||||||
|
<mat-option value="10">10</mat-option>
|
||||||
|
<mat-option value="15">15</mat-option>
|
||||||
|
<mat-option value="30">30</mat-option>
|
||||||
|
<mat-option value="100">100</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row justify-content-md-center top-spacing">
|
<table
|
||||||
<div class="btn-group" role="group">
|
mat-table
|
||||||
<button type="button" id="filterAll" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.AllRequests' | translate}}</button>
|
[dataSource]="dataSource"
|
||||||
<button type="button" id="filterPending" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.PendingRequests' | translate}}</button>
|
class="requests table"
|
||||||
<button type="button" id="filterProcessing" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
matSort
|
||||||
class="grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
[matSortActive]="defaultSort"
|
||||||
<button type="button" id="filterAvailable" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
matSortDisableClear
|
||||||
class="grow">{{'Requests.AvailableRequests' | translate}}</button>
|
[matSortDirection]="defaultOrder"
|
||||||
<button type="button" id="filterDenied" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="grow">{{'Requests.DeniedRequests' | translate}}</button>
|
>
|
||||||
</div>
|
<ng-container matColumnDef="series">
|
||||||
</div>
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.RequestsTitle' | translate }}</th>
|
||||||
|
<td mat-cell id="title{{ element.id }}" *matCellDef="let element">{{ element.parentRequest.title }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<div class="row">
|
<ng-container matColumnDef="requestedBy">
|
||||||
<div class="col-md-2 offset-md-10">
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.RequestedBy' | translate }}</th>
|
||||||
<mat-form-field>
|
<td mat-cell id="requestedBy{{ element.id }}" *matCellDef="let element">
|
||||||
<mat-select id="requestsToDisplayDropdown" placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
{{ element.requestedByAlias ? element.requestedByAlias : element.requestedUser.userAlias }}
|
||||||
<mat-option value="10">10</mat-option>
|
</td>
|
||||||
<mat-option value="15">15</mat-option>
|
</ng-container>
|
||||||
<mat-option value="30">30</mat-option>
|
|
||||||
<mat-option value="100">100</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">
|
<ng-container matColumnDef="requestedDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear>{{ 'Requests.RequestDate' | translate }}</th>
|
||||||
|
<td id="requestedDate{{ element.id }}" mat-cell *matCellDef="let element">
|
||||||
|
{{ element.requestedDate | ombiDate: 'PP' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="requestStatus">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.RequestStatus' | translate }}</th>
|
||||||
|
<td mat-cell id="requestedStatus{{ element.id }}" *matCellDef="let element">{{ element.requestStatus | translate }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="series">
|
<ng-container matColumnDef="status">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestsTitle' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'Requests.Status' | translate }}</th>
|
||||||
<td mat-cell id="title{{element.id}}" *matCellDef="let element"> {{element.parentRequest.title}} </td>
|
<td mat-cell id="status{{ element.id }}" *matCellDef="let element">
|
||||||
</ng-container>
|
{{ element.parentRequest.status | translateStatus }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedBy">
|
<ng-container matColumnDef="watchedByRequestedUser">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef disableClear matTooltip="{{ 'Requests.WatchedProgressTooltip' | translate }}">
|
||||||
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedByAlias ? element.requestedByAlias : element.requestedUser.userAlias}} </td>
|
{{ 'Requests.Watched' | translate }}
|
||||||
</ng-container>
|
</th>
|
||||||
|
<td mat-cell id="requestedUserPlayedProgress{{ element.id }}" *matCellDef="let element">
|
||||||
|
<mat-progress-bar mode="determinate" value="{{ element.requestedUserPlayedProgress }}" class="played-progress"></mat-progress-bar>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedDate">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{'Requests.RequestDate' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
<td id="requestedDate{{element.id}}" mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
{{element.requestedDate | amLocal | amUserLocale | amDateFormat: 'LL'}}
|
<a
|
||||||
</td>
|
id="detailsButton{{ element.id }}"
|
||||||
</ng-container>
|
mat-raised-button
|
||||||
|
color="accent"
|
||||||
|
[routerLink]="'/details/tv/' + element.parentRequest.externalProviderId"
|
||||||
|
>{{ 'Requests.Details' | translate }}</a
|
||||||
|
>
|
||||||
|
<button id="optionsButton{{ element.id }}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">
|
||||||
|
{{ 'Requests.Options' | translate }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestStatus">
|
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestStatus' | translate}} </th>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
<td mat-cell id="requestedStatus{{element.id}}" *matCellDef="let element"> {{element.requestStatus | translate}} </td>
|
</table>
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="status">
|
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.Status' | translate}} </th>
|
|
||||||
<td mat-cell id="status{{element.id}}" *matCellDef="let element">
|
|
||||||
{{element.parentRequest.status | translateStatus }}
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="watchedByRequestedUser">
|
|
||||||
<th
|
|
||||||
mat-header-cell
|
|
||||||
*matHeaderCellDef
|
|
||||||
disableClear
|
|
||||||
matTooltip="{{ 'Requests.WatchedProgressTooltip' | translate}}">
|
|
||||||
{{ 'Requests.Watched' | translate}}
|
|
||||||
</th>
|
|
||||||
<td mat-cell id="requestedUserPlayedProgress{{element.id}}" *matCellDef="let element">
|
|
||||||
<mat-progress-bar mode="determinate" value="{{element.requestedUserPlayedProgress}}" class="played-progress"></mat-progress-bar>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
|
||||||
<td mat-cell *matCellDef="let element">
|
|
||||||
<a id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/tv/' + element.parentRequest.externalProviderId">{{'Requests.Details' | translate}}</a>
|
|
||||||
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">{{'Requests.Options' | translate}}</button>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,288 +1,316 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" [placeholder]="'Search.Search' | translate"
|
<input
|
||||||
(keyup)="search($event)">
|
type="text"
|
||||||
<span class="input-group-btn">
|
id="search"
|
||||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
class="form-control form-control-custom searchwidth"
|
||||||
<i class="fas fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
[placeholder]="'Search.Search' | translate"
|
||||||
</button>
|
(keyup)="search($event)"
|
||||||
|
/>
|
||||||
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
||||||
aria-haspopup="true" aria-expanded="true">
|
<i class="fas fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||||
<i class="fas fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
</button>
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
|
||||||
<li>
|
|
||||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
|
|
||||||
translate }}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{
|
|
||||||
'Requests.SortRequestDateDesc' | translate }}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary-outline dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="true"
|
||||||
|
>
|
||||||
|
<i class="fas fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||||
|
<li>
|
||||||
|
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }} </a>
|
||||||
|
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{ 'Requests.SortRequestDateDesc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate }} </a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div *ngFor="let request of movieRequests">
|
<div *ngFor="let request of movieRequests">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="myBg backdrop" [style.background-image]="request.backgroundPath"></div>
|
<div class="myBg backdrop" [style.background-image]="request.backgroundPath"></div>
|
||||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.6) 100%)"></div>
|
||||||
<div class="col-sm-2 small-padding">
|
<div class="col-sm-2 small-padding">
|
||||||
|
<img class="img-responsive poster" src="{{ request.posterPath }}" alt="poster" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<img class="img-responsive poster" src="{{request.posterPath}}" alt="poster">
|
<div class="col-sm-5 small-padding">
|
||||||
|
<div>
|
||||||
|
<a href="http://www.imdb.com/title/{{ request.imdbId }}/" target="_blank">
|
||||||
|
<h4 class="request-title">{{ request.title }} ({{ request.releaseDate | ombiDate: 'P' }})</h4>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="request-info">
|
||||||
|
<div class="request-by">
|
||||||
|
<span>{{ 'Requests.RequestedBy' | translate }} </span>
|
||||||
|
<span *ngIf="request.requestedByAlias">{{ request.requestedByAlias }}</span>
|
||||||
|
<span *ngIf="!request.requestedByAlias">
|
||||||
|
<span *ngIf="!isAdmin">{{ request.requestedUser.userName }}</span>
|
||||||
|
<span *ngIf="isAdmin && request.requestedUser.alias">{{ request.requestedUser.alias }}</span>
|
||||||
|
<span *ngIf="isAdmin && !request.requestedUser.alias">{{ request.requestedUser.userName }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="request-status">
|
||||||
|
<span>{{ 'Requests.Status' | translate }} </span>
|
||||||
|
<span class="label label-success" id="requestedStatusLabel">{{ request.status }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div class="requested-status">
|
||||||
|
<span>{{ 'Requests.RequestStatus' | translate }} </span>
|
||||||
|
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||||
|
<span
|
||||||
|
*ngIf="request.approved && !request.available"
|
||||||
|
id="processingRequestLabel"
|
||||||
|
class="label label-info"
|
||||||
|
[translate]="'Common.ProcessingRequest'"
|
||||||
|
></span>
|
||||||
|
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
|
||||||
|
<span
|
||||||
|
*ngIf="!request.approved && !request.availble && !request.denied"
|
||||||
|
id="pendingApprovalLabel"
|
||||||
|
class="label label-warning"
|
||||||
|
[translate]="'Common.PendingApproval'"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="request.denied" id="requestDenied">
|
||||||
|
{{ 'Requests.Denied' | translate }}
|
||||||
|
<i style="color: red" class="fas fa-check" pTooltip="{{ request.deniedReason }}"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-5 small-padding">
|
<div id="releaseDate">
|
||||||
<div>
|
{{ 'Requests.TheatricalRelease' | translate: { date: request.releaseDate | ombiDate: 'PP' } }}
|
||||||
<a href="http://www.imdb.com/title/{{request.imdbId}}/" target="_blank">
|
</div>
|
||||||
<h4 class="request-title">{{request.title}} ({{request.releaseDate | amLocal | amDateFormat:
|
<div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">
|
||||||
'YYYY'}})</h4>
|
{{ 'Requests.DigitalRelease' | translate: { date: request.digitalReleaseDate | ombiDate: 'PP' } }}
|
||||||
</a>
|
</div>
|
||||||
</div>
|
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{ request.requestedDate | ombiDate: 'PP' }}</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="request-info">
|
</div>
|
||||||
<div class="request-by">
|
<div *ngIf="isAdmin">
|
||||||
<span>{{ 'Requests.RequestedBy' | translate }} </span>
|
<div *ngIf="request.qualityOverrideTitle" class="quality-override">
|
||||||
<span *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
|
{{ 'Requests.QualityOverride' | translate }}
|
||||||
<span *ngIf="!request.requestedByAlias">
|
<span>{{ request.qualityOverrideTitle }} </span>
|
||||||
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
|
</div>
|
||||||
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
|
<div *ngIf="request.rootPathOverrideTitle" class="root-override">
|
||||||
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
|
{{ 'Requests.RootFolderOverride' | translate }}
|
||||||
</span>
|
<span>{{ request.rootPathOverrideTitle }} </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="request-status">
|
</div>
|
||||||
<span>{{ 'Requests.Status' | translate }} </span>
|
</div>
|
||||||
<span class="label label-success" id="requestedStatusLabel">{{request.status}}</span>
|
<div class="col-sm-3 col-sm-push-3 small-padding">
|
||||||
</div>
|
<div class="row">
|
||||||
|
<div class="col-md-2 col-md-push-6">
|
||||||
|
<a
|
||||||
|
*ngIf="request.showSubscribe && !request.subscribed"
|
||||||
|
style="color: white"
|
||||||
|
(click)="subscribe(request)"
|
||||||
|
pTooltip="Subscribe for notifications"
|
||||||
|
>
|
||||||
|
<i class="fas fa-rss"></i>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
*ngIf="request.showSubscribe && request.subscribed"
|
||||||
|
style="color: red"
|
||||||
|
(click)="unSubscribe(request)"
|
||||||
|
pTooltip="Unsubscribe notification"
|
||||||
|
>
|
||||||
|
<i class="fas fa-rss"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isAdmin">
|
||||||
|
<div *ngIf="!request.approved" id="approveBtn">
|
||||||
|
<form>
|
||||||
|
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit">
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
<div class="requested-status">
|
<!--Radarr Root Folder-->
|
||||||
<span>{{ 'Requests.RequestStatus' | translate }} </span>
|
<div *ngIf="radarrRootFolders?.length > 1" class="btn-group btn-split" id="rootFolderBtn">
|
||||||
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info"
|
<i class="fas fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||||
[translate]="'Common.ProcessingRequest'"></span>
|
</button>
|
||||||
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
|
<button
|
||||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
|
type="button"
|
||||||
class="label label-warning" [translate]="'Common.PendingApproval'"></span>
|
class="btn btn-warning-outline dropdown-toggle"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li *ngFor="let folder of radarrRootFolders">
|
||||||
|
<a href="#" (click)="selectRootFolder(request, folder, $event)">{{ folder.path }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<!--Radarr Quality Profiles -->
|
||||||
<div *ngIf="request.denied" id="requestDenied">
|
<div *ngIf="radarrProfiles?.length > 1" class="btn-group btn-split" id="changeQualityBtn">
|
||||||
{{ 'Requests.Denied' | translate }}
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
<i style="color:red;" class="fas fa-check" pTooltip="{{request.deniedReason}}"></i>
|
<i class="fas fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning-outline dropdown-toggle"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li *ngFor="let profile of radarrProfiles">
|
||||||
|
<a href="#" (click)="selectQualityProfile(request, profile, $event)">{{ profile.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div *ngIf="!request.denied" id="denyBtn">
|
||||||
|
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny">
|
||||||
|
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="markBtnGroup">
|
||||||
|
<button
|
||||||
|
id="unavailableBtn"
|
||||||
|
*ngIf="request.available"
|
||||||
|
(click)="changeAvailability(request, false)"
|
||||||
|
style="text-align: right"
|
||||||
|
value="false"
|
||||||
|
class="btn btn-sm btn-info-outline change"
|
||||||
|
>
|
||||||
|
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="availableBtn"
|
||||||
|
*ngIf="!request.available"
|
||||||
|
(click)="changeAvailability(request, true)"
|
||||||
|
style="text-align: right"
|
||||||
|
value="true"
|
||||||
|
class="btn btn-sm btn-success-outline change"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isAdmin || isRequestUser(request)">
|
||||||
|
<form>
|
||||||
|
<button
|
||||||
|
id="removeBtn"
|
||||||
|
(click)="removeRequest(request)"
|
||||||
|
style="text-align: right"
|
||||||
|
class="btn btn-sm btn-danger-outline delete"
|
||||||
|
>
|
||||||
|
<i class="fas fa-minus"></i> {{ 'Requests.Remove' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary-outline dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="true"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||||
|
<li *ngFor="let cat of issueCategories">
|
||||||
|
<a [routerLink]="" (click)="reportIssue(cat, request)">{{ cat.value }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate |
|
<p-paginator [rows]="10" [totalRecords]="totalMovies" (onPageChange)="paginate($event)"></p-paginator>
|
||||||
amLocal | amUserLocale | amDateFormat: 'LL'} }}</div>
|
|
||||||
<div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">{{ 'Requests.DigitalRelease' |
|
|
||||||
translate: {date: request.digitalReleaseDate | amLocal | amUserLocale | amDateFormat: 'LL'} }}</div>
|
|
||||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
|
|
||||||
| amUserLocale | amDateFormat: 'LL'}}</div>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
<div *ngIf="isAdmin">
|
|
||||||
<div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' |
|
|
||||||
translate }}
|
|
||||||
<span>{{request.qualityOverrideTitle}} </span>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="request.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' |
|
|
||||||
translate }}
|
|
||||||
<span>{{request.rootPathOverrideTitle}} </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-3 col-sm-push-3 small-padding">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 col-md-push-6">
|
|
||||||
|
|
||||||
<a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)"
|
|
||||||
pTooltip="Subscribe for notifications">
|
|
||||||
<i class="fas fa-rss"></i>
|
|
||||||
</a>
|
|
||||||
<a *ngIf="request.showSubscribe && request.subscribed" style="color:red" (click)="unSubscribe(request)"
|
|
||||||
pTooltip="Unsubscribe notification">
|
|
||||||
<i class="fas fa-rss"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="isAdmin">
|
|
||||||
<div *ngIf="!request.approved" id="approveBtn">
|
|
||||||
<form>
|
|
||||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve"
|
|
||||||
type="submit">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!--Radarr Root Folder-->
|
|
||||||
<div *ngIf="radarrRootFolders?.length > 1" class="btn-group btn-split" id="rootFolderBtn">
|
|
||||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown"
|
|
||||||
aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="caret"></span>
|
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li *ngFor="let folder of radarrRootFolders">
|
|
||||||
<a href="#" (click)="selectRootFolder(request, folder, $event)">{{folder.path}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--Radarr Quality Profiles -->
|
|
||||||
<div *ngIf="radarrProfiles?.length > 1" class="btn-group btn-split" id="changeQualityBtn">
|
|
||||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown"
|
|
||||||
aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="caret"></span>
|
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li *ngFor="let profile of radarrProfiles">
|
|
||||||
<a href="#" (click)="selectQualityProfile(request, profile, $event)">{{profile.name}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!request.denied" id="denyBtn">
|
|
||||||
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny">
|
|
||||||
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<form id="markBtnGroup">
|
|
||||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)"
|
|
||||||
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change">
|
|
||||||
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
|
||||||
</button>
|
|
||||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)"
|
|
||||||
style="text-align: right" value="true" class="btn btn-sm btn-success-outline change">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div *ngIf="isAdmin || isRequestUser(request)">
|
|
||||||
<form>
|
|
||||||
<button id="removeBtn" (click)="removeRequest(request)" style="text-align: right" class="btn btn-sm btn-danger-outline delete">
|
|
||||||
<i class="fas fa-minus"></i> {{ 'Requests.Remove' | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
|
|
||||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
|
||||||
aria-haspopup="true" aria-expanded="true">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
|
||||||
<li *ngFor="let cat of issueCategories">
|
|
||||||
<a [routerLink]="" (click)="reportIssue(cat, request)">{{cat.value}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p-paginator [rows]="10" [totalRecords]="totalMovies" (onPageChange)="paginate($event)"></p-paginator>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{ requestToDeny.title }}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||||
<p-footer>
|
<p-footer>
|
||||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
<button type="button" (click)="denyRequest()" label="Reject" class="btn btn-success">Deny</button>
|
||||||
<button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
<button type="button" (click)="denyDisplay = false" label="Close" class="btn btn-danger">Close</button>
|
||||||
</p-footer>
|
</p-footer>
|
||||||
</p-dialog>
|
</p-dialog>
|
||||||
|
|
||||||
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequest?.title"
|
<issue-report
|
||||||
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId"></issue-report>
|
[movie]="true"
|
||||||
|
[visible]="issuesBarVisible"
|
||||||
|
(visibleChange)="issuesBarVisible = $event"
|
||||||
|
[title]="issueRequest?.title"
|
||||||
|
[issueCategory]="issueCategorySelected"
|
||||||
|
[id]="issueRequest?.id"
|
||||||
|
[providerId]="issueProviderId"
|
||||||
|
></issue-report>
|
||||||
|
|
||||||
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
|
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
|
||||||
<h3>{{ 'Requests.Filter' | translate }}</h3>
|
<h3>{{ 'Requests.Filter' | translate }}</h3>
|
||||||
<hr>
|
<hr />
|
||||||
<div>
|
<div>
|
||||||
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
|
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="Available" name="Availability" (click)="filterAvailability(filterType.Available, $event)">
|
<input type="radio" id="Available" name="Availability" (click)="filterAvailability(filterType.Available, $event)" />
|
||||||
<label for="Available">{{ 'Common.Available' | translate }}</label>
|
<label for="Available">{{ 'Common.Available' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="notAvailable" name="Availability" (click)="filterAvailability(filterType.NotAvailable, $event)">
|
<input type="radio" id="notAvailable" name="Availability" (click)="filterAvailability(filterType.NotAvailable, $event)" />
|
||||||
<label for="notAvailable">{{ 'Common.NotAvailable' | translate }}</label>
|
<label for="notAvailable">{{ 'Common.NotAvailable' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>{{ 'Filter.FilterHeaderRequestStatus' | translate }}</h4>
|
<h4>{{ 'Filter.FilterHeaderRequestStatus' | translate }}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="approved" name="Status" (click)="filterStatus(filterType.Approved, $event)">
|
<input type="radio" id="approved" name="Status" (click)="filterStatus(filterType.Approved, $event)" />
|
||||||
<label for="approved">{{ 'Filter.Approved' | translate }}</label>
|
<label for="approved">{{ 'Filter.Approved' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="Processing" name="Status" (click)="filterStatus(filterType.Processing, $event)">
|
<input type="radio" id="Processing" name="Status" (click)="filterStatus(filterType.Processing, $event)" />
|
||||||
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
|
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)">
|
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)" />
|
||||||
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
|
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
|
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
|
||||||
<i class="fas fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}</button>
|
<i class="fas fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}
|
||||||
|
</button>
|
||||||
</p-sidebar>
|
</p-sidebar>
|
||||||
|
|
|
@ -1,116 +1,107 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="search" class="form-control form-control-custom searchwidth" [placeholder]="'Search.Search' | translate"
|
<input
|
||||||
(keyup)="search($event)">
|
type="text"
|
||||||
<span class="input-group-btn">
|
id="search"
|
||||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
class="form-control form-control-custom searchwidth"
|
||||||
<i class="fas fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
[placeholder]="'Search.Search' | translate"
|
||||||
</button>
|
(keyup)="search($event)"
|
||||||
|
/>
|
||||||
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
||||||
aria-haspopup="true" aria-expanded="true">
|
<i class="fas fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||||
<i class="fas fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
</button>
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
|
||||||
<li>
|
|
||||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
|
|
||||||
translate }}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{
|
|
||||||
'Requests.SortRequestDateDesc' | translate }}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary-outline dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="true"
|
||||||
|
>
|
||||||
|
<i class="fas fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||||
|
<li>
|
||||||
|
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }} </a>
|
||||||
|
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{ 'Requests.SortRequestDateDesc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate }} </a>
|
||||||
|
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate }} </a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div *ngFor="let request of albumRequests" class="col-md-4">
|
<div *ngFor="let request of albumRequests" class="col-md-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="album-bg backdrop" [style.background-image]="request.background"></div>
|
<div class="album-bg backdrop" [style.background-image]="request.background"></div>
|
||||||
<div class="album-tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
<div class="album-tint" style="background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.6) 100%)"></div>
|
||||||
|
|
||||||
<div class="col-sm-12 small-padding">
|
<div class="col-sm-12 small-padding">
|
||||||
<img *ngIf="request.disk" class="img-responsive poster album-cover" src="{{request.disk}}" alt="poster">
|
<img *ngIf="request.disk" class="img-responsive poster album-cover" src="{{ request.disk }}" alt="poster" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-12 small-padding">
|
||||||
|
<div>
|
||||||
|
<h4>
|
||||||
|
<a href="" target="_blank">
|
||||||
|
{{ request.title | truncate: 36 }}
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<h5>
|
||||||
|
<a href="">
|
||||||
|
{{ request.artistName }}
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 small-padding">
|
<div class="request-info">
|
||||||
<div>
|
<div class="request-by">
|
||||||
<h4>
|
<span>{{ 'Requests.RequestedBy' | translate }} </span>
|
||||||
<a href="" target="_blank">
|
<span *ngIf="request.requestedByAlias">{{ request.requestedByAlias }}</span>
|
||||||
{{request.title | truncate: 36}}
|
<span *ngIf="!request.requestedByAlias">
|
||||||
</a>
|
<span *ngIf="!isAdmin">{{ request.requestedUser.userName }}</span>
|
||||||
|
<span *ngIf="isAdmin && request.requestedUser.alias">{{ request.requestedUser.alias }}</span>
|
||||||
|
<span *ngIf="isAdmin && !request.requestedUser.alias">{{ request.requestedUser.userName }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
</h4>
|
<div class="requested-status">
|
||||||
<h5>
|
<span>{{ 'Requests.RequestStatus' | translate }} </span>
|
||||||
<a href="">
|
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||||
{{request.artistName}}
|
<span
|
||||||
</a>
|
*ngIf="request.approved && !request.available"
|
||||||
</h5>
|
id="processingRequestLabel"
|
||||||
</div>
|
class="label label-info"
|
||||||
|
[translate]="'Common.ProcessingRequest'"
|
||||||
|
></span>
|
||||||
|
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
|
||||||
|
<span *ngIf="request.deniedReason" title="{{ request.deniedReason }}">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
*ngIf="!request.approved && !request.availble && !request.denied"
|
||||||
|
id="pendingApprovalLabel"
|
||||||
|
class="label label-warning"
|
||||||
|
[translate]="'Common.PendingApproval'"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="request.denied" id="requestDenied">
|
||||||
|
{{ 'Requests.Denied' | translate }}
|
||||||
|
<i style="color: red" class="fas fa-check" pTooltip="{{ request.deniedReason }}"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="request-info">
|
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: { date: request.releaseDate | ombiDate: 'PP' } }}</div>
|
||||||
<div class="request-by">
|
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{ request.requestedDate | ombiDate: 'PP' }}</div>
|
||||||
<span>{{ 'Requests.RequestedBy' | translate }} </span>
|
<br />
|
||||||
<span *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
|
</div>
|
||||||
<span *ngIf="!request.requestedByAlias">
|
<!-- <div *ngIf="isAdmin">
|
||||||
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
|
|
||||||
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
|
|
||||||
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="requested-status">
|
|
||||||
<span>{{ 'Requests.RequestStatus' | translate }} </span>
|
|
||||||
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
|
||||||
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info"
|
|
||||||
[translate]="'Common.ProcessingRequest'"></span>
|
|
||||||
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
|
|
||||||
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
</span>
|
|
||||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
|
|
||||||
class="label label-warning" [translate]="'Common.PendingApproval'"></span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div *ngIf="request.denied" id="requestDenied">
|
|
||||||
{{ 'Requests.Denied' | translate }}
|
|
||||||
<i style="color:red;" class="fas fa-check" pTooltip="{{request.deniedReason}}"></i>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal |
|
|
||||||
amDateFormat: 'LL'} }}</div>
|
|
||||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
|
|
||||||
| amDateFormat: 'LL'}}</div>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
<!-- <div *ngIf="isAdmin">
|
|
||||||
<div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
<div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
||||||
<span>{{request.qualityOverrideTitle}} </span>
|
<span>{{request.qualityOverrideTitle}} </span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,10 +109,9 @@
|
||||||
<span>{{request.rootPathOverrideTitle}} </span>
|
<span>{{request.rootPathOverrideTitle}} </span>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
</div>
|
||||||
</div>
|
<div class="col-sm-12 small-padding">
|
||||||
<div class="col-sm-12 small-padding">
|
<!-- <div class="row">
|
||||||
<!-- <div class="row">
|
|
||||||
<div class="col-md-2 col-md-push-6">
|
<div class="col-md-2 col-md-push-6">
|
||||||
|
|
||||||
<a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)" pTooltip="Subscribe for notifications">
|
<a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)" pTooltip="Subscribe for notifications">
|
||||||
|
@ -132,17 +122,16 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div *ngIf="isAdmin">
|
<div *ngIf="isAdmin">
|
||||||
<div *ngIf="!request.approved" id="approveBtn">
|
<div *ngIf="!request.approved" id="approveBtn">
|
||||||
<form class="col-md-6">
|
<form class="col-md-6">
|
||||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve"
|
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit">
|
||||||
type="submit">
|
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||||
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
</button>
|
||||||
</button>
|
</form>
|
||||||
</form>
|
|
||||||
|
|
||||||
<!--Radarr Root Folder-->
|
<!--Radarr Root Folder-->
|
||||||
<!-- <div *ngIf="radarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
|
<!-- <div *ngIf="radarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
|
||||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
<i class="fas fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -157,8 +146,8 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!--Radarr Quality Profiles -->
|
<!--Radarr Quality Profiles -->
|
||||||
<!-- <div *ngIf="radarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
|
<!-- <div *ngIf="radarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
|
||||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
<i class="fas fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -173,117 +162,129 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div *ngIf="!request.denied" id="denyBtn" class="col-md-6">
|
<div *ngIf="!request.denied" id="denyBtn" class="col-md-6">
|
||||||
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny">
|
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny">
|
||||||
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
|
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form id="markBtnGroup">
|
||||||
|
<button
|
||||||
|
id="unavailableBtn"
|
||||||
|
*ngIf="request.available"
|
||||||
|
(click)="changeAvailability(request, false)"
|
||||||
|
style="text-align: right"
|
||||||
|
value="false"
|
||||||
|
class="btn btn-sm btn-info-outline change"
|
||||||
|
>
|
||||||
|
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="availableBtn"
|
||||||
|
*ngIf="!request.available"
|
||||||
|
(click)="changeAvailability(request, true)"
|
||||||
|
style="text-align: right"
|
||||||
|
value="true"
|
||||||
|
class="btn btn-sm btn-success-outline change"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isAdmin || isRequestUser(request)">
|
||||||
|
<form id="removeBtn">
|
||||||
|
<button (click)="removeRequest(request)" style="text-align: right" class="btn btn-sm btn-danger-outline delete">
|
||||||
|
<i class="fas fa-minus"></i> {{ 'Requests.Remove' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary-outline dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="true"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||||
|
<li *ngFor="let cat of issueCategories">
|
||||||
|
<a [routerLink]="" (click)="reportIssue(cat, request)">{{ cat.value }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
<form id="markBtnGroup">
|
<p-paginator [rows]="10" [totalRecords]="totalAlbums" (onPageChange)="paginate($event)"></p-paginator>
|
||||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)"
|
|
||||||
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change">
|
|
||||||
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
|
||||||
</button>
|
|
||||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)"
|
|
||||||
style="text-align: right" value="true" class="btn btn-sm btn-success-outline change">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div *ngIf="isAdmin || isRequestUser(request)">
|
|
||||||
<form id="removeBtn">
|
|
||||||
<button (click)="removeRequest(request)" style="text-align: right" class="btn btn-sm btn-danger-outline delete">
|
|
||||||
<i class="fas fa-minus"></i> {{ 'Requests.Remove' | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
|
|
||||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
|
||||||
aria-haspopup="true" aria-expanded="true">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
|
||||||
<li *ngFor="let cat of issueCategories">
|
|
||||||
<a [routerLink]="" (click)="reportIssue(cat, request)">{{cat.value}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p-paginator [rows]="10" [totalRecords]="totalAlbums" (onPageChange)="paginate($event)"></p-paginator>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<issue-report
|
||||||
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequest?.title"
|
[movie]="true"
|
||||||
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId"></issue-report>
|
[visible]="issuesBarVisible"
|
||||||
|
(visibleChange)="issuesBarVisible = $event"
|
||||||
|
[title]="issueRequest?.title"
|
||||||
|
[issueCategory]="issueCategorySelected"
|
||||||
|
[id]="issueRequest?.id"
|
||||||
|
[providerId]="issueProviderId"
|
||||||
|
></issue-report>
|
||||||
|
|
||||||
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
|
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
|
||||||
<h3>{{ 'Requests.Filter' | translate }}</h3>
|
<h3>{{ 'Requests.Filter' | translate }}</h3>
|
||||||
<hr>
|
<hr />
|
||||||
<div>
|
<div>
|
||||||
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
|
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="Available" name="Availability" (click)="filterAvailability(filterType.Available, $event)">
|
<input type="radio" id="Available" name="Availability" (click)="filterAvailability(filterType.Available, $event)" />
|
||||||
<label for="Available">{{ 'Common.Available' | translate }}</label>
|
<label for="Available">{{ 'Common.Available' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="notAvailable" name="Availability" (click)="filterAvailability(filterType.NotAvailable, $event)">
|
<input type="radio" id="notAvailable" name="Availability" (click)="filterAvailability(filterType.NotAvailable, $event)" />
|
||||||
<label for="notAvailable">{{ 'Common.NotAvailable' | translate }}</label>
|
<label for="notAvailable">{{ 'Common.NotAvailable' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>{{ 'Filter.FilterHeaderRequestStatus' | translate }}</h4>
|
<h4>{{ 'Filter.FilterHeaderRequestStatus' | translate }}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="approved" name="Status" (click)="filterStatus(filterType.Approved, $event)">
|
<input type="radio" id="approved" name="Status" (click)="filterStatus(filterType.Approved, $event)" />
|
||||||
<label for="approved">{{ 'Filter.Approved' | translate }}</label>
|
<label for="approved">{{ 'Filter.Approved' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="Processing" name="Status" (click)="filterStatus(filterType.Processing, $event)">
|
<input type="radio" id="Processing" name="Status" (click)="filterStatus(filterType.Processing, $event)" />
|
||||||
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
|
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)">
|
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)" />
|
||||||
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
|
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
|
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
|
||||||
<i class="fas fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}</button>
|
<i class="fas fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}
|
||||||
|
</button>
|
||||||
</p-sidebar>
|
</p-sidebar>
|
||||||
|
|
||||||
|
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{ requestToDeny.title }}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||||
|
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||||
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
<p-footer>
|
||||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
<button type="button" (click)="denyRequest()" label="Reject" class="btn btn-success">Deny</button>
|
||||||
<p-footer>
|
<button type="button" (click)="denyDisplay = false" label="Close" class="btn btn-danger">Close</button>
|
||||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
</p-footer>
|
||||||
<button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
|
||||||
</p-footer>
|
|
||||||
</p-dialog>
|
</p-dialog>
|
|
@ -1,128 +1,167 @@
|
||||||
<div *ngIf="childRequests">
|
<div *ngIf="childRequests">
|
||||||
<hr />
|
<hr />
|
||||||
<div *ngFor="let child of childRequests" class="clearfix">
|
<div *ngFor="let child of childRequests" class="clearfix">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<span [translate]="'Requests.RequestedBy'"></span>
|
||||||
|
<span *ngIf="child.requestedByAlias">{{ child.requestedByAlias }}</span>
|
||||||
|
<span *ngIf="!child.requestedByAlias">
|
||||||
|
<span *ngIf="!isAdmin">{{ child.requestedUser.userName }}</span>
|
||||||
|
<span *ngIf="isAdmin && child.requestedUser.alias">{{ child.requestedUser.alias }}</span>
|
||||||
|
<span *ngIf="isAdmin && !child.requestedUser.alias">{{ child.requestedUser.userName }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-md-2">
|
<div class="col-md-1 col-md-push-9">
|
||||||
<span [translate]="'Requests.RequestedBy'"></span>
|
<button
|
||||||
<span *ngIf="child.requestedByAlias">{{child.requestedByAlias}}</span>
|
id="subscribeBtn"
|
||||||
<span *ngIf="!child.requestedByAlias">
|
*ngIf="child.showSubscribe && !child.subscribed"
|
||||||
<span *ngIf="!isAdmin">{{child.requestedUser.userName}}</span>
|
(click)="subscribe(child)"
|
||||||
<span *ngIf="isAdmin && child.requestedUser.alias">{{child.requestedUser.alias}}</span>
|
class="btn btn-sm btn-primary-outline"
|
||||||
<span *ngIf="isAdmin && !child.requestedUser.alias">{{child.requestedUser.userName}}</span>
|
pTooltip="Subscribe for notifications"
|
||||||
</span>
|
type="submit"
|
||||||
</div>
|
>
|
||||||
|
<i class="fas fa-rss"></i> Subscribe
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="subscribeBtn"
|
||||||
|
*ngIf="child.showSubscribe && child.subscribed"
|
||||||
|
(click)="unSubscribe(child)"
|
||||||
|
class="btn btn-sm btn-danger-outline"
|
||||||
|
pTooltip="UnSubscribe for notifications"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<i class="fas fa-rss"></i> UnSubscribe
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="col-md-1 col-md-push-9">
|
<div *ngIf="isAdmin">
|
||||||
<button id="subscribeBtn" *ngIf="child.showSubscribe && !child.subscribed" (click)="subscribe(child)"
|
<button
|
||||||
class="btn btn-sm btn-primary-outline" pTooltip="Subscribe for notifications" type="submit"><i
|
id="approveBtn"
|
||||||
class="fas fa-rss"></i> Subscribe</button>
|
*ngIf="child.canApprove && !child.approved"
|
||||||
<button id="subscribeBtn" *ngIf="child.showSubscribe && child.subscribed" (click)="unSubscribe(child)"
|
(click)="approve(child)"
|
||||||
class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i
|
class="btn btn-sm btn-success-outline"
|
||||||
class="fas fa-rss"></i> UnSubscribe</button>
|
type="submit"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="unavailableBtn"
|
||||||
|
*ngIf="child.available"
|
||||||
|
(click)="changeAvailability(child, false)"
|
||||||
|
style="text-align: right"
|
||||||
|
value="false"
|
||||||
|
class="btn btn-sm btn-info-outline change"
|
||||||
|
>
|
||||||
|
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="availableBtn"
|
||||||
|
*ngIf="!child.available"
|
||||||
|
(click)="changeAvailability(child, true)"
|
||||||
|
style="text-align: right"
|
||||||
|
value="true"
|
||||||
|
class="btn btn-sm btn-success-outline change"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny">
|
||||||
|
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isAdmin || isRequestUser(child)">
|
||||||
|
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny">
|
||||||
|
<i class="fas fa-times"></i> {{ 'Requests.Remove' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<ngb-tabset>
|
||||||
|
<div *ngFor="let season of child.seasonRequests">
|
||||||
|
<ngb-tab [id]="season.seasonNumber" [title]="season.seasonNumber">
|
||||||
|
<ng-template ngbTabContent>
|
||||||
|
<h2>{{ 'Requests.Season' | translate }} {{ season.seasonNumber }}</h2>
|
||||||
|
|
||||||
<div *ngIf="isAdmin">
|
<table class="table table-striped table-hover table-responsive table-condensed">
|
||||||
<button id="approveBtn" *ngIf="child.canApprove && !child.approved" (click)="approve(child)" class="btn btn-sm btn-success-outline"
|
<thead>
|
||||||
type="submit"><i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}</button>
|
<tr>
|
||||||
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)"
|
<th>
|
||||||
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fas fa-minus"></i>
|
<a> # </a>
|
||||||
{{ 'Requests.MarkUnavailable' | translate }}</button>
|
</th>
|
||||||
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right"
|
<th>
|
||||||
value="true" class="btn btn-sm btn-success-outline change"><i class="fas fa-plus"></i> {{
|
<a>
|
||||||
'Requests.MarkAvailable' | translate }}</button>
|
{{ 'Requests.GridTitle' | translate }}
|
||||||
|
</a>
|
||||||
<button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny">
|
</th>
|
||||||
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}</button>
|
<th>
|
||||||
|
<a>
|
||||||
</div>
|
{{ 'Requests.AirDate' | translate }}
|
||||||
<div *ngIf="isAdmin || isRequestUser(child)">
|
</a>
|
||||||
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny"><i
|
</th>
|
||||||
class="fas fa-times"></i> {{ 'Requests.Remove' | translate }}</button>
|
<th>
|
||||||
</div>
|
<a>
|
||||||
|
{{ 'Requests.GridStatus' | translate }}
|
||||||
|
</a>
|
||||||
</div>
|
</th>
|
||||||
</div>
|
</tr>
|
||||||
<div class="col-md-12">
|
</thead>
|
||||||
<ngb-tabset>
|
<tbody>
|
||||||
|
<tr *ngFor="let ep of season.episodes">
|
||||||
<div *ngFor="let season of child.seasonRequests">
|
<td>
|
||||||
<ngb-tab [id]="season.seasonNumber" [title]="season.seasonNumber">
|
{{ ep.episodeNumber }}
|
||||||
<ng-template ngbTabContent>
|
</td>
|
||||||
<h2>{{ 'Requests.Season' | translate }} {{season.seasonNumber}}</h2>
|
<td>
|
||||||
|
{{ ep.title }}
|
||||||
<table class="table table-striped table-hover table-responsive table-condensed">
|
</td>
|
||||||
<thead>
|
<td>
|
||||||
<tr>
|
{{ ep.airDate | ombiDate: 'P' }}
|
||||||
<th>
|
</td>
|
||||||
<a>
|
<td>
|
||||||
#
|
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel" [translate]="'Common.Denied'">
|
||||||
</a>
|
<i style="color: red" class="fas fa-check" pTooltip="{{ child.deniedReason }}"></i>
|
||||||
</th>
|
</span>
|
||||||
<th>
|
<span
|
||||||
<a>
|
*ngIf="!child.denied && ep.available"
|
||||||
{{ 'Requests.GridTitle' | translate }}
|
class="label label-success"
|
||||||
</a>
|
id="availableLabel"
|
||||||
</th>
|
[translate]="'Common.Available'"
|
||||||
<th>
|
></span>
|
||||||
<a>
|
<span
|
||||||
{{ 'Requests.AirDate' | translate }}
|
*ngIf="!child.denied && ep.approved && !ep.available"
|
||||||
</a>
|
class="label label-info"
|
||||||
</th>
|
id="processingRequestLabel"
|
||||||
<th>
|
[translate]="'Common.ProcessingRequest'"
|
||||||
<a>
|
></span>
|
||||||
{{ 'Requests.GridStatus' | translate }}
|
<div *ngIf="!child.denied && !ep.approved">
|
||||||
</a>
|
<div *ngIf="!ep.available">
|
||||||
</th>
|
<span
|
||||||
</tr>
|
class="label label-warning"
|
||||||
</thead>
|
id="pendingApprovalLabel"
|
||||||
<tbody>
|
[translate]="'Common.PendingApproval'"
|
||||||
<tr *ngFor="let ep of season.episodes">
|
></span>
|
||||||
<td>
|
</div>
|
||||||
{{ep.episodeNumber}}
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ep.title}}
|
</tbody>
|
||||||
</td>
|
</table>
|
||||||
<td>
|
</ng-template>
|
||||||
{{ep.airDate | amLocal | amUserLocale | amDateFormat: 'L' }}
|
</ngb-tab>
|
||||||
</td>
|
</div>
|
||||||
<td>
|
</ngb-tabset>
|
||||||
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel"
|
</div>
|
||||||
[translate]="'Common.Denied'">
|
<br />
|
||||||
<i style="color:red;" class="fas fa-check" pTooltip="{{child.deniedReason}}"></i>
|
<br />
|
||||||
</span>
|
<hr />
|
||||||
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel"
|
</div>
|
||||||
[translate]="'Common.Available'"></span>
|
|
||||||
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info"
|
|
||||||
id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
|
|
||||||
<div *ngIf="!child.denied && !ep.approved">
|
|
||||||
<div *ngIf="!ep.available"><span class="label label-warning" id="pendingApprovalLabel"
|
|
||||||
[translate]="'Common.PendingApproval'"></span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</ng-template>
|
|
||||||
</ngb-tab>
|
|
||||||
</div>
|
|
||||||
</ngb-tabset>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
|
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{ requestToDeny.title }}''" [(visible)]="denyDisplay" [draggable]="false">
|
||||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||||
<p-footer>
|
<p-footer>
|
||||||
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
|
<button type="button" (click)="denyRequest()" label="Reject" class="btn btn-success">Deny</button>
|
||||||
<button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
|
<button type="button" (click)="denyDisplay = false" label="Close" class="btn btn-danger">Close</button>
|
||||||
</p-footer>
|
</p-footer>
|
||||||
</p-dialog>
|
</p-dialog>
|
|
@ -1,112 +1,146 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<input type="text" id="search" class="form-control form-control-custom" [placeholder]="'Common.Search' | translate" (keyup)="search($event)">
|
<input
|
||||||
</div>
|
type="text"
|
||||||
|
id="search"
|
||||||
|
class="form-control form-control-custom"
|
||||||
|
[placeholder]="'Common.Search' | translate"
|
||||||
|
(keyup)="search($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div *ngFor="let node of tvRequests.collection">
|
<div *ngFor="let node of tvRequests.collection">
|
||||||
<!--This is the section that holds the parent level results set-->
|
<!--This is the section that holds the parent level results set-->
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="myBg backdrop" [style.background-image]="node?.background"></div>
|
<div class="myBg backdrop" [style.background-image]="node?.background"></div>
|
||||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.6) 100%)"></div>
|
||||||
|
|
||||||
<div class="col-sm-2 small-padding">
|
<div class="col-sm-2 small-padding">
|
||||||
|
<img class="img-responsive poster" src="{{ node.posterPath || null }}" alt="poster" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<img class="img-responsive poster" src="{{node.posterPath || null}}" alt="poster">
|
<div class="col-sm-5 small-padding">
|
||||||
|
<div>
|
||||||
|
<a href="http://www.imdb.com/title/{{ node.imdbId }}/" target="_blank">
|
||||||
|
<h4 class="request-title">{{ node.title }} ({{ node.releaseDate | ombiDate: 'P' }})</h4>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<span>Status: </span>
|
||||||
|
<span class="label label-success">{{ node.status }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div>Release Date: {{ node.releaseDate | ombiDate: 'PP' }}</div>
|
||||||
|
<div *ngIf="isAdmin">
|
||||||
|
<div *ngIf="node.qualityOverrideTitle" class="quality-override">
|
||||||
|
{{ 'Requests.QualityOverride' | translate }}
|
||||||
|
<span>{{ node.qualityOverrideTitle }} </span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="node.rootPathOverrideTitle" class="root-override">
|
||||||
|
{{ 'Requests.RootFolderOverride' | translate }}
|
||||||
|
<span>{{ node.rootPathOverrideTitle }} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-5 small-padding">
|
<br />
|
||||||
<div>
|
</div>
|
||||||
<a href="http://www.imdb.com/title/{{node.imdbId}}/" target="_blank">
|
<div class="col-sm-3 col-sm-push-3 small-padding">
|
||||||
<h4 class="request-title">{{node.title}} ({{node.releaseDate | amLocal| amUserLocale | amDateFormat: 'YYYY'}})</h4>
|
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab(node, $event)">
|
||||||
</a>
|
<i class="fas fa-plus"></i> View
|
||||||
</div>
|
</button>
|
||||||
<br />
|
<div *ngIf="isAdmin">
|
||||||
<div>
|
<!--Sonarr Root Folder-->
|
||||||
<span>Status: </span>
|
<div *ngIf="sonarrRootFolders?.length > 1" class="btn-group btn-split" id="rootFolderBtn">
|
||||||
<span class="label label-success">{{node.status}}</span>
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
</div>
|
<i class="fas fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning-outline dropdown-toggle"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li *ngFor="let folder of sonarrRootFolders">
|
||||||
|
<a href="#" (click)="selectRootFolder(node, folder, $event)">{{ folder.path }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--Sonarr Quality Profiles -->
|
||||||
|
<div *ngIf="sonarrProfiles?.length > 1" class="btn-group btn-split" id="changeQualityBtn">
|
||||||
|
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning-outline dropdown-toggle"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li *ngFor="let profile of sonarrProfiles">
|
||||||
|
<a href="#" (click)="selectQualityProfile(node, profile, $event)">{{ profile.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary-outline dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="true"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||||
|
<li *ngFor="let cat of issueCategories">
|
||||||
|
<a [routerLink]="" (click)="reportIssue(cat, node)">{{ cat.value }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
||||||
|
<div *ngIf="node.open">
|
||||||
|
<tvrequests-children
|
||||||
|
[childRequests]="node.childRequests"
|
||||||
|
[isAdmin]="isAdmin"
|
||||||
|
[currentUser]="currentUser"
|
||||||
|
(requestDeleted)="childRequestDeleted($event)"
|
||||||
|
></tvrequests-children>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>Release Date: {{node.releaseDate | amLocal | amUserLocale | amDateFormat: 'LL'}}</div>
|
<br />
|
||||||
<div *ngIf="isAdmin">
|
<br />
|
||||||
<div *ngIf="node.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }}
|
</div>
|
||||||
<span>{{node.qualityOverrideTitle}} </span>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="node.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }}
|
|
||||||
<span>{{node.rootPathOverrideTitle}} </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
<p-paginator [rows]="10" [totalRecords]="totalTv" (onPageChange)="paginate($event)"></p-paginator>
|
||||||
</div>
|
|
||||||
<div class="col-sm-3 col-sm-push-3 small-padding">
|
|
||||||
|
|
||||||
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab(node,$event)">
|
|
||||||
<i class="fas fa-plus"></i> View</button>
|
|
||||||
<div *ngIf="isAdmin">
|
|
||||||
<!--Sonarr Root Folder-->
|
|
||||||
<div *ngIf="sonarrRootFolders?.length > 1" class="btn-group btn-split" id="rootFolderBtn">
|
|
||||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="caret"></span>
|
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li *ngFor="let folder of sonarrRootFolders">
|
|
||||||
<a href="#" (click)="selectRootFolder(node, folder, $event)">{{folder.path}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--Sonarr Quality Profiles -->
|
|
||||||
<div *ngIf="sonarrProfiles?.length > 1" class="btn-group btn-split" id="changeQualityBtn">
|
|
||||||
<button type="button" class="btn btn-sm btn-warning-outline">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="caret"></span>
|
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li *ngFor="let profile of sonarrProfiles">
|
|
||||||
<a href="#" (click)="selectQualityProfile(node, profile, $event)">{{profile.name}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
|
|
||||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
|
||||||
<li *ngFor="let cat of issueCategories">
|
|
||||||
<a [routerLink]="" (click)="reportIssue(cat, node)">{{cat.value}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
|
|
||||||
<div *ngIf="node.open">
|
|
||||||
<tvrequests-children [childRequests]="node.childRequests" [isAdmin]="isAdmin" [currentUser]="currentUser" (requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p-paginator [rows]="10" [totalRecords]="totalTv" (onPageChange)="paginate($event)"></p-paginator>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title" [issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>
|
<issue-report
|
||||||
|
[movie]="false"
|
||||||
|
[visible]="issuesBarVisible"
|
||||||
|
[title]="issueRequest?.title"
|
||||||
|
[issueCategory]="issueCategorySelected"
|
||||||
|
[id]="issueRequest?.id"
|
||||||
|
[providerId]="issueProviderId"
|
||||||
|
(visibleChange)="issuesBarVisible = $event"
|
||||||
|
></issue-report>
|
||||||
|
|
|
@ -1,28 +1,21 @@
|
||||||
|
<h1 mat-dialog-title><i class="fas fa-code-branch"></i> Latest Version: {{ data.updateVersionString }}</h1>
|
||||||
|
|
||||||
<h1 mat-dialog-title><i class="fas fa-code-branch"></i> Latest Version: {{data.updateVersionString}}</h1>
|
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div [innerHTML]="data.changeLogs">
|
<div [innerHTML]="data.changeLogs"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="mat-table">
|
||||||
|
<div class="mat-header-row">
|
||||||
|
<div class="mat-header-cell">Binary</div>
|
||||||
|
<div class="mat-header-cell">Download</div>
|
||||||
|
</div>
|
||||||
|
<div *ngFor="let d of data.downloads" class="mat-row">
|
||||||
|
<div class="mat-cell">{{ d.name }}</div>
|
||||||
|
<div class="mat-cell"><a href="{{ d.url }}">Download</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<small>Updated at {{ data.updateDate | ombiDate: 'PP' }}</small>
|
||||||
<div class="mat-table">
|
|
||||||
<div class="mat-header-row">
|
|
||||||
<div class="mat-header-cell">Binary</div>
|
|
||||||
<div class="mat-header-cell">Download</div></div>
|
|
||||||
<div *ngFor="let d of data.downloads" class="mat-row" >
|
|
||||||
<div class="mat-cell">{{d.name}}</div>
|
|
||||||
<div class="mat-cell"><a href="{{d.url}}">Download</a></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<small>Updated at {{data.updateDate | amUserLocale | amDateFormat: 'LL' }}</small>
|
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
|
||||||
<div mat-dialog-actions class="right-buttons">
|
<div mat-dialog-actions class="right-buttons">
|
||||||
<button mat-raised-button id="cancelButton" [mat-dialog-close]="" color="warn"><i class="fas fa-times"></i> Close</button>
|
<button mat-raised-button id="cancelButton" [mat-dialog-close]="" color="warn"><i class="fas fa-times"></i> Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
<div class='container'>
|
<div class="container">
|
||||||
<div class='chatbox'>
|
<div class="chatbox">
|
||||||
<div class='chatbox__user-list'>
|
<div class="chatbox__user-list">
|
||||||
<h1>{{ "NavigationBar.UserManagement" | translate }}</h1>
|
<h1>{{ 'NavigationBar.UserManagement' | translate }}</h1>
|
||||||
<div class='chatbox__user--active' *ngFor="let user of userList">
|
<div class="chatbox__user--active" *ngFor="let user of userList">
|
||||||
<p>{{user}}</p>
|
<p>{{ user }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chatbox-message-box">
|
<div class="chatbox-message-box">
|
||||||
<div class="chatbox__messages" *ngFor="let m of messages">
|
<div class="chatbox__messages" *ngFor="let m of messages">
|
||||||
<div class="chatbox__messages__user-message">
|
<div class="chatbox__messages__user-message">
|
||||||
<div class="chatbox__messages__user-message--ind-message" [ngClass]="{'sender': m.chatType === ChatType.Sender, 'reciever':m.chatType === ChatType.Reciever }">
|
<div
|
||||||
<p class="name" *ngIf="m?.username">{{m.username}}</p>
|
class="chatbox__messages__user-message--ind-message"
|
||||||
<br/>
|
[ngClass]="{ sender: m.chatType === ChatType.Sender, reciever: m.chatType === ChatType.Reciever }"
|
||||||
<p class="message">{{m.message}}</p>
|
>
|
||||||
<p class="timestamp">{{m.date | amFromUtc | amLocal | amUserLocale | amDateFormat: 'l LT'}}</p>
|
<p class="name" *ngIf="m?.username">{{ m.username }}</p>
|
||||||
</div>
|
<br />
|
||||||
</div>
|
<p class="message">{{ m.message }}</p>
|
||||||
</div>
|
<p class="timestamp">{{ m.date | ombiDate: 'Ppp' }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form">
|
</div>
|
||||||
<input type="text" [(ngModel)]="currentMessage" [placeholder]="'Issues.EnterYourMessage' | translate">
|
</div>
|
||||||
<button mat-raised-button class="add-message" (click)="addMessage()">{{ "Issues.SendMessageButton" | translate }}</button>
|
</div>
|
||||||
</div>
|
<div class="form">
|
||||||
</div>
|
<input type="text" [(ngModel)]="currentMessage" [placeholder]="'Issues.EnterYourMessage' | translate" />
|
||||||
|
<button mat-raised-button class="add-message" (click)="addMessage()">{{ 'Issues.SendMessageButton' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,74 +1,93 @@
|
||||||
<div mat-dialog-content class="background">
|
<div mat-dialog-content class="background">
|
||||||
<div *ngIf="!requestable">
|
<div *ngIf="!requestable">
|
||||||
{{'MediaDetails.EpisodeSelector.NoEpisodes' | translate}}
|
{{ 'MediaDetails.EpisodeSelector.NoEpisodes' | translate }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="requestable" class="row">
|
<div *ngIf="requestable" class="row">
|
||||||
<div class="col-12 action-buttons-right">
|
<div class="col-12 action-buttons-right">
|
||||||
<button id="episodeModalAllSeasons" (click)="requestAllSeasons()" color="primary" mat-raised-button class="btn-spacing"
|
<button
|
||||||
matTooltip="{{'MediaDetails.EpisodeSelector.AllSeasonsTooltip' | translate}}">{{'Search.TvShows.AllSeasons' | translate }}</button>
|
id="episodeModalAllSeasons"
|
||||||
|
(click)="requestAllSeasons()"
|
||||||
|
color="primary"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
matTooltip="{{ 'MediaDetails.EpisodeSelector.AllSeasonsTooltip' | translate }}"
|
||||||
|
>
|
||||||
|
{{ 'Search.TvShows.AllSeasons' | translate }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button id="episodeModalFirstSeason" (click)="requestFirstSeason()" color="accent" mat-raised-button class="btn-spacing"
|
<button
|
||||||
matTooltip="{{'MediaDetails.EpisodeSelector.FirstSeasonTooltip' | translate}}">{{ 'Search.TvShows.FirstSeason' | translate }}</button>
|
id="episodeModalFirstSeason"
|
||||||
<button id="episodeModalLatestSeason" (click)="requestLatestSeason()" color="warn" mat-raised-button class="btn-spacing"
|
(click)="requestFirstSeason()"
|
||||||
matTooltip="{{'MediaDetails.EpisodeSelector.LatestSeasonTooltip' | translate}}">{{ 'Search.TvShows.LatestSeason' | translate }}</button>
|
color="accent"
|
||||||
</div>
|
mat-raised-button
|
||||||
</div>
|
class="btn-spacing"
|
||||||
|
matTooltip="{{ 'MediaDetails.EpisodeSelector.FirstSeasonTooltip' | translate }}"
|
||||||
|
>
|
||||||
|
{{ 'Search.TvShows.FirstSeason' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="episodeModalLatestSeason"
|
||||||
|
(click)="requestLatestSeason()"
|
||||||
|
color="warn"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-spacing"
|
||||||
|
matTooltip="{{ 'MediaDetails.EpisodeSelector.LatestSeasonTooltip' | translate }}"
|
||||||
|
>
|
||||||
|
{{ 'Search.TvShows.LatestSeason' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12" *ngFor="let season of data.series.seasonRequests">
|
<div class="col-12" *ngFor="let season of data.series.seasonRequests">
|
||||||
<mat-expansion-panel>
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
<mat-checkbox *ngIf="!season.seasonAvailable && isSeasonCheckable(season)" (click)="$event.stopPropagation();" (change)="seasonChanged($event, season)">
|
<mat-checkbox
|
||||||
{{ 'MediaDetails.EpisodeSelector.SeasonNumber' | translate: { number: season.seasonNumber } }}</mat-checkbox>
|
*ngIf="!season.seasonAvailable && isSeasonCheckable(season)"
|
||||||
<span *ngIf="season.seasonAvailable || !isSeasonCheckable(season)">{{ 'MediaDetails.EpisodeSelector.SeasonNumber' | translate: { number: season.seasonNumber } }}</span>
|
(click)="$event.stopPropagation()"
|
||||||
</mat-panel-title>
|
(change)="seasonChanged($event, season)"
|
||||||
<mat-panel-description>
|
>
|
||||||
<!-- Description -->
|
{{ 'MediaDetails.EpisodeSelector.SeasonNumber' | translate: { number: season.seasonNumber } }}</mat-checkbox
|
||||||
</mat-panel-description>
|
>
|
||||||
</mat-expansion-panel-header>
|
<span *ngIf="season.seasonAvailable || !isSeasonCheckable(season)">{{
|
||||||
|
'MediaDetails.EpisodeSelector.SeasonNumber' | translate: { number: season.seasonNumber }
|
||||||
|
}}</span>
|
||||||
|
</mat-panel-title>
|
||||||
|
<mat-panel-description>
|
||||||
|
<!-- Description -->
|
||||||
|
</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
<div class="row" *ngFor="let ep of season.episodes">
|
<div class="row" *ngFor="let ep of season.episodes">
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<div *ngIf="!ep.available && !ep.requested && !ep.approved">
|
<div *ngIf="!ep.available && !ep.requested && !ep.approved">
|
||||||
<mat-checkbox *ngIf="!ep.selected" [ngModel]="ep.selected" (click)="addRequest(ep)"></mat-checkbox>
|
<mat-checkbox *ngIf="!ep.selected" [ngModel]="ep.selected" (click)="addRequest(ep)"></mat-checkbox>
|
||||||
<mat-checkbox *ngIf="ep.selected" [ngModel]="ep.selected" (click)="removeRequest(ep)"></mat-checkbox>
|
<mat-checkbox *ngIf="ep.selected" [ngModel]="ep.selected" (click)="removeRequest(ep)"></mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
{{ep.episodeNumber}}
|
{{ ep.episodeNumber }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
{{ep.title}}
|
{{ ep.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2" *ngIf="ep.airDateDisplay != 'Unknown'">
|
<div class="col-2" *ngIf="ep.airDateDisplay != 'Unknown'">
|
||||||
{{ep.airDate | amLocal | amUserLocale | amDateFormat: 'L' }}
|
{{ ep.airDate | ombiDate: 'P' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2" *ngIf="ep.airDateDisplay == 'Unknown'">
|
<div class="col-2" *ngIf="ep.airDateDisplay == 'Unknown'">
|
||||||
{{ep.airDateDisplay }}
|
{{ ep.airDateDisplay }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
{{ep.requestStatus | translate}}
|
{{ ep.requestStatus | translate }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</mat-expansion-panel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</mat-expansion-panel>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions *ngIf="requestable">
|
<div mat-dialog-actions *ngIf="requestable">
|
||||||
<div class="action-buttons-right">
|
<div class="action-buttons-right">
|
||||||
|
<button (click)="submitRequests()" mat-raised-button class="btn-spacing btn-orange">{{ 'Common.Request' | translate }}</button>
|
||||||
<button (click)="submitRequests()" mat-raised-button class="btn-spacing btn-orange">{{
|
</div>
|
||||||
'Common.Request' | translate }}</button>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,141 +1,141 @@
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AdminRequestDialogComponent } from "./admin-request-dialog/admin-request-dialog.component";
|
import { AdminRequestDialogComponent } from './admin-request-dialog/admin-request-dialog.component';
|
||||||
import { AdvancedSearchDialogComponent } from "./advanced-search-dialog/advanced-search-dialog.component";
|
import { AdvancedSearchDialogComponent } from './advanced-search-dialog/advanced-search-dialog.component';
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from '@angular/common';
|
||||||
import { DetailsGroupComponent } from "../issues/components/details-group/details-group.component";
|
import { DetailsGroupComponent } from '../issues/components/details-group/details-group.component';
|
||||||
import { EpisodeRequestComponent } from "./episode-request/episode-request.component";
|
import { EpisodeRequestComponent } from './episode-request/episode-request.component';
|
||||||
import { GenreSelectComponent } from "./components/genre-select/genre-select.component";
|
import { GenreSelectComponent } from './components/genre-select/genre-select.component';
|
||||||
import { InputSwitchModule } from "primeng/inputswitch";
|
import { InputSwitchModule } from 'primeng/inputswitch';
|
||||||
import { IssuesReportComponent } from "./issues-report.component";
|
import { IssuesReportComponent } from './issues-report.component';
|
||||||
import { KeywordSearchComponent } from "./components/keyword-search/keyword-search.component";
|
import { KeywordSearchComponent } from './components/keyword-search/keyword-search.component';
|
||||||
import { MatAutocompleteModule } from "@angular/material/autocomplete";
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from "@angular/material/card";
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatCheckboxModule } from "@angular/material/checkbox";
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatChipsModule } from "@angular/material/chips";
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
import { MatDialogModule } from "@angular/material/dialog";
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { MatExpansionModule } from "@angular/material/expansion";
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from "@angular/material/input";
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatListModule } from '@angular/material/list';
|
import { MatListModule } from '@angular/material/list';
|
||||||
import {MatMenuModule} from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatNativeDateModule } from '@angular/material/core';
|
import { MatNativeDateModule } from '@angular/material/core';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
import {MatRadioModule} from '@angular/material/radio';
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { MatSortModule } from '@angular/material/sort';
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
import { MatStepperModule } from '@angular/material/stepper';
|
import { MatStepperModule } from '@angular/material/stepper';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
import { MatTabsModule } from "@angular/material/tabs";
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { MatTreeModule } from '@angular/material/tree';
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
import { MomentModule } from "ngx-moment";
|
import { NgModule } from '@angular/core';
|
||||||
import { NgModule } from "@angular/core";
|
import { PipeModule } from '../pipes/pipe.module';
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { RoleModule } from './role-directive/role.module';
|
||||||
import { RoleModule } from "./role-directive/role.module";
|
import { SidebarModule } from 'primeng/sidebar';
|
||||||
import { SidebarModule } from "primeng/sidebar";
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { TranslateModule } from "@ngx-translate/core";
|
import { TruncateModule } from '@yellowspot/ng-truncate';
|
||||||
import { TruncateModule } from "@yellowspot/ng-truncate";
|
import { WatchProvidersSelectComponent } from './components/watch-providers-select/watch-providers-select.component';
|
||||||
import { WatchProvidersSelectComponent } from "./components/watch-providers-select/watch-providers-select.component";
|
import { DateFnsModule } from 'ngx-date-fns';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
IssuesReportComponent,
|
IssuesReportComponent,
|
||||||
EpisodeRequestComponent,
|
EpisodeRequestComponent,
|
||||||
DetailsGroupComponent,
|
DetailsGroupComponent,
|
||||||
AdminRequestDialogComponent,
|
AdminRequestDialogComponent,
|
||||||
AdvancedSearchDialogComponent,
|
AdvancedSearchDialogComponent,
|
||||||
KeywordSearchComponent,
|
KeywordSearchComponent,
|
||||||
GenreSelectComponent,
|
GenreSelectComponent,
|
||||||
WatchProvidersSelectComponent,
|
WatchProvidersSelectComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
RoleModule,
|
RoleModule,
|
||||||
SidebarModule,
|
SidebarModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
InputSwitchModule,
|
InputSwitchModule,
|
||||||
TruncateModule,
|
TruncateModule,
|
||||||
MomentModule,
|
MatCardModule,
|
||||||
MatCardModule,
|
MatProgressSpinnerModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressBarModule,
|
||||||
MatProgressBarModule,
|
MatAutocompleteModule,
|
||||||
MatAutocompleteModule,
|
MatInputModule,
|
||||||
MatInputModule,
|
MatTabsModule,
|
||||||
MatTabsModule,
|
MatRadioModule,
|
||||||
MatRadioModule,
|
MatButtonModule,
|
||||||
MatButtonModule,
|
MatNativeDateModule,
|
||||||
MatNativeDateModule,
|
MatChipsModule,
|
||||||
MatChipsModule,
|
MatIconModule,
|
||||||
MatIconModule,
|
MatMenuModule,
|
||||||
MatMenuModule,
|
MatSidenavModule,
|
||||||
MatSidenavModule,
|
MatListModule,
|
||||||
MatListModule,
|
MatToolbarModule,
|
||||||
MatToolbarModule,
|
MatCheckboxModule,
|
||||||
MatCheckboxModule,
|
TranslateModule,
|
||||||
TranslateModule,
|
MatExpansionModule,
|
||||||
MatExpansionModule,
|
MatDialogModule,
|
||||||
MatDialogModule,
|
MatTooltipModule,
|
||||||
MatTooltipModule,
|
MatSelectModule,
|
||||||
MatSelectModule,
|
MatPaginatorModule,
|
||||||
MatPaginatorModule,
|
MatSortModule,
|
||||||
MatSortModule,
|
MatTreeModule,
|
||||||
MatTreeModule,
|
MatStepperModule,
|
||||||
MatStepperModule,
|
MatSnackBarModule,
|
||||||
MatSnackBarModule,
|
PipeModule,
|
||||||
PipeModule,
|
],
|
||||||
],
|
exports: [
|
||||||
exports: [
|
RoleModule,
|
||||||
RoleModule,
|
TranslateModule,
|
||||||
TranslateModule,
|
CommonModule,
|
||||||
CommonModule,
|
FormsModule,
|
||||||
FormsModule,
|
TranslateModule,
|
||||||
TranslateModule,
|
SidebarModule,
|
||||||
SidebarModule,
|
MatProgressSpinnerModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressBarModule,
|
||||||
MatProgressBarModule,
|
IssuesReportComponent,
|
||||||
IssuesReportComponent,
|
EpisodeRequestComponent,
|
||||||
EpisodeRequestComponent,
|
AdminRequestDialogComponent,
|
||||||
AdminRequestDialogComponent,
|
AdvancedSearchDialogComponent,
|
||||||
AdvancedSearchDialogComponent,
|
GenreSelectComponent,
|
||||||
GenreSelectComponent,
|
KeywordSearchComponent,
|
||||||
KeywordSearchComponent,
|
WatchProvidersSelectComponent,
|
||||||
WatchProvidersSelectComponent,
|
DetailsGroupComponent,
|
||||||
DetailsGroupComponent,
|
TruncateModule,
|
||||||
TruncateModule,
|
InputSwitchModule,
|
||||||
InputSwitchModule,
|
MatTreeModule,
|
||||||
MatTreeModule,
|
MatCardModule,
|
||||||
MomentModule,MatCardModule,
|
MatInputModule,
|
||||||
MatInputModule,
|
MatTabsModule,
|
||||||
MatTabsModule,
|
MatChipsModule,
|
||||||
MatChipsModule,
|
MatButtonModule,
|
||||||
MatButtonModule,
|
MatNativeDateModule,
|
||||||
MatNativeDateModule,
|
MatIconModule,
|
||||||
MatIconModule,
|
MatMenuModule,
|
||||||
MatMenuModule,
|
MatSnackBarModule,
|
||||||
MatSnackBarModule,
|
MatSidenavModule,
|
||||||
MatSidenavModule,
|
MatSelectModule,
|
||||||
MatSelectModule,
|
MatListModule,
|
||||||
MatListModule,
|
MatToolbarModule,
|
||||||
MatToolbarModule,
|
MatTooltipModule,
|
||||||
MatTooltipModule,
|
MatAutocompleteModule,
|
||||||
MatAutocompleteModule,
|
MatCheckboxModule,
|
||||||
MatCheckboxModule,
|
MatExpansionModule,
|
||||||
MatExpansionModule,
|
MatDialogModule,
|
||||||
MatDialogModule,
|
MatTableModule,
|
||||||
MatTableModule,
|
MatPaginatorModule,
|
||||||
MatPaginatorModule,
|
MatSortModule,
|
||||||
MatSortModule,
|
MatStepperModule,
|
||||||
MatStepperModule,
|
MatSlideToggleModule,
|
||||||
MatSlideToggleModule,
|
DateFnsModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedModule {}
|
export class SharedModule {}
|
||||||
|
|
|
@ -1,191 +1,201 @@
|
||||||
<div class="small-middle-container">
|
<div class="small-middle-container">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
<a type="button" mat-raised-button color="primary" data-test="adduserbtn" [routerLink]="['/usermanagement/user']">Add User To Ombi</a>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
style="float: right"
|
||||||
|
mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
(click)="showBulkEdit = !showBulkEdit"
|
||||||
|
[disabled]="this.selection.selected.length <= 0"
|
||||||
|
>
|
||||||
|
Bulk Edit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<table mat-table *ngIf="dataSource" [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
<mat-checkbox
|
||||||
|
(change)="$event ? masterToggle() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||||
|
[aria-label]="checkboxLabel()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let row">
|
||||||
|
<mat-checkbox
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)"
|
||||||
|
[aria-label]="checkboxLabel(row)"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<a type="button" mat-raised-button color="primary" data-test="adduserbtn" [routerLink]="['/usermanagement/user']">Add User To Ombi</a>
|
<ng-container matColumnDef="username">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>Username</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.userName }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<button type="button" style="float:right;" mat-raised-button color="primary" (click)="showBulkEdit = !showBulkEdit" [disabled]="this.selection.selected.length <= 0">Bulk Edit</button>
|
<ng-container matColumnDef="alias">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>Alias</th>
|
||||||
|
<td mat-cell *matCellDef="let element">{{ element.alias }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
</div>
|
<ng-container matColumnDef="email">
|
||||||
<div class="content" >
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>Email</th>
|
||||||
<table mat-table *ngIf="dataSource" [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
<td mat-cell *matCellDef="let element">{{ element.emailAddress }}</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="remainingRequests">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>Requests Remaining</th>
|
||||||
<mat-checkbox (change)="$event ? masterToggle() : null"
|
<td mat-cell *matCellDef="let u">
|
||||||
[checked]="selection.hasValue() && isAllSelected()"
|
<div *ngIf="u.movieRequestQuota != null && u.movieRequestQuota.hasLimit">
|
||||||
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
{{ 'UserManagment.MovieRemaining' | translate: { remaining: u.movieRequestQuota.remaining, total: u.movieRequestLimit } }}
|
||||||
[aria-label]="checkboxLabel()">
|
</div>
|
||||||
</mat-checkbox>
|
<div *ngIf="u.episodeRequestQuota != null && u.episodeRequestQuota.hasLimit">
|
||||||
</th>
|
{{ 'UserManagment.TvRemaining' | translate: { remaining: u.episodeRequestQuota.remaining, total: u.episodeRequestLimit } }}
|
||||||
<td mat-cell *matCellDef="let row">
|
</div>
|
||||||
<mat-checkbox (click)="$event.stopPropagation()"
|
<div *ngIf="u.musicRequestQuota != null && u.musicRequestQuota.hasLimit">
|
||||||
(change)="$event ? selection.toggle(row) : null"
|
{{ 'UserManagment.MusicRemaining' | translate: { remaining: u.musicRequestQuota.remaining, total: u.musicRequestLimit } }}
|
||||||
[checked]="selection.isSelected(row)"
|
</div>
|
||||||
[aria-label]="checkboxLabel(row)">
|
</td>
|
||||||
</mat-checkbox>
|
</ng-container>
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="username">
|
<ng-container matColumnDef="nextRequestDue">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Username </th>
|
<th mat-header-cell *matHeaderCellDef>Next Request Due</th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.userName}} </td>
|
<td mat-cell *matCellDef="let u">
|
||||||
</ng-container>
|
<div *ngIf="u.movieRequestQuota != null && u.movieRequestQuota.remaining != u.movieRequestLimit">
|
||||||
|
{{ 'UserManagment.MovieDue' | translate: { date: (u.movieRequestQuota.nextRequest | ombiDate: 'P') } }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="u.episodeRequestQuota != null && u.episodeRequestQuota.remaining != u.episodeRequestLimit">
|
||||||
|
{{ 'UserManagment.TvDue' | translate: { date: (u.episodeRequestQuota.nextRequest | ombiDate: 'P') } }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="u.musicRequestQuota != null && u.musicRequestQuota.remaining != u.musicRequestLimit">
|
||||||
|
{{ 'UserManagment.MusicDue' | translate: { date: (u.musicRequestQuota.nextRequest | ombiDate: 'P') } }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="lastLoggedIn">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>Last Logged In</th>
|
||||||
|
<td mat-cell *matCellDef="let u">
|
||||||
|
<span *ngIf="u.lastLoggedIn">
|
||||||
|
{{ u.lastLoggedIn | ombiDate: 'Ppp' }}
|
||||||
|
</span>
|
||||||
|
<span *ngIf="!u.lastLoggedIn"> Not logged in yet! </span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="alias">
|
<ng-container matColumnDef="userType">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Alias </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>User Type</th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.alias}} </td>
|
<td mat-cell *matCellDef="let u">
|
||||||
</ng-container>
|
<span *ngIf="u.userType === 1">Local User</span>
|
||||||
|
<span *ngIf="u.userType === 2">Plex User</span>
|
||||||
|
<span *ngIf="u.userType === 3">Emby User</span>
|
||||||
|
<span *ngIf="u.userType === 4">Emby Connect User</span>
|
||||||
|
<span *ngIf="u.userType === 5">Jellyfin User</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="email">
|
<ng-container matColumnDef="roles">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
|
<th mat-header-cell *matHeaderCellDef>Roles</th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.emailAddress}} </td>
|
<td mat-cell *matCellDef="let element">
|
||||||
</ng-container>
|
<div *ngFor="let claim of element.claims">
|
||||||
|
<span *ngIf="claim.enabled">{{ claim.value }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="remainingRequests">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> Requests Remaining </th>
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
<td mat-cell *matCellDef="let u">
|
<td mat-cell *matCellDef="let u">
|
||||||
<div *ngIf="u.movieRequestQuota != null && u.movieRequestQuota.hasLimit">
|
<a id="edit{{ u.userName }}" mat-raised-button color="accent" [routerLink]="['/usermanagement/user/' + u.id]">Edit</a>
|
||||||
{{'UserManagment.MovieRemaining' | translate: {remaining: u.movieRequestQuota.remaining, total: u.movieRequestLimit} }}
|
<button *ngIf="!u.hasLoggedIn" mat-raised-button color="accent" (click)="welcomeEmail(u)" [disabled]="!applicationUrl">
|
||||||
</div>
|
<i class="far fa-paper-plane"></i> Welcome
|
||||||
<div *ngIf="u.episodeRequestQuota != null && u.episodeRequestQuota.hasLimit">
|
</button>
|
||||||
{{'UserManagment.TvRemaining' | translate: {remaining: u.episodeRequestQuota.remaining, total: u.episodeRequestLimit} }}
|
</td>
|
||||||
</div>
|
</ng-container>
|
||||||
<div *ngIf="u.musicRequestQuota != null && u.musicRequestQuota.hasLimit">
|
|
||||||
{{'UserManagment.MusicRemaining' | translate: {remaining: u.musicRequestQuota.remaining, total: u.musicRequestLimit} }}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="nextRequestDue">
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
<th mat-header-cell *matHeaderCellDef> Next Request Due </th>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||||
<td mat-cell *matCellDef="let u">
|
</table>
|
||||||
<div *ngIf="u.movieRequestQuota != null && u.movieRequestQuota.remaining != u.movieRequestLimit">
|
|
||||||
{{'UserManagment.MovieDue' | translate: {date: (u.movieRequestQuota.nextRequest | amLocal | amUserLocale | amDateFormat: 'l')} }}
|
|
||||||
</div>
|
|
||||||
<div *ngIf="u.episodeRequestQuota != null && u.episodeRequestQuota.remaining != u.episodeRequestLimit">
|
|
||||||
{{'UserManagment.TvDue' | translate: {date: (u.episodeRequestQuota.nextRequest | amLocal | amUserLocale | amDateFormat: 'l')} }}
|
|
||||||
</div>
|
|
||||||
<div *ngIf="u.musicRequestQuota != null && u.musicRequestQuota.remaining != u.musicRequestLimit">
|
|
||||||
{{'UserManagment.MusicDue' | translate: {date: (u.musicRequestQuota.nextRequest | amLocal | amUserLocale | amDateFormat: 'l')} }}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container matColumnDef="lastLoggedIn">
|
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Last Logged In </th>
|
|
||||||
<td mat-cell *matCellDef="let u">
|
|
||||||
<span *ngIf="u.lastLoggedIn">
|
|
||||||
{{u.lastLoggedIn | amFromUtc | amLocal | amUserLocale | amDateFormat: 'l LT'}}
|
|
||||||
</span>
|
|
||||||
<span *ngIf="!u.lastLoggedIn">
|
|
||||||
Not logged in yet!
|
|
||||||
</span> </td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="userType">
|
<!-- Table -->
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> User Type </th>
|
|
||||||
<td mat-cell *matCellDef="let u">
|
|
||||||
<span *ngIf="u.userType === 1">Local User</span>
|
|
||||||
<span *ngIf="u.userType === 2">Plex User</span>
|
|
||||||
<span *ngIf="u.userType === 3">Emby User</span>
|
|
||||||
<span *ngIf="u.userType === 4">Emby Connect User</span>
|
|
||||||
<span *ngIf="u.userType === 5">Jellyfin User</span>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container matColumnDef="roles">
|
<p-sidebar [(visible)]="showBulkEdit" position="right" [modal]="false" [style]="{ width: '40em' }">
|
||||||
<th mat-header-cell *matHeaderCellDef> Roles </th>
|
<div>
|
||||||
<td mat-cell *matCellDef="let element">
|
<div *ngFor="let c of availableClaims">
|
||||||
<div *ngFor="let claim of element.claims">
|
<div class="form-group">
|
||||||
<span *ngIf="claim.enabled">{{claim.value}}</span>
|
<div class="checkbox">
|
||||||
</div>
|
<mat-slide-toggle id="create{{ c.value }}" [(ngModel)]="c.enabled" [attr.name]="'create' + c.value">
|
||||||
</td>
|
<small>{{ c.value | humanize }}</small></mat-slide-toggle
|
||||||
</ng-container>
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field appearance="outline" class="full">
|
||||||
|
<mat-label>Movie Request Limit</mat-label>
|
||||||
|
<input matInput id="movieRequestLimit" name="movieRequestLimit" [(ngModel)]="bulkMovieLimit" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-label>Movie Request Limit Type</mat-label>
|
||||||
|
<mat-select id="movieRequestLimitType" [(value)]="movieRequestLimitType">
|
||||||
|
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
|
||||||
|
{{ RequestLimitType[value] }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field appearance="outline" class="full">
|
||||||
|
<mat-label>Episode Request Limit</mat-label>
|
||||||
|
<input matInput id="episodeRequestLimit" name="episodeRequestLimit" [(ngModel)]="bulkEpisodeLimit" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-label>Episode Request Limit Type</mat-label>
|
||||||
|
<mat-select id="episodeRequestLimitType" [(value)]="episodeRequestLimitType">
|
||||||
|
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
|
||||||
|
{{ RequestLimitType[value] }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field appearance="outline" class="full">
|
||||||
|
<mat-label>Music Request Limit</mat-label>
|
||||||
|
<input matInput id="musicRequestLimit" name="musicRequestLimit" [(ngModel)]="bulkMusicLimit" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-label>Music Request Limit Type</mat-label>
|
||||||
|
<mat-select id="musicRequestLimitType" [(value)]="musicRequestLimitType">
|
||||||
|
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
|
||||||
|
{{ RequestLimitType[value] }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-form-field appearance="outline" class="full">
|
||||||
|
<mat-label [translate]="'UserPreferences.StreamingCountry'"></mat-label>
|
||||||
|
<mat-select [(value)]="bulkStreaming">
|
||||||
|
<mat-option *ngFor="let value of countries" [value]="value">
|
||||||
|
{{ value }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
<button type="button" mat-raised-button color="primary" (click)="bulkUpdate()">Update Users</button>
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
</p-sidebar>
|
||||||
<td mat-cell *matCellDef="let u">
|
</div>
|
||||||
<a id="edit{{u.userName}}" mat-raised-button color="accent" [routerLink]="['/usermanagement/user/' + u.id]">Edit</a>
|
|
||||||
<button *ngIf="!u.hasLoggedIn" mat-raised-button color="accent" (click)="welcomeEmail(u)" [disabled]="!applicationUrl"><i class="far fa-paper-plane"></i> Welcome</button>
|
|
||||||
</td>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- Table -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p-sidebar [(visible)]="showBulkEdit" position="right" [modal]="false" [style]="{width:'40em'}">
|
|
||||||
<div>
|
|
||||||
<div *ngFor="let c of availableClaims">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="checkbox">
|
|
||||||
<mat-slide-toggle id="create{{c.value}}" [(ngModel)]="c.enabled" [attr.name]="'create' + c.value">
|
|
||||||
<small>{{c.value | humanize}}</small></mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<mat-form-field appearance="outline" class="full">
|
|
||||||
<mat-label>Movie Request Limit</mat-label>
|
|
||||||
<input matInput id="movieRequestLimit" name="movieRequestLimit" [(ngModel)]="bulkMovieLimit">
|
|
||||||
</mat-form-field></div>
|
|
||||||
<div class="col-6">
|
|
||||||
<mat-label>Movie Request Limit Type</mat-label>
|
|
||||||
<mat-select id="movieRequestLimitType" [(value)]="movieRequestLimitType">
|
|
||||||
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
|
|
||||||
{{RequestLimitType[value]}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</div></div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<mat-form-field appearance="outline" class="full">
|
|
||||||
<mat-label>Episode Request Limit</mat-label>
|
|
||||||
<input matInput id="episodeRequestLimit" name="episodeRequestLimit" [(ngModel)]="bulkEpisodeLimit">
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<mat-label>Episode Request Limit Type</mat-label>
|
|
||||||
<mat-select id="episodeRequestLimitType" [(value)]="episodeRequestLimitType">
|
|
||||||
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
|
|
||||||
{{RequestLimitType[value]}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<mat-form-field appearance="outline" class="full">
|
|
||||||
<mat-label>Music Request Limit</mat-label>
|
|
||||||
<input matInput id="musicRequestLimit" name="musicRequestLimit" [(ngModel)]="bulkMusicLimit">
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<mat-label>Music Request Limit Type</mat-label>
|
|
||||||
<mat-select id="musicRequestLimitType" [(value)]="musicRequestLimitType">
|
|
||||||
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
|
|
||||||
{{RequestLimitType[value]}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<mat-form-field appearance="outline" class="full">
|
|
||||||
<mat-label [translate]="'UserPreferences.StreamingCountry'"></mat-label>
|
|
||||||
<mat-select [(value)]="bulkStreaming">
|
|
||||||
<mat-option *ngFor="let value of countries" [value]="value">
|
|
||||||
{{value}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
|
|
||||||
<button type="button" mat-raised-button color="primary" (click)="bulkUpdate()">Update Users</button>
|
|
||||||
</p-sidebar>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,53 +1,40 @@
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { IdentityService, PlexService, RadarrService, SonarrService } from "../services";
|
import { IdentityService, PlexService, RadarrService, SonarrService } from '../services';
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from '../auth/auth.guard';
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from '@angular/common';
|
||||||
import { ConfirmDialogModule } from "primeng/confirmdialog";
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||||
import { MultiSelectModule } from "primeng/multiselect";
|
import { MultiSelectModule } from 'primeng/multiselect';
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from '@angular/core';
|
||||||
import { OrderModule } from "ngx-order-pipe";
|
import { PipeModule } from '../pipes/pipe.module';
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { SharedModule } from "../shared/shared.module";
|
import { SidebarModule } from 'primeng/sidebar';
|
||||||
import { SidebarModule } from "primeng/sidebar";
|
import { TooltipModule } from 'primeng/tooltip';
|
||||||
import { TooltipModule } from "primeng/tooltip";
|
import { UserManagementComponent } from './usermanagement.component';
|
||||||
import { UserManagementComponent } from "./usermanagement.component";
|
import { UserManagementUserComponent } from './usermanagement-user.component';
|
||||||
import { UserManagementUserComponent } from "./usermanagement-user.component";
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", component: UserManagementComponent, canActivate: [AuthGuard] },
|
{ path: '', component: UserManagementComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "user", component: UserManagementUserComponent, canActivate: [AuthGuard] },
|
{ path: 'user', component: UserManagementUserComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "user/:id", component: UserManagementUserComponent, canActivate: [AuthGuard] },
|
{ path: 'user/:id', component: UserManagementUserComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
MultiSelectModule,
|
MultiSelectModule,
|
||||||
PipeModule,
|
PipeModule,
|
||||||
ConfirmDialogModule,
|
ConfirmDialogModule,
|
||||||
TooltipModule,
|
TooltipModule,
|
||||||
OrderModule,
|
SidebarModule,
|
||||||
SidebarModule,
|
SharedModule,
|
||||||
SharedModule,
|
],
|
||||||
],
|
declarations: [UserManagementComponent, UserManagementUserComponent],
|
||||||
declarations: [
|
exports: [RouterModule],
|
||||||
UserManagementComponent,
|
providers: [IdentityService, PlexService, RadarrService, SonarrService],
|
||||||
UserManagementUserComponent,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
RouterModule,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
IdentityService,
|
|
||||||
PlexService,
|
|
||||||
RadarrService,
|
|
||||||
SonarrService,
|
|
||||||
],
|
|
||||||
|
|
||||||
})
|
})
|
||||||
export class UserManagementModule { }
|
export class UserManagementModule {}
|
||||||
|
|
|
@ -1,39 +1,23 @@
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { OrderModule } from "ngx-order-pipe";
|
import { OverlayPanelModule } from 'primeng/overlaypanel';
|
||||||
import { OverlayPanelModule } from "primeng/overlaypanel";
|
import { TabViewModule } from 'primeng/tabview';
|
||||||
import { TabViewModule } from "primeng/tabview";
|
|
||||||
|
|
||||||
import { VoteService } from "../services";
|
import { VoteService } from '../services';
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from '../auth/auth.guard';
|
||||||
|
|
||||||
import { SharedModule as OmbiShared } from "../shared/shared.module";
|
import { SharedModule as OmbiShared } from '../shared/shared.module';
|
||||||
|
|
||||||
import { VoteComponent } from "./vote.component";
|
import { VoteComponent } from './vote.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [{ path: '', component: VoteComponent, canActivate: [AuthGuard] }];
|
||||||
{ path: "", component: VoteComponent, canActivate: [AuthGuard] },
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [RouterModule.forChild(routes), OmbiShared, TabViewModule, OverlayPanelModule],
|
||||||
RouterModule.forChild(routes),
|
declarations: [VoteComponent],
|
||||||
OrderModule,
|
exports: [RouterModule],
|
||||||
OmbiShared,
|
providers: [VoteService],
|
||||||
TabViewModule,
|
|
||||||
OverlayPanelModule,
|
|
||||||
],
|
|
||||||
declarations: [
|
|
||||||
VoteComponent,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
RouterModule,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
VoteService,
|
|
||||||
],
|
|
||||||
|
|
||||||
})
|
})
|
||||||
export class VoteModule { }
|
export class VoteModule {}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
|
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
|
||||||
*/
|
*/
|
||||||
import "core-js/es7/reflect";
|
import 'core-js/es7/reflect';
|
||||||
import "zone.js/dist/zone";
|
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/@types"
|
"node_modules/@types",
|
||||||
|
"./node_modules/@types",
|
||||||
|
"../node_modules/@types",
|
||||||
|
"../../node_modules/@types"
|
||||||
],
|
],
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2017",
|
"es2017",
|
||||||
|
|
|
@ -14,16 +14,21 @@
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"moduleResolution": "node",
|
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "ES2022",
|
"module": "ES2022",
|
||||||
"lib": [
|
"lib": [
|
||||||
"ES2022",
|
"ES2022",
|
||||||
"dom"
|
"dom",
|
||||||
|
"ESNext.Intl",
|
||||||
|
"ES2022.Intl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"types": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"typeRoots": [ "./node_modules/@types", "../node_modules/@types", "../../node_modules/@types" ],
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"enableI18nLegacyMessageIdFormat": false,
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
"strictInjectionParameters": true,
|
"strictInjectionParameters": true,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Ombi.Api.Radarr;
|
using Ombi.Api.Radarr;
|
||||||
using Ombi.Api.Radarr.Models;
|
using Ombi.Api.Radarr.Models;
|
||||||
|
using Ombi.Api.Radarr.Models.V3;
|
||||||
using Ombi.Attributes;
|
using Ombi.Attributes;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
@ -77,7 +78,7 @@ namespace Ombi.Controllers.V1.External
|
||||||
{
|
{
|
||||||
return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri));
|
return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri));
|
||||||
}
|
}
|
||||||
return null;
|
return Ok(new List<RadarrV3QualityProfile>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -94,7 +95,7 @@ namespace Ombi.Controllers.V1.External
|
||||||
{
|
{
|
||||||
return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri));
|
return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri));
|
||||||
}
|
}
|
||||||
return null;
|
return Ok(new List<RadarrV3QualityProfile>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
<PackageReference Include="AutoMapper" Version="12.0.0" />
|
<PackageReference Include="AutoMapper" Version="12.0.0" />
|
||||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
|
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.9" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.26" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
|
25
src/Ombi/Ombi.sln
Normal file
25
src/Ombi/Ombi.sln
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.002.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi.csproj", "{41251D1C-AAFD-4B5B-9510-BB50B9C223EA}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{41251D1C-AAFD-4B5B-9510-BB50B9C223EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{41251D1C-AAFD-4B5B-9510-BB50B9C223EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{41251D1C-AAFD-4B5B-9510-BB50B9C223EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{41251D1C-AAFD-4B5B-9510-BB50B9C223EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {291EBBD3-9266-41E3-9E52-A50070394075}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -22,13 +22,13 @@
|
||||||
"Monitored": "Monitorado",
|
"Monitored": "Monitorado",
|
||||||
"NotAvailable": "Não Disponível",
|
"NotAvailable": "Não Disponível",
|
||||||
"ProcessingRequest": "Processando Solicitação",
|
"ProcessingRequest": "Processando Solicitação",
|
||||||
"ProcessingRequest4K": "Processing Request 4K",
|
"ProcessingRequest4K": "Processando Pedido 4K",
|
||||||
"PendingApproval": "Aprovação Pendente",
|
"PendingApproval": "Aprovação Pendente",
|
||||||
"PendingApproval4K": "Pending Approval 4K",
|
"PendingApproval4K": "Aprovação 4K pendente",
|
||||||
"RequestDenied": "Solicitação Negada",
|
"RequestDenied": "Solicitação Negada",
|
||||||
"RequestDenied4K": "Request Denied 4K",
|
"RequestDenied4K": "Solicitação 4K Negada",
|
||||||
"NotRequested": "Não Solicitado",
|
"NotRequested": "Não Solicitado",
|
||||||
"NotRequested4K": "Not Requested 4K",
|
"NotRequested4K": "Não Solicitado 4K",
|
||||||
"Requested": "Solicitado",
|
"Requested": "Solicitado",
|
||||||
"Requested4K": "4K requisitado",
|
"Requested4K": "4K requisitado",
|
||||||
"Search": "Buscar",
|
"Search": "Buscar",
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
"RequestedBy": "Solicitado por",
|
"RequestedBy": "Solicitado por",
|
||||||
"Status": "Status",
|
"Status": "Status",
|
||||||
"RequestStatus": "Status da solicitação",
|
"RequestStatus": "Status da solicitação",
|
||||||
"Watched": "Watched",
|
"Watched": "Assistido",
|
||||||
"WatchedTooltip": "The user who made the request has watched it",
|
"WatchedTooltip": "The user who made the request has watched it",
|
||||||
"WatchedProgressTooltip": "Shows how much the user who made the request has watched it",
|
"WatchedProgressTooltip": "Shows how much the user who made the request has watched it",
|
||||||
"WatchedByUsersCount": "{{count}} users have watched this.",
|
"WatchedByUsersCount": "{{count}} users have watched this.",
|
||||||
|
|
|
@ -1,163 +1,162 @@
|
||||||
import { movieDetailsPage as Page } from "@/integration/page-objects";
|
import { movieDetailsPage as Page } from '@/integration/page-objects';
|
||||||
|
|
||||||
describe("Movie Details Buttons", () => {
|
describe('Movie Details Buttons', () => {
|
||||||
it("Movie Requested by Admin should be auto approved", () => {
|
it('Movie Requested by Admin should be auto approved', () => {
|
||||||
cy.login();
|
cy.login();
|
||||||
|
|
||||||
Page.visit("587807");
|
Page.visit('587807');
|
||||||
Page.requestButton.click();
|
Page.requestButton.click();
|
||||||
Page.adminOptionsDialog.isOpen();
|
Page.adminOptionsDialog.isOpen();
|
||||||
|
|
||||||
Page.adminOptionsDialog.requestButton.click();
|
Page.adminOptionsDialog.requestButton.click();
|
||||||
|
|
||||||
cy.verifyNotification("Request for Tom & Jerry has been added successfully");
|
cy.verifyNotification('Request for Tom & Jerry has been added successfully');
|
||||||
|
|
||||||
Page.requestedButton.should("be.visible");
|
Page.requestedButton.should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Movie Requested by Regular user should be pending", () => {
|
it('Movie Requested by Regular user should be pending', () => {
|
||||||
cy.generateUniqueId().then((id) => {
|
cy.generateUniqueId().then((id) => {
|
||||||
cy.login();
|
cy.login();
|
||||||
const roles = [];
|
const roles = [];
|
||||||
roles.push({ value: "RequestMovie", enabled: true });
|
roles.push({ value: 'RequestMovie', enabled: true });
|
||||||
cy.createUser(id, "a", roles).then(() => {
|
cy.createUser(id, 'a', roles).then(() => {
|
||||||
cy.loginWithCreds(id, "a");
|
cy.loginWithCreds(id, 'a');
|
||||||
|
|
||||||
Page.visit("651571");
|
Page.visit('651571');
|
||||||
|
|
||||||
Page.requestButton.click();
|
Page.requestButton.click();
|
||||||
cy.verifyNotification("Request for Breach has been added successfully");
|
cy.verifyNotification('Request for Breach has been added successfully');
|
||||||
|
|
||||||
Page.requestedButton.should("be.visible");
|
Page.requestedButton.should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Movie Requested by Regular with no movie permission", () => {
|
it('Movie Requested by Regular with no movie permission', () => {
|
||||||
cy.generateUniqueId().then((id) => {
|
cy.generateUniqueId().then((id) => {
|
||||||
cy.login();
|
cy.login();
|
||||||
const roles = [];
|
const roles = [];
|
||||||
roles.push({ value: "RequestTv", enabled: true });
|
roles.push({ value: 'RequestTv', enabled: true });
|
||||||
cy.createUser(id, "a", roles).then(() => {
|
cy.createUser(id, 'a', roles).then(() => {
|
||||||
cy.loginWithCreds(id, "a");
|
cy.loginWithCreds(id, 'a');
|
||||||
|
|
||||||
Page.visit("791373");
|
Page.visit('791373');
|
||||||
|
|
||||||
Page.requestButton.click();
|
Page.requestButton.click();
|
||||||
cy.verifyNotification("You do not have permissions to Request a Movie");
|
cy.verifyNotification('You do not have permissions to Request a Movie');
|
||||||
|
|
||||||
Page.requestedButton.should("not.exist");
|
Page.requestedButton.should('not.exist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Movie Requested by Regular can be approved by admin", () => {
|
it('Movie Requested by Regular can be approved by admin', () => {
|
||||||
cy.generateUniqueId().then((id) => {
|
cy.generateUniqueId().then((id) => {
|
||||||
cy.login();
|
cy.login();
|
||||||
const roles = [];
|
const roles = [];
|
||||||
roles.push({ value: "RequestMovie", enabled: true });
|
roles.push({ value: 'RequestMovie', enabled: true });
|
||||||
cy.createUser(id, "a", roles).then(() => {
|
cy.createUser(id, 'a', roles).then(() => {
|
||||||
cy.loginWithCreds(id, "a");
|
cy.loginWithCreds(id, 'a');
|
||||||
|
|
||||||
Page.visit("793723");
|
Page.visit('793723');
|
||||||
|
|
||||||
Page.requestButton.click();
|
Page.requestButton.click();
|
||||||
cy.verifyNotification("Request for Sentinelle has been added successfully");
|
cy.verifyNotification('Request for Sentinelle has been added successfully');
|
||||||
|
|
||||||
Page.requestedButton.should("be.visible");
|
Page.requestedButton.should('be.visible');
|
||||||
|
|
||||||
// Login as admin now
|
// Login as admin now
|
||||||
cy.removeLogin();
|
cy.removeLogin();
|
||||||
cy.login();
|
cy.login();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
|
|
||||||
Page.visit("793723");
|
cy.intercept('GET', '**/Request/movie/info/**').as('requestCall');
|
||||||
|
|
||||||
Page.approveButton.should("exist");
|
Page.visit('793723');
|
||||||
Page.approveButton.click();
|
|
||||||
|
|
||||||
cy.verifyNotification("Successfully Approved");
|
cy.wait('@requestCall').then((__) => {
|
||||||
});
|
Page.approveButton.should('exist');
|
||||||
});
|
Page.approveButton.click();
|
||||||
});
|
|
||||||
|
|
||||||
it("Movie Requested, mark as available", () => {
|
cy.verifyNotification('Successfully Approved');
|
||||||
cy.login();
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Page.visit("12444");
|
it('Movie Requested, mark as available', () => {
|
||||||
|
cy.login();
|
||||||
|
|
||||||
Page.requestButton.click();
|
Page.visit('12444');
|
||||||
Page.adminOptionsDialog.isOpen();
|
|
||||||
Page.adminOptionsDialog.requestButton.click();
|
|
||||||
cy.verifyNotification(
|
|
||||||
"Request for Harry Potter and the Deathly Hallows: Part 1 has been added successfully"
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.reload();
|
Page.requestButton.click();
|
||||||
|
Page.adminOptionsDialog.isOpen();
|
||||||
|
Page.adminOptionsDialog.requestButton.click();
|
||||||
|
cy.verifyNotification('Request for Harry Potter and the Deathly Hallows: Part 1 has been added successfully');
|
||||||
|
|
||||||
Page.markAvailableButton.should("exist");
|
cy.intercept('GET', '**/Images/banner/movie/**').as('bannerLoad');
|
||||||
Page.markAvailableButton.click();
|
cy.reload();
|
||||||
|
|
||||||
cy.waitUntil(() => {
|
cy.wait('@bannerLoad').then((__) => {
|
||||||
return Page.availableButton.should("be.visible");
|
Page.markAvailableButton.should('exist');
|
||||||
})
|
Page.markAvailableButton.click();
|
||||||
|
|
||||||
cy.verifyNotification("Request is now available");
|
cy.verifyNotification('Request is now available');
|
||||||
Page.availableButton.should("exist");
|
Page.availableButton.should('exist');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it.skip("Movie Requested, Deny Movie", () => {
|
it.skip('Movie Requested, Deny Movie', () => {
|
||||||
cy.login();
|
cy.login();
|
||||||
|
|
||||||
Page.visit("671");
|
Page.visit('671');
|
||||||
|
|
||||||
Page.requestButton.click();
|
Page.requestButton.click();
|
||||||
Page.adminOptionsDialog.isOpen();
|
Page.adminOptionsDialog.isOpen();
|
||||||
Page.adminOptionsDialog.requestButton.click();
|
Page.adminOptionsDialog.requestButton.click();
|
||||||
cy.verifyNotification(
|
cy.verifyNotification("Request for Harry Potter and the Philosopher's Stone has been added successfully");
|
||||||
"Request for Harry Potter and the Philosopher's Stone has been added successfully"
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
|
|
||||||
Page.denyButton.should("exist");
|
Page.denyButton.should('exist');
|
||||||
Page.denyButton.click();
|
Page.denyButton.click();
|
||||||
|
|
||||||
Page.denyModal.denyReason.type("Automation Tests");
|
Page.denyModal.denyReason.type('Automation Tests');
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
Page.denyModal.denyButton.click();
|
Page.denyModal.denyButton.click();
|
||||||
|
|
||||||
Page.deniedButton.should('exist');
|
Page.deniedButton.should('exist');
|
||||||
|
|
||||||
cy.verifyNotification("Denied Request");
|
cy.verifyNotification('Denied Request');
|
||||||
|
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
Page.informationPanel.denyReason.should('have.text', "Automation Tests");
|
Page.informationPanel.denyReason.should('have.text', 'Automation Tests');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Movie View Collection should be available", () => {
|
it('Movie View Collection should be available', () => {
|
||||||
cy.login();
|
cy.login();
|
||||||
|
|
||||||
Page.visit("671");
|
Page.visit('671');
|
||||||
|
|
||||||
Page.viewCollectionButton.should('be.visible');
|
Page.viewCollectionButton.should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Non requested movie valid buttons", () => {
|
it('Non requested movie valid buttons', () => {
|
||||||
cy.login();
|
cy.login();
|
||||||
|
|
||||||
Page.visit("590706");
|
Page.visit('590706');
|
||||||
|
|
||||||
Page.viewCollectionButton.should('not.exist');
|
Page.viewCollectionButton.should('not.exist');
|
||||||
Page.approveButton.should('not.exist');
|
Page.approveButton.should('not.exist');
|
||||||
Page.denyButton.should('not.exist');
|
Page.denyButton.should('not.exist');
|
||||||
Page.deniedButton.should('not.exist');
|
Page.deniedButton.should('not.exist');
|
||||||
Page.markAvailableButton.should('not.exist');
|
Page.markAvailableButton.should('not.exist');
|
||||||
Page.viewOnEmbyButton.should('not.exist');
|
Page.viewOnEmbyButton.should('not.exist');
|
||||||
Page.viewOnJellyfinButton.should('not.exist');
|
Page.viewOnJellyfinButton.should('not.exist');
|
||||||
Page.viewOnPlexButton.should('not.exist');
|
Page.viewOnPlexButton.should('not.exist');
|
||||||
Page.requestedButton.should('not.exist');
|
Page.requestedButton.should('not.exist');
|
||||||
Page.reportIssueButton.should('not.exist'); // Issuess not enabled
|
Page.reportIssueButton.should('not.exist'); // Issuess not enabled
|
||||||
Page.requestButton.should('exist');
|
Page.requestButton.should('exist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"version": "4.43.10"
|
"version": "4.43.14"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue