* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(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)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 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)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#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)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

* fix(sonarr): 🐛 Improved the error handling in the sonarr settings page in the UI

This should hopefully prevent some odd situations where the settings are in a odd state #4877

* chore: update deps

* chore: more deps

* bump

* chore(release): 🚀 v4.35.12 [skip ci]

* fix(sonarr): 🐛 Added some more error handling and information around testing sonarr

#4877

* chore(release): 🚀 v4.35.13 [skip ci]

* fix: Some minor tweaks to the movie info panel (#4883)

* fix: Hide denied reason label if there is no value

* fix: Movie would show as pending approval when denied

* chore(release): 🚀 v4.35.14 [skip ci]

* fix(sonarr): 🐛 Stop the sonarr version endpoint from breaking when Sonarr is down #4895

* chore(release): 🚀 v4.35.15 [skip ci]

* fix: Support duplicates in Emby/JF collections (#4902)

Support same movie that belongs in different collections
in Emby or Jellyfin

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.16 [skip ci]

* fix(discover): Fix denied requests displayed as approved (#4901)

* fix: Fix denied movie shown as 'processing request' in details view (#4900)

* chore(release): 🚀 v4.35.17 [skip ci]

* fix(#4906): 🐛 Fixed an issue with power users and permissions

* chore(release): 🚀 v4.35.18 [skip ci]

* fix(radarr): Fixed an issue where the radarr sync would break

* chore(release): 🚀 v4.35.19 [skip ci]

* feat(discover): Add deny option to recently requested (#4907)

* chore(release): 🚀 v4.36.0 [skip ci]

* fix(healthchecks): Removed redundant ping check

* chore(release): 🚀 v4.36.1 [skip ci]

* feat: Search by genre

[skip-ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.0 [skip ci]

* fix(discover): 🚸 Improved the new Genre buttons, it now includes TV results

* chore: 👥 Updated Contributors [skip ci]

* fix: Cron Validation (#4842)

* Add Cron Next Time Validation

The cron job can't be created if the year is more than 100 years in the future.
Getting the next valid time will return null if this is the case.

* add next cron validation to api
* add next cron validation to job settings page

* Add Missing Import

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4806)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(lidarr): Change monitor to Existing to properly add artist #3597

Discussed and tested manually in https://github.com/Lidarr/Lidarr/issues/3597#issuecomment-1530804055

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.1 [skip ci]

* fix(jellyfin): Fixed an issue where the sync could stop working. Removed unused properties so the deseralization no longer fails

* chore(release): 🚀 v4.37.2 [skip ci]

* fix: Show the ApiAlias in the requests-list

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.3 [skip ci]

* feat(emby): Show watched status for Movie requests

* First step towards played sync

* Change TMDB id format to integer

This will better integrate with TMDB id type in the request model

* Display played state in the requests list

* Fix played status filter

* Run played sync job after content sync instead of on its own

* Add a toggle to activate played sync

* Hoovering

* FIx played sync job not being triggered

* Expose played state according to hide requests setting

* Fix tests

* Fix tests for real

* Add MySql migrations

[skip ci]

* fix: remove sort header

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.0 [skip ci]

* fix(notificaitons): Add the RequestedByAlias field to the Notification Message

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4921)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(API): Allow RequestOnBehalf rights if requested from the API (#4919)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.1 [skip ci]

* Merge pull request from GHSA-28j3-84m7-gpjp

* chore(release): 🚀 v4.38.2 [skip ci]

* Develop master (#4930)

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(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)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 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)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#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)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* Develop master (#4931) [skip ci]

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(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)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 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)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#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)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* fix(emby): Fix Emby played sync running a full sync during recently added sync (#4932)

* feat: Hide watched status when request is not available (#4934)

* chore(release): 🚀 v4.39.0 [skip ci]

* ignore flaky test

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>
Co-authored-by: ryan-c44 <54028283+ryan-c44@users.noreply.github.com>
Co-authored-by: Alexander Russell <ajex94@gmail.com>
Co-authored-by: Grygon <647846+Grygon@users.noreply.github.com>
Co-authored-by: phildups7 <60622768+phildups7@users.noreply.github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
pull/4938/head^2
Jamie 12 months ago committed by GitHub
parent 12a90f1eba
commit e297f863a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,345 +1,375 @@
## [4.35.11](https://github.com/Ombi-app/Ombi/compare/v4.35.10...v4.35.11) (2023-05-17)
# [4.39.0](https://github.com/Ombi-app/Ombi/compare/v4.38.2...v4.39.0) (2023-05-17)
### Bug Fixes
## [4.35.10](https://github.com/Ombi-app/Ombi/compare/v4.22.5...v4.35.10) (2023-02-25)
* **emby:** Fix Emby played sync running a full sync during recently added sync ([#4932](https://github.com/Ombi-app/Ombi/issues/4932)) ([9424586](https://github.com/Ombi-app/Ombi/commit/9424586e9c1b622b6475aeb8ee3cf4a8f346da6e))
### Features
## [4.22.5](https://github.com/Ombi-app/Ombi/compare/v4.16.12...v4.22.5) (2022-08-05)
* Hide watched status when request is not available ([#4934](https://github.com/Ombi-app/Ombi/issues/4934)) ([82c7f1c](https://github.com/Ombi-app/Ombi/commit/82c7f1c44fd7c87d57cc2b0c34a10fcda7628f4e))
## [4.16.12](https://github.com/Ombi-app/Ombi/compare/v4.16.11...v4.16.12) (2022-04-19)
## [4.38.2](https://github.com/Ombi-app/Ombi/compare/v4.38.1...v4.38.2) (2023-05-17)
## [4.16.11](https://github.com/Ombi-app/Ombi/compare/v4.16.10...v4.16.11) (2022-04-14)
## [4.38.1](https://github.com/Ombi-app/Ombi/compare/v4.38.0...v4.38.1) (2023-05-09)
### Bug Fixes
* Set the default job for the watchlist import to hourly instead of daily ([75906af](https://github.com/Ombi-app/Ombi/commit/75906af0adee3e3c68d825c3aaa8f7b918461b1f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([0e8a64b](https://github.com/Ombi-app/Ombi/commit/0e8a64b8ca00d210fbe843ac2c3f6af218d80cbc))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([7b0ad61](https://github.com/Ombi-app/Ombi/commit/7b0ad61bfcff3986b33180dc64022cba7ea8eefb))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([4fc2c1f](https://github.com/Ombi-app/Ombi/commit/4fc2c1f24534085a783a3d5791f5533b68272153))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([76ab733](https://github.com/Ombi-app/Ombi/commit/76ab733b91791e4d93d184f3c7d0779c6a388695))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([06e4cef](https://github.com/Ombi-app/Ombi/commit/06e4cefa7b4e55b860da9a64f461f6ec8fa17367))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([c12d89d](https://github.com/Ombi-app/Ombi/commit/c12d89d6781a337520977ad285f8d08c93f434dd))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([bc0c2f6](https://github.com/Ombi-app/Ombi/commit/bc0c2f622e34fb5a2711039d9ed7aad34f982b15))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([e4b00e6](https://github.com/Ombi-app/Ombi/commit/e4b00e6b3468bd9389eeb02fc6ad7daf27abc3b3))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d1998d3](https://github.com/Ombi-app/Ombi/commit/d1998d326f999a38586d0a351a20c5448df95842))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([bee4ccb](https://github.com/Ombi-app/Ombi/commit/bee4ccb804594e7385b1fbdc9fe2ef5c42e0d21f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([80233ed](https://github.com/Ombi-app/Ombi/commit/80233ed560cc976e83570d0655c3472f20171fb3))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([8a78adc](https://github.com/Ombi-app/Ombi/commit/8a78adc9bb62f277f2b213dcb3847ed6d0089fcb))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d04c60a](https://github.com/Ombi-app/Ombi/commit/d04c60aa5909b47ba6bffa6f66b03079cbd43521))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([92a785e](https://github.com/Ombi-app/Ombi/commit/92a785e736fa4b72a45270da2d0f4661df433078))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([634982d](https://github.com/Ombi-app/Ombi/commit/634982df2661cefab5ea9f5163fe04a005cc0171))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([b404baa](https://github.com/Ombi-app/Ombi/commit/b404baad6d0aeaa1561701e0db8db4e78613a364))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d14f11e](https://github.com/Ombi-app/Ombi/commit/d14f11e0eb20ab0a68e765ee77968b3b3e54e995))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([7cf64f9](https://github.com/Ombi-app/Ombi/commit/7cf64f909d78908edaabeffb8a39a7d02e73fe7e))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([0c9e1ec](https://github.com/Ombi-app/Ombi/commit/0c9e1ec090827080cc8f7393e5e91456ff37d691))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([3b0b730](https://github.com/Ombi-app/Ombi/commit/3b0b730cb02efe24f6d4026e5fdb20d37e495119))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([6ed1a03](https://github.com/Ombi-app/Ombi/commit/6ed1a03b7ff4077f09ea9e13394b18b0d138f4c3))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([2941acd](https://github.com/Ombi-app/Ombi/commit/2941acd3b2ec74a5e6aeea275ab5a39d2653f37f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([c075a1a](https://github.com/Ombi-app/Ombi/commit/c075a1a66784d975eaf60f2dfbbcbe048f2f63d7))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([76bd81c](https://github.com/Ombi-app/Ombi/commit/76bd81c3ca55a98c6ec944a838dc01294a6193a6))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([0d38275](https://github.com/Ombi-app/Ombi/commit/0d3827507e002bcf58f673e97ffcc3bd25dcf337))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([5c99601](https://github.com/Ombi-app/Ombi/commit/5c99601b07aec1a65d0186a4c4327440811e64c6))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([01546a0](https://github.com/Ombi-app/Ombi/commit/01546a0f7f86379528b486463246ef9bdfb9033e))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d7fea78](https://github.com/Ombi-app/Ombi/commit/d7fea7843aaaab7ddff8dc31ca6d2a9117471dcc))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([1a6b95d](https://github.com/Ombi-app/Ombi/commit/1a6b95d45c220310213b8d811272a63f0f6ff42b))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([fa10174](https://github.com/Ombi-app/Ombi/commit/fa1017422c4efd4b0897871bd3c671151774d7c3))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([0c31e62](https://github.com/Ombi-app/Ombi/commit/0c31e628df376aac6d56ae67c7c705a9a4a7c080))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([6399643](https://github.com/Ombi-app/Ombi/commit/63996437a02fe10ffae6822ffa15369bec0a6b36))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([5826e2d](https://github.com/Ombi-app/Ombi/commit/5826e2d9a1c3f1210a87fa270dc0c81bac32944a))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d434514](https://github.com/Ombi-app/Ombi/commit/d43451405be489254d7cdc7755d5f516a1e495a5))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([0b9596d](https://github.com/Ombi-app/Ombi/commit/0b9596d807178f5e071113ec0347868ec7f0960b))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([8c4c0b2](https://github.com/Ombi-app/Ombi/commit/8c4c0b262978c1303767af360d802c4b4c2b4d24))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([289ab77](https://github.com/Ombi-app/Ombi/commit/289ab77b0e04aae235b6f6cebc86e0a8d1f0cf2b))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([30e3417](https://github.com/Ombi-app/Ombi/commit/30e3417285a4eed18d429d7776f0e74096e834c0))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([6c0a5da](https://github.com/Ombi-app/Ombi/commit/6c0a5dadd4b8f37760252eb0fe7f88908f55506d))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d5bf969](https://github.com/Ombi-app/Ombi/commit/d5bf9692ce1fc0ccfe7beca6dd200c78be177bdc))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([8a9e7ea](https://github.com/Ombi-app/Ombi/commit/8a9e7ea588aefbcd73ed82625887e3614e1703ea))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([01047a3](https://github.com/Ombi-app/Ombi/commit/01047a3fd67153f3ff16f860d2c7b50213e8d9b2))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([698a23f](https://github.com/Ombi-app/Ombi/commit/698a23fb83f323cdd1dd57cb49803079d44214a7))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([24eb842](https://github.com/Ombi-app/Ombi/commit/24eb842fc4424f7bcc3ec2949d7f5472492e96f6))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([ac8b16a](https://github.com/Ombi-app/Ombi/commit/ac8b16a3051ad71dbd54a8973c7dd847b564a515))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([f428ce6](https://github.com/Ombi-app/Ombi/commit/f428ce6a700c081437703839bc84d2f2b1138bcc))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([94b16df](https://github.com/Ombi-app/Ombi/commit/94b16dfe09bf1d2cd6286777d74eb5d4496abbbb))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([4881775](https://github.com/Ombi-app/Ombi/commit/4881775eda69a8f136ce0d8fbbf970e3d0406dc9))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([8297db9](https://github.com/Ombi-app/Ombi/commit/8297db91e85da308bde6fb09ad78347dee063630))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([d1152ab](https://github.com/Ombi-app/Ombi/commit/d1152ab7674243daa528c524c0cdc87d81ad49c9))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([eb2788b](https://github.com/Ombi-app/Ombi/commit/eb2788b761b55c487a59a049427ca08f6c10e836))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([21a794c](https://github.com/Ombi-app/Ombi/commit/21a794cbc0a5fa735ca0347c8f7f1ac04a487fbc))
## [4.10.2](https://github.com/Ombi-app/Ombi/compare/v4.10.1...v4.10.2) (2022-01-22)
## [4.16.10](https://github.com/Ombi-app/Ombi/compare/v4.16.9...v4.16.10) (2022-04-13)
## [4.16.9](https://github.com/Ombi-app/Ombi/compare/v4.16.8...v4.16.9) (2022-04-13)
* **API:** Allow RequestOnBehalf rights if requested from the API ([#4919](https://github.com/Ombi-app/Ombi/issues/4919)) ([bb6dedd](https://github.com/Ombi-app/Ombi/commit/bb6deddfaecb3d6c7c3c6970414444b619bb9106))
* **notificaitons:** Add the RequestedByAlias field to the Notification Message ([7e9c8be](https://github.com/Ombi-app/Ombi/commit/7e9c8bec6b02bb4e11f8db50394e493d4dd07723))
# [4.38.0](https://github.com/Ombi-app/Ombi/compare/v4.37.3...v4.38.0) (2023-05-07)
### Bug Fixes
* **plex-watchlist:** Only request the latest season when importing from the watchlist ([77a47ff](https://github.com/Ombi-app/Ombi/commit/77a47ff157c6c5feafe3f2a29a3fcba8df4fdfef))
* remove sort header ([969bc7b](https://github.com/Ombi-app/Ombi/commit/969bc7bb25ea900ab9199509b079b36843e5bd6f))
### Features
## [4.16.8](https://github.com/Ombi-app/Ombi/compare/v4.16.7...v4.16.8) (2022-04-13)
* **emby:** Show watched status for Movie requests ([9cfb10b](https://github.com/Ombi-app/Ombi/commit/9cfb10bb1ee69067a6f47bd2c8a72d4e6834350e))
## [4.37.3](https://github.com/Ombi-app/Ombi/compare/v4.37.2...v4.37.3) (2023-05-07)
### Bug Fixes
* **availability:** Fixed an issue where we wouldn't mark a available 4k movie as available (when 4K request feature is disabled) ([b492699](https://github.com/Ombi-app/Ombi/commit/b49269961d4830a530e3054976a47f519524948b))
* Show the ApiAlias in the requests-list ([9ff624c](https://github.com/Ombi-app/Ombi/commit/9ff624ce4646815b239fbb8327117947f0a90e4b))
## [4.16.7](https://github.com/Ombi-app/Ombi/compare/v4.16.6...v4.16.7) (2022-04-12)
## [4.37.2](https://github.com/Ombi-app/Ombi/compare/v4.37.1...v4.37.2) (2023-05-03)
### Bug Fixes
## [4.16.6](https://github.com/Ombi-app/Ombi/compare/v4.16.5...v4.16.6) (2022-04-11)
* **jellyfin:** Fixed an issue where the sync could stop working. Removed unused properties so the deseralization no longer fails ([0e5e0ad](https://github.com/Ombi-app/Ombi/commit/0e5e0adf862701d0f672beff14ec0aa75e4b5220))
## [4.16.5](https://github.com/Ombi-app/Ombi/compare/v4.16.4...v4.16.5) (2022-04-08)
## [4.37.1](https://github.com/Ombi-app/Ombi/compare/v4.37.0...v4.37.1) (2023-05-02)
### Bug Fixes
* **watchlist:** actually fixed it this time... ([d962a32](https://github.com/Ombi-app/Ombi/commit/d962a3211eca29520662ddce962676e3aea17ec5))
* Cron Validation ([#4842](https://github.com/Ombi-app/Ombi/issues/4842)) ([97cc42f](https://github.com/Ombi-app/Ombi/commit/97cc42ffa8672e7d0d0996b5fbda7f7fe699da2d))
* **discover:** :children_crossing: Improved the new Genre buttons, it now includes TV results ([b087d60](https://github.com/Ombi-app/Ombi/commit/b087d606ff36565208e564f8856903f2a4098db5))
* **lidarr:** Change monitor to Existing to properly add artist [#3597](https://github.com/Ombi-app/Ombi/issues/3597) ([506f607](https://github.com/Ombi-app/Ombi/commit/506f60773bf1031d0be51ccd34289b855a04ea40)), closes [/github.com/Lidarr/Lidarr/issues/3597#issuecomment-1530804055](https://github.com//github.com/Lidarr/Lidarr/issues/3597/issues/issuecomment-1530804055)
## [4.16.4](https://github.com/Ombi-app/Ombi/compare/v4.16.3...v4.16.4) (2022-04-08)
# [4.37.0](https://github.com/Ombi-app/Ombi/compare/v4.36.1...v4.37.0) (2023-04-24)
### Features
## [4.16.3](https://github.com/Ombi-app/Ombi/compare/v4.16.2...v4.16.3) (2022-04-08)
* Search by genre ([1837419](https://github.com/Ombi-app/Ombi/commit/18374198f9f2462ba85c5781b0fcc05892728b21))
## [4.36.1](https://github.com/Ombi-app/Ombi/compare/v4.36.0...v4.36.1) (2023-04-20)
### Bug Fixes
* **plex-watchlist:** :bug: Fixed the issue where the watchlist didn't work for users logging in via OAuth ([6398f6a](https://github.com/Ombi-app/Ombi/commit/6398f6a4f7755281ebeac537e3ff623df5cfa0f3))
* **healthchecks:** Removed redundant ping check ([1751305](https://github.com/Ombi-app/Ombi/commit/1751305064176d2c0135f867773ccc46b03915ec))
# [4.36.0](https://github.com/Ombi-app/Ombi/compare/v4.35.19...v4.36.0) (2023-04-20)
### Features
* **discover:** Add deny option to recently requested ([#4907](https://github.com/Ombi-app/Ombi/issues/4907)) ([78f340e](https://github.com/Ombi-app/Ombi/commit/78f340ee5f309c55690497170897533801957668))
## [4.16.2](https://github.com/Ombi-app/Ombi/compare/v4.16.1...v4.16.2) (2022-04-07)
## [4.35.19](https://github.com/Ombi-app/Ombi/compare/v4.35.18...v4.35.19) (2023-04-20)
### Bug Fixes
* **wizard:** Fixed an issue when using Plex OAuth it could fail setting up ([b743cf4](https://github.com/Ombi-app/Ombi/commit/b743cf4fafa7341ad1b163276f006d7ab0e9dcff))
* **radarr:** Fixed an issue where the radarr sync would break ([de4baad](https://github.com/Ombi-app/Ombi/commit/de4baade9f87248d77106ff1a313a498870f4fb3))
## [4.16.1](https://github.com/Ombi-app/Ombi/compare/v4.16.0...v4.16.1) (2022-04-07)
## [4.35.18](https://github.com/Ombi-app/Ombi/compare/v4.35.17...v4.35.18) (2023-04-15)
### Bug Fixes
* **#4906:** :bug: Fixed an issue with power users and permissions ([80884bc](https://github.com/Ombi-app/Ombi/commit/80884bcd725c329867c278ad235cd4096cd4fe7a))
## [4.35.17](https://github.com/Ombi-app/Ombi/compare/v4.35.16...v4.35.17) (2023-04-15)
# [4.16.0](https://github.com/Ombi-app/Ombi/compare/v4.15.6...v4.16.0) (2022-04-07)
### Bug Fixes
* **discover:** Fix denied requests displayed as approved ([#4901](https://github.com/Ombi-app/Ombi/issues/4901)) ([1e87f20](https://github.com/Ombi-app/Ombi/commit/1e87f2010491b0f3fdda70d2b19d9afd94438df7))
* Fix denied movie shown as 'processing request' in details view ([#4900](https://github.com/Ombi-app/Ombi/issues/4900)) ([0069bfd](https://github.com/Ombi-app/Ombi/commit/0069bfdf54e0785bad45c832ca052f19fd4b940b))
## [4.15.6](https://github.com/Ombi-app/Ombi/compare/v4.15.5...v4.15.6) (2022-04-07)
## [4.35.16](https://github.com/Ombi-app/Ombi/compare/v4.35.15...v4.35.16) (2023-04-13)
### Bug Fixes
* **radarr:** Fixed an issue where we couldn't sync radarr content [#4577](https://github.com/Ombi-app/Ombi/issues/4577) ([a5355a3](https://github.com/Ombi-app/Ombi/commit/a5355a3023e6900c4dd1b0da4722d7596c03907f))
* Support duplicates in Emby/JF collections ([#4902](https://github.com/Ombi-app/Ombi/issues/4902)) ([141f96d](https://github.com/Ombi-app/Ombi/commit/141f96da5e45d5b3fa5f496806b102e473da6607))
## [4.35.15](https://github.com/Ombi-app/Ombi/compare/v4.35.14...v4.35.15) (2023-04-06)
## [4.15.5](https://github.com/Ombi-app/Ombi/compare/v4.15.4...v4.15.5) (2022-04-06)
### Bug Fixes
* **sonarr:** :bug: Stop the sonarr version endpoint from breaking when Sonarr is down [#4895](https://github.com/Ombi-app/Ombi/issues/4895) ([7bb8bec](https://github.com/Ombi-app/Ombi/commit/7bb8becfb140ef6012356752a71d53b5b404e482))
## [4.15.4](https://github.com/Ombi-app/Ombi/compare/v4.15.3...v4.15.4) (2022-03-29)
## [4.35.14](https://github.com/Ombi-app/Ombi/compare/v4.35.13...v4.35.14) (2023-04-06)
### Bug Fixes
## [4.15.3](https://github.com/Ombi-app/Ombi/compare/v4.15.2...v4.15.3) (2022-03-24)
* Some minor tweaks to the movie info panel ([#4883](https://github.com/Ombi-app/Ombi/issues/4883)) ([1244487](https://github.com/Ombi-app/Ombi/commit/12444871df2f7602200f73971fce962f06b4a80b))
## [4.15.2](https://github.com/Ombi-app/Ombi/compare/v4.15.1...v4.15.2) (2022-03-23)
## [4.35.13](https://github.com/Ombi-app/Ombi/compare/v4.35.12...v4.35.13) (2023-03-28)
### Bug Fixes
* **metadata:** improved the metadata job to also lookup the media in Plex to see if it has any more uptodate metadata ([83d1a15](https://github.com/Ombi-app/Ombi/commit/83d1a15cc9d0ee91be73bd91c4672cf1bcf2728a))
* **sonarr:** :bug: Added some more error handling and information around testing sonarr ([bd2c2d3](https://github.com/Ombi-app/Ombi/commit/bd2c2d3901e239393010fd582b207f1571fb4b7e)), closes [#4877](https://github.com/Ombi-app/Ombi/issues/4877)
## [4.15.1](https://github.com/Ombi-app/Ombi/compare/v4.15.0...v4.15.1) (2022-03-18)
## [4.35.12](https://github.com/Ombi-app/Ombi/compare/v4.35.9...v4.35.12) (2023-03-25)
### Bug Fixes
* **mediaserver:** fixed an issue where we were not detecting available content correctly [#4542](https://github.com/Ombi-app/Ombi/issues/4542) ([9cdd6f4](https://github.com/Ombi-app/Ombi/commit/9cdd6f41cdab8825a984905c089611409c53c753))
* **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.15.0](https://github.com/Ombi-app/Ombi/compare/v4.14.4...v4.15.0) (2022-03-17)
## [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
* **jellyfin:** :bug: Fixed an issue where Jellyfin content was showing the Play on Emby button ([18b167d](https://github.com/Ombi-app/Ombi/commit/18b167d16a3d682b5060ee36dedbbb069bef09de)), closes [#4542](https://github.com/Ombi-app/Ombi/issues/4542)
* **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.14.4](https://github.com/Ombi-app/Ombi/compare/v4.14.3...v4.14.4) (2022-03-10)
## [4.35.7](https://github.com/Ombi-app/Ombi/compare/v4.35.6...v4.35.7) (2023-02-10)
### Bug Fixes
* :bug: Fixed the Request On Behalf autocomplete not filtering correctly ([a8ba2f3](https://github.com/Ombi-app/Ombi/commit/a8ba2f3544a1c01c57f217c4036a277ab0e67a09)), closes [#4539](https://github.com/Ombi-app/Ombi/issues/4539)
* **translations:** 🌐 New translations from Crowdin [skip ci] ([356c742](https://github.com/Ombi-app/Ombi/commit/356c7424e0ce8c1c5063b04bc6ed9b809f214d65))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([6fcaecf](https://github.com/Ombi-app/Ombi/commit/6fcaecf80b766f2d43ac7082d74364238e1638b7))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([132f4d4](https://github.com/Ombi-app/Ombi/commit/132f4d4e609b7fb7e37f38ee2f395926e2911abe))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([f292006](https://github.com/Ombi-app/Ombi/commit/f292006a08894a8d0ba899c8c6e9fe863e558dda))
* **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.14.3](https://github.com/Ombi-app/Ombi/compare/v4.14.2...v4.14.3) (2022-03-06)
## [4.35.6](https://github.com/Ombi-app/Ombi/compare/v4.35.5...v4.35.6) (2023-01-31)
### Bug Fixes
* **availability:** :bug: Fixed an issue where with 4k content, we could repeat notifications ([f9ebc1c](https://github.com/Ombi-app/Ombi/commit/f9ebc1cc2e13c7cd335121cd86295b10eda529ba))
* 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.14.2](https://github.com/Ombi-app/Ombi/compare/v4.14.1...v4.14.2) (2022-03-05)
## [4.35.5](https://github.com/Ombi-app/Ombi/compare/v4.35.4...v4.35.5) (2023-01-24)
### Bug Fixes
* **Sonarr:** :bug: Fixed an issue where some seasons were not being monitored correctly in sonarr ([60cfd41](https://github.com/Ombi-app/Ombi/commit/60cfd41f68e9006555c1a419dcff1aaa24b3e09f)), closes [#4506](https://github.com/Ombi-app/Ombi/issues/4506)
* **radarr-settings:** 🐛 Fixed a typo ([4a50a00](https://github.com/Ombi-app/Ombi/commit/4a50a00d4729d99f4359874b9af4dbc58a0c220b))
## [4.14.1](https://github.com/Ombi-app/Ombi/compare/v4.14.0...v4.14.1) (2022-03-03)
## [4.35.4](https://github.com/Ombi-app/Ombi/compare/v4.35.3...v4.35.4) (2023-01-22)
### Bug Fixes
# [4.14.0](https://github.com/Ombi-app/Ombi/compare/v4.13.2...v4.14.0) (2022-03-02)
* **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.13.2](https://github.com/Ombi-app/Ombi/compare/v4.13.1...v4.13.2) (2022-03-01)
## [4.35.3](https://github.com/Ombi-app/Ombi/compare/v4.35.2...v4.35.3) (2023-01-13)
### Bug Fixes
* **requests:** :bug: Fixed an issue where you couldn't approve movies from the request list ([1611ef9](https://github.com/Ombi-app/Ombi/commit/1611ef9198befbb7a4db50a4f0953e50f29a788f))
* **#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.13.1](https://github.com/Ombi-app/Ombi/compare/v4.13.0...v4.13.1) (2022-03-01)
## [4.35.2](https://github.com/Ombi-app/Ombi/compare/v4.35.1...v4.35.2) (2023-01-08)
### Bug Fixes
* **details:** :bug: Fixed the missing Play on Media server button for 4k content [#4529](https://github.com/Ombi-app/Ombi/issues/4529) ([68600f3](https://github.com/Ombi-app/Ombi/commit/68600f3b45376e12dd2ef263d81ca4040c84cbca))
* **discover:** :bug: Fixed the issue where there was an option on the discover to request 4k shows (that's not supported currently) ([dcfd688](https://github.com/Ombi-app/Ombi/commit/dcfd688c8d2337e55fa9c6c33b7c3e80fc560cda))
* **requests:** :bug: Fixed the issue where we could no longer approve TV Requests from the requests list ([19fe4e3](https://github.com/Ombi-app/Ombi/commit/19fe4e342efe5578c26ab8ba7ee2f2e64bbc9418))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4526](https://github.com/Ombi-app/Ombi/issues/4526)) ([7e9f54f](https://github.com/Ombi-app/Ombi/commit/7e9f54fc80a09c938184e6be40ce5f49ce9673ef))
* **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.13.0](https://github.com/Ombi-app/Ombi/compare/v4.12.7...v4.13.0) (2022-02-25)
## [4.35.1](https://github.com/Ombi-app/Ombi/compare/v4.35.0...v4.35.1) (2023-01-06)
### Bug Fixes
* **4k:** Hide 'Has 4K Request' column list if 4k feature is disabled ([#4521](https://github.com/Ombi-app/Ombi/issues/4521)) ([a9a6067](https://github.com/Ombi-app/Ombi/commit/a9a60678e74d22fa7ba34051a2645db86b600b4a))
* **issues:** Fix label ID in chatbox page ([#4520](https://github.com/Ombi-app/Ombi/issues/4520)) ([76882ad](https://github.com/Ombi-app/Ombi/commit/76882adf231f92e1cdd396239933c13467c112b3))
* **localisation:** Localize request types in notifications ([#4516](https://github.com/Ombi-app/Ombi/issues/4516)) ([e09435d](https://github.com/Ombi-app/Ombi/commit/e09435da455b12fc429f129372de31e0654da797))
* **notifications:** Remove generic admin email in favour of admins' email ([#4519](https://github.com/Ombi-app/Ombi/issues/4519)) ([b90fc5f](https://github.com/Ombi-app/Ombi/commit/b90fc5fea771a83e6cf576c71a307066efd59ea4))
* **tv:** Display TV show as requested if all episodes are requested ([#4518](https://github.com/Ombi-app/Ombi/issues/4518)) ([2ed8c48](https://github.com/Ombi-app/Ombi/commit/2ed8c48d128a69f0d144c5d332286dbf3b0bdf28))
* **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
* **email-notifications:** Add a link to Ombi details page in email notifications ([#4517](https://github.com/Ombi-app/Ombi/issues/4517)) ([a3e97b3](https://github.com/Ombi-app/Ombi/commit/a3e97b31e2298d95e7deebd71268095b8ed5e9dc))
* **media-details:** Add Trakt to social icons ([#4522](https://github.com/Ombi-app/Ombi/issues/4522)) ([d6ae79c](https://github.com/Ombi-app/Ombi/commit/d6ae79ce9eddbd5b7b888ab1b9f7e342d9d9ff9e))
* 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.12.7](https://github.com/Ombi-app/Ombi/compare/v4.12.6...v4.12.7) (2022-02-23)
# [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.12.6](https://github.com/Ombi-app/Ombi/compare/v4.12.5...v4.12.6) (2022-02-22)
## [4.33.1](https://github.com/Ombi-app/Ombi/compare/v4.33.0...v4.33.1) (2022-12-22)
### Bug Fixes
* **emby/jellyfin:** :bug: Fixed another issue where we were not correctly displaying the correct status' for movies ([5c0556e](https://github.com/Ombi-app/Ombi/commit/5c0556e6f44b8997a611f3a4d8e9e4e05d08bd13))
* **mediaserver:** fixed some more issues in the media server sync and availability checks ([f3ea979](https://github.com/Ombi-app/Ombi/commit/f3ea979b8bd77842780ce8e6928b16237dd779cf))
* **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.12.5](https://github.com/Ombi-app/Ombi/compare/v4.12.4...v4.12.5) (2022-02-21)
## [4.32.3](https://github.com/Ombi-app/Ombi/compare/v4.32.2...v4.32.3) (2022-11-24)
### Bug Fixes
* **emby:** :bug: Fixed the emby content sync [#4513](https://github.com/Ombi-app/Ombi/issues/4513) ([2927504](https://github.com/Ombi-app/Ombi/commit/2927504f0e0b4e7251e69b44e0e30c7ec9519980))
* **emby:** :bug: Fixed the emby content sync [#4513](https://github.com/Ombi-app/Ombi/issues/4513) ([bd441cb](https://github.com/Ombi-app/Ombi/commit/bd441cb54fd77d6befb03fae321dc36c29f0de2e))
* **sonarr:** V4 actually works this time around ([f62e70f](https://github.com/Ombi-app/Ombi/commit/f62e70fc493c7971da5e4508ce10522f5df0bbf7))
## [4.12.4](https://github.com/Ombi-app/Ombi/compare/v4.12.3...v4.12.4) (2022-02-17)
## [4.32.2](https://github.com/Ombi-app/Ombi/compare/v4.32.1...v4.32.2) (2022-11-23)
### Bug Fixes
## [4.12.3](https://github.com/Ombi-app/Ombi/compare/v4.12.2...v4.12.3) (2022-02-16)
* **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.12.2](https://github.com/Ombi-app/Ombi/compare/v4.12.1...v4.12.2) (2022-02-16)
## [4.32.1](https://github.com/Ombi-app/Ombi/compare/v4.32.0...v4.32.1) (2022-11-21)
### Bug Fixes
* **requests:** :bug: Fixed the approve 4k option on the requests list not working as expected ([c0189da](https://github.com/Ombi-app/Ombi/commit/c0189dad478ea375beda61ba3bee3f029a39b8e5))
* **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.12.1](https://github.com/Ombi-app/Ombi/compare/v4.12.0...v4.12.1) (2022-02-16)
# [4.32.0](https://github.com/Ombi-app/Ombi/compare/v4.31.0...v4.32.0) (2022-11-18)
### Bug Fixes
* **requests:** :bug: Fixed the issue where Approving a 4K Request wouldn't send it to the correct 4K radarr instance ([87cb990](https://github.com/Ombi-app/Ombi/commit/87cb9903db30e1dead25ee8c5ea34305eb084a03)), closes [#4509](https://github.com/Ombi-app/Ombi/issues/4509)
* **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.12.0](https://github.com/Ombi-app/Ombi/compare/v4.11.8...v4.12.0) (2022-02-14)
# [4.30.0](https://github.com/Ombi-app/Ombi/compare/v4.29.3...v4.30.0) (2022-11-17)
### Features
* **radarr:** 4K Requests and Radarr 4K support ([ba88848](https://github.com/Ombi-app/Ombi/commit/ba88848866b0a9dedb1e79b55c4d81a0fd453843))
* **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.11.8](https://github.com/Ombi-app/Ombi/compare/v4.11.7...v4.11.8) (2022-02-13)
## [4.29.2](https://github.com/Ombi-app/Ombi/compare/v4.29.1...v4.29.2) (2022-10-24)
### Bug Fixes
* **settings:** :bug: Fixed an issue where we were not displaying the excluded keyworks correctly in the TheMovieDbSettings page ([d3b3316](https://github.com/Ombi-app/Ombi/commit/d3b3316cbac18356b2f6b0912a3deb2c183e6534))
* **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))

@ -85,13 +85,6 @@ Here are some of the features Ombi has:
<sub><b>Twan Ariens</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Drewster727">
<img src="https://avatars.githubusercontent.com/u/4528753?v=4" width="50;" alt="Drewster727"/>
<br />
<sub><b>Drew</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sephrat">
<img src="https://avatars.githubusercontent.com/u/34862846?v=4" width="50;" alt="sephrat"/>
@ -112,15 +105,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Magikarp Lvl 4</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/MrTopCat">
<img src="https://avatars.githubusercontent.com/u/774415?v=4" width="50;" alt="MrTopCat"/>
<br />
<sub><b>James Carty</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/smcpeck">
<img src="https://avatars.githubusercontent.com/u/8724583?v=4" width="50;" alt="smcpeck"/>
@ -155,15 +148,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Joshua M. Boniface</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/bruvv">
<img src="https://avatars.githubusercontent.com/u/3063928?v=4" width="50;" alt="bruvv"/>
<br />
<sub><b>Bruvv</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/louis-lau">
<img src="https://avatars.githubusercontent.com/u/1346804?v=4" width="50;" alt="louis-lau"/>
@ -198,15 +191,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Jim MacKenize</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/Unimatrix0">
<img src="https://avatars.githubusercontent.com/u/357984?v=4" width="50;" alt="Unimatrix0"/>
<br />
<sub><b>Avi</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/kitzin">
<img src="https://avatars.githubusercontent.com/u/3277321?v=4" width="50;" alt="kitzin"/>
@ -241,15 +234,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Francesco Servida</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/Patricol">
<img src="https://avatars.githubusercontent.com/u/13428020?v=4" width="50;" alt="Patricol"/>
<br />
<sub><b>Patrick Collins</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/xweskingx">
<img src="https://avatars.githubusercontent.com/u/6268446?v=4" width="50;" alt="xweskingx"/>
@ -284,15 +277,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Aptalca</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/dr3am37">
<img src="https://avatars.githubusercontent.com/u/91037083?v=4" width="50;" alt="dr3am37"/>
<br />
<sub><b>Dr3amer</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/mhann">
<img src="https://avatars.githubusercontent.com/u/17162399?v=4" width="50;" alt="mhann"/>
@ -307,6 +300,13 @@ Here are some of the features Ombi has:
<sub><b>Ombi-bot</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/phildups7">
<img src="https://avatars.githubusercontent.com/u/60622768?v=4" width="50;" alt="phildups7"/>
<br />
<sub><b>Phildups7</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/snyk-bot">
<img src="https://avatars.githubusercontent.com/u/19733683?v=4" width="50;" alt="snyk-bot"/>
@ -415,6 +415,13 @@ Here are some of the features Ombi has:
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/aj3x">
<img src="https://avatars.githubusercontent.com/u/15078358?v=4" width="50;" alt="aj3x"/>
<br />
<sub><b>Alexander Russell</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/XanderStrike">
<img src="https://avatars.githubusercontent.com/u/1565303?v=4" width="50;" alt="XanderStrike"/>
@ -449,15 +456,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>Calvin</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/origamirobot">
<img src="https://avatars.githubusercontent.com/u/1346803?v=4" width="50;" alt="origamirobot"/>
<br />
<sub><b>Chris Lees</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/cdemi">
<img src="https://avatars.githubusercontent.com/u/8025435?v=4" width="50;" alt="cdemi"/>
@ -492,15 +499,15 @@ Here are some of the features Ombi has:
<br />
<sub><b>David Torosyan</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/onedr0p">
<img src="https://avatars.githubusercontent.com/u/213795?v=4" width="50;" alt="onedr0p"/>
<br />
<sub><b>Devin Buhl</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/elisspace">
<img src="https://avatars.githubusercontent.com/u/18365129?v=4" width="50;" alt="elisspace"/>
@ -515,6 +522,13 @@ Here are some of the features Ombi has:
<sub><b>Fish2</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Grygon">
<img src="https://avatars.githubusercontent.com/u/647846?v=4" width="50;" alt="Grygon"/>
<br />
<sub><b>Grygon</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ketsapiwiq">
<img src="https://avatars.githubusercontent.com/u/26697460?v=4" width="50;" alt="ketsapiwiq"/>
@ -528,7 +542,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Haries Ramdhani</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/comigor">
<img src="https://avatars.githubusercontent.com/u/735858?v=4" width="50;" alt="comigor"/>
@ -542,8 +557,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Imgbot</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/JPyke3">
<img src="https://avatars.githubusercontent.com/u/13283054?v=4" width="50;" alt="JPyke3"/>
@ -571,7 +585,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Joe Harvey</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/jonbloom">
<img src="https://avatars.githubusercontent.com/u/492819?v=4" width="50;" alt="jonbloom"/>
@ -585,8 +600,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Jono Cairns</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/krisklosterman">
<img src="https://avatars.githubusercontent.com/u/7139579?v=4" width="50;" alt="krisklosterman"/>
@ -614,7 +628,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Lightkeeper</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Lucane">
<img src="https://avatars.githubusercontent.com/u/7999446?v=4" width="50;" alt="Lucane"/>
@ -628,8 +643,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Madeleine Schönemann</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/marleypowell">
<img src="https://avatars.githubusercontent.com/u/55280588?v=4" width="50;" alt="marleypowell"/>
@ -657,7 +671,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Micky</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/mvicomoya">
<img src="https://avatars.githubusercontent.com/u/24613599?v=4" width="50;" alt="mvicomoya"/>
@ -671,8 +686,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Nathan Miller</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/cqxmzz">
<img src="https://avatars.githubusercontent.com/u/3071863?v=4" width="50;" alt="cqxmzz"/>
@ -700,7 +714,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Sean Callinan</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/shoghicp">
<img src="https://avatars.githubusercontent.com/u/516482?v=4" width="50;" alt="shoghicp"/>
@ -714,8 +729,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Teifun2</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/thomasvt1">
<img src="https://avatars.githubusercontent.com/u/2271011?v=4" width="50;" alt="thomasvt1"/>
@ -743,7 +757,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Torkil</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/bybeet">
<img src="https://avatars.githubusercontent.com/u/1662279?v=4" width="50;" alt="bybeet"/>
@ -757,8 +772,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Xirg</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/bazhip">
<img src="https://avatars.githubusercontent.com/u/10350445?v=4" width="50;" alt="bazhip"/>
@ -786,7 +800,8 @@ Here are some of the features Ombi has:
<br />
<sub><b>Michael DiStaula</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/baikunz">
<img src="https://avatars.githubusercontent.com/u/984911?v=4" width="50;" alt="baikunz"/>
@ -800,8 +815,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Echel0n</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/m4tta">
<img src="https://avatars.githubusercontent.com/u/427218?v=4" width="50;" alt="m4tta"/>
@ -829,6 +843,14 @@ Here are some of the features Ombi has:
<br />
<sub><b>Mkgeeky</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/ryan-c44">
<img src="https://avatars.githubusercontent.com/u/54028283?v=4" width="50;" alt="ryan-c44"/>
<br />
<sub><b>Ryan-c44</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sir-marv">
@ -843,8 +865,7 @@ Here are some of the features Ombi has:
<br />
<sub><b>Tdorsey</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/thegame3202">
<img src="https://avatars.githubusercontent.com/u/22148848?v=4" width="50;" alt="thegame3202"/>

@ -248,5 +248,37 @@ namespace Ombi.Api.Emby
req.AddContentHeader("Content-Type", "application/json");
req.AddHeader("Device", "Ombi");
}
public async Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await GetPlayed<EmbyMovie>("Movie", apiKey, userId, baseUri, startIndex, count, parentIdFilder);
}
private async Task<EmbyItemContainer<T>> GetPlayed<T>(string type, string apiKey, string userId, string baseUri, int startIndex, int count, string parentIdFilder = default)
{
var request = new Request($"emby/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", "ProviderIds");
request.AddQueryString("UserId", userId);
request.AddQueryString("isPlayed", true.ToString());
// paginate and display recently played items first
request.AddQueryString("sortBy", "DatePlayed");
request.AddQueryString("SortOrder", "Descending");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
if (!string.IsNullOrEmpty(parentIdFilder))
{
request.AddQueryString("ParentId", parentIdFilder);
}
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
}
}

@ -32,5 +32,7 @@ namespace Ombi.Api.Emby
Task<EmbyItemContainer<EmbyMovie>> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
}
}

@ -5,30 +5,8 @@ namespace Ombi.Api.Emby.Models.Movie
public class EmbyMovie
{
public string Name { get; set; }
public string ServerId { get; set; }
public string Id { get; set; }
public string Container { get; set; }
public DateTime PremiereDate { get; set; }
public object[] ProductionLocations { get; set; }
public string OfficialRating { get; set; }
public float CommunityRating { get; set; }
public long RunTimeTicks { get; set; }
public string PlayAccess { get; set; }
public int ProductionYear { get; set; }
public bool IsPlaceHolder { get; set; }
public bool IsHD { get; set; }
public bool IsFolder { get; set; }
public string Type { get; set; }
public int LocalTrailerCount { get; set; }
public EmbyUserdata UserData { get; set; }
public string VideoType { get; set; }
public EmbyImagetags ImageTags { get; set; }
public string[] BackdropImageTags { get; set; }
public string LocationType { get; set; }
public string MediaType { get; set; }
public bool HasSubtitles { get; set; }
public int CriticRating { get; set; }
public string Overview { get; set; }
public EmbyProviderids ProviderIds { get; set; }
public EmbyMediastream[] MediaStreams { get; set; }
}

@ -5,30 +5,8 @@ namespace Ombi.Api.Jellyfin.Models.Movie
public class JellyfinMovie
{
public string Name { get; set; }
public string ServerId { get; set; }
public string Id { get; set; }
public string Container { get; set; }
public DateTime PremiereDate { get; set; }
public object[] ProductionLocations { get; set; }
public string OfficialRating { get; set; }
public float CommunityRating { get; set; }
public long RunTimeTicks { get; set; }
public string PlayAccess { get; set; }
public int ProductionYear { get; set; }
public bool IsPlaceHolder { get; set; }
public bool IsHD { get; set; }
public bool IsFolder { get; set; }
public string Type { get; set; }
public int LocalTrailerCount { get; set; }
public JellyfinUserdata UserData { get; set; }
public string VideoType { get; set; }
public JellyfinImagetags ImageTags { get; set; }
public string[] BackdropImageTags { get; set; }
public string LocationType { get; set; }
public string MediaType { get; set; }
public bool HasSubtitles { get; set; }
public int CriticRating { get; set; }
public string Overview { get; set; }
public JellyfinProviderids ProviderIds { get; set; }
public JellyfinMediastream[] MediaStreams { get; set; }
}

@ -3,23 +3,6 @@ namespace Ombi.Api.Sonarr
public class SystemStatus
{
public string version { get; set; }
public string buildTime { get; set; }
public bool isDebug { get; set; }
public bool isProduction { get; set; }
public bool isAdmin { get; set; }
public bool isUserInteractive { get; set; }
public string startupPath { get; set; }
public string appData { get; set; }
public string osVersion { get; set; }
public bool isMonoRuntime { get; set; }
public bool isMono { get; set; }
public bool isLinux { get; set; }
public bool isOsx { get; set; }
public bool isWindows { get; set; }
public string branch { get; set; }
public string authentication { get; set; }
public string sqliteVersion { get; set; }
public string urlBase { get; set; }
public string runtimeVersion { get; set; }
}
}

@ -52,6 +52,7 @@ namespace Ombi.Core.Tests.Engine
_subject = _mocker.CreateInstance<MovieRequestEngine>();
var list = DbHelper.GetQueryableMockDbSet(new RequestSubscription());
_mocker.Setup<IRepository<RequestSubscription>, IQueryable<RequestSubscription>>(x => x.GetAll()).Returns(new List<RequestSubscription>().AsQueryable().BuildMock());
_mocker.Setup<IUserPlayedMovieRepository, IQueryable<UserPlayedMovie>>(x => x.GetAll()).Returns(new List<UserPlayedMovie>().AsQueryable().BuildMock());
}
[Test]

@ -46,8 +46,9 @@ namespace Ombi.Core.Tests.Engine.V2
var requestSubs = new Mock<IRepository<RequestSubscription>>();
var mediaCache = new Mock<IMediaCacheService>();
var featureService = new Mock<IFeatureService>();
var userPlayedMovieRepository = new Mock<IUserPlayedMovieRepository>();
_engine = new MovieRequestEngine(movieApi.Object, requestService.Object, user.Object, notificationHelper.Object, rules.Object, movieSender.Object,
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object);
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object, userPlayedMovieRepository.Object);
}
[Test]

@ -173,6 +173,7 @@ namespace Ombi.Core.Tests.Services
[Test]
[Ignore("Flaky")]
public async Task GetRecentlyRequested_HideUsernames()
{
_mocker.Setup<ISettingsService<CustomizationSettings>, Task<CustomizationSettings>>(x => x.GetSettingsAsync())

@ -33,7 +33,8 @@ namespace Ombi.Core.Engine
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
IFeatureService featureService)
IFeatureService featureService,
IUserPlayedMovieRepository userPlayedMovieRepository)
: base(user, requestService, r, manager, cache, ombiSettings, sub)
{
MovieApi = movieApi;
@ -43,6 +44,7 @@ namespace Ombi.Core.Engine
_requestLog = rl;
_mediaCacheService = mediaCacheService;
_featureService = featureService;
_userPlayedMovieRepository = userPlayedMovieRepository;
}
private IMovieDbApi MovieApi { get; }
@ -52,6 +54,7 @@ namespace Ombi.Core.Engine
private readonly IRepository<RequestLog> _requestLog;
private readonly IMediaCacheService _mediaCacheService;
private readonly IFeatureService _featureService;
protected readonly IUserPlayedMovieRepository _userPlayedMovieRepository;
/// <summary>
/// Requests the movie.
@ -77,7 +80,8 @@ namespace Ombi.Core.Engine
var userDetails = await GetUser();
var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
if (canRequestOnBehalf && !isAdmin)
{
@ -252,7 +256,7 @@ namespace Ombi.Core.Engine
var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count)
.ToListAsync();
await CheckForSubscription(shouldHide.UserId, requests);
await FillAdditionalFields(shouldHide, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -296,7 +300,7 @@ namespace Ombi.Core.Engine
var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList();
await CheckForSubscription(shouldHide.UserId, requests);
await FillAdditionalFields(shouldHide, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -381,7 +385,7 @@ namespace Ombi.Core.Engine
// TODO fix this so we execute this on the server
requests = requests.Skip(position).Take(count).ToList();
await CheckForSubscription(shouldHide.UserId, requests);
await FillAdditionalFields(shouldHide, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -424,7 +428,7 @@ namespace Ombi.Core.Engine
var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList();
await CheckForSubscription(shouldHide.UserId, requests);
await FillAdditionalFields(shouldHide, requests);
return new RequestsViewModel<MovieRequests>
{
Collection = requests,
@ -506,18 +510,25 @@ namespace Ombi.Core.Engine
allRequests = await MovieRepository.GetWithUser().ToListAsync();
}
await CheckForSubscription(shouldHide.UserId, allRequests);
await FillAdditionalFields(shouldHide, allRequests);
return allRequests;
}
public async Task<MovieRequests> GetRequest(int requestId)
{
var shouldHide = await HideFromOtherUsers();
// TODO: this query should return the request only if the user is allowed to see it (see shouldHide implementations)
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
await CheckForSubscription((await GetUser()).Id, new List<MovieRequests> { request });
await FillAdditionalFields(shouldHide, new List<MovieRequests> { request });
return request;
}
private async Task FillAdditionalFields(HideResult shouldHide, List<MovieRequests> requests)
{
await CheckForSubscription(shouldHide.UserId, requests);
await CheckForPlayed(shouldHide, requests);
}
private async Task CheckForSubscription(string UserId, List<MovieRequests> movieRequests)
{
@ -543,6 +554,23 @@ namespace Ombi.Core.Engine
}
}
}
private async Task CheckForPlayed(HideResult shouldHide, List<MovieRequests> movieRequests)
{
var theMovieDbIds = movieRequests.Select(x => x.TheMovieDbId);
var plays = await _userPlayedMovieRepository.GetAll().Where(x =>
theMovieDbIds.Contains(x.TheMovieDbId))
.ToListAsync();
foreach (var request in movieRequests)
{
request.WatchedByRequestedUser = plays.Exists(x => x.TheMovieDbId == request.TheMovieDbId && x.UserId == request.RequestedUserId);
if (!shouldHide.Hide)
{
request.PlayedByUsersCount = plays.Count(x => x.TheMovieDbId == request.TheMovieDbId);
}
}
}
/// <summary>
/// Searches the movie request.
@ -563,7 +591,7 @@ namespace Ombi.Core.Engine
}
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
await CheckForSubscription(shouldHide.UserId, results);
await FillAdditionalFields(shouldHide, results);
return results;
}

@ -161,7 +161,7 @@ namespace Ombi.Core.Engine
var user = await GetUser();
var canRequestOnBehalf = tv.RequestOnBehalf.HasValue();
var isAdmin = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase) || await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
if (tv.RequestOnBehalf.HasValue() && !isAdmin)
{
return new RequestEngineResult

@ -16,6 +16,7 @@ namespace Ombi.Core.Models.Requests
public string Overview { get; set; }
public DateTime ReleaseDate { get; set; }
public bool Approved { get; set; }
public bool Denied { get; set; }
public string MediaId { get; set; }
public string PosterPath { get; set; }

@ -5,5 +5,6 @@
public bool IsValid { get; set; }
public string Version { get; set; }
public string ExpectedSubDir { get; set; }
public string AdditionalInformation { get; set; }
}
}

@ -100,7 +100,7 @@ namespace Ombi.Core.Senders
addOptions = new Addoptions
{
monitored = true,
monitor = MonitorTypes.None,
monitor = MonitorTypes.Existing,
searchForMissingAlbums = false,
AlbumsToMonitor = new[] {model.ForeignAlbumId}
},
@ -199,4 +199,4 @@ namespace Ombi.Core.Senders
return new SenderResult { Message = "Could not set album to monitored", Sent = false, Success = false };
}
}
}
}

@ -88,6 +88,7 @@ namespace Ombi.Core.Services
Title = item.Title,
Type = RequestType.Movie,
Approved = item.Approved,
Denied = item.Denied ?? false,
UserId = item.RequestedUserId,
Username = item.RequestedUser.UserAlias,
MediaId = item.TheMovieDbId.ToString(),
@ -108,6 +109,7 @@ namespace Ombi.Core.Services
Available = item.Available,
Overview = item.ArtistName,
Approved = item.Approved,
Denied = item.Denied ?? false,
ReleaseDate = item.ReleaseDate,
RequestDate = item.RequestedDate,
Title = item.Title,
@ -135,6 +137,7 @@ namespace Ombi.Core.Services
Overview = item.ParentRequest.Overview,
ReleaseDate = item.ParentRequest.ReleaseDate,
Approved = item.Approved,
Denied = item.Denied ?? false,
RequestDate = item.RequestedDate,
TvPartiallyAvailable = partialAvailability,
Title = item.ParentRequest.Title,

@ -197,6 +197,7 @@ namespace Ombi.DependencyInjection
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
services.AddScoped<IUserPlayedMovieRepository, UserPlayedMovieRepository>();
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
@ -244,6 +245,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IPlexContentSync, PlexContentSync>();
services.AddTransient<IPlexWatchlistImport, PlexWatchlistImport>();
services.AddTransient<IEmbyContentSync, EmbyContentSync>();
services.AddTransient<IEmbyPlayedSync, EmbyPlayedSync>();
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
services.AddTransient<IJellyfinContentSync, JellyfinContentSync>();

@ -1,47 +0,0 @@
using HealthChecks.Network;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.HealthChecks.Checks
{
public class OmbiPingHealthCheck
: IHealthCheck
{
private readonly OmbiPingHealthCheckOptions _options;
public OmbiPingHealthCheck(OmbiPingHealthCheckOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
var configuredHosts = _options.ConfiguredHosts.Values;
try
{
foreach (var (host, timeout, status) in configuredHosts)
{
using (var ping = new Ping())
{
var pingReply = await ping.SendPingAsync(host, timeout);
if (pingReply.Status != IPStatus.Success)
{
return new HealthCheckResult(status, description: $"Ping check for host {host} is failed with status reply:{pingReply.Status}");
}
}
}
return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
}
}
}
}

@ -1,16 +0,0 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Collections.Generic;
namespace Ombi.HealthChecks.Checks
{
public class OmbiPingHealthCheckOptions
{
internal Dictionary<string, (string Host, int TimeOut, HealthStatus status)> ConfiguredHosts { get; } = new Dictionary<string, (string, int, HealthStatus)>();
public OmbiPingHealthCheckOptions AddHost(string host, int timeout, HealthStatus status)
{
ConfiguredHosts.Add(host, (host, timeout, status));
return this;
}
}
}

@ -27,7 +27,7 @@ namespace Ombi.HealthChecks.Checks
var settings = await settingsProvider.GetSettingsAsync();
if (settings == null)
{
return HealthCheckResult.Healthy("Plex is not confiured.");
return HealthCheckResult.Healthy("Plex is not configured.");
}
var taskResult = new List<Task<PlexStatus>>();

@ -18,39 +18,8 @@ namespace Ombi.HealthChecks
builder.AddCheck<RadarrHealthCheck>("Radarr", tags: new string[] { "DVR" });
builder.AddCheck<CouchPotatoHealthCheck>("CouchPotato", tags: new string[] { "DVR" });
builder.AddCheck<SickrageHealthCheck>("SickRage", tags: new string[] { "DVR" });
builder.AddOmbiPingHealthCheck(options =>
{
options.AddHost("www.google.co.uk", 5000, HealthStatus.Unhealthy);
options.AddHost("www.google.com", 3000, HealthStatus.Degraded);
}, "External Ping", tags: new string[] { "System" });
return builder;
}
/// <summary>
/// Add a health check for network ping.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="setup">The action to configure the ping parameters.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'ping' will be used for the name.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <param name="timeout">An optional System.TimeSpan representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns></param>
public static IHealthChecksBuilder AddOmbiPingHealthCheck(this IHealthChecksBuilder builder, Action<OmbiPingHealthCheckOptions> setup, string name = default, HealthStatus? failureStatus = default, IEnumerable<string> tags = default, TimeSpan? timeout = default)
{
var options = new OmbiPingHealthCheckOptions();
setup?.Invoke(options);
return builder.Add(new HealthCheckRegistration(
name,
sp => new OmbiPingHealthCheck(options),
failureStatus,
tags,
timeout));
}
}
}

@ -118,13 +118,13 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="NewAlbums" xml:space="preserve">
<value>New Albums</value>
<value>Új album</value>
</data>
<data name="NewMovies" xml:space="preserve">
<value>New Movies</value>
<value>Új film</value>
</data>
<data name="NewTV" xml:space="preserve">
<value>New TV</value>
<value>Új sorozat</value>
</data>
<data name="GenresLabel" xml:space="preserve">
<value>Műfaj:</value>
@ -139,18 +139,18 @@
<value>Epizódok:</value>
</data>
<data name="PoweredBy" xml:space="preserve">
<value>Powered by</value>
<value>Biztosítja a(z)</value>
</data>
<data name="Unsubscribe" xml:space="preserve">
<value>Unsubscribe</value>
<value>Leiratkozás</value>
</data>
<data name="Album" xml:space="preserve">
<value>Album</value>
</data>
<data name="Movie" xml:space="preserve">
<value>Movie</value>
<value>Film</value>
</data>
<data name="TvShow" xml:space="preserve">
<value>TV Show</value>
<value>Sorozat</value>
</data>
</root>

@ -148,9 +148,9 @@
<value>Album</value>
</data>
<data name="Movie" xml:space="preserve">
<value>Movie</value>
<value>Film</value>
</data>
<data name="TvShow" xml:space="preserve">
<value>TV Show</value>
<value>TV-serie</value>
</data>
</root>

@ -118,16 +118,16 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="NewAlbums" xml:space="preserve">
<value>New Albums</value>
<value>Nye Album</value>
</data>
<data name="NewMovies" xml:space="preserve">
<value>New Movies</value>
<value>Nye filmer</value>
</data>
<data name="NewTV" xml:space="preserve">
<value>New TV</value>
<value>Nye TV-serier</value>
</data>
<data name="GenresLabel" xml:space="preserve">
<value>Genres:</value>
<value>Sjangere:</value>
</data>
<data name="AlbumTypeLabel" xml:space="preserve">
<value>Type:</value>
@ -136,21 +136,21 @@
<value>Sesong:</value>
</data>
<data name="EpisodesLabel" xml:space="preserve">
<value>Episodes:</value>
<value>Episoder:</value>
</data>
<data name="PoweredBy" xml:space="preserve">
<value>Powered by</value>
<value>Drevet av</value>
</data>
<data name="Unsubscribe" xml:space="preserve">
<value>Unsubscribe</value>
<value>Avslutt abonnement</value>
</data>
<data name="Album" xml:space="preserve">
<value>Album</value>
</data>
<data name="Movie" xml:space="preserve">
<value>Movie</value>
<value>Film</value>
</data>
<data name="TvShow" xml:space="preserve">
<value>TV Show</value>
<value>TV-serie</value>
</data>
</root>

@ -153,6 +153,7 @@ namespace Ombi.Notifications
RequestedUser = req?.RequestedUser?.UserName;
RequestedDate = req?.RequestedDate.ToString("D");
DetailsUrl = GetDetailsUrl(s, req);
RequestedByAlias = req?.RequestedByAlias;
if (Type.IsNullOrEmpty())
{
@ -276,6 +277,7 @@ namespace Ombi.Notifications
// User Defined
public string RequestId { get; set; }
public string RequestedUser { get; set; }
public string RequestedByAlias { get; set; }
public string UserName { get; set; }
public string IssueUser => UserName;
public string Alias { get; set; }
@ -339,6 +341,7 @@ namespace Ombi.Notifications
{ nameof(IssueUser), IssueUser },
{ nameof(UserName), UserName },
{ nameof(Alias), Alias },
{ nameof(RequestedByAlias), RequestedByAlias },
{ nameof(UserPreference), UserPreference },
{ nameof(DenyReason), DenyReason },
{ nameof(AvailableDate), AvailableDate },

@ -8,10 +8,12 @@ using Ombi.Api.Emby;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
using Ombi.Core.Services;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers;
using Ombi.Hubs;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Quartz;
@ -19,108 +21,43 @@ using MediaType = Ombi.Store.Entities.MediaType;
namespace Ombi.Schedule.Jobs.Emby
{
public class EmbyContentSync : IEmbyContentSync
public class EmbyContentSync : EmbyLibrarySync, IEmbyContentSync
{
public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
IEmbyContentRepository repo, INotificationHubService notification)
public EmbyContentSync(
ISettingsService<EmbySettings> settings,
IEmbyApiFactory api,
ILogger<EmbyContentSync> logger,
IEmbyContentRepository repo,
INotificationHubService notification,
IFeatureService feature):
base(settings, api, logger, notification)
{
_logger = logger;
_settings = settings;
_apiFactory = api;
_repo = repo;
_notification = notification;
_feature = feature;
}
private readonly ILogger<EmbyContentSync> _logger;
private readonly ISettingsService<EmbySettings> _settings;
private readonly IEmbyApiFactory _apiFactory;
private readonly IEmbyContentRepository _repo;
private readonly INotificationHubService _notification;
private readonly IFeatureService _feature;
private const int AmountToTake = 100;
private IEmbyApi Api { get; set; }
public async Task Execute(IJobExecutionContext context)
public async override Task Execute(IJobExecutionContext context)
{
JobDataMap dataMap = context.JobDetail.JobDataMap;
var recentlyAddedSearch = false;
if (dataMap.TryGetValue(JobDataKeys.EmbyRecentlyAddedSearch, out var recentlyAddedObj))
{
recentlyAddedSearch = Convert.ToBoolean(recentlyAddedObj);
}
var embySettings = await _settings.GetSettingsAsync();
if (!embySettings.Enable)
return;
Api = _apiFactory.CreateClient(embySettings);
await _notification.SendNotificationToAdmins(recentlyAddedSearch ? "Emby Recently Added Started" : "Emby Content Sync Started");
foreach (var server in embySettings.Servers)
{
try
{
await StartServerCache(server, recentlyAddedSearch);
}
catch (Exception e)
{
await _notification.SendNotificationToAdmins("Emby Content Sync Failed");
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name);
}
}
await base.Execute(context);
await _notification.SendNotificationToAdmins("Emby Content Sync Finished");
// Episodes
await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyEpisodeSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAdded.ToString() } }));
await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyEpisodeSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAddedSearch.ToString() } }));
}
private async Task StartServerCache(EmbyServers server, bool recentlyAdded)
{
if (!ValidateSettings(server))
{
return;
}
if (server.EmbySelectedLibraries.Any() && server.EmbySelectedLibraries.Any(x => x.Enabled))
{
var movieLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "movies");
foreach (var movieParentIdFilder in movieLibsToFilter)
{
_logger.LogInformation($"Scanning Lib '{movieParentIdFilder.Title}'");
await ProcessMovies(server, recentlyAdded, movieParentIdFilder.Key);
}
var tvLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "tvshows");
foreach (var tvParentIdFilter in tvLibsToFilter)
{
_logger.LogInformation($"Scanning Lib '{tvParentIdFilter.Title}'");
await ProcessTv(server, recentlyAdded, tvParentIdFilter.Key);
}
var mixedLibs = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "mixed");
foreach (var m in mixedLibs)
{
_logger.LogInformation($"Scanning Lib '{m.Title}'");
await ProcessTv(server, recentlyAdded, m.Key);
await ProcessMovies(server, recentlyAdded, m.Key);
}
}
else
// Played state
var isPlayedSyncEnabled = await _feature.FeatureEnabled(FeatureNames.PlayedSync);
if(isPlayedSyncEnabled)
{
await ProcessMovies(server, recentlyAdded);
await ProcessTv(server, recentlyAdded);
await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyPlayedSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAdded.ToString() } }));
}
}
private async Task ProcessTv(EmbyServers server, bool recentlyAdded, string parentId = default)
protected async override Task ProcessTv(EmbyServers server, string parentId = default)
{
// TV Time
var mediaToAdd = new HashSet<EmbyContent>();
@ -196,7 +133,7 @@ namespace Ombi.Schedule.Jobs.Emby
await _repo.AddRange(mediaToAdd);
}
private async Task ProcessMovies(EmbyServers server, bool recentlyAdded, string parentId = default)
protected override async Task ProcessMovies(EmbyServers server, string parentId = default)
{
EmbyItemContainer<EmbyMovie> movies;
if (recentlyAdded)
@ -263,7 +200,12 @@ namespace Ombi.Schedule.Jobs.Emby
// Check if it exists
var existingMovie = await _repo.GetByEmbyId(movieInfo.Id);
var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id);
if (existingMovie == null && !alreadyGoingToAdd)
if (alreadyGoingToAdd)
{
_logger.LogDebug($"Detected duplicate for {movieInfo.Name}");
return;
}
if (existingMovie == null)
{
if (!movieInfo.ProviderIds.Any())
{
@ -319,36 +261,6 @@ namespace Ombi.Schedule.Jobs.Emby
content.Quality = has4K ? null : quality;
content.Has4K = has4K;
}
private bool ValidateSettings(EmbyServers server)
{
if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey))
{
_logger.LogInformation(LoggingEvents.EmbyContentCacher, $"Server {server?.Name} is not configured correctly");
return false;
}
return true;
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
//_settings?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

@ -0,0 +1,146 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Emby;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers;
using Ombi.Hubs;
using Quartz;
namespace Ombi.Schedule.Jobs.Emby
{
public abstract class EmbyLibrarySync
{
public EmbyLibrarySync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
INotificationHubService notification)
{
_logger = logger;
_settings = settings;
_apiFactory = api;
_notification = notification;
}
protected readonly ILogger<EmbyContentSync> _logger;
protected readonly ISettingsService<EmbySettings> _settings;
protected readonly IEmbyApiFactory _apiFactory;
protected bool recentlyAdded;
protected readonly INotificationHubService _notification;
protected const int AmountToTake = 100;
protected IEmbyApi Api { get; set; }
public virtual async Task Execute(IJobExecutionContext context)
{
JobDataMap dataMap = context.MergedJobDataMap;
if (dataMap.TryGetValue(JobDataKeys.EmbyRecentlyAddedSearch, out var recentlyAddedObj))
{
recentlyAdded = Convert.ToBoolean(recentlyAddedObj);
}
await _notification.SendNotificationToAdmins(recentlyAdded ? "Emby Recently Added Started" : "Emby Content Sync Started");
var embySettings = await _settings.GetSettingsAsync();
if (!embySettings.Enable)
return;
Api = _apiFactory.CreateClient(embySettings);
foreach (var server in embySettings.Servers)
{
try
{
await StartServerCache(server);
}
catch (Exception e)
{
await _notification.SendNotificationToAdmins("Emby Content Sync Failed");
_logger.LogError(e, "Exception when caching Emby for server {0}", server.Name);
}
}
await _notification.SendNotificationToAdmins("Emby Content Sync Finished");
}
private async Task StartServerCache(EmbyServers server)
{
if (!ValidateSettings(server))
{
return;
}
if (server.EmbySelectedLibraries.Any() && server.EmbySelectedLibraries.Any(x => x.Enabled))
{
var movieLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "movies");
foreach (var movieParentIdFilder in movieLibsToFilter)
{
_logger.LogInformation($"Scanning Lib '{movieParentIdFilder.Title}'");
await ProcessMovies(server, movieParentIdFilder.Key);
}
var tvLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "tvshows");
foreach (var tvParentIdFilter in tvLibsToFilter)
{
_logger.LogInformation($"Scanning Lib '{tvParentIdFilter.Title}'");
await ProcessTv(server, tvParentIdFilter.Key);
}
var mixedLibs = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "mixed");
foreach (var m in mixedLibs)
{
_logger.LogInformation($"Scanning Lib '{m.Title}'");
await ProcessTv(server, m.Key);
await ProcessMovies(server, m.Key);
}
}
else
{
await ProcessMovies(server);
await ProcessTv(server);
}
}
protected abstract Task ProcessTv(EmbyServers server, string parentId = default);
protected abstract Task ProcessMovies(EmbyServers server, string parentId = default);
private bool ValidateSettings(EmbyServers server)
{
if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey))
{
_logger.LogInformation(LoggingEvents.EmbyContentCacher, $"Server {server?.Name} is not configured correctly");
return false;
}
return true;
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
//_settings?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Api.Emby;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Movie;
using Ombi.Core.Authentication;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers;
using Ombi.Hubs;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
namespace Ombi.Schedule.Jobs.Emby
{
public class EmbyPlayedSync : EmbyLibrarySync, IEmbyPlayedSync
{
public EmbyPlayedSync(ISettingsService<EmbySettings> settings, IEmbyApiFactory api, ILogger<EmbyContentSync> logger,
IUserPlayedMovieRepository repo, INotificationHubService notification, OmbiUserManager user) : base(settings, api, logger, notification)
{
_userManager = user;
_repo = repo;
}
private OmbiUserManager _userManager { get; }
private readonly IUserPlayedMovieRepository _repo;
protected override Task ProcessTv(EmbyServers server, string parentId = default)
{
// TODO
return Task.CompletedTask;
}
protected async override Task ProcessMovies(EmbyServers server, string parentId = default)
{
var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync();
foreach (var user in allUsers)
{
await ProcessMoviesUser(server, user, parentId);
}
}
private async Task ProcessMoviesUser(EmbyServers server, OmbiUser user, string parentId = default)
{
EmbyItemContainer<EmbyMovie> movies;
if (recentlyAdded)
{
var recentlyAddedAmountToTake = 5; // to be adjusted?
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, user.ProviderUserId, server.FullUri);
// Setting this so we don't attempt to grab more than we need
if (movies.TotalRecordCount > recentlyAddedAmountToTake)
{
movies.TotalRecordCount = recentlyAddedAmountToTake;
}
}
else
{
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, 0, AmountToTake, user.ProviderUserId, server.FullUri);
}
var totalCount = movies.TotalRecordCount;
var processed = 0;
var mediaToAdd = new HashSet<UserPlayedMovie>();
while (processed < totalCount)
{
foreach (var movie in movies.Items)
{
await ProcessMovie(movie, user, mediaToAdd, server);
processed++;
}
// Get the next batch
// Recently Added should never be checked as the TotalRecords should equal the amount to take
if (!recentlyAdded)
{
movies = await Api.GetMoviesPlayed(server.ApiKey, parentId, processed, AmountToTake, user.ProviderUserId, server.FullUri);
}
await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();
}
}
private async Task ProcessMovie(EmbyMovie movieInfo, OmbiUser user, ICollection<UserPlayedMovie> content, EmbyServers server)
{
if (movieInfo.ProviderIds.Tmdb.IsNullOrEmpty())
{
_logger.LogWarning($"Movie {movieInfo.Name} has no relevant metadata. Skipping.");
return;
}
var userPlayedMovie = new UserPlayedMovie()
{
TheMovieDbId = int.Parse(movieInfo.ProviderIds.Tmdb),
UserId = user.Id
};
// Check if it exists
var existingMovie = await _repo.Get(userPlayedMovie.TheMovieDbId, userPlayedMovie.UserId);
var alreadyGoingToAdd = content.Any(x => x.TheMovieDbId == userPlayedMovie.TheMovieDbId && x.UserId == userPlayedMovie.UserId);
if (existingMovie == null && !alreadyGoingToAdd)
{
content.Add(userPlayedMovie);
}
}
}
}

@ -0,0 +1,6 @@
namespace Ombi.Schedule.Jobs.Emby
{
public interface IEmbyPlayedSync : IBaseJob
{
}
}

@ -228,7 +228,12 @@ namespace Ombi.Schedule.Jobs.Jellyfin
// Check if it exists
var existingMovie = await _repo.GetByJellyfinId(movieInfo.Id);
var alreadyGoingToAdd = content.Any(x => x.JellyfinId == movieInfo.Id);
if (existingMovie == null && !alreadyGoingToAdd)
if (alreadyGoingToAdd)
{
_logger.LogDebug($"Detected duplicate for {movieInfo.Name}");
return;
}
if (existingMovie == null)
{
if (!movieInfo.ProviderIds.Any())
{

@ -15,15 +15,22 @@ namespace Ombi.Schedule.Jobs.Ombi
{
public class MediaDatabaseRefresh : IMediaDatabaseRefresh
{
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IJellyfinContentRepository jellyfinRepo,
ISettingsService<EmbySettings> embySettings, ISettingsService<JellyfinSettings> jellyfinSettings)
public MediaDatabaseRefresh(
ISettingsService<PlexSettings> s,
ILogger<MediaDatabaseRefresh> log,
IPlexContentRepository plexRepo,
IEmbyContentRepository embyRepo,
IJellyfinContentRepository jellyfinRepo,
IUserPlayedMovieRepository userPlayedRepo,
ISettingsService<EmbySettings> embySettings,
ISettingsService<JellyfinSettings> jellyfinSettings)
{
_plexSettings = s;
_log = log;
_plexRepo = plexRepo;
_embyRepo = embyRepo;
_jellyfinRepo = jellyfinRepo;
_userPlayedRepo = userPlayedRepo;
_embySettings = embySettings;
_jellyfinSettings = jellyfinSettings;
_plexSettings.ClearCache();
@ -34,6 +41,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly IPlexContentRepository _plexRepo;
private readonly IEmbyContentRepository _embyRepo;
private readonly IJellyfinContentRepository _jellyfinRepo;
private readonly IUserPlayedMovieRepository _userPlayedRepo;
private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
@ -41,6 +49,7 @@ namespace Ombi.Schedule.Jobs.Ombi
{
try
{
await RemovePlayedData();
await RemovePlexData();
await RemoveEmbyData();
await RemoveJellyfinData();
@ -52,6 +61,20 @@ namespace Ombi.Schedule.Jobs.Ombi
}
private async Task RemovePlayedData()
{
try
{
const string movieSql = "DELETE FROM UserPlayedMovie";
await _userPlayedRepo.ExecuteSql(movieSql);
}
catch (Exception e)
{
_log.LogError(LoggingEvents.MediaReferesh, e, "Refreshing Played Data Failed");
}
}
private async Task RemoveEmbyData()
{
try

@ -46,8 +46,8 @@ namespace Ombi.Schedule.Jobs.Radarr
await tran.CommitAsync();
var radarrSettings = _radarrSettings.GetSettingsAsync();
var radarr4kSettings = _radarr4kSettings.GetSettingsAsync();
await Process(await radarrSettings);
var radarr4kSettings = _radarr4kSettings.GetSettingsAsync();
await Process(await radarr4kSettings);
}
catch (Exception)

@ -99,6 +99,7 @@ namespace Ombi.Schedule
await OmbiQuartz.Instance.AddJob<IEmbyContentSync>(nameof(IEmbyContentSync), "Emby", JobSettingsHelper.EmbyContent(s));
await OmbiQuartz.Instance.AddJob<IEmbyContentSync>(nameof(IEmbyContentSync) + "RecentlyAdded", "Emby", JobSettingsHelper.EmbyRecentlyAddedSync(s), new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, "true" } });
await OmbiQuartz.Instance.AddJob<IEmbyEpisodeSync>(nameof(IEmbyEpisodeSync), "Emby", null);
await OmbiQuartz.Instance.AddJob<IEmbyPlayedSync>(nameof(IEmbyPlayedSync), "Emby", null);
await OmbiQuartz.Instance.AddJob<IEmbyAvaliabilityChecker>(nameof(IEmbyAvaliabilityChecker), "Emby", null);
await OmbiQuartz.Instance.AddJob<IEmbyUserImporter>(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s));
}

@ -21,5 +21,6 @@ namespace Ombi.Settings.Settings.Models
{
public const string Movie4KRequests = nameof(Movie4KRequests);
public const string OldTrendingSource = nameof(OldTrendingSource);
public const string PlayedSync = nameof(PlayedSync);
}
}

@ -1,5 +1,6 @@
using Ombi.Helpers;
using Quartz;
using System;
namespace Ombi.Settings.Settings.Models
{
@ -104,7 +105,9 @@ namespace Ombi.Settings.Settings.Models
private static string ValidateCron(string cron)
{
if (CronExpression.IsValidExpression(cron))
CronExpression expression = new CronExpression(cron);
DateTimeOffset? nextFireUTCTime = expression.GetNextValidTimeAfter(DateTime.Now);
if (CronExpression.IsValidExpression(cron) && nextFireUTCTime != null)
{
return cron;
}

@ -41,6 +41,7 @@ namespace Ombi.Store.Context
public DbSet<SonarrEpisodeCache> SonarrEpisodeCache { get; set; }
public DbSet<SickRageCache> SickRageCache { get; set; }
public DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
public DbSet<UserPlayedMovie> UserPlayedMovie { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{

@ -84,5 +84,10 @@ namespace Ombi.Store.Entities.Requests
[NotMapped]
public override bool CanApprove => !Approved && !Available || !Approved4K && !Available4K;
[NotMapped]
public bool WatchedByRequestedUser { get; set; }
[NotMapped]
public int PlayedByUsersCount { get; set; }
}
}

@ -0,0 +1,8 @@
namespace Ombi.Store.Entities
{
public class UserPlayedMovie : Entity
{
public int TheMovieDbId { get; set; }
public string UserId { get; set; }
}
}

@ -0,0 +1,566 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context.MySql;
#nullable disable
namespace Ombi.Store.Migrations.ExternalMySql
{
[DbContext(typeof(ExternalMySqlContext))]
[Migration("20230406152218_MovieUserPlayed")]
partial class MovieUserPlayed
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<string>("EmbyId")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<string>("Quality")
.HasColumnType("longtext");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<string>("Url")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<string>("EmbyId")
.HasColumnType("longtext");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("ParentId")
.HasColumnType("varchar(255)");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("JellyfinId")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<string>("Quality")
.HasColumnType("longtext");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<string>("Url")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("JellyfinContent");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("JellyfinId")
.HasColumnType("longtext");
b.Property<string>("ParentId")
.HasColumnType("varchar(255)");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("JellyfinEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<int>("ArtistId")
.HasColumnType("int");
b.Property<string>("ForeignAlbumId")
.HasColumnType("longtext");
b.Property<bool>("Monitored")
.HasColumnType("tinyint(1)");
b.Property<decimal>("PercentOfTracks")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("ReleaseDate")
.HasColumnType("datetime(6)");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<int>("TrackCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("LidarrAlbumCache");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ArtistId")
.HasColumnType("int");
b.Property<string>("ArtistName")
.HasColumnType("longtext");
b.Property<string>("ForeignArtistId")
.HasColumnType("longtext");
b.Property<bool>("Monitored")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("LidarrArtistCache");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<string>("GrandparentKey")
.HasColumnType("varchar(255)");
b.Property<string>("Key")
.HasColumnType("longtext");
b.Property<string>("ParentKey")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<string>("Title")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ParentKey")
.HasColumnType("longtext");
b.Property<string>("PlexContentId")
.HasColumnType("longtext");
b.Property<int?>("PlexServerContentId")
.HasColumnType("int");
b.Property<string>("SeasonKey")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<string>("Quality")
.HasColumnType("longtext");
b.Property<string>("ReleaseYear")
.HasColumnType("longtext");
b.Property<int?>("RequestId")
.HasColumnType("int");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<string>("Url")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("TmdbId")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PlexWatchlistHistory");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<bool>("HasFile")
.HasColumnType("tinyint(1)");
b.Property<bool>("HasRegular")
.HasColumnType("tinyint(1)");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<bool>("HasFile")
.HasColumnType("tinyint(1)");
b.Property<int>("MovieDbId")
.HasColumnType("int");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.Property<string>("UserId")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("JellyfinId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Navigation("Episodes");
b.Navigation("Seasons");
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.ExternalMySql
{
public partial class MovieUserPlayed : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserPlayedMovie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
TheMovieDbId = table.Column<int>(type: "int", nullable: false),
UserId = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_UserPlayedMovie", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserPlayedMovie");
}
}
}

@ -16,7 +16,7 @@ namespace Ombi.Store.Migrations.ExternalMySql
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
@ -488,6 +488,23 @@ namespace Ombi.Store.Migrations.ExternalMySql
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.Property<string>("UserId")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")

@ -0,0 +1,564 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context.Sqlite;
#nullable disable
namespace Ombi.Store.Migrations.ExternalSqlite
{
[DbContext(typeof(ExternalSqliteContext))]
[Migration("20230310130339_MovieUserPlayed")]
partial class MovieUserPlayed
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<string>("EmbyId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<string>("Quality")
.HasColumnType("TEXT");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<string>("EmbyId")
.HasColumnType("TEXT");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("ParentId")
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("JellyfinId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<string>("Quality")
.HasColumnType("TEXT");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("JellyfinContent");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("JellyfinId")
.HasColumnType("TEXT");
b.Property<string>("ParentId")
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("JellyfinEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<int>("ArtistId")
.HasColumnType("INTEGER");
b.Property<string>("ForeignAlbumId")
.HasColumnType("TEXT");
b.Property<bool>("Monitored")
.HasColumnType("INTEGER");
b.Property<decimal>("PercentOfTracks")
.HasColumnType("TEXT");
b.Property<DateTime>("ReleaseDate")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<int>("TrackCount")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("LidarrAlbumCache");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("ArtistId")
.HasColumnType("INTEGER");
b.Property<string>("ArtistName")
.HasColumnType("TEXT");
b.Property<string>("ForeignArtistId")
.HasColumnType("TEXT");
b.Property<bool>("Monitored")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("LidarrArtistCache");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<string>("GrandparentKey")
.HasColumnType("TEXT");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("ParentKey")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ParentKey")
.HasColumnType("TEXT");
b.Property<string>("PlexContentId")
.HasColumnType("TEXT");
b.Property<int?>("PlexServerContentId")
.HasColumnType("INTEGER");
b.Property<string>("SeasonKey")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Quality")
.HasColumnType("TEXT");
b.Property<string>("ReleaseYear")
.HasColumnType("TEXT");
b.Property<int?>("RequestId")
.HasColumnType("INTEGER");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("TmdbId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("PlexWatchlistHistory");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<bool>("HasFile")
.HasColumnType("INTEGER");
b.Property<bool>("HasRegular")
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<bool>("HasFile")
.HasColumnType("INTEGER");
b.Property<int>("MovieDbId")
.HasColumnType("INTEGER");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("JellyfinId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Navigation("Episodes");
b.Navigation("Seasons");
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.ExternalSqlite
{
public partial class MovieUserPlayed : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserPlayedMovie",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
TheMovieDbId = table.Column<int>(type: "INTEGER", nullable: false),
UserId = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserPlayedMovie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserPlayedMovie");
}
}
}

@ -15,7 +15,7 @@ namespace Ombi.Store.Migrations.ExternalSqlite
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.0");
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
@ -486,6 +486,23 @@ namespace Ombi.Store.Migrations.ExternalSqlite
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ombi.Store.Entities;
namespace Ombi.Store.Repository
{
public interface IUserPlayedMovieRepository : IExternalRepository<UserPlayedMovie>
{
Task<UserPlayedMovie> Get(int theMovieDbId, string userId);
}
}

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Ombi.Store.Context;
using Ombi.Store.Entities;
namespace Ombi.Store.Repository
{
public class UserPlayedMovieRepository : ExternalRepository<UserPlayedMovie>, IUserPlayedMovieRepository
{
protected ExternalContext Db { get; }
public UserPlayedMovieRepository(ExternalContext db) : base(db)
{
Db = db;
}
public async Task<UserPlayedMovie> Get(int theMovieDbId, string userId)
{
return await Db.UserPlayedMovie.FirstOrDefaultAsync(x => x.TheMovieDbId == theMovieDbId && x.UserId == userId);
}
}
}

@ -1 +1,5 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"

@ -13,26 +13,26 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.1",
"@angular/animations": "^15.0.4",
"@angular/cdk": "^14.2.7",
"@angular/common": "^15.0.1",
"@angular/compiler": "^15.0.1",
"@angular/core": "^15.0.1",
"@angular/forms": "^15.0.1",
"@angular/localize": "^15.0.1",
"@angular/common": "^15.0.4",
"@angular/compiler": "^15.0.4",
"@angular/core": "^15.0.4",
"@angular/forms": "^15.0.4",
"@angular/localize": "^15.0.4",
"@angular/material": "^14.2.7",
"@angular/platform-browser": "^15.0.1",
"@angular/platform-browser-dynamic": "^15.0.1",
"@angular/platform-server": "^15.0.1",
"@angular/router": "^15.0.1",
"@angular/platform-browser": "^15.0.4",
"@angular/platform-browser-dynamic": "^15.0.4",
"@angular/platform-server": "^15.0.4",
"@angular/router": "^15.0.4",
"@angularclass/hmr": "^3.0.0",
"@auth0/angular-jwt": "^5.0.2",
"@fortawesome/fontawesome-free": "^6.0.0",
"@microsoft/signalr": "^6.0.7",
"@ngx-translate/core": "^14.0.0",
"@ngx-translate/http-loader": "^7.0.0",
"@ngxs/devtools-plugin": "^3.7.3",
"@ngxs/store": "^3.7.3",
"@ngxs/devtools-plugin": "3.7.3",
"@ngxs/store": "3.7.3",
"@types/jquery": "^3.5.14",
"@yellowspot/ng-truncate": "^2.0.0",
"angular-router-loader": "^0.8.5",
@ -58,7 +58,7 @@
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.2",
"@angular/cli": "^15.0.2",
"@angular/compiler-cli": "^15.0.1",
"@angular/compiler-cli": "^15.0.4",
"@babel/core": "^7.18.9",
"@compodoc/compodoc": "^1.1.19",
"@storybook/angular": "^6.5.9",

@ -22,7 +22,23 @@
</div>
<div class="row action-items">
<div class="col-12" *ngIf="isAdmin">
<button *ngIf="!request.approved" id="detailed-request-approve-{{request.mediaId}}" color="accent" mat-raised-button (click)="approve()">{{'Common.Approve' | translate}}</button>
<div *ngIf="!request.approved && !request.denied">
<button
id="detailed-request-approve-{{request.mediaId}}"
color="accent"
mat-raised-button
(click)="approve()"
matTooltip="{{'Common.Approve' | translate}}">
<mat-icon>check-circle</mat-icon>
</button>
<button
id="detailed-request-deny-{{request.mediaId}}"
color="accent"
mat-raised-button
(click)="deny()"
matTooltip="{{'Requests.Deny' | translate}}">
<mat-icon>cancel</mat-icon></button>
</div>
</div>
</div>
</div>

@ -50,5 +50,9 @@
position: absolute;
}
}
.action-items button {
margin: 4px;
}
}

@ -22,6 +22,7 @@ export class DetailedCardComponent implements OnInit, OnDestroy {
@Input() public isAdmin: boolean = false;
@Output() public onClick: EventEmitter<void> = new EventEmitter<void>();
@Output() public onApprove: EventEmitter<void> = new EventEmitter<void>();
@Output() public onDeny: EventEmitter<void> = new EventEmitter<void>();
public RequestType = RequestType;
public loading: false;
@ -41,6 +42,9 @@ export class DetailedCardComponent implements OnInit, OnDestroy {
}
public getStatus(request: IRecentlyRequested) {
if (request.denied) {
return "Common.Denied";
}
if (request.available) {
return "Common.Available";
}
@ -62,7 +66,14 @@ export class DetailedCardComponent implements OnInit, OnDestroy {
this.onApprove.emit();
}
public deny() {
this.onDeny.emit();
}
public getClass(request: IRecentlyRequested) {
if (request.denied) {
return "danger";
}
if (request.available || request.tvPartiallyAvailable) {
return "success";
}

@ -1,5 +1,8 @@
<div class="small-middle-container">
<div class="section">
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{'Discovery.Genres' | translate}}</h2>
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
</div>
<div class="section">
<h2>{{'Discovery.RecentlyRequestedTab' | translate}}</h2>
<div>

@ -0,0 +1,13 @@
<div class="genre-container" *ngIf="movieGenreList$ | async as movies">
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of movies"
class="discover-filter-buttons-group">
<mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle>
</mat-button-toggle-group>
</div>
<div class="genre-container" *ngIf="tvGenreList$ | async as tv">
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of tv"
class="discover-filter-buttons-group">
<mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle>
</mat-button-toggle-group>
<mat-spinner *ngIf="isLoading" [diameter]="30"></mat-spinner>
</div>

@ -0,0 +1,35 @@
h2{
margin-top:40px;
margin-left:40px;
font-size: 24px;
}
.discover-filter-buttons-group {
border: 1px solid #293a4c;
border-radius: 15px;
color:#fff;
margin-bottom:5px;
margin-right: 5px;
.discover-filter-button{
transform: scale(0.9);
background:inherit;
color:inherit;
padding:0 0px;
border-radius: 30px;
padding-left: 10px;
padding-right: 10px;
border-left:none;
}
}
.button-active{
background:#293a4c;
}
.genre-container {
margin-left: 35px;
}

@ -0,0 +1,49 @@
import { Component, OnInit } from "@angular/core";
import { SearchV2Service } from "../../../services";
import { MatButtonToggleChange } from "@angular/material/button-toggle";
import { RequestType } from "../../../interfaces";
import { AdvancedSearchDialogDataService } from "app/shared/advanced-search-dialog/advanced-search-dialog-data.service";
import { Router } from "@angular/router";
import { map, Observable } from "rxjs";
interface IGenreSelect {
name: string;
id: number;
type: "movie"|"tv";
}
@Component({
selector: "genre-button-select",
templateUrl: "./genre-button-select.component.html",
styleUrls: ["./genre-button-select.component.scss"],
})
export class GenreButtonSelectComponent implements OnInit {
public movieGenreList$: Observable<IGenreSelect[]> = null;
public tvGenreList$: Observable<IGenreSelect[]> = null;
isLoading: boolean = false;
constructor(private searchService: SearchV2Service,
private advancedSearchService: AdvancedSearchDialogDataService,
private router: Router) { }
public ngOnInit(): void {
this.movieGenreList$ = this.searchService.getGenres("movie").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "movie" }))));
this.tvGenreList$ = this.searchService.getGenres("tv").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "tv" }))));
}
public async toggleChanged(event: MatButtonToggleChange, type: "movie"|"tv") {
this.isLoading = true;
const genres: number[] = [event.value];
const data = await this.searchService.advancedSearch({
watchProviders: [],
genreIds: genres,
keywordIds: [],
type: type,
}, 0, 30);
this.advancedSearchService.setData(data, type == "movie" ? RequestType.movie : RequestType.tvShow);
this.advancedSearchService.setOptions([], genres, [], null, type == "movie" ? RequestType.movie : RequestType.tvShow, 30);
this.router.navigate([`discover/advanced/search`]);
}
}

@ -12,6 +12,7 @@ import { MatDialog } from "@angular/material/dialog";
import { RequestServiceV2 } from "../../services/requestV2.service";
import { Routes } from "@angular/router";
import { DetailedCardComponent } from "app/components";
import { GenreButtonSelectComponent } from "./genre/genre-button-select.component";
export const components: any[] = [
DiscoverComponent,
@ -22,6 +23,7 @@ export const components: any[] = [
CarouselListComponent,
RecentlyRequestedListComponent,
DetailedCardComponent,
GenreButtonSelectComponent
];
export const providers: any[] = [

@ -3,8 +3,13 @@
<p-carousel #carousel [value]="requests" [numVisible]="3" [numScroll]="1"
[responsiveOptions]="responsiveOptions" [page]="0">
<ng-template let-result pTemplate="item">
<ombi-detailed-card [request]="result" [isAdmin]="isAdmin" (onClick)="navigate(result)"
(onApprove)="approve(result)"></ombi-detailed-card>
<ombi-detailed-card
[request]="result"
[isAdmin]="isAdmin"
(onClick)="navigate(result)"
(onApprove)="approve(result)"
(onDeny)="deny(result)">
</ombi-detailed-card>
</ng-template>
</p-carousel>
</div>

@ -8,6 +8,8 @@ import { Router } from "@angular/router";
import { AuthService } from "app/auth/auth.service";
import { NotificationService, RequestService } from "app/services";
import { TranslateService } from "@ngx-translate/core";
import { DenyDialogComponent } from '../../../media-details/components/shared/deny-dialog/deny-dialog.component';
import { MatDialog } from "@angular/material/dialog";
export enum DiscoverType {
Upcoming,
@ -42,7 +44,8 @@ export class RecentlyRequestedListComponent implements OnInit, OnDestroy {
private router: Router,
private authService: AuthService,
private notificationService: NotificationService,
private translateService: TranslateService) {
private translateService: TranslateService,
public dialog: MatDialog) {
Carousel.prototype.onTouchMove = () => {};
this.responsiveOptions = ResponsiveOptions;
}
@ -81,6 +84,20 @@ export class RecentlyRequestedListComponent implements OnInit, OnDestroy {
}
}
public deny(request: IRecentlyRequested) {
const dialogRef = this.dialog.open(DenyDialogComponent, {
width: '250px',
data: { requestId: request.requestId, is4K: false, requestType: request.type }
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.notificationService.success(this.translateService.instant("Requests.SuccessfullyDenied"));
request.denied = true;
}
});
}
private handleApproval(result: IRequestEngineResult, request: IRecentlyRequested) {
if (result.result) {
this.notificationService.success(this.translateService.instant("Requests.SuccessfullyApproved"));

@ -9,7 +9,7 @@
[infiniteScrollDistance]="3"
[infiniteScrollThrottle]="200"
(scrolled)="onScroll()">
<div id="searchResults" class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults" data-test="searchResultsCount" attr.search-count="{{discoverResults.length}}">
<div id="searchResults" class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding card-container" *ngFor="let result of discoverResults" data-test="searchResultsCount" attr.search-count="{{discoverResults.length}}">
<discover-card [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card>
</div>
</div>

@ -11,6 +11,7 @@ export interface IRecentlyRequested {
overview: string;
releaseDate: Date;
approved: boolean;
denied: boolean;
mediaId: string;
type: RequestType;

@ -23,6 +23,8 @@ export interface IMovieRequests extends IFullBaseRequest {
deniedReason4K: string;
requestedDate4k: Date;
requestedDate: Date;
watchedByRequestedUser: boolean;
playedByUsersCount: number;
// For the UI
rootPathOverrideTitle: string;
@ -212,4 +214,4 @@ export class BaseRequestOptions {
requestOnBehalf: string | undefined;
rootFolderOverride: number | undefined;
qualityPathOverride: number | undefined;
}
}

@ -2,4 +2,5 @@ export interface ITesterResult {
isValid: boolean;
version?: string;
expectedSubDir?: string;
additionalInformation?: string;
}

@ -36,18 +36,7 @@
</div>
<div *ngIf="(!movie.available && movie.requested) || (!movie.available4K && movie.has4KRequest)">
<span class="label">{{'MediaDetails.RequestStatus' | translate }}</span>
<div *ngIf="!movie.available && movie.requested">
<div *ngIf="movie.denied">{{'Common.RequestDenied' | translate}}</div>
<div *ngIf="movie.approved && !movie.available">{{'Common.ProcessingRequest' | translate}}</div>
<div *ngIf="movie.requested && !movie.approved">{{'Common.PendingApproval' | translate}}</div>
<!--<div *ngIf="!movie.requested && !movie.available && !movie.approved">{{'Common.NotRequested' | translate}}</div>-->
</div>
<div *ngIf="!movie.available4K && movie.has4KRequest">
<div *ngIf="movie.denied4K">{{'Common.RequestDenied4K' | translate}}</div>
<div *ngIf="movie.approved4K && !movie.available4K">{{'Common.ProcessingRequest4K' | translate}}</div>
<div *ngIf="movie.requested4K && !movie.approved4K && !movie.available4K">{{'Common.PendingApproval4K' | translate}}</div>
<!--<div *ngIf="!movie.requested4K && !movie.available4K && !movie.approved4K">{{'Common.NotRequested4K' | translate}}</div>-->
</div>
<div>{{getStatus(movie) | translate}}</div>
</div>
<div *ngIf="request">
@ -66,12 +55,12 @@
{{RequestSource[request.source]}}
</div>
<div *ngIf="request">
<div *ngIf="request && ( request.deniedReason || request.deniedReason4K )">
<span class="label">{{'MediaDetails.DeniedReason' | translate }}</span>
<div *ngIf="request.denied">
<div *ngIf="request.deniedReason">
<span id="deniedReasonInfo">{{request.deniedReason}}</span>
</div>
<div *ngIf="request.denied4K">
<div *ngIf="request.deniedReason4K">
<span id="deniedReasonInfo4K">{{request.deniedReason4K}}</span>
</div>
</div>

@ -34,4 +34,28 @@ export class MovieInformationPanelComponent implements OnInit {
this.searchService.getMovieStreams(this.movie.id).subscribe(x => this.streams = x);
}
public getStatus(movie: ISearchMovieResultV2) {
if (!movie.available && movie.requested) {
if (movie.denied) {
return "Common.RequestDenied";
}
if (movie.approved) {
return "Common.ProcessingRequest";
} else {
return "Common.PendingApproval";
}
}
if (!movie.available4K && movie.has4KRequest) {
if (movie.denied4K) {
return "Common.RequestDenied4K";
}
if (movie.approved4K) {
return "Common.ProcessingRequest4K";
} else {
return "Common.PendingApproval4K";
}
}
}
}

@ -63,6 +63,7 @@ $ombi-accent: #258a6d;
::ng-deep .discoverResults{
margin-top:40px;
margin-left: 35px;
}
::ng-deep button:focus{

@ -52,7 +52,7 @@
<ng-container matColumnDef="requestedUser.requestedBy">
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedUser?.userAlias}} </td>
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedByAlias ? element.requestedByAlias : element.requestedUser?.userAlias}} </td>
</ng-container>
@ -80,6 +80,23 @@
<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">

@ -24,10 +24,11 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
public dataSource: MatTableDataSource<IMovieRequests>;
public resultsLength: number;
public isLoadingResults = true;
public displayedColumns: string[] = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate', 'actions'];
public displayedColumns: string[];
public gridCount: string = "15";
public isAdmin: boolean;
public is4kEnabled = false;
public isPlayedSyncEnabled = false;
public manageOwnRequests: boolean;
public defaultSort: string = "requestedDate";
public defaultOrder: string = "desc";
@ -60,15 +61,10 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
public ngOnInit() {
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.manageOwnRequests = this.auth.hasRole("ManageOwnRequests")
if (this.isAdmin) {
this.displayedColumns.unshift('select');
}
this.is4kEnabled = this.featureFacade.is4kEnabled();
if ((this.isAdmin || this.auth.hasRole("Request4KMovie"))
&& this.is4kEnabled) {
this.displayedColumns.splice(4, 0, 'has4kRequest');
}
this.isPlayedSyncEnabled = this.featureFacade.isPlayedSyncEnabled();
const defaultCount = this.storageService.get(this.storageKeyGridCount);
const defaultSort = this.storageService.get(this.storageKey);
@ -88,8 +84,31 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
}
}
setDisplayedColumns() {
this.displayedColumns = ['title', 'requestedUser.requestedBy', 'status', 'requestStatus','requestedDate'];
if (this.isAdmin) {
this.displayedColumns.unshift('select');
}
if ((this.isAdmin || this.auth.hasRole("Request4KMovie"))
&& this.is4kEnabled) {
this.displayedColumns.splice(4, 0, 'has4kRequest');
}
if (this.isPlayedSyncEnabled
&& ( this.currentFilter == RequestFilterType.All || this.currentFilter == RequestFilterType.Available ) ) {
this.displayedColumns.push('watchedByRequestedUser');
}
// always put the actions column at the end
this.displayedColumns.push('actions');
}
public async ngAfterViewInit() {
this.setDisplayedColumns();
this.storageService.save(this.storageKeyGridCount, this.gridCount);
this.storageService.save(this.storageKeyCurrentFilter, (+this.currentFilter).toString());
@ -263,4 +282,4 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
}
return request.requestedDate;
}
}
}

@ -38,7 +38,7 @@
<ng-container matColumnDef="requestedBy">
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedUser.userAlias}} </td>
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedByAlias ? element.requestedByAlias : element.requestedUser.userAlias}} </td>
</ng-container>
<ng-container matColumnDef="requestedDate">
@ -73,4 +73,4 @@
</table>
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
</div>
</div>

@ -1,7 +1,7 @@
import { Component, OnInit } from "@angular/core";
import { UntypedFormBuilder, FormControl, UntypedFormGroup, Validators } from "@angular/forms";
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { SonarrFacade } from "app/state/sonarr/sonarr.facade";
import { finalize, map } from "rxjs";
import { catchError, finalize, map, of } from "rxjs";
import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder, ITag } from "../../interfaces";
@ -95,7 +95,7 @@ export class SonarrComponent implements OnInit {
this.tags = [];
this.animeTags = [];
if (version.length > 0) {
if (version?.length > 0) {
this.sonarrVersion = version[0];
}
@ -132,11 +132,19 @@ export class SonarrComponent implements OnInit {
public getProfiles(form: UntypedFormGroup) {
this.profilesRunning = true;
this.sonarrService.getQualityProfiles(form.value)
.pipe(catchError((_) => {
this.notificationService.error("Could not load Quality Profiles");
return of([]);
}))
.subscribe(x => {
this.profilesRunning = false;
if (x.length === 0) {
return;
}
this.qualities = x;
this.qualitiesAnime = x;
this.qualities.unshift({ name: "Please Select", id: -1 });
this.profilesRunning = false;
this.notificationService.success("Successfully retrieved the Quality Profiles");
});
}
@ -144,12 +152,19 @@ export class SonarrComponent implements OnInit {
public getRootFolders(form: UntypedFormGroup) {
this.rootFoldersRunning = true;
this.sonarrService.getRootFolders(form.value)
.pipe(catchError((_) => {
this.notificationService.error("Could not load Root Folders");
return of([]);
}))
.subscribe(x => {
this.rootFoldersRunning = false;
if (x.length === 0) {
return;
}
this.rootFolders = x;
this.rootFolders.unshift({ path: "Please Select", id: -1 });
this.rootFoldersAnime = x;
this.rootFoldersRunning = false;
this.notificationService.success("Successfully retrieved the Root Folders");
});
}
@ -157,11 +172,18 @@ export class SonarrComponent implements OnInit {
public getLanguageProfiles(form: UntypedFormGroup) {
this.langRunning = true;
this.sonarrService.getV3LanguageProfiles(form.value)
.pipe(catchError((_) => {
this.notificationService.error("Could not load Language Profiles");
return of([]);
}))
.subscribe(x => {
this.langRunning = false;
if (x.length === 0) {
return;
}
this.languageProfiles = x;
this.languageProfilesAnime = x;
this.langRunning = false;
this.notificationService.success("Successfully retrieved the Language Profiles");
});
}
@ -169,11 +191,18 @@ export class SonarrComponent implements OnInit {
public getTags(form: UntypedFormGroup) {
this.tagsRunning = true;
this.sonarrService.getTags(form.value).pipe(
catchError((_) => {
this.notificationService.error("Could not load Tags");
return of([]);
}),
finalize(() => {
this.tagsRunning = false;
if (this.tags.length === 0) {
return;
}
this.animeTags.unshift({ label: "None", id: -1 });
this.tags.unshift({ label: "None", id: -1 });
this.notificationService.success("Successfully retrieved the Tags");
this.notificationService.success("Successfully retrieved the Tags")
}),
map(result => {
this.tags = result;
@ -191,7 +220,11 @@ export class SonarrComponent implements OnInit {
} else if (result.expectedSubDir) {
this.notificationService.error("Your Sonarr Base URL must be set to " + result.expectedSubDir);
} else {
this.notificationService.error("We could not connect to Sonarr!");
if (result.additionalInformation) {
this.notificationService.error(result.additionalInformation);
} else {
this.notificationService.error("We could not connect to Sonarr!");
}
}
});
}
@ -204,16 +237,19 @@ export class SonarrComponent implements OnInit {
if (form.controls.defaultQualityProfile) {
if (form.controls.defaultQualityProfile.value === "-1") {
this.notificationService.error("Please check your entered values");
return;
}
}
if (form.controls.defaultRootPath) {
if (form.controls.defaultRootPath.value === "Please Select") {
this.notificationService.error("Please check your entered values");
return;
}
}
if (form.controls.languageProfile) {
if (form.controls.languageProfile.value === "Please Select") {
this.notificationService.error("Please check your entered values");
return;
}
}
if (form.controls.animeTag.value == -1) {

@ -23,4 +23,6 @@ export class FeaturesFacade {
public is4kEnabled = (): boolean => this.store.selectSnapshot(FeaturesSelectors.is4kEnabled);
}
public isPlayedSyncEnabled = (): boolean => this.store.selectSnapshot(FeaturesSelectors.isPlayedSyncEnabled);
}

@ -15,4 +15,9 @@ export class FeaturesSelectors {
return features.filter(x => x.name === "Movie4KRequests")[0].enabled;
}
}
@Selector([FeaturesSelectors.features])
public static isPlayedSyncEnabled(features: IFeatureEnablement[]): boolean {
return features.filter(x => x.name === "PlayedSync")[0].enabled;
}
}

@ -18,7 +18,7 @@ export class RadarrSettingsState {
@Action(LoadSettings)
public load({ setState }: StateContext<RadarrState>): Observable<RadarrState> {
const isAdmin = this.authService.isAdmin();
const isAdmin = this.authService.hasRole("Admin");
const calls = isAdmin ? [this.settingsService.getRadarr()] : [of({})];
return combineLatest(calls).pipe(

@ -18,7 +18,7 @@ export class SonarrSettingsState {
@Action(LoadSettings)
public load({ setState }: StateContext<SonarrState>): Observable<SonarrState> {
const isAdmin = this.authService.isAdmin();
const isAdmin = this.authService.hasRole("Admin");
const calls = isAdmin ? [this.sonarrService.getVersion(), this.settingsService.getSonarr()] : [of(""), of({})];
return combineLatest(calls).pipe(
@ -31,7 +31,7 @@ export class SonarrSettingsState {
}
@Action(UpdateSettings)
public enable(ctx: StateContext<SonarrState>, { settings }: UpdateSettings): Observable<SonarrState> {
public update(ctx: StateContext<SonarrState>, { settings }: UpdateSettings): Observable<SonarrState> {
const state = ctx.getState();
return this.settingsService.saveSonarr(settings).pipe(
tap((_) => ctx.setState({...state, settings})),

@ -204,14 +204,14 @@ __metadata:
languageName: node
linkType: hard
"@angular/animations@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/animations@npm:15.0.1"
"@angular/animations@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/animations@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/core": 15.0.1
checksum: 88b75c0c93acd462a793ab82f26463dc0868dcfd2552ede91ef76f72ec9d8723a48a15e026050b741944befb969461c4b1b18494dac2f926a1decee290caee3d
"@angular/core": 15.2.4
checksum: 8fe91a126cac08f1bda95fe7d78bf789e61255211e545dfdd0378eb9f52e0a4a3c4683f1fc925fceed2bfaf9146ce872a7df11f73e92b184fc042565a41609b3
languageName: node
linkType: hard
@ -260,98 +260,98 @@ __metadata:
languageName: node
linkType: hard
"@angular/common@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/common@npm:15.0.1"
"@angular/common@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/common@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/core": 15.0.1
"@angular/core": 15.2.4
rxjs: ^6.5.3 || ^7.4.0
checksum: 8cc0f0df94e92c83fffeb72b11e2b8f4d322cdcaedfc9b94cddaef9f2ef7a1a48a8db8db386c41245f9cffba922bec423f30674cb62d7f53262cdaea5a4198aa
checksum: 089436758dc98d7a653bcf0301d090f588ee931d142033c06de5da569db2fd5e924ee84e8baac4fd8bdebdbd1d20b8af1611cc2b2a9bc4f7c7e7c2e9606f1934
languageName: node
linkType: hard
"@angular/compiler-cli@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/compiler-cli@npm:15.0.1"
"@angular/compiler-cli@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/compiler-cli@npm:15.2.4"
dependencies:
"@babel/core": ^7.17.2
"@babel/core": 7.19.3
"@jridgewell/sourcemap-codec": ^1.4.14
chokidar: ^3.0.0
convert-source-map: ^1.5.1
dependency-graph: ^0.11.0
magic-string: ^0.26.0
magic-string: ^0.27.0
reflect-metadata: ^0.1.2
semver: ^7.0.0
sourcemap-codec: ^1.4.8
tslib: ^2.3.0
yargs: ^17.2.1
peerDependencies:
"@angular/compiler": 15.0.1
typescript: ">=4.8.2 <4.9"
"@angular/compiler": 15.2.4
typescript: ">=4.8.2 <5.0"
bin:
ng-xi18n: bundles/src/bin/ng_xi18n.js
ngc: bundles/src/bin/ngc.js
ngcc: bundles/ngcc/main-ngcc.js
checksum: f92b6f579d72b7159ad33e5240e1dd87c109bee3628bb4a8c0dc8dd263dd021547eb6fbcd8ec7a4b7bec162b464cd8a40829c3d20cba649b0ee1685330591f62
checksum: 9ef61841627f336c5c40d0c3e3ef505390b2171f26d36dd242a00bef7c30c985b0edf198ca2d965ad1a2c779caab27204452d88dc865993af0e395023e261a53
languageName: node
linkType: hard
"@angular/compiler@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/compiler@npm:15.0.1"
"@angular/compiler@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/compiler@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/core": 15.0.1
"@angular/core": 15.2.4
peerDependenciesMeta:
"@angular/core":
optional: true
checksum: 3c0d932805cd7e4164accdf6d8d00899dff286c85ab75f30434277a7ba8f92f2cbbbfc4dc1074dc49ce8c6250adb2e12e4f357a439c8ffe71fae14da3612898d
checksum: f47676eb0160be35b5f44e625a3bea43e51fc84c3aa05a9bbebd1fcd357fa73331dc4de32af2dbec7e1f254fb0e65ed9e149e8e7cf61eb81464453c666ff78d1
languageName: node
linkType: hard
"@angular/core@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/core@npm:15.0.1"
"@angular/core@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/core@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
rxjs: ^6.5.3 || ^7.4.0
zone.js: ~0.11.4 || ~0.12.0
checksum: c87fbbd4fefe237e311901d48f84e7790788acde30ee489af3e763f2423446a0010a4bc25d1e659b1170f8c1597ca306ae95e2e6956e93e02a7a708a1acb233b
zone.js: ~0.11.4 || ~0.12.0 || ~0.13.0
checksum: ab7ad7b6b55fe24ff1f390ea09c04d52301146075488fc4ee4700d77e9f24ad8b3a03f3ca48b6f4306f6b2106e22b541b23bfb5df3fca94606b94fd95b20df7b
languageName: node
linkType: hard
"@angular/forms@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/forms@npm:15.0.1"
"@angular/forms@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/forms@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/common": 15.0.1
"@angular/core": 15.0.1
"@angular/platform-browser": 15.0.1
"@angular/common": 15.2.4
"@angular/core": 15.2.4
"@angular/platform-browser": 15.2.4
rxjs: ^6.5.3 || ^7.4.0
checksum: fd60fb50a8af40bbe4f725391b4b6c8f454953f980e3b50493b9d3cd637dd5f8d51ae30871661ab369996a7d8df37297eb816252447b87650ea32ca56b35d180
checksum: cc23288506d62f2e1e86e22ab795176b46d63aa0f821515b9549bb10b583975e45b67667d14d1eb52c24a645ad81e7e8693855d5d7f521c27ab4ef0ee871791b
languageName: node
linkType: hard
"@angular/localize@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/localize@npm:15.0.1"
"@angular/localize@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/localize@npm:15.2.4"
dependencies:
"@babel/core": 7.19.3
glob: 8.0.3
glob: 8.1.0
yargs: ^17.2.1
peerDependencies:
"@angular/compiler": 15.0.1
"@angular/compiler-cli": 15.0.1
"@angular/compiler": 15.2.4
"@angular/compiler-cli": 15.2.4
bin:
localize-extract: tools/bundles/src/extract/cli.js
localize-migrate: tools/bundles/src/migrate/cli.js
localize-translate: tools/bundles/src/translate/cli.js
checksum: b03f50c4540a6c00e646ab67483ae732aeed57ec1d7c5a301031b6ca3304ec2a4846ae8911d8d3c6ca21fb2b59d33688a5c0f55bdff499abd529664206610697
checksum: 999976488ea1aacfd61f738bbb4933dff7e6b144bf066e3ede2ebd1bf267e70c8fffaeb8ef8666fe0c823827f35fc597dfb222014237c87cba92c06e753d0db5
languageName: node
linkType: hard
@ -372,65 +372,65 @@ __metadata:
languageName: node
linkType: hard
"@angular/platform-browser-dynamic@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/platform-browser-dynamic@npm:15.0.1"
"@angular/platform-browser-dynamic@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/platform-browser-dynamic@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/common": 15.0.1
"@angular/compiler": 15.0.1
"@angular/core": 15.0.1
"@angular/platform-browser": 15.0.1
checksum: 8e8c9645b6d662f79ed769b96cd9c8da3edd536532d6accae53e69054dfd77841b449e76c271e756adae679ee0e4b2bc3f3ee06aec1133db50d067db3e2575df
"@angular/common": 15.2.4
"@angular/compiler": 15.2.4
"@angular/core": 15.2.4
"@angular/platform-browser": 15.2.4
checksum: e48fe83b1d9723ee26a1b4343d7dfd1537027e01f559382b819174f4f25e700ff46c0390fc35dbc4550fa374d37a6d449f7a9504c55d0d7b8e7f90e2785c2048
languageName: node
linkType: hard
"@angular/platform-browser@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/platform-browser@npm:15.0.1"
"@angular/platform-browser@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/platform-browser@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/animations": 15.0.1
"@angular/common": 15.0.1
"@angular/core": 15.0.1
"@angular/animations": 15.2.4
"@angular/common": 15.2.4
"@angular/core": 15.2.4
peerDependenciesMeta:
"@angular/animations":
optional: true
checksum: 87ceb94dea9f9d4de3a444bcc307e15f8a7b53218143ae7dc30886cc64eba9e9a9b430e96a70d7f37b6d06aacb9a2e33c17dffac87452576c52b8099e02a3a0e
checksum: 67a52b676362614840df1056a579daf9f3763bacec9c2e4fff64109070624b3d2daa3bd2bb8fad776a79e65239f90c9227aa97de3a1b22e8ac397b84038c02e9
languageName: node
linkType: hard
"@angular/platform-server@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/platform-server@npm:15.0.1"
"@angular/platform-server@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/platform-server@npm:15.2.4"
dependencies:
domino: ^2.1.2
tslib: ^2.3.0
xhr2: ^0.2.0
peerDependencies:
"@angular/animations": 15.0.1
"@angular/common": 15.0.1
"@angular/compiler": 15.0.1
"@angular/core": 15.0.1
"@angular/platform-browser": 15.0.1
"@angular/platform-browser-dynamic": 15.0.1
checksum: bfbb51013f21a6c889c39ba200573b922f58390dd8a86f1803a9f24d2ad984e5ab890e38dbcec612c5a35298aca92287de48d16a5762807972c3fe67a4fc3d1a
"@angular/animations": 15.2.4
"@angular/common": 15.2.4
"@angular/compiler": 15.2.4
"@angular/core": 15.2.4
"@angular/platform-browser": 15.2.4
"@angular/platform-browser-dynamic": 15.2.4
checksum: e6f3d0b6222356c4af9201e8d8747e4657f1977beb223d39ab7ef60634937d49d89c0826db11ed3d8b46b92bf8dda38dffc1bb001195266ed1618edc8f378d96
languageName: node
linkType: hard
"@angular/router@npm:^15.0.1":
version: 15.0.1
resolution: "@angular/router@npm:15.0.1"
"@angular/router@npm:^15.0.4":
version: 15.2.4
resolution: "@angular/router@npm:15.2.4"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/common": 15.0.1
"@angular/core": 15.0.1
"@angular/platform-browser": 15.0.1
"@angular/common": 15.2.4
"@angular/core": 15.2.4
"@angular/platform-browser": 15.2.4
rxjs: ^6.5.3 || ^7.4.0
checksum: f9a8b80398cf8024dfac30c2b36d1870c25b4f1f07df8e8039226e975fc7c7b977ca9e5c050dc707e8e2036f1577f799d74796c15dead8f256fa8c0199e6bd1a
checksum: ef98f2f22e78379fdaf6dd6f4732c161aa6e596d236e8f7e8e893ab488342f28e3f76127b2b24076697800073dccaea29a6c6c376082cd2354be859f3faa273d
languageName: node
linkType: hard
@ -568,7 +568,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:^7.17.2, @babel/core@npm:^7.17.5, @babel/core@npm:^7.18.9":
"@babel/core@npm:^7.17.5, @babel/core@npm:^7.18.9":
version: 7.18.10
resolution: "@babel/core@npm:7.18.10"
dependencies:
@ -2391,7 +2391,7 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10":
"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14":
version: 1.4.14
resolution: "@jridgewell/sourcemap-codec@npm:1.4.14"
checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97
@ -2508,28 +2508,28 @@ __metadata:
languageName: node
linkType: hard
"@ngxs/devtools-plugin@npm:^3.7.3":
version: 3.7.6
resolution: "@ngxs/devtools-plugin@npm:3.7.6"
"@ngxs/devtools-plugin@npm:3.7.3":
version: 3.7.3
resolution: "@ngxs/devtools-plugin@npm:3.7.3"
dependencies:
tslib: ^1.9.0
peerDependencies:
"@angular/core": ">=6.1.0 <16.0.0"
"@ngxs/store": ^3.7.6 || ^3.7.6-dev
"@angular/core": ">=6.1.0 <14.0.0"
"@ngxs/store": ^3.7.3 || ^3.7.3-dev
rxjs: ">=6.5.5"
checksum: f20a5ecf0cdfb7af17ff3075819f7eb6c324dce07f4b111d518d66e573037d3f3f842f3ee4f4ec7a1b8603e8293355c9e5e3455ca532749e2bdcc7ac4fed5c00
checksum: c6c4bcbda46be3d73bbece2c6f9f5002df2564850d2bb6ab66f66751e3b54d6aba4363a29e26ca0f205b509468ec5d8a82c25409b3eb377d4efe3026663baa6c
languageName: node
linkType: hard
"@ngxs/store@npm:^3.7.3":
version: 3.7.6
resolution: "@ngxs/store@npm:3.7.6"
"@ngxs/store@npm:3.7.3":
version: 3.7.3
resolution: "@ngxs/store@npm:3.7.3"
dependencies:
tslib: ^1.9.0
peerDependencies:
"@angular/core": ">=6.1.0 <16.0.0"
"@angular/core": ">=6.1.0 <14.0.0"
rxjs: ">=6.5.5"
checksum: b1582d5157f36dfe1700d9f41ec6a935c794d732d474e998d3a276d6a5005e725071124e5137b83248ced2e1591c10b1fbff97c5db5d5fac21e3fc5bb70cb55b
checksum: 756c47c3463a30bb0dc97a62d9a22928fbd4de1f15e05735457b3dfc0dadd6292273c5e8e71bf4b06f2ea9b339e995143284094264fa5136b82317c46ed61ae0
languageName: node
linkType: hard
@ -2674,6 +2674,13 @@ __metadata:
languageName: node
linkType: hard
"@scarf/scarf@npm:^1.1.0":
version: 1.1.1
resolution: "@scarf/scarf@npm:1.1.1"
checksum: f3205e7a76fed1ec3328643a5f276f4aab5c67d0e17ca0a414755aacae529f4a3492b44607f9ad066470ecb3ee003e72b3d9581f58dc6c507c18180611ff113e
languageName: node
linkType: hard
"@schematics/angular@npm:15.0.2":
version: 15.0.2
resolution: "@schematics/angular@npm:15.0.2"
@ -9221,6 +9228,19 @@ cors@latest:
languageName: node
linkType: hard
"glob@npm:8.1.0":
version: 8.1.0
resolution: "glob@npm:8.1.0"
dependencies:
fs.realpath: ^1.0.0
inflight: ^1.0.4
inherits: 2
minimatch: ^5.0.1
once: ^1.3.0
checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47
languageName: node
linkType: hard
"glob@npm:^7.0.3, glob@npm:^7.0.6, glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0":
version: 7.2.3
resolution: "glob@npm:7.2.3"
@ -11425,12 +11445,12 @@ cors@latest:
languageName: node
linkType: hard
"magic-string@npm:^0.26.0":
version: 0.26.2
resolution: "magic-string@npm:0.26.2"
"magic-string@npm:^0.27.0":
version: 0.27.0
resolution: "magic-string@npm:0.27.0"
dependencies:
sourcemap-codec: ^1.4.8
checksum: b4db4e2b370ac8d9ffc6443a2b591b75364bf1fc9121b5a4068d5b89804abff6709d1fa4a0e0c2d54f2e61e0e44db83efdfe219a5ab0ba6d25ee1f2b51fbed55
"@jridgewell/sourcemap-codec": ^1.4.13
checksum: 273faaa50baadb7a2df6e442eac34ad611304fc08fe16e24fe2e472fd944bfcb73ffb50d2dc972dc04e92784222002af46868cb9698b1be181c81830fd95a13e
languageName: node
linkType: hard
@ -12223,15 +12243,13 @@ cors@latest:
languageName: node
linkType: hard
"ngx-infinite-scroll@npm:^14.0.0":
version: 14.0.1
resolution: "ngx-infinite-scroll@npm:14.0.1"
"ngx-infinite-scroll@npm:^9.0.0":
version: 9.1.0
resolution: "ngx-infinite-scroll@npm:9.1.0"
dependencies:
tslib: ^2.3.0
peerDependencies:
"@angular/common": ">=14.0.0 <15.0.0"
"@angular/core": ">=14.0.0 <15.0.0"
checksum: 7df67fc87a6638d4fa3ade5ea83549ccaf4ecf28e68eb2da81d8f8aec96c2edd69d7452de842f23d558f414cfb424738602aef509310ec25117eb25005056b75
"@scarf/scarf": ^1.1.0
opencollective-postinstall: ^2.0.2
checksum: c1cb914ed49d377daeddce346485d2228826a22af418f9b72f6aa02823f50feb85b248fde04664bb2075c64070b6c8958e4f06a61ec0f0c153a75a4edfff4945
languageName: node
linkType: hard
@ -12773,20 +12791,20 @@ cors@latest:
resolution: "ombi@workspace:."
dependencies:
"@angular-devkit/build-angular": ^15.0.2
"@angular/animations": ^15.0.1
"@angular/animations": ^15.0.4
"@angular/cdk": ^14.2.7
"@angular/cli": ^15.0.2
"@angular/common": ^15.0.1
"@angular/compiler": ^15.0.1
"@angular/compiler-cli": ^15.0.1
"@angular/core": ^15.0.1
"@angular/forms": ^15.0.1
"@angular/localize": ^15.0.1
"@angular/common": ^15.0.4
"@angular/compiler": ^15.0.4
"@angular/compiler-cli": ^15.0.4
"@angular/core": ^15.0.4
"@angular/forms": ^15.0.4
"@angular/localize": ^15.0.4
"@angular/material": ^14.2.7
"@angular/platform-browser": ^15.0.1
"@angular/platform-browser-dynamic": ^15.0.1
"@angular/platform-server": ^15.0.1
"@angular/router": ^15.0.1
"@angular/platform-browser": ^15.0.4
"@angular/platform-browser-dynamic": ^15.0.4
"@angular/platform-server": ^15.0.4
"@angular/router": ^15.0.4
"@angularclass/hmr": ^3.0.0
"@auth0/angular-jwt": ^5.0.2
"@babel/core": ^7.18.9
@ -12795,8 +12813,8 @@ cors@latest:
"@microsoft/signalr": ^6.0.7
"@ngx-translate/core": ^14.0.0
"@ngx-translate/http-loader": ^7.0.0
"@ngxs/devtools-plugin": ^3.7.3
"@ngxs/store": ^3.7.3
"@ngxs/devtools-plugin": 3.7.3
"@ngxs/store": 3.7.3
"@storybook/angular": ^6.5.9
"@types/jquery": ^3.5.14
"@yellowspot/ng-truncate": ^2.0.0
@ -12810,7 +12828,7 @@ cors@latest:
moment: ^2.29.1
ng2-cookies: ^1.0.12
ngx-clipboard: ^12.1.0
ngx-infinite-scroll: ^14.0.0
ngx-infinite-scroll: ^9.0.0
ngx-moment: ^3.0.1
ngx-order-pipe: ^2.2.0
popper.js: ^1.14.3
@ -12898,7 +12916,7 @@ cors@latest:
languageName: node
linkType: hard
"opencollective-postinstall@npm:^2.0.3":
"opencollective-postinstall@npm:^2.0.2, opencollective-postinstall@npm:^2.0.3":
version: 2.0.3
resolution: "opencollective-postinstall@npm:2.0.3"
bin:

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -59,6 +60,7 @@ namespace Ombi.Controllers.V1.External
[PowerUser]
public async Task<IEnumerable<SonarrProfile>> GetProfiles()
{
SonarrSettings.ClearCache();
var settings = await SonarrSettings.GetSettingsAsync();
if (settings.Enabled)
{
@ -75,6 +77,7 @@ namespace Ombi.Controllers.V1.External
[PowerUser]
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders()
{
SonarrSettings.ClearCache();
var settings = await SonarrSettings.GetSettingsAsync();
if (settings.Enabled)
{
@ -92,6 +95,7 @@ namespace Ombi.Controllers.V1.External
[PowerUser]
public async Task<IEnumerable<LanguageProfiles>> GetLanguageProfiles()
{
SonarrSettings.ClearCache();
var settings = await SonarrSettings.GetSettingsAsync();
if (settings.Enabled)
{
@ -147,6 +151,7 @@ namespace Ombi.Controllers.V1.External
[PowerUser]
public async Task<bool> Enabled()
{
SonarrSettings.ClearCache();
var settings = await SonarrSettings.GetSettingsAsync();
return settings.Enabled;
}
@ -155,13 +160,21 @@ namespace Ombi.Controllers.V1.External
[PowerUser]
public async Task<string> SonarrVersion()
{
SonarrSettings.ClearCache();
var settings = await SonarrSettings.GetSettingsAsync();
if (!settings.Enabled)
{
return string.Empty;
}
var status = await SonarrV3Api.SystemStatus(settings.ApiKey, settings.FullUri);
return status.version;
try
{
var status = await SonarrV3Api.SystemStatus(settings.ApiKey, settings.FullUri);
return status.version;
}
catch (Exception)
{
return string.Empty;
}
}
}
}

@ -410,6 +410,30 @@ namespace Ombi.Controllers.V1.External
{
try
{
if (string.IsNullOrEmpty(settings.ApiKey))
{
return new TesterResultModel
{
IsValid = false,
AdditionalInformation = "NullApiKey"
};
}
if (string.IsNullOrEmpty(settings.Ip))
{
return new TesterResultModel
{
IsValid = false,
AdditionalInformation = "NullIp"
};
}
if (settings.Port <= 0)
{
return new TesterResultModel
{
IsValid = false,
AdditionalInformation = "BadPort"
};
}
var result = await SonarrApi.SystemStatus(settings.ApiKey, settings.FullUri);
return new TesterResultModel

@ -652,7 +652,9 @@ namespace Ombi.Controllers.V1
try
{
var isValid = CronExpression.IsValidExpression(expression);
if (!isValid)
CronExpression cron = new CronExpression(expression);
DateTimeOffset? nextFireUTCTime = cron.GetNextValidTimeAfter(DateTime.Now);
if (!isValid || nextFireUTCTime == null)
{
return new JobSettingsViewModel
{

@ -44,25 +44,33 @@ namespace Ombi.Controllers.V2
}
[HttpGet("logs/{logFileName}")]
public async Task<IActionResult> ReadLogFile(string logFileName, CancellationToken token)
public async Task<IActionResult> ReadLogFile(string logFileName)
{
var logFile = Path.Combine(string.IsNullOrEmpty(Ombi.Helpers.StartupSingleton.Instance.StoragePath) ? _hosting.ContentRootPath : Helpers.StartupSingleton.Instance.StoragePath, "Logs", logFileName);
using (var fs = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader reader = new StreamReader(fs))
var logsFolder = Path.Combine(string.IsNullOrEmpty(Ombi.Helpers.StartupSingleton.Instance.StoragePath) ? _hosting.ContentRootPath : Helpers.StartupSingleton.Instance.StoragePath, "Logs");
var files = Directory.EnumerateFiles(logsFolder);
var matchingFile = files.FirstOrDefault(x => Path.GetFileName(x).Equals(logFileName));
if (matchingFile != null)
{
using var fs = new FileStream(matchingFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader reader = new(fs);
return Ok(await reader.ReadToEndAsync());
}
return NotFound();
}
[HttpGet("logs/download/{logFileName}")]
public IActionResult Download(string logFileName, CancellationToken token)
public IActionResult Download(string logFileName)
{
var logFile = Path.Combine(string.IsNullOrEmpty(Ombi.Helpers.StartupSingleton.Instance.StoragePath) ? _hosting.ContentRootPath : Helpers.StartupSingleton.Instance.StoragePath, "Logs", logFileName);
using (var fs = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader reader = new StreamReader(fs))
var logsFolder = Path.Combine(string.IsNullOrEmpty(Ombi.Helpers.StartupSingleton.Instance.StoragePath) ? _hosting.ContentRootPath : Helpers.StartupSingleton.Instance.StoragePath, "Logs");
var files = Directory.EnumerateFiles(logsFolder);
var matchingFile = files.FirstOrDefault(x => Path.GetFileName(x).Equals(logFileName));
if (matchingFile != null)
{
using var fs = new FileStream(matchingFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader reader = new(fs);
return File(reader.BaseStream, "application/octet-stream", logFileName);
}
return NotFound();
}
}
}

@ -159,6 +159,9 @@
"RequestedBy": "Заявено от",
"Status": "Състояние",
"RequestStatus": "Състояние на заявката",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Отказано:",
"TheatricalRelease": "Кино премиера: {{date}}",
"ReleaseDate": "Премиера: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
@ -403,6 +407,7 @@
"Movies": "Филми",
"Combined": "Комбинирано",
"Tv": "Телевизия",
"Genres": "Genres",
"CardDetails": {
"Availability": "Наличност",
"Studio": "Студио",

@ -159,6 +159,9 @@
"RequestedBy": "Sol·licitat per",
"Status": "Estat",
"RequestStatus": "Estat de la sol·licitud",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Denegat:",
"TheatricalRelease": "En cines: {{date}}",
"ReleaseDate": "Llançament: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Els elements seleccionats s'han denegat correctament"
},
"SuccessfullyApproved": "Aprovat correctament",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "La sol·licitud s'ha suprimit correctament",
"NowAvailable": "La sol·licitud ja està disponible",
"NowUnavailable": "La sol·licitud ja no està disponible",
@ -403,6 +407,7 @@
"Movies": "Pel·lícules",
"Combined": "Combinat",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Disponibilitat",
"Studio": "Estudi",

@ -159,6 +159,9 @@
"RequestedBy": "Požadováno od",
"Status": "Stav",
"RequestStatus": "Stav požadavku",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Zamítnuto:",
"TheatricalRelease": "V kinech od: {{date}}",
"ReleaseDate": "Vydáno: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Úspěšně schváleno",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Požadavek byl úspěšně odstraněn",
"NowAvailable": "Požadavek je nyní k dispozici",
"NowUnavailable": "Požadavek je nyní nedostupný",
@ -403,6 +407,7 @@
"Movies": "Filmy",
"Combined": "Kombinované",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Dostupnost",
"Studio": "Studio",

@ -159,6 +159,9 @@
"RequestedBy": "Anmodet af",
"Status": "Status",
"RequestStatus": "Status for anmodning",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Afvist:",
"TheatricalRelease": "Biografudgivelse: {{date}}",
"ReleaseDate": "Udgivet: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Godkendt",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Anmodningen blev slettet",
"NowAvailable": "Anmodningen er nu tilgængelig",
"NowUnavailable": "Anmodningen er nu utilgængelig",
@ -403,6 +407,7 @@
"Movies": "Film",
"Combined": "Kombineret",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Tilgængelighed",
"Studio": "Studie",

@ -159,6 +159,9 @@
"RequestedBy": "Angefordert von",
"Status": "Status",
"RequestStatus": "Anfrage Status",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Abgelehnt:",
"TheatricalRelease": "Kinostart: {{date}}",
"ReleaseDate": "Veröffentlicht: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Ausgewählte Elemente erfolgreich abgelehnt"
},
"SuccessfullyApproved": "Erfolgreich genehmigt",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Anfrage erfolgreich gelöscht",
"NowAvailable": "Anfrage ist jetzt verfügbar",
"NowUnavailable": "Anfrage ist jetzt verfügbar",
@ -403,6 +407,7 @@
"Movies": "Filme",
"Combined": "Kombiniert",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Verfügbarkeit",
"Studio": "Studio",

@ -159,6 +159,9 @@
"RequestedBy": "Requested By",
"Status": "Status",
"RequestStatus": "Request status",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Denied:",
"TheatricalRelease": "Theatrical Release: {{date}}",
"ReleaseDate": "Released: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
@ -403,6 +407,7 @@
"Movies": "Movies",
"Combined": "Combined",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Availability",
"Studio": "Studio",

@ -159,6 +159,9 @@
"RequestedBy": "Solicitado por",
"Status": "Estado",
"RequestStatus": "Estado de la solicitud",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Denegado:",
"TheatricalRelease": "En cines: {{date}}",
"ReleaseDate": "Publicado: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Elementos seleccionados rechazados con éxito"
},
"SuccessfullyApproved": "Se ha aprobado con éxito",
"SuccessfullyDenied": "Denegado Correctamente",
"SuccessfullyDeleted": "Solicitud eliminada con éxito",
"NowAvailable": "La solicitud está disponible",
"NowUnavailable": "La solicitud no está disponible",
@ -403,6 +407,7 @@
"Movies": "Películas",
"Combined": "Combinado",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Disponibilidad",
"Studio": "Estudio",

@ -159,6 +159,9 @@
"RequestedBy": "Demandé par",
"Status": "Statut",
"RequestStatus": "Statut de la demande",
"Watched": "Vu",
"WatchedTooltip": "L'utilisateur qui a fait la demande l'a regardé",
"WatchedByUsersCount": "{{count}} utilisateurs l'ont regardé.",
"Denied": " Refusé :",
"TheatricalRelease": "Sortie en salle : {{date}}",
"ReleaseDate": "Sortie : {{date}}",
@ -221,6 +224,7 @@
"Denied": "Les éléments sélectionnés ont été refusés"
},
"SuccessfullyApproved": "Approuvée avec succès",
"SuccessfullyDenied": "Refusé avec succès",
"SuccessfullyDeleted": "Demande supprimée avec succès",
"NowAvailable": "La demande est maintenant disponible",
"NowUnavailable": "La demande est maintenant indisponible",
@ -403,6 +407,7 @@
"Movies": "Films",
"Combined": "Tous",
"Tv": "Séries",
"Genres": "Genres",
"CardDetails": {
"Availability": "Disponibilité",
"Studio": "Studio",

@ -14,7 +14,7 @@
"Common": {
"ContinueButton": "Tovább",
"Available": "Elérhető",
"Available4K": "Available 4K",
"Available4K": "Elérhető 4K",
"Approved": "Jóváhagyva",
"Approve4K": "Approve 4K",
"Pending": "Függőben",
@ -24,7 +24,7 @@
"ProcessingRequest": "Kérés feldolgozása",
"ProcessingRequest4K": "Processing Request 4K",
"PendingApproval": "Jóváhagyásra vár",
"PendingApproval4K": "Pending Approval 4K",
"PendingApproval4K": "Függőben 4K",
"RequestDenied": "Kérés megtagadva",
"RequestDenied4K": "Request Denied 4K",
"NotRequested": "Nincs kérve",
@ -159,6 +159,9 @@
"RequestedBy": "Kérte",
"Status": "Állapot",
"RequestStatus": "Kérés állapota",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Megtagadta:",
"TheatricalRelease": "Mozis kiadás: {{date}}",
"ReleaseDate": "Kiadva: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Sikeresen jóváhagyva",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Kérés sikeresen törölve",
"NowAvailable": "Kérés elérhető",
"NowUnavailable": "Kérés nem elérhető",
@ -312,7 +316,7 @@
},
"MediaDetails": {
"Denied": "Megtagadva",
"Denied4K": "Denied 4K",
"Denied4K": "Megtagadva 4K",
"Trailers": "Előzetesek",
"RecommendationsTitle": "Ajánlások",
"SimilarTitle": "Hasonló",
@ -365,7 +369,7 @@
"CastTitle": "Szereplők"
},
"Crews": {
"CrewTitle": "Crew"
"CrewTitle": "Stáb"
},
"EpisodeSelector": {
"AllSeasonsTooltip": "Ezzel kérni fogja a sorozat összes évadát",
@ -392,7 +396,7 @@
"StartDate": "Kezdés dátuma:",
"EndDate": "Befejezés dátuma:"
},
"RequestSource": "Source:"
"RequestSource": "Forrás:"
},
"Discovery": {
"PopularTab": "Népszerű",
@ -403,6 +407,7 @@
"Movies": "Filmek",
"Combined": "Kombinált",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Elérhetőség",
"Studio": "Stúdió",

@ -159,6 +159,9 @@
"RequestedBy": "Richiesta da",
"Status": "Stato",
"RequestStatus": "Stato della richiesta",
"Watched": "Visti",
"WatchedTooltip": "L'utente che ha fatto la richiesta l'ha visto",
"WatchedByUsersCount": "Visto da {{count}} utenti.",
"Denied": "Negata:",
"TheatricalRelease": "Uscita nei cinema: {{date}}",
"ReleaseDate": "Rilasciato il {{date}}",
@ -221,6 +224,7 @@
"Denied": "Elementi selezionati rifiutati con successo"
},
"SuccessfullyApproved": "Approvata",
"SuccessfullyDenied": "Negata Con Successo",
"SuccessfullyDeleted": "Richiesta eliminata correttamente",
"NowAvailable": "Richiesta ora disponibile",
"NowUnavailable": "Richiesta ora non disponibile",
@ -365,7 +369,7 @@
"CastTitle": "Trasmetti"
},
"Crews": {
"CrewTitle": "Crew"
"CrewTitle": "Gruppo"
},
"EpisodeSelector": {
"AllSeasonsTooltip": "Richiederà tutte le stagioni per questa serie",
@ -403,6 +407,7 @@
"Movies": "Film",
"Combined": "Combinato",
"Tv": "TV",
"Genres": "Generi",
"CardDetails": {
"Availability": "Disponibilità",
"Studio": "Studio",

@ -30,10 +30,10 @@
"NotRequested": "Niet verzocht",
"NotRequested4K": "Niet aangevraagd 4K",
"Requested": "Aangevraagd",
"Requested4K": "Requested 4K",
"Requested4K": "Aangevraagd 4K",
"Search": "Zoeken",
"Request": "Aanvragen",
"Request4K": "Request 4K",
"Request4K": "Aanvragen 4K",
"Denied": "Afgewezen",
"Approve": "Accepteer",
"PartlyAvailable": "Deels Beschikbaar",
@ -43,7 +43,7 @@
},
"Cancel": "Annuleren",
"Submit": "Verzenden",
"Update": "Update",
"Update": "Bijwerken",
"tvShow": "Tv programma",
"movie": "Film",
"album": "Album"
@ -64,14 +64,14 @@
"CheckPageForUpdates": "Controleer deze pagina voor updates."
},
"ErrorPages": {
"NotFound": "Page not found",
"SomethingWentWrong": "Something went wrong!"
"NotFound": "Pagina niet gevonden",
"SomethingWentWrong": "Sorry, er ging iets mis!"
},
"NavigationBar": {
"Discover": "Ontdekken",
"Search": "Zoeken",
"Requests": "Verzoeken",
"UserManagement": "Gebruikersmanagement",
"UserManagement": "Gebruikers",
"Issues": "Problemen",
"Vote": "Stem",
"Donate": "Doneer!",
@ -87,7 +87,7 @@
"ChangeTheme": "Thema wijzigen",
"Calendar": "Agenda",
"UserPreferences": "Instellingen",
"FeatureSuggestion": "Feature Suggestion",
"FeatureSuggestion": "Ideeën delen",
"FeatureSuggestionTooltip": "Heb je een geweldig nieuw idee? Stel het hier voor!",
"Filter": {
"Movies": "Films",
@ -105,8 +105,8 @@
"MoviesTab": "Films",
"TvTab": "TV Series",
"MusicTab": "Muziek",
"AdvancedSearch": "You can fill in any of the below to discover new media. All of the results are sorted by popularity",
"AdvancedSearchHeader": "Advanced Search",
"AdvancedSearch": "Je kunt hieronder een van de onderstaande invullen om nieuwe media te ontdekken. Alle resultaten zijn gesorteerd op populariteit",
"AdvancedSearchHeader": "Geavanceerd Zoeken",
"Suggestions": "Suggesties",
"NoResults": "Sorry, er zijn geen resultaten gevonden!",
"DigitalDate": "Digitale Uitgave: {{date}}",
@ -141,12 +141,12 @@
"Season": "Seizoen {{seasonNumber}}",
"SelectAllInSeason": "Selecteer Alles in het Seizoen {{seasonNumber}}"
},
"AdvancedSearchInstructions": "Please choose what type of media you are searching for:",
"YearOfRelease": "Year of Release",
"SearchGenre": "Search Genre",
"SearchKeyword": "Search Keyword",
"SearchProvider": "Search Provider",
"KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb"
"AdvancedSearchInstructions": "Kies naar welk type media je zoekt:",
"YearOfRelease": "Jaar van de uitgave",
"SearchGenre": "Zoeken op genre",
"SearchKeyword": "Trefwoorden zoeken",
"SearchProvider": "Zoekmachine",
"KeywordSearchingDisclaimer": "Houd er rekening mee dat het zoeken naar trefwoord erg hit en miss is door de inconsistente gegevens in TheMovieDb"
},
"Requests": {
"Title": "Verzoeken",
@ -159,6 +159,9 @@
"RequestedBy": "Verzocht Door",
"Status": "Status",
"RequestStatus": "Aanvraagstatus",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Geweigerd:",
"TheatricalRelease": "Cinema Uitgave: {{date}}",
"ReleaseDate": "Uitgekomen: {{date}}",
@ -170,15 +173,15 @@
"ChangeRootFolder": "Hoofdmap wijzigen",
"ChangeQualityProfile": "Kwaliteitsprofiel wijzigen",
"MarkUnavailable": "Markeren als onbeschikbaar",
"MarkUnavailable4K": "Mark Unavailable 4K",
"MarkUnavailable4K": "Markeer niet beschikbaar 4K",
"MarkAvailable": "Markeren als beschikbaar",
"MarkAvailable4K": "Mark Available 4K",
"MarkAvailable4K": "Markeer beschikbaar 4K",
"Remove": "Verwijderen",
"Deny": "Weigeren",
"Deny4K": "Deny 4K",
"Has4KRequest": "Has 4K Request",
"Deny4K": "Weiger 4K",
"Has4KRequest": "Heeft 4K verzoek",
"DenyReason": "Reden van afwijzing",
"DeniedReason": "Denied Reason",
"DeniedReason": "Reden van afwijzing",
"Season": "Seizoen",
"GridTitle": "Titel",
"AirDate": "Uitzenddatum",
@ -212,52 +215,53 @@
"RequestPanel": {
"Delete": "Verwijder Verzoek",
"Approve": "Verzoek Goedkeuren",
"Deny": "Deny Request",
"Approve4K": "Approve 4K Request",
"Deny4K": "Deny 4K Request",
"Deny": "Aanvraag weigeren",
"Approve4K": "4K Verzoek Goedkeuren",
"Deny4K": "4K Verzoek Weigeren",
"ChangeAvailability": "Markeer beschikbaar",
"Deleted": "Successfully deleted selected items",
"Approved": "Successfully approved selected items",
"Denied": "Successfully denied selected items"
"Deleted": "Geselecteerde items succesvol verwijderd",
"Approved": "Geselecteerde items succesvol goedgekeurd",
"Denied": "Geselecteerde items succesvol afgekeurd"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
"SuccessfullyReprocessed": "Successfully Re-processed the request",
"DeniedRequest": "Denied Request",
"RequestCollection": "Request Collection",
"CollectionSuccesfullyAdded": "The collection {{name}} has been successfully added!",
"NeedToSelectEpisodes": "You need to select some episodes!",
"SuccessfullyApproved": "Succesvol goedgekeurd",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Verzoek succesvol verwijderd",
"NowAvailable": "Verzoek is nu beschikbaar",
"NowUnavailable": "Verzoek is nu niet beschikbaar",
"SuccessfullyReprocessed": "De aanvraag is met succes opnieuw verwerkt",
"DeniedRequest": "Geweigerde verzoek(en)",
"RequestCollection": "Collectie aanvragen",
"CollectionSuccesfullyAdded": "De collectie {{name}} is succesvol toegevoegd!",
"NeedToSelectEpisodes": "Je moet enkele afleveringen selecteren!",
"RequestAddedSuccessfully": "Aanvraag voor {{title}} is succesvol toegevoegd",
"ErrorCodes": {
"AlreadyRequested": "This has already been requested",
"EpisodesAlreadyRequested": "We already have episodes requested from this series",
"NoPermissionsOnBehalf": "You do not have the correct permissions to request on behalf of users!",
"NoPermissions": "You do not have the correct permissions!",
"RequestDoesNotExist": "Request does not exist",
"AlreadyRequested": "Dit is reeds aangevraagd",
"EpisodesAlreadyRequested": "We hebben al aanvragen voor deze serie",
"NoPermissionsOnBehalf": "Je hebt niet de juiste rechten om namens gebruikers aan te vragen!",
"NoPermissions": "Je hebt de juiste rechten niet!",
"RequestDoesNotExist": "Verzoek bestaat niet",
"ChildRequestDoesNotExist": "Child Request does not exist",
"NoPermissionsRequestMovie": "You do not have permissions to Request a Movie",
"NoPermissionsRequestTV": "You do not have permissions to Request a TV Show",
"NoPermissionsRequestAlbum": "You do not have permissions to Request an Album",
"MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!",
"TvRequestQuotaExceeded": "You have exceeded your Episode request quota!",
"AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!"
"NoPermissionsRequestMovie": "Je bent niet gemachtigd om een film aan te vragen",
"NoPermissionsRequestTV": "Je bent niet gemachtigd om een serie aan te vragen",
"NoPermissionsRequestAlbum": "Je bent niet gemachtigd om een album aan te vragen",
"MovieRequestQuotaExceeded": "Je hebt het maximale aantal film aanvragen bereikt!",
"TvRequestQuotaExceeded": "Je hebt het maximale aantal afleveringen aanvragen bereikt!",
"AlbumRequestQuotaExceeded": "Je hebt het maximale aantal albums aanvragen bereikt!"
},
"Notify": "Notify",
"RemoveNotification": "Remove Notifications",
"SuccessfulNotify": "You will now be notified for title {{title}}",
"SuccessfulUnNotify": "You will no longer be notified for title {{title}}",
"CouldntNotify": "Couldn't notify title {{title}}"
"Notify": "Notificatie",
"RemoveNotification": "Notificaties verwijderen",
"SuccessfulNotify": "Je krijgt nu een melding voor titel {{title}}",
"SuccessfulUnNotify": "Je wordt niet langer op de hoogte gehouden voor titel {{title}}",
"CouldntNotify": "Kan titel {{title}} niet melden"
},
"Issues": {
"Title": "Problemen",
"IssuesForTitle": "Issues for {{title}}",
"IssuesForTitle": "Problemen voor {{title}}",
"PendingTitle": "Onopgeloste Problemen",
"InProgressTitle": "Problemen in Behandeling",
"ResolvedTitle": "Opgeloste Problemen",
"ColumnTitle": "Titel",
"Count": "Count",
"Count": "Aantal",
"Category": "Categorie",
"Status": "Status",
"Details": "Details",
@ -284,10 +288,10 @@
"MarkedAsInProgress": "Dit probleem is nu gemarkeerd als in behandeling!",
"Delete": "Verwijder probleem",
"DeletedIssue": "Probleem is verwijderd",
"Chat": "Chat",
"EnterYourMessage": "Enter Your Message",
"Chat": "Chatten",
"EnterYourMessage": "Voer je bericht in",
"Requested": "Aangevraagd",
"UserOnDate": "{{user}} on {{date}}"
"UserOnDate": "{{user}} op {{date}}"
},
"Filter": {
"ClearFilter": "Verwijder Filter",
@ -295,8 +299,8 @@
"FilterHeaderRequestStatus": "Status",
"Approved": "Goedgekeurd",
"PendingApproval": "In afwachting van goedkeuring",
"WatchProviders": "Watch Providers",
"Keywords": "Keywords"
"WatchProviders": "Bekijk aanbieders",
"Keywords": "Trefwoorden"
},
"UserManagment": {
"TvRemaining": "Tv: {{remaining}}/{{total}} Resterend",
@ -312,7 +316,7 @@
},
"MediaDetails": {
"Denied": "Afgewezen",
"Denied4K": "Denied 4K",
"Denied4K": "Geweigerd 4K",
"Trailers": "Trailers",
"RecommendationsTitle": "Aanbevelingen",
"SimilarTitle": "Vergelijkbaar",
@ -324,28 +328,28 @@
"ViewCollection": "Bekijk collectie",
"NotEnoughInfo": "Helaas is er nog niet genoeg informatie over deze tv-serie!",
"AdvancedOptions": "Geavanceerde opties",
"AutoApproveOptions": "You can configure the request here, once requested it will be sent to your DVR application and will be auto approved! Please note, this is optional, just press Request to skip!",
"AutoApproveOptionsTv": "You can configure the request here, once requested it will be sent to your DVR application and will be auto approved! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it! Please note, this is optional, just press Request to skip!",
"AutoApproveOptionsTvShort": "You can configure the request here, once requested it will be sent to your DVR application! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it! Please note, this is optional, just press Request to skip!",
"AutoApproveOptions": "U kunt het verzoek hier configureren, eenmaal gevraagd zal het naar uw DVR-applicatie worden gestuurd en automatisch worden goedgekeurd! Let op, dit is optioneel, druk op Verzoek om over te slaan!",
"AutoApproveOptionsTv": "U kunt het verzoek hier configureren, eenmaal gevraagd zal het naar uw DVR-applicatie worden gestuurd en automatisch worden goedgekeurd! Als het verzoek al in Sonarr is, zullen we de hoofdmap of het kwaliteitsprofiel niet wijzigen als je het instelt! Let op, dit is optioneel, druk op Verzoek om over te slaan!",
"AutoApproveOptionsTvShort": "U kunt het verzoek hier configureren, eenmaal gevraagd zal het naar uw DVR-applicatie worden gestuurd en automatisch worden goedgekeurd! Als het verzoek al in Sonarr is, zullen we de hoofdmap of het kwaliteitsprofiel niet wijzigen als je het instelt! Let op, dit is optioneel, druk op Verzoek om over te slaan!",
"QualityProfilesSelect": "Selecteer een kwaliteitsprofiel",
"RootFolderSelect": "Selecteer een hoofdmap",
"LanguageProfileSelect": "Select A Language Profile",
"LanguageProfileSelect": "Selecteer een taalprofiel",
"Status": "Status:",
"StatusValues": {
"Rumored": "Rumored",
"Planned": "Planned",
"In Production": "In Production",
"Post Production": "Post Production",
"Released": "Released",
"Running": "Running",
"Returning Series": "Returning Series",
"Ended": "Ended",
"Canceled": "Canceled"
"Rumored": "Geruchten",
"Planned": "Gepland",
"In Production": "In productie",
"Post Production": "Postproductie",
"Released": "Uitgebracht",
"Running": "Lopende series",
"Returning Series": "Terugkerende series",
"Ended": "Beëindigd",
"Canceled": "Geannuleerd"
},
"Seasons": "Seasons:",
"Seasons": "Seizoenen:",
"Episodes": "Afleveringen:",
"Availability": "Beschikbaarheid:",
"RequestStatus": "Request Status:",
"RequestStatus": "Aanvraag status:",
"Quality": "Kwaliteit:",
"RootFolderOverride": "Hoofdmap overschrijven:",
"QualityOverride": "Kwaliteit overschrijven:",
@ -371,7 +375,7 @@
"AllSeasonsTooltip": "Dit verzoekt ieder seizoen van deze serie",
"FirstSeasonTooltip": "Dit verzoekt alleen het eerste seizoen van deze serie",
"LatestSeasonTooltip": "Dit verzoekt alleen het laatste seizoen van deze show",
"NoEpisodes": "There unfortunately is no episode data for this show yet!",
"NoEpisodes": "Er zijn helaas nog geen aflevering gegevens voor deze TV-serie!",
"SeasonNumber": "Seizoen {{number}}"
},
"SonarrConfiguration": "Sonarr configuratie",
@ -380,29 +384,30 @@
"PleaseSelectUser": "Selecteer een gebruiker",
"StreamingOn": "Streamt op:",
"RequestedBy": "Verzocht Door:",
"OnDate": "On:",
"RequestedByOn": "Requested By {{user}} on {{date}}",
"OnDate": "Op:",
"RequestedByOn": "Aangevraagd door {{user}} op {{date}}",
"RequestDate": "Aanvraag Datum:",
"DeniedReason": "Denied Reason:",
"ReProcessRequest": "Re-Process Request",
"ReProcessRequest4K": "Re-Process 4K Request",
"DeniedReason": "Reden van afwijzing:",
"ReProcessRequest": "Aanvraag opnieuw verwerken",
"ReProcessRequest4K": "Aanvraag opnieuw verwerken",
"Music": {
"Type": "Type:",
"Country": "Country:",
"StartDate": "Start Date:",
"EndDate": "EndDate:"
"Country": "Land:",
"StartDate": "Startdatum:",
"EndDate": "Einddatum:"
},
"RequestSource": "Source:"
"RequestSource": "Bron:"
},
"Discovery": {
"PopularTab": "Populair",
"TrendingTab": "Populair",
"UpcomingTab": "Aankomend",
"SeasonalTab": "Seasonal",
"RecentlyRequestedTab": "Recently Requested",
"SeasonalTab": "Seizoen",
"RecentlyRequestedTab": "Recente verzoeken",
"Movies": "Films",
"Combined": "Gecombineerd",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Beschikbaarheid",
"Studio": "Studio",
@ -423,37 +428,37 @@
"DarkMode": "Donkere Modus",
"Updated": "Succesvol bijgewerkt",
"StreamingCountry": "Streaming Land",
"StreamingCountryDescription": "This is the country code that we will display streaming information for. If you are in the US please select US and you will get US related streaming information.",
"StreamingCountryDescription": "Dit is de landcode waarvoor we streaming informatie tonen. Als u in de VS bent, selecteer dan US en u krijgt Amerikaanse gerelateerde streaming informatie.",
"LanguageDescription": "Dit is de taal waarin Ombi in wordt weergegeven.",
"MobileQRCode": "QR code voor mobiel",
"LegacyApp": "Start legacy app",
"NoQrCode": "Please contact your administrator to enable QR codes",
"UserType": "User Type:",
"ChangeDetails": "Change Details",
"NeedCurrentPassword": "You need your current password to make any changes here",
"CurrentPassword": "Current Password",
"NoQrCode": "Neem contact op met de beheerder om QR-codes in te schakelen",
"UserType": "Gebruikerstype:",
"ChangeDetails": "Gegevens wijzigen",
"NeedCurrentPassword": "Je hebt je huidige wachtwoord nodig om hier wijzigingen aan te brengen",
"CurrentPassword": "Huidige wachtwoord",
"EmailAddress": "E-mail adres",
"NewPassword": "New Password",
"NewPasswordConfirm": "New Password Confirm",
"Security": "Security",
"Profile": "Profile",
"UpdatedYourInformation": "Updated your information",
"Unsubscribed": "Unsubscribed!"
"NewPassword": "Nieuw Wachtwoord",
"NewPasswordConfirm": "Nieuw wachtwoord bevestigen",
"Security": "Beveiliging",
"Profile": "Profiel",
"UpdatedYourInformation": "Gegevens bijgewerkt",
"Unsubscribed": "Afgemeld!"
},
"UserTypeLabel": {
"1": "Local User",
"2": "Plex User",
"3": "Emby User",
"4": "Emby Connect User",
"5": "Jellyfin User"
"1": "Lokale gebruiker",
"2": "Plex gebruiker",
"3": "Emby gebruiker",
"4": "Emby Connect gebruiker",
"5": "Jellyfin gebruiker"
},
"Paginator": {
"itemsPerPageLabel": "Items per page:",
"nextPageLabel": "Next page",
"previousPageLabel": "Previous page",
"firstPageLabel": "First page",
"lastPageLabel": "Last page",
"rangePageLabel1": "0 of {{length}}",
"rangePageLabel2": "{{startIndex}} {{endIndex}} of {{length}}"
"itemsPerPageLabel": "Items per pagina:",
"nextPageLabel": "Volgende pagina",
"previousPageLabel": "Vorige pagina",
"firstPageLabel": "Eerste pagina",
"lastPageLabel": "Laatste pagina",
"rangePageLabel1": "0 van {{length}}",
"rangePageLabel2": "{{startIndex}} - {{endIndex}} van {{length}}"
}
}

@ -14,26 +14,26 @@
"Common": {
"ContinueButton": "Gå videre",
"Available": "Tilgjengelig",
"Available4K": "Available 4K",
"Available4K": "Tilgjengelig i 4K",
"Approved": "Godkjent",
"Approve4K": "Approve 4K",
"Pending": "Pending",
"Approve4K": "Godkjenn 4K",
"Pending": "Ventende",
"PartiallyAvailable": "Delvis tilgjengelig",
"Monitored": "Overvåket",
"NotAvailable": "Ikke tilgjengelig",
"ProcessingRequest": "Behandler forespørsel",
"ProcessingRequest4K": "Processing Request 4K",
"ProcessingRequest4K": "Behandler Forespørsel 4K",
"PendingApproval": "Venter på godkjenning",
"PendingApproval4K": "Pending Approval 4K",
"PendingApproval4K": "Venter på godkjenning 4K",
"RequestDenied": "Forespørsel avslått",
"RequestDenied4K": "Request Denied 4K",
"RequestDenied4K": "Forespørsel Avvist 4K",
"NotRequested": "Ikke forespurt",
"NotRequested4K": "Not Requested 4K",
"NotRequested4K": "Ikke Forespurt 4K",
"Requested": "Forespurt",
"Requested4K": "Requested 4K",
"Requested4K": "Forespurt 4K",
"Search": "Søk",
"Request": "Forespørsel",
"Request4K": "Request 4K",
"Request4K": "Forespørsel 4K",
"Denied": "Avslått",
"Approve": "Godkjenn",
"PartlyAvailable": "Delvis tilgjengelig",
@ -42,10 +42,10 @@
"Validation": "Kontroller de angitte verdiene"
},
"Cancel": "Avbryt",
"Submit": "Submit",
"Update": "Update",
"tvShow": "TV Show",
"movie": "Movie",
"Submit": "Send",
"Update": "Oppdater",
"tvShow": "TV Serie",
"movie": "Film",
"album": "Album"
},
"PasswordReset": {
@ -64,11 +64,11 @@
"CheckPageForUpdates": "Sjekk denne siden for kontinuerlige oppdateringer."
},
"ErrorPages": {
"NotFound": "Page not found",
"SomethingWentWrong": "Something went wrong!"
"NotFound": "Siden ble ikke funnet",
"SomethingWentWrong": "Noe gikk galt!"
},
"NavigationBar": {
"Discover": "Discover",
"Discover": "Oppdag",
"Search": "Søk",
"Requests": "Forespørsler",
"UserManagement": "Brukeradministrasjon",
@ -84,20 +84,20 @@
"Logout": "Logg av",
"OpenMobileApp": "Åpne mobilapp",
"RecentlyAdded": "Nylig lagt til",
"ChangeTheme": "Change Theme",
"Calendar": "Calendar",
"UserPreferences": "Preferences",
"ChangeTheme": "Endre Tema",
"Calendar": "Kalender",
"UserPreferences": "Brukervalg",
"FeatureSuggestion": "Feature Suggestion",
"FeatureSuggestionTooltip": "Have a great new idea? Suggest it here!",
"FeatureSuggestionTooltip": "Har du en god idé? Foreslå den her!",
"Filter": {
"Movies": "Filmer",
"TvShows": "TV serier",
"Music": "Musikk",
"People": "People"
"People": "Personer"
},
"MorningWelcome": "Good morning!",
"AfternoonWelcome": "Good afternoon!",
"EveningWelcome": "Good evening!"
"MorningWelcome": "God morgen!",
"AfternoonWelcome": "God ettermiddag!",
"EveningWelcome": "God kveld!"
},
"Search": {
"Title": "Søk",
@ -105,15 +105,15 @@
"MoviesTab": "Filmer",
"TvTab": "TV serier",
"MusicTab": "Musikk",
"AdvancedSearch": "You can fill in any of the below to discover new media. All of the results are sorted by popularity",
"AdvancedSearchHeader": "Advanced Search",
"AdvancedSearch": "Du kan fylle ut hvilket som helst av punktene nedenfor for å oppdage nye medier. Alle resultatene er sortert etter popularitet",
"AdvancedSearchHeader": "Avansert Søk",
"Suggestions": "Forslag",
"NoResults": "Beklager, vi fant ingen resultater!",
"DigitalDate": "Digital utgivelse: {{date}}",
"TheatricalRelease": "Kinopremiere: {{date}}",
"ViewOnPlex": "Spill av på Plex",
"ViewOnEmby": "Spill av på Emby",
"ViewOnJellyfin": "Play On Jellyfin",
"ViewOnJellyfin": "Spill av i Jellyfin",
"RequestAdded": "Forespørsel om {{title}} er lagt til",
"Similar": "Lignende",
"Refine": "Spesifiser",
@ -141,24 +141,27 @@
"Season": "Sesong {{seasonNumber}}",
"SelectAllInSeason": "Velg alle i sesong {{seasonNumber}}"
},
"AdvancedSearchInstructions": "Please choose what type of media you are searching for:",
"YearOfRelease": "Year of Release",
"SearchGenre": "Search Genre",
"SearchKeyword": "Search Keyword",
"SearchProvider": "Search Provider",
"KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb"
"AdvancedSearchInstructions": "Velg medietypen du søker etter:",
"YearOfRelease": "Utgitt",
"SearchGenre": "Søk etter Sjanger",
"SearchKeyword": "Søk etter nøkkelord",
"SearchProvider": "Søkeleverandør",
"KeywordSearchingDisclaimer": "Vær oppmerksom på at nøkkelordsøk kan gi varierende kvalitet i resultater grunnet inkonsekvente data i TheMovieDb"
},
"Requests": {
"Title": "Forespørsler",
"Paragraph": "Nedenfor kan du se dine og alle andres forespørsler, du ser også status for nedlasting og godkjenning.",
"MoviesTab": "Filmer",
"ArtistName": "Artist",
"AlbumName": "Album Name",
"AlbumName": "Albumnavn",
"TvTab": "TV serier",
"MusicTab": "Musikk",
"RequestedBy": "Etterspurt av",
"Status": "Status",
"RequestStatus": "Status for forespørsel",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Avslått:",
"TheatricalRelease": "Kinopremiere: {{date}}",
"ReleaseDate": "Utgitt: {{date}}",
@ -170,15 +173,15 @@
"ChangeRootFolder": "Endre rotmappe",
"ChangeQualityProfile": "Endre kvalitetsprofil",
"MarkUnavailable": "Merk utilgjengelig",
"MarkUnavailable4K": "Mark Unavailable 4K",
"MarkUnavailable4K": "Merk Utilgjengelig 4K",
"MarkAvailable": "Merk tilgjengelig",
"MarkAvailable4K": "Mark Available 4K",
"MarkAvailable4K": "Merk Tilgjengelig 4K",
"Remove": "Fjern",
"Deny": "Avslå",
"Deny4K": "Deny 4K",
"Has4KRequest": "Has 4K Request",
"DenyReason": "Deny Reason",
"DeniedReason": "Denied Reason",
"Deny4K": "Avvis 4K",
"Has4KRequest": "Har 4K-forespørsel",
"DenyReason": "Avslå årsak",
"DeniedReason": "Årsak for avslag",
"Season": "Sesong",
"GridTitle": "Tittel",
"AirDate": "Sendedato",
@ -200,64 +203,65 @@
"NextMinutes": "En ny foresøprel vil bli lagt til om {{time}} minutter",
"NextMinute": "En ny foresøprel vil bli lagt til om {{time}} minutt"
},
"AllRequests": "All Requests",
"PendingRequests": "Pending Requests",
"ProcessingRequests": "Processing Requests",
"AvailableRequests": "Available Requests",
"DeniedRequests": "Denied Requests",
"RequestsToDisplay": "Requests to display",
"AllRequests": "Alle Forespørsler",
"PendingRequests": "Ventende Forespørsler",
"ProcessingRequests": "Behandler Forespørsler",
"AvailableRequests": "Tilgjengelige Forespørsler",
"DeniedRequests": "Avviste Forespørsler",
"RequestsToDisplay": "Forespørsler å vise",
"RequestsTitle": "Tittel",
"Details": "Detaljer",
"Options": "Options",
"Options": "Alternativer",
"RequestPanel": {
"Delete": "Delete Request",
"Approve": "Approve Request",
"Deny": "Deny Request",
"Approve4K": "Approve 4K Request",
"Deny4K": "Deny 4K Request",
"Delete": "Slett forespørsel",
"Approve": "Godkjenn forespørsel",
"Deny": "Avslå forespørsel",
"Approve4K": "Godkjenn 4K forespørsel",
"Deny4K": "Avslå 4K Forespørsel",
"ChangeAvailability": "Merk tilgjengelig",
"Deleted": "Successfully deleted selected items",
"Approved": "Successfully approved selected items",
"Denied": "Successfully denied selected items"
"Deleted": "Valgte elementer ble slettet",
"Approved": "Valgte elementer ble godkjent",
"Denied": "Valgte elementer ble avvist"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
"SuccessfullyReprocessed": "Successfully Re-processed the request",
"DeniedRequest": "Denied Request",
"RequestCollection": "Request Collection",
"CollectionSuccesfullyAdded": "The collection {{name}} has been successfully added!",
"NeedToSelectEpisodes": "You need to select some episodes!",
"SuccessfullyApproved": "Godkjenning vellykket",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Forespørselen ble slettet",
"NowAvailable": "Forespørselen er nå tilgjengelig",
"NowUnavailable": "Forespørselen er nå utilgjengelig",
"SuccessfullyReprocessed": "Forespørselen ble behandlet på nytt",
"DeniedRequest": "Avvist forespørsel",
"RequestCollection": "Be om samling",
"CollectionSuccesfullyAdded": "Samlingen {{name}} har blitt lagt til!",
"NeedToSelectEpisodes": "Du må velge noen episoder!",
"RequestAddedSuccessfully": "Forespørsel om {{title}} er lagt til",
"ErrorCodes": {
"AlreadyRequested": "This has already been requested",
"EpisodesAlreadyRequested": "We already have episodes requested from this series",
"NoPermissionsOnBehalf": "You do not have the correct permissions to request on behalf of users!",
"NoPermissions": "You do not have the correct permissions!",
"RequestDoesNotExist": "Request does not exist",
"ChildRequestDoesNotExist": "Child Request does not exist",
"NoPermissionsRequestMovie": "You do not have permissions to Request a Movie",
"NoPermissionsRequestTV": "You do not have permissions to Request a TV Show",
"NoPermissionsRequestAlbum": "You do not have permissions to Request an Album",
"MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!",
"TvRequestQuotaExceeded": "You have exceeded your Episode request quota!",
"AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!"
"AlreadyRequested": "Dette har allerede blitt forespurt",
"EpisodesAlreadyRequested": "Vi har allerede forespurte episoder fra denne serien",
"NoPermissionsOnBehalf": "Du har ikke rettigheter til å be om innhold på vegne av andre brukere!",
"NoPermissions": "Du har ikke riktige rettigheter!",
"RequestDoesNotExist": "Forespørselen eksisterer ikke",
"ChildRequestDoesNotExist": "Underforespørsel eksisterer ikke",
"NoPermissionsRequestMovie": "Du har ikke rettigheter til å be om en film",
"NoPermissionsRequestTV": "Du har ikke rettigheter til å be om en TV-serie",
"NoPermissionsRequestAlbum": "Du har ikke rettigheter til å be om et album",
"MovieRequestQuotaExceeded": "Du har overskredet din kvote for forespørsler av Filmer!",
"TvRequestQuotaExceeded": "Du har overskredet din kvote for forespørsler av Episoder!",
"AlbumRequestQuotaExceeded": "Du har overskredet din kvote for forespørsler av Album!"
},
"Notify": "Notify",
"RemoveNotification": "Remove Notifications",
"SuccessfulNotify": "You will now be notified for title {{title}}",
"SuccessfulUnNotify": "You will no longer be notified for title {{title}}",
"CouldntNotify": "Couldn't notify title {{title}}"
"Notify": "Gi beskjed",
"RemoveNotification": "Fjern Varslinger",
"SuccessfulNotify": "Du vil nå bli varslet om tittelen {{title}}",
"SuccessfulUnNotify": "Du vil ikke lenger bli varslet om tittelen {{title}}",
"CouldntNotify": "Kunne ikke varsle for tittelen {{title}}"
},
"Issues": {
"Title": "Mangler",
"IssuesForTitle": "Issues for {{title}}",
"IssuesForTitle": "Problemer med {{title}}",
"PendingTitle": "Ventende løsninger",
"InProgressTitle": "Mangler under behandling",
"ResolvedTitle": "Løste mangler",
"ColumnTitle": "Tittel",
"Count": "Count",
"Count": "Antall",
"Category": "Kategori",
"Status": "Status",
"Details": "Detaljer",
@ -271,23 +275,23 @@
"WriteMessagePlaceholder": "Skriv meldingen din her...",
"ReportedBy": "Rapportert av",
"IssueDialog": {
"Title": "Report an issue",
"DescriptionPlaceholder": "Please describe the issue",
"TitlePlaceholder": "Short title of your issue",
"SelectCategory": "Select Category",
"IssueCreated": "Issue has been created"
"Title": "Rapporter et problem",
"DescriptionPlaceholder": "Beskriv problemet",
"TitlePlaceholder": "Kort tittel på saken din",
"SelectCategory": "Velg Kategori",
"IssueCreated": "Saken er opprettet"
},
"Outstanding": "There are outstanding issues",
"ResolvedDate": "Resolved date",
"CreatedDate": "Raised on",
"MarkedAsResolved": "This issue has now been marked as resolved!",
"MarkedAsInProgress": "This issue has now been marked as in progress!",
"Delete": "Delete issue",
"DeletedIssue": "Issue has been deleted",
"Chat": "Chat",
"EnterYourMessage": "Enter Your Message",
"Outstanding": "Det finnes utestående saker",
"ResolvedDate": "Løst dato",
"CreatedDate": "Opprettet den",
"MarkedAsResolved": "Denne saken er nå markert som løst!",
"MarkedAsInProgress": "Saken har nå blitt merket som pågår!",
"Delete": "Slett sak",
"DeletedIssue": "Saken har blitt slettet",
"Chat": "Chatte",
"EnterYourMessage": "Skriv meldingen din",
"Requested": "Forespurt",
"UserOnDate": "{{user}} on {{date}}"
"UserOnDate": "{{user}} den {{date}}"
},
"Filter": {
"ClearFilter": "Tøm filter",
@ -295,8 +299,8 @@
"FilterHeaderRequestStatus": "Status",
"Approved": "Godkjent",
"PendingApproval": "Venter på godkjenning",
"WatchProviders": "Watch Providers",
"Keywords": "Keywords"
"WatchProviders": "Se tilbydere",
"Keywords": "Nøkkelord"
},
"UserManagment": {
"TvRemaining": "TV: {{remaining}}/{{total}} gjenstående",
@ -312,148 +316,149 @@
},
"MediaDetails": {
"Denied": "Avslått",
"Denied4K": "Denied 4K",
"Trailers": "Trailers",
"RecommendationsTitle": "Recommendations",
"Denied4K": "Avvist 4K",
"Trailers": "Trailere",
"RecommendationsTitle": "Anbefalinger",
"SimilarTitle": "Lignende",
"VideosTitle": "Videos",
"AlbumsTitle": "Albums",
"RequestAllAlbums": "Request All Albums",
"ClearSelection": "Clear Selection",
"RequestSelectedAlbums": "Request Selected Albums",
"ViewCollection": "View Collection",
"NotEnoughInfo": "Unfortunately there is not enough information about this show yet!",
"AdvancedOptions": "Advanced Options",
"AutoApproveOptions": "You can configure the request here, once requested it will be sent to your DVR application and will be auto approved! Please note, this is optional, just press Request to skip!",
"AutoApproveOptionsTv": "You can configure the request here, once requested it will be sent to your DVR application and will be auto approved! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it! Please note, this is optional, just press Request to skip!",
"AutoApproveOptionsTvShort": "You can configure the request here, once requested it will be sent to your DVR application! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it! Please note, this is optional, just press Request to skip!",
"QualityProfilesSelect": "Select A Quality Profile",
"RootFolderSelect": "Select A Root Folder",
"LanguageProfileSelect": "Select A Language Profile",
"VideosTitle": "Videoer",
"AlbumsTitle": "Album",
"RequestAllAlbums": "Forespør alle album",
"ClearSelection": "Opphev valg",
"RequestSelectedAlbums": "Forespør valgte album",
"ViewCollection": "Vis samling",
"NotEnoughInfo": "Det er dessverre ikke nok informasjon om denne serien enda!",
"AdvancedOptions": "Avanserte alternativer",
"AutoApproveOptions": "Du kan konfigurere forespørselen her. Ved forespørsel blir den sendt til din DVR-applikasjon og godkjent automatisk! Merk: Dette er valgfritt. Trykk på Forespør for å hoppe over!",
"AutoApproveOptionsTv": "Du kan konfigurere forespørselen her. Når forespurt vil den bli sendt til DVR-programmet ditt og vil automatisk bli godkjent. Hvis forespørselen allerede eksisterer i Sonarr, vil vi ikke endre rotmappen eller kvalitetsprofilen hvis du har valgt det! Vær oppmerksom på at dette er valgfritt, bare trykk på Forespør for å hoppe over!",
"AutoApproveOptionsTvShort": "Du kan konfigurere forespørselen her. Når forespurt vil den bli sendt til DVR-applikasjonen din! Hvis forespørselen allerede eksisterer i Sonarr, vil vi ikke forandre rotmappen eller kvalitetsprofilen hvis du endrer denne. Merk: Dette er valgfritt, trykk på Forespør for å hoppe over!",
"QualityProfilesSelect": "Velg en kvalitetsprofil",
"RootFolderSelect": "Velg en rotmappe",
"LanguageProfileSelect": "Velg en språkprofil",
"Status": "Status:",
"StatusValues": {
"Rumored": "Rumored",
"Planned": "Planned",
"In Production": "In Production",
"Post Production": "Post Production",
"Released": "Released",
"Running": "Running",
"Returning Series": "Returning Series",
"Ended": "Ended",
"Canceled": "Canceled"
"Rumored": "Ryktede",
"Planned": "Planlagte",
"In Production": "I Produksjon",
"Post Production": "Post-produksjon",
"Released": "Utgitt",
"Running": "Løpende",
"Returning Series": "Serier som returnerer",
"Ended": "Avsluttet",
"Canceled": "Kansellert"
},
"Seasons": "Seasons:",
"Episodes": "Episodes:",
"Seasons": "Sesonger:",
"Episodes": "Episoder:",
"Availability": "Tilgjengelighet:",
"RequestStatus": "Request Status:",
"Quality": "Quality:",
"RequestStatus": "Status for forespørsel:",
"Quality": "Kvalitet:",
"RootFolderOverride": "Overstyring av rotmappe:",
"QualityOverride": "Overstyr kvalitet:",
"Network": "Network:",
"GenresLabel": "Genres:",
"Genres": "Genres",
"FirstAired": "First Aired:",
"Network": "Nettverk:",
"GenresLabel": "Sjangere:",
"Genres": "Sjangere",
"FirstAired": "Først sendt:",
"TheatricalRelease": "Kinopremiere:",
"DigitalRelease": "Digital Release:",
"Votes": "Votes:",
"Runtime": "Runtime:",
"Minutes": "{{runtime}} Minutes",
"Revenue": "Revenue:",
"Budget": "Budget:",
"Keywords": "Keywords/Tags:",
"DigitalRelease": "Digital utgivelse:",
"Votes": "Stemmer:",
"Runtime": "Spilletid:",
"Minutes": "{{runtime}} Minutter",
"Revenue": "Omsetning:",
"Budget": "Budsjett:",
"Keywords": "Nøkkelord/Etiketter:",
"Casts": {
"CastTitle": "Cast"
"CastTitle": "Rollebesetning"
},
"Crews": {
"CrewTitle": "Crew"
},
"EpisodeSelector": {
"AllSeasonsTooltip": "This will request every season for this show",
"FirstSeasonTooltip": "This will only request the First Season for this show",
"LatestSeasonTooltip": "This will only request the Latest Season for this show",
"NoEpisodes": "There unfortunately is no episode data for this show yet!",
"AllSeasonsTooltip": "Dette vil be om hver sesong for denne serien",
"FirstSeasonTooltip": "Dette vil kun be om første sesong for denne serien",
"LatestSeasonTooltip": "Dette vil kun be om siste sesong for denne serien",
"NoEpisodes": "Det finnes dessverre ingen episode-data for denne serien ennå!",
"SeasonNumber": "Sesong {{number}}"
},
"SonarrConfiguration": "Sonarr Configuration",
"RadarrConfiguration": "Radarr Configuration",
"RequestOnBehalf": "Request on behalf of",
"PleaseSelectUser": "Please select a user",
"StreamingOn": "Streaming On:",
"SonarrConfiguration": "Sonarr Konfigurasjon",
"RadarrConfiguration": "Radarr Konfigurasjon",
"RequestOnBehalf": "Forespør på vegne av",
"PleaseSelectUser": "Vennligst velg en bruker",
"StreamingOn": "Strømmer på:",
"RequestedBy": "Etterspurt av:",
"OnDate": "On:",
"RequestedByOn": "Requested By {{user}} on {{date}}",
"OnDate": ":",
"RequestedByOn": "Forespurt av {{user}} den {{date}}",
"RequestDate": "Dato for forespørsel:",
"DeniedReason": "Denied Reason:",
"ReProcessRequest": "Re-Process Request",
"ReProcessRequest4K": "Re-Process 4K Request",
"DeniedReason": "Årsak for avslag:",
"ReProcessRequest": "Re-Prosessér Forespørsel",
"ReProcessRequest4K": "Re-Prosessér 4K-forespørsel",
"Music": {
"Type": "Type:",
"Country": "Country:",
"StartDate": "Start Date:",
"EndDate": "EndDate:"
"Country": "Land:",
"StartDate": "Startdato:",
"EndDate": "Sluttdato:"
},
"RequestSource": "Source:"
"RequestSource": "Kilde:"
},
"Discovery": {
"PopularTab": "Populært",
"TrendingTab": "På vei opp",
"UpcomingTab": "Upcoming",
"SeasonalTab": "Seasonal",
"RecentlyRequestedTab": "Recently Requested",
"UpcomingTab": "Kommende",
"SeasonalTab": "Årstid",
"RecentlyRequestedTab": "Nylig Forespurte",
"Movies": "Filmer",
"Combined": "Combined",
"Combined": "Kombinert",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Tilgjengelighet",
"Studio": "Studio",
"Network": "Network",
"UnknownNetwork": "Unknown",
"RequestStatus": "Request Status",
"Director": "Director",
"InCinemas": "In Cinemas",
"FirstAired": "First Aired",
"Writer": "Writer",
"ExecProducer": "Exec Producer"
"Network": "Nettverk",
"UnknownNetwork": "Ukjent",
"RequestStatus": "Status for Forespørsel",
"Director": "Regissør",
"InCinemas": "På Kino",
"FirstAired": "Først Sendt",
"Writer": "Forfatter",
"ExecProducer": "Eksekutiv Produsent"
},
"NoSearch": "Sorry, nothing matches your search!"
"NoSearch": "Beklager, ingenting samsvarer med søket!"
},
"UserPreferences": {
"Welcome": "Velkommen {{username}}!",
"OmbiLanguage": "Language",
"DarkMode": "Dark Mode",
"Updated": "Successfully Updated",
"StreamingCountry": "Streaming Country",
"StreamingCountryDescription": "This is the country code that we will display streaming information for. If you are in the US please select US and you will get US related streaming information.",
"LanguageDescription": "This is the language you would like the Ombi interface to be displayed in.",
"MobileQRCode": "Mobile QR Code",
"LegacyApp": "Launch Legacy App",
"NoQrCode": "Please contact your administrator to enable QR codes",
"UserType": "User Type:",
"ChangeDetails": "Change Details",
"NeedCurrentPassword": "You need your current password to make any changes here",
"CurrentPassword": "Current Password",
"OmbiLanguage": "Språk",
"DarkMode": "Mørkt Tema",
"Updated": "Oppdatering Vellykket",
"StreamingCountry": "Strømmeland",
"StreamingCountryDescription": "Dette er landskoden vi vil vise strømmeinformasjon for. Hvis du er i USA, kan du velge USA og du vil motta USAs relaterte strømmeinformasjon.",
"LanguageDescription": "Dette er språket du ønsker at Ombis grensesnitt skal vises i.",
"MobileQRCode": "QR-kode for mobil",
"LegacyApp": "Start tidligere app",
"NoQrCode": "Kontakt din administrator for å aktivere QR-koder",
"UserType": "Brukertype:",
"ChangeDetails": "Endre Detaljer",
"NeedCurrentPassword": "Du trenger ditt nåværende passord for å gjøre endringer her",
"CurrentPassword": "Nåværende Passord",
"EmailAddress": "E-postadresse",
"NewPassword": "New Password",
"NewPasswordConfirm": "New Password Confirm",
"Security": "Security",
"Profile": "Profile",
"UpdatedYourInformation": "Updated your information",
"Unsubscribed": "Unsubscribed!"
"NewPassword": "Nytt passord",
"NewPasswordConfirm": "Bekreft nytt passord",
"Security": "Sikkerhet",
"Profile": "Profil",
"UpdatedYourInformation": "Oppdaterte informasjonen din",
"Unsubscribed": "Avsluttet abonnement!"
},
"UserTypeLabel": {
"1": "Local User",
"2": "Plex User",
"3": "Emby User",
"4": "Emby Connect User",
"5": "Jellyfin User"
"1": "Lokal Bruker",
"2": "Plex Bruker",
"3": "Emby Bruker",
"4": "Emby koble til bruker",
"5": "Jellyfin Bruker"
},
"Paginator": {
"itemsPerPageLabel": "Items per page:",
"nextPageLabel": "Next page",
"previousPageLabel": "Previous page",
"firstPageLabel": "First page",
"lastPageLabel": "Last page",
"rangePageLabel1": "0 of {{length}}",
"rangePageLabel2": "{{startIndex}} {{endIndex}} of {{length}}"
"itemsPerPageLabel": "Elementer per side:",
"nextPageLabel": "Neste side",
"previousPageLabel": "Forrige side",
"firstPageLabel": "Første side",
"lastPageLabel": "Siste side",
"rangePageLabel1": "0 av {{length}}",
"rangePageLabel2": "{{startIndex}} {{endIndex}} av {{length}}"
}
}

@ -159,6 +159,9 @@
"RequestedBy": "Zgłoszone przez",
"Status": "Stan",
"RequestStatus": "Status zgłoszenia",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": "Odrzucono:",
"TheatricalRelease": "Premiera kinowa: {{date}}",
"ReleaseDate": "Wydany: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Zatwierdzono pomyślnie",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Prośba pomyślnie usunięta",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
@ -403,6 +407,7 @@
"Movies": "Filmy",
"Combined": "Połączone",
"Tv": "Seriale",
"Genres": "Genres",
"CardDetails": {
"Availability": "Dostępność",
"Studio": "Studio",

@ -159,6 +159,9 @@
"RequestedBy": "Solicitado por",
"Status": "Status",
"RequestStatus": "Status da solicitação",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Negados:",
"TheatricalRelease": "Lançamento nos Cinemas: {{date}}",
"ReleaseDate": "Lançado: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Itens selecionados negados com sucesso"
},
"SuccessfullyApproved": "Aprovado com Sucesso",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Solicitação excluída com sucesso",
"NowAvailable": "O pedido agora está disponível",
"NowUnavailable": "A solicitação está indisponível",
@ -403,6 +407,7 @@
"Movies": "Filmes",
"Combined": "Combinados",
"Tv": "Serie",
"Genres": "Genres",
"CardDetails": {
"Availability": "Disponibilidade",
"Studio": "Estúdio",

@ -159,6 +159,9 @@
"RequestedBy": "Requested By",
"Status": "Status",
"RequestStatus": "Request status",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Denied:",
"TheatricalRelease": "Theatrical Release: {{date}}",
"ReleaseDate": "Released: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
@ -403,6 +407,7 @@
"Movies": "Movies",
"Combined": "Combined",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Availability",
"Studio": "Studio",

@ -159,6 +159,9 @@
"RequestedBy": "Автор запроса",
"Status": "Статус",
"RequestStatus": "Статус запроса",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Отказано:",
"TheatricalRelease": "Релиз в кинотеатрах: {{date}}",
"ReleaseDate": "Дата выхода: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
@ -403,6 +407,7 @@
"Movies": "Фильмы",
"Combined": "Combined",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Доступность",
"Studio": "Studio",

@ -159,6 +159,9 @@
"RequestedBy": "Vyžiadané od",
"Status": "Stav",
"RequestStatus": "Stav požiadavky",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Zamietnuté:",
"TheatricalRelease": "Kino vydanie: {{date}}",
"ReleaseDate": "Vydané: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Úspešne odstránené vybrané položky"
},
"SuccessfullyApproved": "Úspešne schválené",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Žiadosť bola úspešne vymazaná",
"NowAvailable": "Žiadosť je teraz k dispozícii",
"NowUnavailable": "Žiadosť je teraz nedostupná",
@ -403,6 +407,7 @@
"Movies": "Filmy",
"Combined": "Kombinované",
"Tv": "Seriály",
"Genres": "Genres",
"CardDetails": {
"Availability": "Dostupnosť",
"Studio": "Štúdio",

@ -159,6 +159,9 @@
"RequestedBy": "Efterfrågats av",
"Status": "Status",
"RequestStatus": "Status för begäran",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": " Nekad:",
"TheatricalRelease": "Biopremiär: {{date}}",
"ReleaseDate": "Releasedatum: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "Successfully Approved",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "Request successfully deleted",
"NowAvailable": "Request is now available",
"NowUnavailable": "Request is now unavailable",
@ -403,6 +407,7 @@
"Movies": "Filmer",
"Combined": "Kombinerad",
"Tv": "TV",
"Genres": "Genres",
"CardDetails": {
"Availability": "Tillgänglighet",
"Studio": "Filmstudio",

@ -159,6 +159,9 @@
"RequestedBy": "请求者",
"Status": "发行状态",
"RequestStatus": "申请状态",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": "已拒绝:",
"TheatricalRelease": "剧场版发行:{{date}}",
"ReleaseDate": "已发行: {{date}}",
@ -221,6 +224,7 @@
"Denied": "Successfully denied selected items"
},
"SuccessfullyApproved": "批准成功",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "删除请求成功",
"NowAvailable": "请求现在可观看",
"NowUnavailable": "请求现在不可观看",
@ -403,6 +407,7 @@
"Movies": "电影",
"Combined": "混合",
"Tv": "电视节目",
"Genres": "Genres",
"CardDetails": {
"Availability": "可用性",
"Studio": "工作室",

@ -159,6 +159,9 @@
"RequestedBy": "请求者",
"Status": "发行状态",
"RequestStatus": "申请状态",
"Watched": "Watched",
"WatchedTooltip": "The user who made the request has watched it",
"WatchedByUsersCount": "{{count}} users have watched this.",
"Denied": "已拒绝:",
"TheatricalRelease": "剧场版发行:{{date}}",
"ReleaseDate": "已发行: {{date}}",
@ -221,6 +224,7 @@
"Denied": "所选项目已拒绝"
},
"SuccessfullyApproved": "批准成功",
"SuccessfullyDenied": "Successfully Denied",
"SuccessfullyDeleted": "删除请求成功",
"NowAvailable": "请求现在可观看",
"NowUnavailable": "请求现在不可观看",
@ -403,6 +407,7 @@
"Movies": "电影",
"Combined": "混合",
"Tv": "电视节目",
"Genres": "Genres",
"CardDetails": {
"Availability": "可用性",
"Studio": "工作室",

@ -1,3 +1,3 @@
{
"version": "4.35.11"
"version": "4.39.0"
}
Loading…
Cancel
Save