diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1047f965..6191c7a55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ name: CI Build on: push: branches: [ develop, master ] + workflow_dispatch: jobs: build-ui: @@ -38,7 +39,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.x' + dotnet-version: '6.0.x' - name: Nuget Cache uses: actions/cache@v2 @@ -67,6 +68,7 @@ jobs: uses: TriPSs/conventional-changelog-action@v3 with: version-file: 'version.json' + release-count: 40 skip-on-empty: 'false' git-message: 'chore(release): :rocket: {version}' @@ -102,6 +104,12 @@ jobs: format: tar.gz steps: - uses: actions/checkout@v2 + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.0.x' + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: '5.0.x' - name: Nuget Cache uses: actions/cache@v2 diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 499cd272d..2e382eee3 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -7,6 +7,7 @@ on: branches: [ develop ] schedule: - cron: '0 0 * * *' + workflow_dispatch: jobs: automation-tests: @@ -18,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - uses: actions/setup-node@v2 with: node-version: '14' @@ -38,7 +39,7 @@ jobs: - name: Start Backend run: | - nohup dotnet run -p ./src/Ombi -- --host http://*:3577 & + nohup dotnet run --project ./src/Ombi -- --host http://*:3577 & - name: Start Frontend run: | nohup yarn --cwd ./src/Ombi/ClientApp start & diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index e353dce4d..961871f8a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -3,6 +3,7 @@ name: PR Build on: pull_request: types: [opened, synchronize, reopened] + workflow_dispatch: jobs: build-ui: @@ -31,7 +32,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.x' + dotnet-version: '6.0.x' - name: Nuget Cache uses: actions/cache@v2 @@ -89,6 +90,9 @@ jobs: format: tar.gz steps: - uses: actions/checkout@v2 + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.0.x' - name: Nuget Cache uses: actions/cache@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0191c7064..44be4a73c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,248 @@ +## [4.10.1](https://github.com/Ombi-app/Ombi/compare/v4.10.0...v4.10.1) (2022-01-22) + + + +## [4.3.3](https://github.com/Ombi-app/Ombi/compare/v4.3.2...v4.3.3) (2021-11-05) + + + +# [4.10.0](https://github.com/Ombi-app/Ombi/compare/v4.9.2...v4.10.0) (2022-01-14) + + +### Features + +* **notifications:** :sparkles: Send new request email notifications to power users ([#4462](https://github.com/Ombi-app/Ombi/issues/4462)) ([10cc0c0](https://github.com/Ombi-app/Ombi/commit/10cc0c0951f13221179516f8ff5c44dbecc9a0fd)) + + + +## [4.9.2](https://github.com/Ombi-app/Ombi/compare/v4.9.1...v4.9.2) (2022-01-14) + + +### Bug Fixes + +* :bug: Add UI for Emby recently added cronjob settings ([#4469](https://github.com/Ombi-app/Ombi/issues/4469)) ([7d47bbe](https://github.com/Ombi-app/Ombi/commit/7d47bbe92204855bf75d70b8fa548f9c3f3612bc)) +* **sonarr:** :bug: Fixed an issue where we could attempt to add a series to sonarr before sonarr has got all the metadata [#4459](https://github.com/Ombi-app/Ombi/issues/4459) ([5c691dc](https://github.com/Ombi-app/Ombi/commit/5c691dc98437a4cd24560ff625414fe05dd22f89)) +* **wizard:** :bug: Fixed the issue where the Application Url wasn't validated in the wizard ([33b8d11](https://github.com/Ombi-app/Ombi/commit/33b8d1111a1c6663d8c0bbd912be4660da7d013f)), closes [#4417](https://github.com/Ombi-app/Ombi/issues/4417) + + + +## [4.9.1](https://github.com/Ombi-app/Ombi/compare/v4.9.0...v4.9.1) (2022-01-14) + + +### Bug Fixes + +* **discover:** 🌐 Localize episodes names in TV details ([#4467](https://github.com/Ombi-app/Ombi/issues/4467)) [skip ci] ([35806ea](https://github.com/Ombi-app/Ombi/commit/35806ea2d2c866d628cf08577026a02ab04e49d9)) +* **email-notifications:** :bug: Fixed the issue where legacy requests were showing broken poster images [#4452](https://github.com/Ombi-app/Ombi/issues/4452) ([0ece2fd](https://github.com/Ombi-app/Ombi/commit/0ece2fd6e0eb01e0b7d4d2a01e1a276c7a9c5a51)) +* **emby/jellyfin:** :bug: A more reliable Emby and Jellyfin sync [skip ci] ([ad677fa](https://github.com/Ombi-app/Ombi/commit/ad677fa02eb75633014e9c9791c21ed2d6a23229)) + + + +# [4.9.0](https://github.com/Ombi-app/Ombi/compare/v4.8.1...v4.9.0) (2022-01-05) + + +### Features + +* **customization:** :sparkles: Added possibility for custom favicons ([40af659](https://github.com/Ombi-app/Ombi/commit/40af6593b668d4712327c18f92f5b7b5a0a65e26)) + + + +## [4.8.1](https://github.com/Ombi-app/Ombi/compare/v4.8.0...v4.8.1) (2022-01-04) + + + +# [4.8.0](https://github.com/Ombi-app/Ombi/compare/v4.7.11...v4.8.0) (2021-12-22) + + +### Bug Fixes + +* **auto-delete:** :bug: We now also auto delete music requests, this was previously missing ([9fe1f8e](https://github.com/Ombi-app/Ombi/commit/9fe1f8e988aa31d36e7a685ae19f72d9c8414dc0)) + + +### Features + +* **details:** :sparkles: Added the notify button back into the details pages for requests ([8b33cdc](https://github.com/Ombi-app/Ombi/commit/8b33cdccef83db8794414b247438214b00860fac)) + + + +## [4.7.11](https://github.com/Ombi-app/Ombi/compare/v4.7.10...v4.7.11) (2021-12-17) + + +### Bug Fixes + +* **availability-rules:** :bug: Show the 'Requested' button when a show has all of the episodes marked as requested ([cb7ecf6](https://github.com/Ombi-app/Ombi/commit/cb7ecf684ac3ab204f329a28baecfd4f6cd408f7)) + + + +## [4.7.10](https://github.com/Ombi-app/Ombi/compare/v4.7.9...v4.7.10) (2021-12-16) + + +### Bug Fixes + +* **discover:** :bug: Fixed an issue where monitored movies in radarr were not correctly represented on the search results ([75b15bc](https://github.com/Ombi-app/Ombi/commit/75b15bc7cba21f0a14a18c8e64fd52482f5c6325)) + + + +## [4.7.9](https://github.com/Ombi-app/Ombi/compare/v4.7.8...v4.7.9) (2021-12-16) + + +### Bug Fixes + +* **sonarr:** :bug: Fixed an issue where we were sometimes incorrectly setting the state of episodes that are already monitored in sonarr ([fd1acb8](https://github.com/Ombi-app/Ombi/commit/fd1acb88cbc5e73f91b7f81e6e28ee06d66b277e)) + + + +## [4.7.8](https://github.com/Ombi-app/Ombi/compare/v4.7.7...v4.7.8) (2021-12-11) + + +### Bug Fixes + +* **notifications:** :bug: Fixed the DenyReason sometimes not appearing in the notification message [#4409](https://github.com/Ombi-app/Ombi/issues/4409) ([209e311](https://github.com/Ombi-app/Ombi/commit/209e31175c95f6ee8909d878d45bf8269a9842d9)) +* **oauth:** :lock: Fixed the issue where some users running their browsers in a different language could not open the Plex OAuth window ([d5404ea](https://github.com/Ombi-app/Ombi/commit/d5404eaad7837010d6e4563cd8f7a1009231d362)), closes [#4408](https://github.com/Ombi-app/Ombi/issues/4408) +* **translations:** 🌐 New translations from Crowdin ([5cfb76c](https://github.com/Ombi-app/Ombi/commit/5cfb76cad7a25eed8b452bf9c01cef8c32804369)) + + + +## [4.7.7](https://github.com/Ombi-app/Ombi/compare/v4.7.6...v4.7.7) (2021-12-08) + + +### Bug Fixes + +* **notifications:** 🐛 Do not notify user upon auto approval of a TV show ([#4432](https://github.com/Ombi-app/Ombi/issues/4432)) ([3ad3bdd](https://github.com/Ombi-app/Ombi/commit/3ad3bddd8313d607ee2a39a51a92e61a3673082c)), closes [#4431](https://github.com/Ombi-app/Ombi/issues/4431) +* **translations:** 🌐 New translations from Crowdin ([8943a97](https://github.com/Ombi-app/Ombi/commit/8943a978bf459eaeb496d50c61c4d1506c727366)) + + + +## [4.7.6](https://github.com/Ombi-app/Ombi/compare/v4.7.5...v4.7.6) (2021-12-02) + + +### Bug Fixes + +* **user-management:** :bug: Fixed an issue where you couldn't 'unset' a users custom quality and root folders ([bae96af](https://github.com/Ombi-app/Ombi/commit/bae96af17f50a80ae3ade235a5ef68d5d2dc12ba)) + + + +## [4.7.5](https://github.com/Ombi-app/Ombi/compare/v4.7.4...v4.7.5) (2021-11-28) + + +### Bug Fixes + +* **notifications:** fixed an error that could happen when Ombi sends out a issue notification ([7442dcf](https://github.com/Ombi-app/Ombi/commit/7442dcf59da5d2190cc3087b10402e85bcfcf83b)) +* **translations:** 🌐 Fix incorrect text translation reference RequestedByOn ([#4420](https://github.com/Ombi-app/Ombi/issues/4420)) ([202d155](https://github.com/Ombi-app/Ombi/commit/202d155493c29a6ddd4c5507186bf376a28f4c1d)) +* **translations:** 🌐 New translations from Crowdin ([473c172](https://github.com/Ombi-app/Ombi/commit/473c1724922515fe376e0b2058ac391807c923f2)) + + + +## [4.7.4](https://github.com/Ombi-app/Ombi/compare/v4.7.3...v4.7.4) (2021-11-25) + + +### Bug Fixes + +* **availability-rules:** :bug: Fixed a small issue where some shows would not appear as Available even know they had no future unaired episodes listed ([914b096](https://github.com/Ombi-app/Ombi/commit/914b096781c9b73292a533a010a5dd05ecfd0aac)) +* **emby:** :bug: Fixed an issue where we were not properly syncing episodes ([75529dd](https://github.com/Ombi-app/Ombi/commit/75529dd972c5102f3c5234a2acf6fe664a1bcfad)) + + + +## [4.7.3](https://github.com/Ombi-app/Ombi/compare/v4.7.2...v4.7.3) (2021-11-23) + + + +## [4.7.2](https://github.com/Ombi-app/Ombi/compare/v4.7.1...v4.7.2) (2021-11-22) + + +### Bug Fixes + +* **request-list:** :bug: Fixed an issue where the bulk delete would not work for movie requests ([4b540fb](https://github.com/Ombi-app/Ombi/commit/4b540fb45bcc389664f0953159802288d005db9f)) + + + +## [4.7.1](https://github.com/Ombi-app/Ombi/compare/v4.7.0...v4.7.1) (2021-11-22) + + +### Bug Fixes + +* **emby:** :bug: Fixed an issue where we slightly broke the full sync ([332d934](https://github.com/Ombi-app/Ombi/commit/332d9344d002a5ffd5aeac516c7441dcdec52248)) + + + +# [4.7.0](https://github.com/Ombi-app/Ombi/compare/v4.6.5...v4.7.0) (2021-11-19) + + +### Features + +* **emby:** :sparkles: Added a emby recently added sync! ([a0e1406](https://github.com/Ombi-app/Ombi/commit/a0e14068f4bc457f8a4a565de71707a8f16c803c)) + + + +## [4.6.5](https://github.com/Ombi-app/Ombi/compare/v4.6.4...v4.6.5) (2021-11-15) + + +### Bug Fixes + +* **issues:** :bug: Added the issue category to the issue 'cards' [#4403](https://github.com/Ombi-app/Ombi/issues/4403) ([a3739f3](https://github.com/Ombi-app/Ombi/commit/a3739f375c49f48e34da12f0a74e4e068f12ab40)) +* **issues:** :bug: Added the issues back to the details page for TV Shows ([0225000](https://github.com/Ombi-app/Ombi/commit/02250000c08a253e57d8a0a855c2d30b8a1e5baa)) +* **issues:** :bug: Fixed an issue where you couldn't navigate to the details page from TV issues ([1a2825b](https://github.com/Ombi-app/Ombi/commit/1a2825bf3839b891b16e1dde4030afe53efe090e)) +* **issues:** :bug: Fixed where we did not show the poster when an issue is raised for media we do not have a request for [#4402](https://github.com/Ombi-app/Ombi/issues/4402) ([15e37b5](https://github.com/Ombi-app/Ombi/commit/15e37b532a83097dbdf1a9fea3eead7d0e211898)) + + + +## [4.6.4](https://github.com/Ombi-app/Ombi/compare/v4.6.3...v4.6.4) (2021-11-12) + + + +## [4.6.3](https://github.com/Ombi-app/Ombi/compare/v4.6.2...v4.6.3) (2021-11-11) + + +### Bug Fixes + +* **discover:** :bug: Display TV + movies on actor page in user language ([#4395](https://github.com/Ombi-app/Ombi/issues/4395)) ([fe635c7](https://github.com/Ombi-app/Ombi/commit/fe635c7106bc487ff879bdc8a73bab16cb389b97)) +* **permissions:** :bug: Improved the security around the role "Manage Own Requests" ([#4397](https://github.com/Ombi-app/Ombi/issues/4397)) ([334a32b](https://github.com/Ombi-app/Ombi/commit/334a32bca42f90198d9b720d2bdb710a583be47f)), closes [#4391](https://github.com/Ombi-app/Ombi/issues/4391) +* **search:** Fixed some cases where search wouldn't work correctly ([#4398](https://github.com/Ombi-app/Ombi/issues/4398)) ([4410790](https://github.com/Ombi-app/Ombi/commit/4410790bc096826bc11554098f846e3acb59589a)) + + + +## [4.6.2](https://github.com/Ombi-app/Ombi/compare/v4.6.1...v4.6.2) (2021-11-10) + + +### Bug Fixes + +* **discover:** TV shows now display on the Actor Pages ([#4388](https://github.com/Ombi-app/Ombi/issues/4388)) ([6b716e7](https://github.com/Ombi-app/Ombi/commit/6b716e722076e3d1e6bf2097c5263645d5ea9edf)) + + + +## [4.6.1](https://github.com/Ombi-app/Ombi/compare/v4.6.0...v4.6.1) (2021-11-10) + + +### Bug Fixes + +* :bug: Fixed the MySQL issue after .net 6 upgrade [#4393](https://github.com/Ombi-app/Ombi/issues/4393) ([fea7ff0](https://github.com/Ombi-app/Ombi/commit/fea7ff05139e9ff50c8097fa5389b4ef9ad21a15)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([c6acb45](https://github.com/Ombi-app/Ombi/commit/c6acb45f8d3f371c0b4024c4272849d0d0cc867f)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([18c220a](https://github.com/Ombi-app/Ombi/commit/18c220a0cd0d19e45a07d0c319da2b9512778a8a)) + + + +# [4.6.0](https://github.com/Ombi-app/Ombi/compare/v4.4.0...v4.6.0) (2021-11-09) + + +### Features + +* :sparkles: Upgrade Ombi to .NET 6 ([#4390](https://github.com/Ombi-app/Ombi/issues/4390)) ([719eb7d](https://github.com/Ombi-app/Ombi/commit/719eb7dbe37b3a72d264e2f670067518eef70694)), closes [#4392](https://github.com/Ombi-app/Ombi/issues/4392) + + + +# [4.4.0](https://github.com/Ombi-app/Ombi/compare/v4.3.3...v4.4.0) (2021-11-06) + + +### Bug Fixes + +* **request-list:** :bug: Fixed an issue where the request options were not appearing for Music requests ([c0406a2](https://github.com/Ombi-app/Ombi/commit/c0406a2ddebafb03d98ed25cdf7d89dc9a600c7d)) + + +### Features + +* **mass-email:** :sparkles: Added the ability to configure the Mass Email, we can now send BCC and we are less likely to be rate limited when not using bcc [#4377](https://github.com/Ombi-app/Ombi/issues/4377) ([ca655ae](https://github.com/Ombi-app/Ombi/commit/ca655ae57042dec44106a2f2ef5ba2e6f1019ee4)) + + + ## [4.3.3](https://github.com/Ombi-app/Ombi/compare/v4.3.2...v4.3.3) (2021-11-05) @@ -60,3 +305,78 @@ +## [4.2.12](https://github.com/Ombi-app/Ombi/compare/v4.2.11...v4.2.12) (2021-10-20) + + +### Bug Fixes + +* **newsletter:** :bug: Fixed a few small bugs in the newsletter ([21dba4c](https://github.com/Ombi-app/Ombi/commit/21dba4c524b98b9f2b883d97e7e13329425a8762)) +* **translations:** 🌐 New translations en.json from Crowdin [skip ci] ([52eda6a](https://github.com/Ombi-app/Ombi/commit/52eda6ab917a73842bc02b0d8e0c442e564ca8f0)) +* **translations:** 🌐 New translations en.json from Crowdin [skip ci] ([1095d52](https://github.com/Ombi-app/Ombi/commit/1095d524962648a1e427f0bcd8105fa734dd5b60)) + + + +## [4.2.11](https://github.com/Ombi-app/Ombi/compare/v4.2.10...v4.2.11) (2021-10-18) + + + +## [4.2.10](https://github.com/Ombi-app/Ombi/compare/v4.2.9...v4.2.10) (2021-10-15) + + +### Bug Fixes + +* :bug: Really really fix it this time? ([543d36e](https://github.com/Ombi-app/Ombi/commit/543d36e5615341bc8378cac377b843a3dbc1ef99)) + + + +## [4.2.9](https://github.com/Ombi-app/Ombi/compare/v4.2.8...v4.2.9) (2021-10-15) + + +### Bug Fixes + +* :fire: Really fix the base url issue this time ([9f36923](https://github.com/Ombi-app/Ombi/commit/9f36923c51bfabf9cb026f2da14f9947050af0d9)) + + + +## [4.2.8](https://github.com/Ombi-app/Ombi/compare/v4.2.7...v4.2.8) (2021-10-15) + + +### Bug Fixes + +* :adhesive_bandage: See if this fixes the proxy issue ([74d1aca](https://github.com/Ombi-app/Ombi/commit/74d1acae499707a7e21401f53eb2bb90c5bb9cfa)) +* :bug: Fixed Ombi not writing the baseUrl correctly ([e9cc8b6](https://github.com/Ombi-app/Ombi/commit/e9cc8b6fe71d3e10c1a901e70227989b3362afe3)) + + + +## [4.2.7](https://github.com/Ombi-app/Ombi/compare/v4.2.6...v4.2.7) (2021-10-14) + + +### Bug Fixes + +* :bug: Fixed the issue parsing TheMovieDB dates. They have broken something... ([6e397e0](https://github.com/Ombi-app/Ombi/commit/6e397e02e95f894a92e8bf02428efdcac1275b31)) + + + +## [4.2.6](https://github.com/Ombi-app/Ombi/compare/v4.2.5...v4.2.6) (2021-10-14) + + +### Performance Improvements + +* :zap: Use ngxs store for the whole customization section of the app ([97b493d](https://github.com/Ombi-app/Ombi/commit/97b493d869feee59d360b484a6c59388a2aead1f)) + + + +## [4.2.5](https://github.com/Ombi-app/Ombi/compare/v4.2.4...v4.2.5) (2021-10-14) + + + +## [4.2.4](https://github.com/Ombi-app/Ombi/compare/v4.2.3...v4.2.4) (2021-10-13) + + +### Bug Fixes + +* **#4344:** :bug: Fixed an issue where we errored on Plex Episode Scan ([cd5532f](https://github.com/Ombi-app/Ombi/commit/cd5532fa8f7ebbfaf942841398672bafb9a405d4)) +* **#4345:** :bug: Fixed the issue where denied requests we not appearing correctly ([5a2f652](https://github.com/Ombi-app/Ombi/commit/5a2f652a28f5699dd667afef8dde129817e53392)) + + + diff --git a/README.md b/README.md index 636c58c3e..6b785e94f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Don't worry, it's grandma friendly, and more importantly; has wife approval cert | Service | Stable | Develop |----------|:---------------------------:|:----------------------------:| -| Build Status | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4) +| Build Status | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4) | Download |[![Download](https://img.shields.io/badge/-Download-blue)](https://github.com/Ombi-app/Ombi/releases) | [![Download](https://img.shields.io/badge/-Download-blue)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [![Download](https://img.shields.io/badge/-Download-blue)](https://github.com/ombi-app/ombi/releases) | # Feature Requests @@ -135,6 +135,13 @@ Here are some of the features Ombi has: Matt Jeanes + + + sephrat +
+ Sephrat +
+ bernarden @@ -148,15 +155,15 @@ Here are some of the features Ombi has:
Dhruv Bhavsar
- + + joshuaboniface
Joshua M. Boniface
- - + bruvv @@ -164,13 +171,6 @@ Here are some of the features Ombi has: Bruvv - - - sephrat -
- Sephrat -
- louis-lau @@ -222,10 +222,10 @@ Here are some of the features Ombi has: - - stefangross + + steffokeffo
- Stefangross + Steffokeffo
@@ -730,6 +730,13 @@ Here are some of the features Ombi has: M4tta + + + maartenheebink +
+ Maartenheebink +
+ masterhuck @@ -750,15 +757,15 @@ Here are some of the features Ombi has:
Tdorsey
- + + thegame3202
Mike
- - + zobe123 diff --git a/makefile b/makefile new file mode 100644 index 000000000..62c11e5b5 --- /dev/null +++ b/makefile @@ -0,0 +1,17 @@ +backend: + cd src/Ombi && dotnet watch run -- --host http://*:3577 + +frontend: + cd src/Ombi/ClientApp && yarn start + +install-frontend: + cd src/Ombi/ClientApp && yarn + +install-frontend-tests: + cd tests && yarn + +frontend-tests: + cd tests && npx cypress run + +backend-tests: + cd src/Ombi.Core.Tests && dotnet test \ No newline at end of file diff --git a/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj b/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj index 0701967d1..5939549de 100644 --- a/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj +++ b/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj b/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj +++ b/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj b/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj index 20c8a9aef..4945a2fb2 100644 --- a/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj +++ b/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj b/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj +++ b/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index c413ecded..d911ca212 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Internal; using Newtonsoft.Json; using Ombi.Api.Emby.Models; -using Ombi.Api.Emby.Models.Media; using Ombi.Api.Emby.Models.Media.Tv; using Ombi.Api.Emby.Models.Movie; using Ombi.Helpers; @@ -124,22 +120,36 @@ namespace Ombi.Api.Emby return response; } - public async Task> GetAllMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) { return await GetAll("Movie", apiKey, userId, baseUri, true, startIndex, count, parentIdFilder); } + public async Task> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) + { + return await RecentlyAdded("Movie", apiKey, userId, baseUri, true, startIndex, count, parentIdFilder); + } + public async Task> GetAllEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) { return await GetAll("Episode", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder); } + public async Task> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) + { + return await RecentlyAdded("Episode", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder); + } + public async Task> GetAllShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) { return await GetAll("Series", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder); } + public async Task> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) + { + return await RecentlyAdded("Series", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder); + } + public async Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl) { return await GetInformation(mediaId, apiKey, userId, baseUrl); @@ -154,6 +164,31 @@ namespace Ombi.Api.Emby return await GetInformation(mediaId, apiKey, userId, baseUrl); } + private async Task> RecentlyAdded(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count, string parentIdFilder = default) + { + var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get); + + request.AddQueryString("Recursive", true.ToString()); + request.AddQueryString("IncludeItemTypes", type); + request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); + request.AddQueryString("startIndex", startIndex.ToString()); + request.AddQueryString("limit", count.ToString()); + request.AddQueryString("sortBy", "DateCreated"); + request.AddQueryString("SortOrder", "Descending"); + if (!string.IsNullOrEmpty(parentIdFilder)) + { + request.AddQueryString("ParentId", parentIdFilder); + } + + request.AddQueryString("IsVirtualItem", "False"); + + AddHeaders(request, apiKey); + + + var obj = await Api.Request>(request); + return obj; + } + private async Task GetInformation(string mediaId, string apiKey, string userId, string baseUrl) { var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get); diff --git a/src/Ombi.Api.Emby/IBaseEmbyApi.cs b/src/Ombi.Api.Emby/IBaseEmbyApi.cs index 4de81073d..248c0a88f 100644 --- a/src/Ombi.Api.Emby/IBaseEmbyApi.cs +++ b/src/Ombi.Api.Emby/IBaseEmbyApi.cs @@ -29,5 +29,8 @@ namespace Ombi.Api.Emby Task GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); Task GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl); Task GetPublicInformation(string baseUrl); + Task> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri); + Task> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri); + Task> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri); } } \ No newline at end of file diff --git a/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj b/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj index 3008c9230..f167146af 100644 --- a/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj +++ b/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj b/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj index 20c8a9aef..4945a2fb2 100644 --- a/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj +++ b/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Github/Ombi.Api.Github.csproj b/src/Ombi.Api.Github/Ombi.Api.Github.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.Github/Ombi.Api.Github.csproj +++ b/src/Ombi.Api.Github/Ombi.Api.Github.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj b/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj index 7065a1e65..1285c073c 100644 --- a/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj +++ b/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj b/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj +++ b/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj b/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj index 3008c9230..f167146af 100644 --- a/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj +++ b/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj b/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj +++ b/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj index 5742fd3a5..a25024b6c 100644 --- a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj +++ b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj b/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj index 65b4175d4..584b980ce 100644 --- a/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj +++ b/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj index 312aa49f2..f84a8add6 100644 --- a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj +++ b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj b/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj index 20c8a9aef..4945a2fb2 100644 --- a/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj +++ b/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj b/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj index 20c8a9aef..4945a2fb2 100644 --- a/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj +++ b/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj b/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj index 7065a1e65..1285c073c 100644 --- a/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj +++ b/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index c8ace891e..f14df4f5f 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj b/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj index 795629ea6..6353f4bf9 100644 --- a/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj +++ b/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj @@ -1,7 +1,7 @@ - + - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 0b83f88e0..a344de142 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -13,7 +13,7 @@ - + diff --git a/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj b/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj +++ b/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj b/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj index 20c8a9aef..4945a2fb2 100644 --- a/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj +++ b/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj b/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj index 20c8a9aef..4945a2fb2 100644 --- a/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj +++ b/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj b/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj index 320c51076..6353f4bf9 100644 --- a/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj +++ b/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj b/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj index f499f594e..e07f40c60 100644 --- a/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj +++ b/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj b/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj index 3008c9230..f167146af 100644 --- a/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj +++ b/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj b/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj index a5bb9ea19..8e12c7c67 100644 --- a/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj +++ b/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj b/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj index 109e3e3f1..7748aad45 100644 --- a/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj +++ b/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api/ApiHelper.cs b/src/Ombi.Api/ApiHelper.cs index fa9d3e7ab..f88525869 100644 --- a/src/Ombi.Api/ApiHelper.cs +++ b/src/Ombi.Api/ApiHelper.cs @@ -66,6 +66,8 @@ namespace Ombi.Api startingTag = builder.Query.Contains("?") ? "&" : "?"; } + value = Uri.EscapeDataString(value); + builder.Query = hasQuery ? $"{builder.Query}{startingTag}{parameter}={value}" : $"{startingTag}{parameter}={value}"; diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 02969e665..83c3ee62e 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -11,8 +11,8 @@ - - + + diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index fcbdab967..4e01e32cd 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 true true Debug;Release;NonUiBuild @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs index ea9208cf7..11860ce28 100644 --- a/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/ExistingRequestRuleTests.cs @@ -130,5 +130,76 @@ namespace Ombi.Core.Tests.Rule.Search Assert.False(search.Approved); Assert.False(search.Requested); } + + [Test] + public async Task ShouldBeFullyAvailable_NoFutureAiredEpisodes_NoRequest() + { + var search = new SearchTvShowViewModel() + { + Id = 999, + SeasonRequests = new List + { + new SeasonRequests + { + Episodes = new List + { + new EpisodeRequests + { + Available = true, + AirDate = new System.DateTime(2020,01,01) + }, + new EpisodeRequests + { + Available = true, + AirDate = new System.DateTime(2020,01,02) + }, + } + } + } + }; + var result = await Rule.Execute(search); + + Assert.True(result.Success); + Assert.That(search.FullyAvailable, Is.True); + Assert.That(search.PartlyAvailable, Is.False); + } + + [Test] + public async Task ShouldBeFullyAvailable_AndPartly_FutureAiredEpisodes_NoRequest() + { + var search = new SearchTvShowViewModel() + { + Id = 999, + SeasonRequests = new List + { + new SeasonRequests + { + Episodes = new List + { + new EpisodeRequests + { + Available = true, + AirDate = new System.DateTime(2020,01,01) + }, + new EpisodeRequests + { + Available = true, + AirDate = new System.DateTime(2020,01,02) + }, + new EpisodeRequests + { + Available = true, + AirDate = new System.DateTime(2029,01,02) + }, + } + } + } + }; + var result = await Rule.Execute(search); + + Assert.True(result.Success); + Assert.That(search.FullyAvailable, Is.True); + Assert.That(search.PartlyAvailable, Is.True); + } } } \ No newline at end of file diff --git a/src/Ombi.Core.Tests/Senders/MassEmailSenderTests.cs b/src/Ombi.Core.Tests/Senders/MassEmailSenderTests.cs new file mode 100644 index 000000000..5c5cedabe --- /dev/null +++ b/src/Ombi.Core.Tests/Senders/MassEmailSenderTests.cs @@ -0,0 +1,228 @@ +using Microsoft.Extensions.Logging; +using MockQueryable.Moq; +using Moq; +using Moq.AutoMock; +using NUnit.Framework; +using Ombi.Core.Authentication; +using Ombi.Core.Models; +using Ombi.Core.Senders; +using Ombi.Notifications; +using Ombi.Notifications.Models; +using Ombi.Settings.Settings.Models.Notifications; +using Ombi.Store.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ombi.Core.Tests.Senders +{ + [TestFixture] + public class MassEmailSenderTests + { + + private MassEmailSender _subject; + private AutoMocker _mocker; + + [SetUp] + public void Setup() + { + _mocker = new AutoMocker(); + _subject = _mocker.CreateInstance(); + } + + [Test] + public async Task SendMassEmail_SingleUser() + { + var model = new MassEmailModel + { + Body = "Test", + Subject = "Subject", + Users = new List + { + new OmbiUser + { + Id = "a" + } + } + }; + + _mocker.Setup>(x => x.Users).Returns(new List + { + new OmbiUser + { + Id = "a", + Email = "Test@test.com" + } + }.AsQueryable().BuildMock().Object); + + var result = await _subject.SendMassEmail(model); + + _mocker.Verify(x => x.SendAdHoc(It.Is(m => m.Subject == model.Subject + && m.Message == model.Body + && m.To == "Test@test.com"), It.IsAny()), Times.Once); + } + + [Test] + public async Task SendMassEmail_MultipleUsers() + { + var model = new MassEmailModel + { + Body = "Test", + Subject = "Subject", + Users = new List + { + new OmbiUser + { + Id = "a" + }, + new OmbiUser + { + Id = "b" + } + } + }; + + _mocker.Setup>(x => x.Users).Returns(new List + { + new OmbiUser + { + Id = "a", + Email = "Test@test.com" + }, + new OmbiUser + { + Id = "b", + Email = "b@test.com" + } + }.AsQueryable().BuildMock().Object); + + var result = await _subject.SendMassEmail(model); + + _mocker.Verify(x => x.SendAdHoc(It.Is(m => m.Subject == model.Subject + && m.Message == model.Body + && m.To == "Test@test.com"), It.IsAny()), Times.Once); + _mocker.Verify(x => x.SendAdHoc(It.Is(m => m.Subject == model.Subject + && m.Message == model.Body + && m.To == "b@test.com"), It.IsAny()), Times.Once); + } + + [Test] + public async Task SendMassEmail_UserNoEmail() + { + var model = new MassEmailModel + { + Body = "Test", + Subject = "Subject", + Users = new List + { + new OmbiUser + { + Id = "a" + } + } + }; + + _mocker.Setup>(x => x.Users).Returns(new List + { + new OmbiUser + { + Id = "a", + } + }.AsQueryable().BuildMock().Object); + + var result = await _subject.SendMassEmail(model); + _mocker.Verify>( + x => x.Log( + LogLevel.Information, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>()), + Times.Once); + + _mocker.Verify(x => x.SendAdHoc(It.IsAny(), It.IsAny()), Times.Never); + } + + [Test] + public async Task SendMassEmail_Bcc() + { + var model = new MassEmailModel + { + Body = "Test", + Subject = "Subject", + Bcc = true, + Users = new List + { + new OmbiUser + { + Id = "a" + }, + new OmbiUser + { + Id = "b" + } + } + }; + + _mocker.Setup>(x => x.Users).Returns(new List + { + new OmbiUser + { + Id = "a", + Email = "Test@test.com" + }, + new OmbiUser + { + Id = "b", + Email = "b@test.com" + } + }.AsQueryable().BuildMock().Object); + + var result = await _subject.SendMassEmail(model); + + _mocker.Verify(x => x.SendAdHoc(It.Is(m => m.Subject == model.Subject + && m.Message == model.Body + && m.Other["bcc"] == "Test@test.com,b@test.com"), It.IsAny()), Times.Once); + } + + [Test] + public async Task SendMassEmail_Bcc_NoEmails() + { + var model = new MassEmailModel + { + Body = "Test", + Subject = "Subject", + Bcc = true, + Users = new List + { + new OmbiUser + { + Id = "a" + }, + new OmbiUser + { + Id = "b" + } + } + }; + + _mocker.Setup>(x => x.Users).Returns(new List + { + new OmbiUser + { + Id = "a", + }, + new OmbiUser + { + Id = "b", + } + }.AsQueryable().BuildMock().Object); + + var result = await _subject.SendMassEmail(model); + + _mocker.Verify(x => x.SendAdHoc(It.IsAny(), It.IsAny()), Times.Never); + } + + } +} diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs index 91f8a58b0..277144ab1 100644 --- a/src/Ombi.Core/Engine/BaseMediaEngine.cs +++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs @@ -78,6 +78,32 @@ namespace Ombi.Core.Engine return _dbTv; } + protected async Task CheckCanManageRequest(BaseRequest request) { + var errorResult = new RequestEngineResult { + Result = false, + ErrorCode = ErrorCode.NoPermissions + }; + var successResult = new RequestEngineResult { Result = true }; + + // Admins can always manage requests + var isAdmin = await IsInRole(OmbiRoles.PowerUser) || await IsInRole(OmbiRoles.Admin); + if (isAdmin) { + return successResult; + } + + // Users with 'ManageOwnRequests' can only manage their own requests + var canManageOwnRequests = await IsInRole(OmbiRoles.ManageOwnRequests); + if (!canManageOwnRequests) { + return errorResult; + } + var isRequestedBySameUser = ( await GetUser() ).Id == request.RequestedUser?.Id; + if (isRequestedBySameUser) { + return successResult; + } + + return errorResult; + } + public RequestCountModel RequestCount() { var movieQuery = MovieRepository.GetAll(); diff --git a/src/Ombi.Core/Engine/IMusicRequestEngine.cs b/src/Ombi.Core/Engine/IMusicRequestEngine.cs index 4dbb3b16a..fcdb06496 100644 --- a/src/Ombi.Core/Engine/IMusicRequestEngine.cs +++ b/src/Ombi.Core/Engine/IMusicRequestEngine.cs @@ -18,7 +18,7 @@ namespace Ombi.Core.Engine Task GetTotal(); Task MarkAvailable(int modelId); Task MarkUnavailable(int modelId); - Task RemoveAlbumRequest(int requestId); + Task RemoveAlbumRequest(int requestId); Task RequestAlbum(MusicAlbumRequestViewModel model); Task> SearchAlbumRequest(string search); Task UserHasRequest(string userId); diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index dfcd1b1da..7cc64bee2 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -14,7 +14,7 @@ namespace Ombi.Core.Engine.Interfaces Task> SearchMovieRequest(string search); Task RequestCollection(int collectionId, CancellationToken cancellationToken); - Task RemoveMovieRequest(int requestId); + Task RemoveMovieRequest(int requestId); Task RemoveAllMovieRequests(); Task GetRequest(int requestId); Task UpdateMovieRequest(MovieRequests request); diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs index c93403b3e..6fdf52c56 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs @@ -20,7 +20,7 @@ namespace Ombi.Core.Engine.Interfaces Task UpdateTvRequest(TvRequests request); Task> GetAllChldren(int tvId); Task UpdateChildRequest(ChildRequests request); - Task RemoveTvChild(int requestId); + Task RemoveTvChild(int requestId); Task ApproveChildRequest(int id); Task> GetRequestsLite(); Task UpdateQualityProfile(int requestId, int profileId); diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs index 25a3c2621..2e01e739e 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Models.Search; using Ombi.Core.Models.Search.V2; @@ -10,6 +11,7 @@ namespace Ombi.Core { Task GetShowInformation(string tvdbid, CancellationToken token); Task GetShowByRequest(int requestId, CancellationToken token); + Task GetTvByActor(int actorId, string langCode); Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken); Task> Popular(int currentlyLoaded, int amountToLoad, string langCustomCode = null); Task> Anticipated(int currentlyLoaded, int amountToLoad); diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 63aa0d376..f69e890be 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -527,11 +527,12 @@ namespace Ombi.Core.Engine request.Denied = true; request.DeniedReason = denyReason; - // We are denying a request - await NotificationHelper.Notify(request, NotificationType.RequestDeclined); await MovieRepository.Update(request); await _mediaCacheService.Purge(); + // We are denying a request + await NotificationHelper.Notify(request, NotificationType.RequestDeclined); + return new RequestEngineResult { Result = true, @@ -654,11 +655,20 @@ namespace Ombi.Core.Engine /// /// The request identifier. /// - public async Task RemoveMovieRequest(int requestId) + public async Task RemoveMovieRequest(int requestId) { var request = await MovieRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); + + var result = await CheckCanManageRequest(request); + if (result.IsError) + return result; + await MovieRepository.Delete(request); await _mediaCacheService.Purge(); + return new RequestEngineResult + { + Result = true, + }; } public async Task RemoveAllMovieRequests() diff --git a/src/Ombi.Core/Engine/MusicRequestEngine.cs b/src/Ombi.Core/Engine/MusicRequestEngine.cs index d0a9cc5d7..b9aa183d2 100644 --- a/src/Ombi.Core/Engine/MusicRequestEngine.cs +++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs @@ -404,10 +404,20 @@ namespace Ombi.Core.Engine /// /// The request identifier. /// - public async Task RemoveAlbumRequest(int requestId) + public async Task RemoveAlbumRequest(int requestId) { var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); + + var result = await CheckCanManageRequest(request); + if (result.IsError) + return result; + await MusicRepository.Delete(request); + + return new RequestEngineResult + { + Result = true, + }; } public async Task UserHasRequest(string userId) diff --git a/src/Ombi.Core/Engine/RequestEngineResult.cs b/src/Ombi.Core/Engine/RequestEngineResult.cs index 08c61b7ae..a2f4f93f4 100644 --- a/src/Ombi.Core/Engine/RequestEngineResult.cs +++ b/src/Ombi.Core/Engine/RequestEngineResult.cs @@ -7,7 +7,7 @@ namespace Ombi.Core.Engine { public bool Result { get; set; } public string Message { get; set; } - public bool IsError => !string.IsNullOrEmpty(ErrorMessage); + public bool IsError => ( !string.IsNullOrEmpty(ErrorMessage) || ErrorCode != null ); public string ErrorMessage { get; set; } public ErrorCode? ErrorCode { get; set; } public int RequestId { get; set; } diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index fd306f951..edfa7b392 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -710,7 +710,11 @@ namespace Ombi.Core.Engine if (request.Approved) { - await NotificationHelper.Notify(request, NotificationType.RequestApproved); + var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification, string.Empty); + if (canNotify.Success) + { + await NotificationHelper.Notify(request, NotificationType.RequestApproved); + } // Autosend await TvSender.Send(request); } @@ -749,10 +753,14 @@ namespace Ombi.Core.Engine return request; } - public async Task RemoveTvChild(int requestId) + public async Task RemoveTvChild(int requestId) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); + var result = await CheckCanManageRequest(request); + if (result.IsError) + return result; + TvRepository.Db.ChildRequests.Remove(request); var all = TvRepository.Db.TvRequests.Include(x => x.ChildRequests); var parent = all.FirstOrDefault(x => x.Id == request.ParentRequestId); @@ -766,6 +774,11 @@ namespace Ombi.Core.Engine await TvRepository.Db.SaveChangesAsync(); await _mediaCacheService.Purge(); + + return new RequestEngineResult + { + Result = true, + }; } public async Task RemoveTvRequest(int requestId) @@ -948,7 +961,11 @@ namespace Ombi.Core.Engine if (model.Approved) { // Autosend - await NotificationHelper.Notify(model, NotificationType.RequestApproved); + var canNotify = await RunSpecificRule(model, SpecificRules.CanSendNotification, string.Empty); + if (canNotify.Success) + { + await NotificationHelper.Notify(model, NotificationType.RequestApproved); + } var result = await TvSender.Send(model); if (result.Success) { diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index e865c6465..7c69e4d5d 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -323,6 +323,7 @@ namespace Ombi.Core.Engine.V2 public async Task GetMoviesByActor(int actorId, string langCode) { + langCode = await DefaultLanguageCode(langCode); var result = await Cache.GetOrAddAsync(nameof(GetMoviesByActor) + actorId + langCode, () => MovieApi.GetActorMovieCredits(actorId, langCode), DateTimeOffset.Now.AddHours(12)); // Later we run this through the rules engine diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 26cba7c14..07f8ce981 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -89,7 +89,7 @@ namespace Ombi.Core.Engine.V2 foreach (var tvSeason in show.seasons.Where(x => x.season_number != 0)) // skip the first season { - var seasonEpisodes = (await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token)); + var seasonEpisodes = (await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token, langCode)); MapSeasons(mapped.SeasonRequests, tvSeason, seasonEpisodes); } @@ -147,6 +147,13 @@ namespace Ombi.Core.Engine.V2 return await processed; } + public async Task GetTvByActor(int actorId, string langCode) + { + langCode = await DefaultLanguageCode(langCode); + var result = await Cache.GetOrAddAsync(nameof(GetTvByActor) + actorId + langCode, + () => _movieApi.GetActorTvCredits(actorId, langCode), DateTimeOffset.Now.AddHours(12)); + return result; + } public async Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken) { diff --git a/src/Ombi.Core/Models/MassEmailModel.cs b/src/Ombi.Core/Models/MassEmailModel.cs index ad09f0cb9..e175c0886 100644 --- a/src/Ombi.Core/Models/MassEmailModel.cs +++ b/src/Ombi.Core/Models/MassEmailModel.cs @@ -35,6 +35,8 @@ namespace Ombi.Core.Models public string Subject { get; set; } public string Body { get; set; } + public bool Bcc { get; set; } + public List Users { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 857de0bab..359792e0d 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -13,8 +13,8 @@ - - + + diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs index a7cd43336..58c2508a6 100644 --- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs @@ -85,11 +85,18 @@ namespace Ombi.Core.Rule.Rules.Search { request.FullyAvailable = true; } - if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available && e.AirDate > DateTime.MinValue))) + if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.Available && e.AirDate > DateTime.MinValue && e.AirDate <= DateTime.UtcNow))) { request.PartlyAvailable = true; } + var hasUnairedRequests = request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.Any(e => e.AirDate >= DateTime.UtcNow)); + + if (request.FullyAvailable) + { + request.PartlyAvailable = hasUnairedRequests; + } + return Success(); } if (obj.Type == RequestType.Album) diff --git a/src/Ombi.Core/Senders/MassEmailSender.cs b/src/Ombi.Core/Senders/MassEmailSender.cs index 604224b34..106a63f49 100644 --- a/src/Ombi.Core/Senders/MassEmailSender.cs +++ b/src/Ombi.Core/Senders/MassEmailSender.cs @@ -25,7 +25,9 @@ // ************************************************************************/ #endregion +using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -64,6 +66,63 @@ namespace Ombi.Core.Senders var customization = await _customizationService.GetSettingsAsync(); var email = await _emailService.GetSettingsAsync(); var messagesSent = new List(); + if (model.Bcc) + { + await SendBccMails(model, customization, email, messagesSent); + } + else + { + await SendIndividualEmails(model, customization, email, messagesSent); + } + + await Task.WhenAll(messagesSent); + + return true; + } + + private async Task SendBccMails(MassEmailModel model, CustomizationSettings customization, EmailNotificationSettings email, List messagesSent) + { + var resolver = new NotificationMessageResolver(); + var curlys = new NotificationMessageCurlys(); + + var validUsers = new List(); + foreach (var user in model.Users) + { + var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id); + if (!fullUser.Email.HasValue()) + { + _log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName); + continue; + } + + validUsers.Add(fullUser); + } + + if (!validUsers.Any()) + { + return; + } + + var firstUser = validUsers.FirstOrDefault(); + + var bccAddress = string.Join(',', validUsers.Select(x => x.Email)); + curlys.Setup(firstUser, customization); + var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject }; + var content = resolver.ParseMessage(template, curlys); + var msg = new NotificationMessage + { + Message = content.Message, + Subject = content.Subject, + Other = new Dictionary { { "bcc", bccAddress } } + }; + + messagesSent.Add(_email.SendAdHoc(msg, email)); + } + + private async Task SendIndividualEmails(MassEmailModel model, CustomizationSettings customization, EmailNotificationSettings email, List messagesSent) + { + var resolver = new NotificationMessageResolver(); + var curlys = new NotificationMessageCurlys(); foreach (var user in model.Users) { var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id); @@ -72,8 +131,6 @@ namespace Ombi.Core.Senders _log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName); continue; } - var resolver = new NotificationMessageResolver(); - var curlys = new NotificationMessageCurlys(); curlys.Setup(fullUser, customization); var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject }; var content = resolver.ParseMessage(template, curlys); @@ -83,13 +140,19 @@ namespace Ombi.Core.Senders To = fullUser.Email, Subject = content.Subject }; - messagesSent.Add(_email.SendAdHoc(msg, email)); + messagesSent.Add(DelayEmail(msg, email)); _log.LogInformation("Sent mass email to user {0} @ {1}", fullUser.UserName, fullUser.Email); } + } - await Task.WhenAll(messagesSent); - - return true; + /// + /// This will add a 2 second delay, this is to help with concurrent connection limits + /// + /// + private async Task DelayEmail(NotificationMessage msg, EmailNotificationSettings email) + { + await Task.Delay(2000); + await _email.SendAdHoc(msg, email); } } } \ No newline at end of file diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 2f42a12c0..9f60d2ba0 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -309,6 +309,22 @@ namespace Ombi.Core.Senders private async Task SendToSonarr(ChildRequests model, SonarrSeries result, SonarrSettings s) { + // Check to ensure we have the all the seasons, ensure the Sonarr metadata has grabbed all the data + Season existingSeason = null; + foreach (var season in model.SeasonRequests) + { + var attempt = 0; + existingSeason = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); + while (existingSeason == null && attempt < 5) + { + attempt++; + Logger.LogInformation("There was no season numer {0} in Sonarr for title {1}. Will try again as the metadata did not get created", season.SeasonNumber, model.ParentRequest.Title); + result = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri); + existingSeason = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); + await Task.Delay(500); + } + } + var episodesToUpdate = new List(); // Ok, now let's sort out the episodes. @@ -327,24 +343,20 @@ namespace Ombi.Core.Senders await Task.Delay(500); } + var seriesChanges = false; - foreach (var req in model.SeasonRequests) + foreach (var season in model.SeasonRequests) { - foreach (var ep in req.Episodes) + foreach (var ep in season.Episodes) { var sonarrEp = sonarrEpList.FirstOrDefault(x => - x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber); + x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == season.SeasonNumber); if (sonarrEp != null && !sonarrEp.monitored) { sonarrEp.monitored = true; episodesToUpdate.Add(sonarrEp); } } - } - var seriesChanges = false; - - foreach (var season in model.SeasonRequests) - { var sonarrEpisodeList = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber).ToList(); var sonarrEpCount = sonarrEpisodeList.Count; var ourRequestCount = season.Episodes.Count; @@ -356,15 +368,7 @@ namespace Ombi.Core.Senders //// NOTE, not sure if needed since ombi ui displays future episodes anyway... //ourEpisodes.AddRange(unairedEpisodes); //var distinctEpisodes = ourEpisodes.Distinct().ToList(); - //var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes); - - var existingSeason = - result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); - if (existingSeason == null) - { - Logger.LogError("There was no season numer {0} in Sonarr for title {1}", season.SeasonNumber, model.ParentRequest.Title); - continue; - } + //var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes); if (sonarrEpCount == ourRequestCount /*|| !missingEpisodes.Any()*/) diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 9d6efcd69..e65f2d5a2 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -11,10 +11,10 @@ - + - - + + diff --git a/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj b/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj index 1abcc9a8d..e228dd0d2 100644 --- a/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj +++ b/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj index 53725b2b0..370440d1c 100644 --- a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj +++ b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 false @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Helpers/JobDataKeys.cs b/src/Ombi.Helpers/JobDataKeys.cs index e0e2f7451..277834431 100644 --- a/src/Ombi.Helpers/JobDataKeys.cs +++ b/src/Ombi.Helpers/JobDataKeys.cs @@ -3,6 +3,7 @@ public class JobDataKeys { public const string RecentlyAddedSearch = "recentlyAddedSearch"; + public const string EmbyRecentlyAddedSearch = nameof(EmbyRecentlyAddedSearch); public const string NotificationOptions = nameof(NotificationOptions); } } \ No newline at end of file diff --git a/src/Ombi.Helpers/LinqHelpers.cs b/src/Ombi.Helpers/LinqHelpers.cs index af8d44633..4e8012653 100644 --- a/src/Ombi.Helpers/LinqHelpers.cs +++ b/src/Ombi.Helpers/LinqHelpers.cs @@ -6,16 +6,6 @@ namespace Ombi.Helpers { public static class LinqHelpers { - public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) - { - HashSet knownKeys = new HashSet(); - foreach (TSource source1 in source) - { - if (knownKeys.Add(keySelector(source1))) - yield return source1; - } - } - public static HashSet ToHashSet( this IEnumerable source, IEqualityComparer comparer = null) diff --git a/src/Ombi.Helpers/MediaCacheService.cs b/src/Ombi.Helpers/MediaCacheService.cs index d99090b62..8d244f4b3 100644 --- a/src/Ombi.Helpers/MediaCacheService.cs +++ b/src/Ombi.Helpers/MediaCacheService.cs @@ -53,17 +53,18 @@ namespace Ombi.Helpers _memoryCache.Set(CacheKey, mediaServiceCache); } - public async Task Purge() + public Task Purge() { var keys = _memoryCache.Get>(CacheKey); if (keys == null) { - return; + return Task.CompletedTask; } foreach (var key in keys) { base.Remove(key); } + return Task.CompletedTask; } } diff --git a/src/Ombi.Helpers/NotificationSubstitues.cs b/src/Ombi.Helpers/NotificationSubstitues.cs new file mode 100644 index 000000000..69b91304d --- /dev/null +++ b/src/Ombi.Helpers/NotificationSubstitues.cs @@ -0,0 +1,18 @@ +namespace Ombi.Helpers +{ + public static class NotificationSubstitues + { + public const string Title = nameof(Title); + public const string IssueDescription = nameof(IssueDescription); + public const string IssueCategory = nameof(IssueCategory); + public const string IssueStatus = nameof(IssueStatus); + public const string IssueSubject = nameof(IssueSubject); + public const string IssueUser = nameof(IssueUser); + public const string IssueUserAlias = nameof(IssueUserAlias); + public const string RequestType = nameof(RequestType); + public const string PosterPath = nameof(PosterPath); + public const string NewIssueComment = nameof(NewIssueComment); + public const string IssueId = nameof(IssueId); + public const string AdminComment = nameof(AdminComment); + } +} diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 2c32cc1bc..afc881d94 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -13,8 +13,8 @@ - - + + diff --git a/src/Ombi.Hubs/Ombi.Hubs.csproj b/src/Ombi.Hubs/Ombi.Hubs.csproj index 613221552..fc65e2dc2 100644 --- a/src/Ombi.Hubs/Ombi.Hubs.csproj +++ b/src/Ombi.Hubs/Ombi.Hubs.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Mapping/Ombi.Mapping.csproj b/src/Ombi.Mapping/Ombi.Mapping.csproj index f6a5194d9..9ad1f1135 100644 --- a/src/Ombi.Mapping/Ombi.Mapping.csproj +++ b/src/Ombi.Mapping/Ombi.Mapping.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs index 15ec9b286..455c2d182 100644 --- a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs +++ b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs @@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates if (string.IsNullOrEmpty(_templateLocation)) { #if DEBUG - _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net5.0", "Templates", + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates", "BasicTemplate.html"); #else _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); diff --git a/src/Ombi.Notifications.Templates/NewsletterTemplate.cs b/src/Ombi.Notifications.Templates/NewsletterTemplate.cs index f2f0cbf12..ef31dfc4c 100644 --- a/src/Ombi.Notifications.Templates/NewsletterTemplate.cs +++ b/src/Ombi.Notifications.Templates/NewsletterTemplate.cs @@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates if (string.IsNullOrEmpty(_templateLocation)) { #if DEBUG - _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net5.0", "Templates", "NewsletterTemplate.html"); + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates", "NewsletterTemplate.html"); #else _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "NewsletterTemplate.html"); #endif diff --git a/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj b/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj index 47f701083..33088a591 100644 --- a/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj +++ b/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs b/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs index 960e552df..783844a82 100644 --- a/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs +++ b/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs @@ -76,6 +76,7 @@ namespace Ombi.Notifications.Tests { "IssueUser", "User" }, { "IssueUserAlias", "alias" }, { "RequestType", "Movie" }, + { "PosterPath", "aaaaa" } } }; var req = F.Build() @@ -94,6 +95,39 @@ namespace Ombi.Notifications.Tests Assert.That("Movie", Is.EqualTo(sut.Type)); } + [Test] + public void IssueNotificationTests_NoRequest() + { + var notificationOptions = new NotificationOptions + { + Substitutes = new Dictionary + { + { "IssueDescription", "Desc" }, + { "IssueCategory", "Cat" }, + { "IssueStatus", "state" }, + { "IssueSubject", "sub" }, + { "NewIssueComment", "a" }, + { "IssueUser", "User" }, + { "IssueUserAlias", "alias" }, + { "RequestType", "Movie" }, + { "PosterPath", "aaaaa" } + } + }; + + var customization = new CustomizationSettings(); + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, (MovieRequests)null, customization, userPrefs); + + Assert.That("Desc", Is.EqualTo(sut.IssueDescription)); + Assert.That("Cat", Is.EqualTo(sut.IssueCategory)); + Assert.That("state", Is.EqualTo(sut.IssueStatus)); + Assert.That("a", Is.EqualTo(sut.NewIssueComment)); + Assert.That("User", Is.EqualTo(sut.UserName)); + Assert.That("alias", Is.EqualTo(sut.Alias)); + Assert.That("Movie", Is.EqualTo(sut.Type)); + Assert.That("https://image.tmdb.org/t/p/w300/aaaaa", Is.EqualTo(sut.PosterImage)); + } + [Test] public void MovieNotificationUserPreferences() { diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index 495df8cdc..b3edbcf6d 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Debug;Release;NonUiBuild @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 0cc0c9b89..ae344116f 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Discord; using Ombi.Api.Discord.Models; @@ -21,8 +22,8 @@ namespace Ombi.Notifications.Agents public DiscordNotification(IDiscordApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) - : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) + : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index f41406d0e..cb96fcdb6 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using MailKit.Net.Smtp; @@ -22,7 +23,7 @@ namespace Ombi.Notifications.Agents { public EmailNotification(ISettingsService settings, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, IEmailProvider prov, ISettingsService c, ILogger log, UserManager um, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(settings, r, m, t, c, log, sub, music, userPref) + IRepository userPref) : base(settings, r, m, t, c, log, sub, music, userPref, um) { EmailProvider = prov; Logger = log; @@ -114,7 +115,15 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.NewRequest, model, settings); message.Other.Add("PlainTextBody", plaintext); - await Send(message, settings); + foreach (var recipient in (await GetPrivilegedUsers()).DistinctBy(x => x.Email)) + { + if (recipient.Email.IsNullOrEmpty()) + { + continue; + } + message.To = recipient.Email; + await Send(message, settings); + } } protected override async Task NewIssue(NotificationOptions model, EmailNotificationSettings settings) diff --git a/src/Ombi.Notifications/Agents/GotifyNotification.cs b/src/Ombi.Notifications/Agents/GotifyNotification.cs index 00b62c343..e948c4d89 100644 --- a/src/Ombi.Notifications/Agents/GotifyNotification.cs +++ b/src/Ombi.Notifications/Agents/GotifyNotification.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Gotify; using Ombi.Core.Settings; @@ -17,7 +18,7 @@ namespace Ombi.Notifications.Agents { public GotifyNotification(IGotifyApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs b/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs index c2d71e0d0..9df7c6a94 100644 --- a/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs +++ b/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs @@ -23,7 +23,7 @@ namespace Ombi.Notifications.Agents public LegacyMobileNotification(IOneSignalApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository notification, UserManager um, IRepository sub, IMusicRequestRepository music, IRepository issueRepository, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref, um) { _api = api; _logger = log; @@ -59,7 +59,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.NewRequest); + var playerIds = await GetPrivilegedUsersPlayerIds(); await Send(playerIds, notification, settings, model, true); } @@ -77,7 +77,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.Issue); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model); } @@ -106,7 +106,7 @@ namespace Ombi.Notifications.Agents else { // Send to admin - var playerIds = await GetAdmins(NotificationType.IssueComment); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model); } } @@ -147,7 +147,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.Test); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model); } @@ -241,15 +241,25 @@ namespace Ombi.Notifications.Agents await Send(playerIds, notification, settings, model); } - private async Task> GetAdmins(NotificationType type) + private async Task> GetAdmins() { - var adminUsers = (await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin)).Select(x => x.Id).ToList(); + return await GetNotificationRecipients(await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin)); + } + private async Task> GetPrivilegedUsersPlayerIds() + { + return await GetNotificationRecipients(await GetPrivilegedUsers()); + } + + private async Task> GetNotificationRecipients(IEnumerable users) + { + + var adminUsers = users.Select(x => x.Id).ToList(); var notificationUsers = _notifications.GetAll().Include(x => x.User).Where(x => adminUsers.Contains(x.UserId)); var playerIds = await notificationUsers.Select(x => x.PlayerId).ToListAsync(); if (!playerIds.Any()) { _logger.LogInformation( - $"there are no admins to send a notification for {type}, for agent {NotificationAgent.Mobile}"); + $"there are no users to send a notification for agent {NotificationAgent.Mobile}"); return null; } return playerIds; diff --git a/src/Ombi.Notifications/Agents/MattermostNotification.cs b/src/Ombi.Notifications/Agents/MattermostNotification.cs index 39e87e334..4b5b570c7 100644 --- a/src/Ombi.Notifications/Agents/MattermostNotification.cs +++ b/src/Ombi.Notifications/Agents/MattermostNotification.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Mattermost; using Ombi.Api.Mattermost.Models; @@ -19,7 +20,7 @@ namespace Ombi.Notifications.Agents { public MattermostNotification(IMattermostApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index b7ada4838..0d21f39a5 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -23,7 +23,7 @@ namespace Ombi.Notifications.Agents public MobileNotification(ICloudMobileNotification api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository notification, UserManager um, IRepository sub, IMusicRequestRepository music, IRepository issueRepository, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref, um) { _api = api; _logger = log; @@ -62,7 +62,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.NewRequest); + var playerIds = await GetPrivilegedUsersPlayerIds(); await Send(playerIds, notification, settings, model, true); } @@ -82,7 +82,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.Issue); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model); } @@ -112,7 +112,7 @@ namespace Ombi.Notifications.Agents else { // Send to admin - var playerIds = await GetAdmins(NotificationType.IssueComment); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model); } } @@ -157,7 +157,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.Test); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model); } @@ -279,15 +279,25 @@ namespace Ombi.Notifications.Agents await Send(playerIds, notification, settings, model); } - private async Task> GetAdmins(NotificationType type) + private async Task> GetAdmins() { - var adminUsers = (await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin)).Select(x => x.Id).ToList(); + return await GetNotificationRecipients(await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin)); + } + private async Task> GetPrivilegedUsersPlayerIds() + { + return await GetNotificationRecipients(await GetPrivilegedUsers()); + } + + private async Task> GetNotificationRecipients(IEnumerable users) + { + + var adminUsers = users.Select(x => x.Id).ToList(); var notificationUsers = _notifications.GetAll().Include(x => x.User).Where(x => adminUsers.Contains(x.UserId)); var playerIds = await notificationUsers.Select(x => x.Token).ToListAsync(); if (!playerIds.Any()) { _logger.LogInformation( - $"there are no admins to send a notification for {type}, for agent {NotificationAgent.Mobile}"); + $"there are no users to send a notification for agent {NotificationAgent.Mobile}"); return null; } return playerIds; @@ -377,7 +387,7 @@ namespace Ombi.Notifications.Agents }; // Get admin devices - var playerIds = await GetAdmins(NotificationType.PartiallyAvailable); + var playerIds = await GetAdmins(); await Send(playerIds, notification, settings, model, true); } } diff --git a/src/Ombi.Notifications/Agents/PushbulletNotification.cs b/src/Ombi.Notifications/Agents/PushbulletNotification.cs index fd4be12a2..3823afe13 100644 --- a/src/Ombi.Notifications/Agents/PushbulletNotification.cs +++ b/src/Ombi.Notifications/Agents/PushbulletNotification.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Pushbullet; using Ombi.Core.Settings; @@ -17,7 +18,7 @@ namespace Ombi.Notifications.Agents { public PushbulletNotification(IPushbulletApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/PushoverNotification.cs b/src/Ombi.Notifications/Agents/PushoverNotification.cs index 21352b8d6..3edcd62e2 100644 --- a/src/Ombi.Notifications/Agents/PushoverNotification.cs +++ b/src/Ombi.Notifications/Agents/PushoverNotification.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Pushbullet; using Ombi.Api.Pushover; @@ -18,7 +19,7 @@ namespace Ombi.Notifications.Agents { public PushoverNotification(IPushoverApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/SlackNotification.cs b/src/Ombi.Notifications/Agents/SlackNotification.cs index 8dbf14a7d..7ea5b628a 100644 --- a/src/Ombi.Notifications/Agents/SlackNotification.cs +++ b/src/Ombi.Notifications/Agents/SlackNotification.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Slack; using Ombi.Api.Slack.Models; @@ -18,7 +19,7 @@ namespace Ombi.Notifications.Agents { public SlackNotification(ISlackApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/TelegramNotification.cs b/src/Ombi.Notifications/Agents/TelegramNotification.cs index 6561a2494..7d5d9ea54 100644 --- a/src/Ombi.Notifications/Agents/TelegramNotification.cs +++ b/src/Ombi.Notifications/Agents/TelegramNotification.cs @@ -10,6 +10,7 @@ using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; using Ombi.Api.Telegram; +using Microsoft.AspNetCore.Identity; namespace Ombi.Notifications.Agents { @@ -19,7 +20,7 @@ namespace Ombi.Notifications.Agents INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s , IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t,s,log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t,s,log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/WebhookNotification.cs b/src/Ombi.Notifications/Agents/WebhookNotification.cs index c226d048a..6ed62605e 100644 --- a/src/Ombi.Notifications/Agents/WebhookNotification.cs +++ b/src/Ombi.Notifications/Agents/WebhookNotification.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Ombi.Api.Webhook; using Ombi.Core.Settings; @@ -18,7 +19,7 @@ namespace Ombi.Notifications.Agents { public WebhookNotification(IWebhookApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t, s, log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/WhatsAppNotification.cs b/src/Ombi.Notifications/Agents/WhatsAppNotification.cs index cf97f498f..7956af18a 100644 --- a/src/Ombi.Notifications/Agents/WhatsAppNotification.cs +++ b/src/Ombi.Notifications/Agents/WhatsAppNotification.cs @@ -10,6 +10,7 @@ using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; using Ombi.Api.Twilio; +using Microsoft.AspNetCore.Identity; namespace Ombi.Notifications.Agents { @@ -19,7 +20,7 @@ namespace Ombi.Notifications.Agents INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s , IRepository sub, IMusicRequestRepository music, - IRepository userPref) : base(sn, r, m, t,s,log, sub, music, userPref) + IRepository userPref, UserManager um) : base(sn, r, m, t,s,log, sub, music, userPref, um) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index ac9de5736..9397767bf 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Core.Settings; @@ -19,7 +21,7 @@ namespace Ombi.Notifications { protected BaseNotification(ISettingsService settings, INotificationTemplatesRepository templateRepo, IMovieRequestRepository movie, ITvRequestRepository tv, ISettingsService customization, ILogger> log, IRepository sub, IMusicRequestRepository album, - IRepository notificationUserPreferences) + IRepository notificationUserPreferences, UserManager um) { Settings = settings; TemplateRepository = templateRepo; @@ -30,6 +32,7 @@ namespace Ombi.Notifications _log = log; AlbumRepository = album; UserNotificationPreferences = notificationUserPreferences; + _userManager = um; Settings.ClearCache(); } @@ -43,6 +46,7 @@ namespace Ombi.Notifications protected IRepository UserNotificationPreferences { get; set; } private ISettingsService CustomizationSettings { get; } private readonly ILogger> _log; + private readonly UserManager _userManager; protected ChildRequests TvRequest { get; set; } @@ -210,6 +214,13 @@ namespace Ombi.Notifications .FirstOrDefault(x => x.Agent == agent && x.UserId == userId); } + protected async Task> GetPrivilegedUsers() + { + IEnumerable recipients = await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin); + recipients = recipients.Concat(await _userManager.GetUsersInRoleAsync(OmbiRoles.PowerUser)); + return recipients; + } + private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template, NotificationAgent agent) { var resolver = new NotificationMessageResolver(); diff --git a/src/Ombi.Notifications/GenericEmailProvider.cs b/src/Ombi.Notifications/GenericEmailProvider.cs index 15f17af92..fdeb1f49c 100644 --- a/src/Ombi.Notifications/GenericEmailProvider.cs +++ b/src/Ombi.Notifications/GenericEmailProvider.cs @@ -64,7 +64,20 @@ namespace Ombi.Notifications MessageId = messageId }; message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress)); - message.To.Add(new MailboxAddress(model.To, model.To)); + if (model.To.HasValue()) + { + message.To.Add(new MailboxAddress(model.To, model.To)); + } + + // Check for BCC + if (model.Other.TryGetValue("bcc", out var bcc)) + { + var bccList = bcc.Split(',', StringSplitOptions.RemoveEmptyEntries); + foreach (var item in bccList) + { + message.Bcc.Add(new MailboxAddress(item, item)); + } + } using (var client = new SmtpClient()) { diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index 392fc9412..924864c62 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -39,7 +39,22 @@ namespace Ombi.Notifications Year = req?.ReleaseDate.Year.ToString(); Overview = req?.Overview; AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; - PosterImage = $"https://image.tmdb.org/t/p/w300/{req?.PosterPath?.TrimStart('/') ?? string.Empty}"; + + var img = req?.PosterPath ?? string.Empty; + if (img.HasValue()) + { + if (img.StartsWith("http")) + { + // This means it's a legacy request from when we used TvMaze as a provider. + // The poster url is the fully qualified address, so just use it + PosterImage = img; + } + else + { + PosterImage = + $"https://image.tmdb.org/t/p/w300/{img?.TrimStart('/') ?? string.Empty}"; + } + } CalculateRequestStatus(req); } @@ -53,8 +68,21 @@ namespace Ombi.Notifications Year = req?.ParentRequest?.ReleaseDate.Year.ToString(); Overview = req?.ParentRequest?.Overview; AdditionalInformation = opts.AdditionalInformation; - PosterImage = - $"https://image.tmdb.org/t/p/w300/{req?.ParentRequest?.PosterPath?.TrimStart('/') ?? string.Empty}"; + var img = req?.ParentRequest?.PosterPath ?? string.Empty; + if (img.HasValue()) + { + if (img.StartsWith("http")) + { + // This means it's a legacy request from when we used TvMaze as a provider. + // The poster url is the fully qualified address, so just use it + PosterImage = img; + } + else + { + PosterImage = + $"https://image.tmdb.org/t/p/w300/{img?.TrimStart('/') ?? string.Empty}"; + } + } // Generate episode list. StringBuilder epSb = new StringBuilder(); @@ -94,16 +122,24 @@ namespace Ombi.Notifications private void LoadIssues(NotificationOptions opts) { - IssueDescription = opts.Substitutes.TryGetValue("IssueDescription", out string val) ? val : string.Empty; - IssueCategory = opts.Substitutes.TryGetValue("IssueCategory", out val) ? val : string.Empty; - IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty; - IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty; - NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty; - UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty; - Alias = opts.Substitutes.TryGetValue("IssueUserAlias", out val) ? val : string.Empty; - Type = opts.Substitutes.TryGetValue("RequestType", out val) && Enum.TryParse(val, out RequestType type) + IssueDescription = opts.Substitutes.TryGetValue(NotificationSubstitues.IssueDescription, out string val) ? val : string.Empty; + IssueCategory = opts.Substitutes.TryGetValue(NotificationSubstitues.IssueCategory, out val) ? val : string.Empty; + IssueStatus = opts.Substitutes.TryGetValue(NotificationSubstitues.IssueStatus, out val) ? val : string.Empty; + IssueSubject = opts.Substitutes.TryGetValue(NotificationSubstitues.IssueSubject, out val) ? val : string.Empty; + NewIssueComment = opts.Substitutes.TryGetValue(NotificationSubstitues.NewIssueComment, out val) ? val : string.Empty; + UserName = opts.Substitutes.TryGetValue(NotificationSubstitues.IssueUser, out val) ? val : string.Empty; + Alias = opts.Substitutes.TryGetValue(NotificationSubstitues.IssueUserAlias, out val) ? val : string.Empty; + Type = opts.Substitutes.TryGetValue(NotificationSubstitues.RequestType, out val) && Enum.TryParse(val, out RequestType type) ? HumanizeReturnType(type) : string.Empty; + if (opts.Substitutes.TryGetValue(NotificationSubstitues.PosterPath, out val) && val.HasValue()) + { + PosterImage = $"https://image.tmdb.org/t/p/w300/{val.TrimStart('/')}"; + } + else + { + PosterImage = string.Empty; + } } private void LoadCommon(BaseRequest req, CustomizationSettings s, UserNotificationPreferences pref, NotificationOptions opts) diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index ab65285b3..6b497c23e 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index 1203d731d..fd23ee60b 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Debug;Release;NonUiBuild @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 0630c0b28..154ae784f 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -5,6 +5,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; 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.Settings; using Ombi.Core.Settings.Models.External; @@ -36,24 +38,33 @@ namespace Ombi.Schedule.Jobs.Emby private readonly IEmbyContentRepository _repo; private readonly IHubContext _notification; + private const int AmountToTake = 100; + private IEmbyApi Api { get; set; } - public async Task Execute(IJobExecutionContext job) + public async 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.Clients.Clients(NotificationHub.AdminConnectionIds) - .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Started"); + .SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Emby Recently Added Started" : "Emby Content Sync Started"); foreach (var server in embySettings.Servers) { try { - await StartServerCache(server, embySettings); + await StartServerCache(server, recentlyAddedSearch); } catch (Exception e) { @@ -67,11 +78,12 @@ namespace Ombi.Schedule.Jobs.Emby .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Finished"); // Episodes - await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby"); + + await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyEpisodeSync), "Emby"), new JobDataMap(new Dictionary { { JobDataKeys.EmbyRecentlyAddedSearch, recentlyAddedSearch.ToString() } })); } - private async Task StartServerCache(EmbyServers server, EmbySettings settings) + private async Task StartServerCache(EmbyServers server, bool recentlyAdded) { if (!ValidateSettings(server)) return; @@ -86,14 +98,14 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var movieParentIdFilder in movieLibsToFilter) { _logger.LogInformation($"Scanning Lib '{movieParentIdFilder.Title}'"); - await ProcessMovies(server, movieParentIdFilder.Key); + 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, tvParentIdFilter.Key); + await ProcessTv(server, recentlyAdded, tvParentIdFilter.Key); } @@ -101,68 +113,74 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var m in mixedLibs) { _logger.LogInformation($"Scanning Lib '{m.Title}'"); - await ProcessTv(server, m.Key); - await ProcessMovies(server, m.Key); + await ProcessTv(server, recentlyAdded, m.Key); + await ProcessMovies(server, recentlyAdded, m.Key); } } else { - await ProcessMovies(server); - await ProcessTv(server); + await ProcessMovies(server, recentlyAdded); + await ProcessTv(server, recentlyAdded); } } - private async Task ProcessTv(EmbyServers server, string parentId = default) + private async Task ProcessTv(EmbyServers server, bool recentlyAdded, string parentId = default) { // TV Time var mediaToAdd = new HashSet(); - var tv = await Api.GetAllShows(server.ApiKey, parentId, 0, 200, server.AdministratorId, server.FullUri); + EmbyItemContainer tv; + if (recentlyAdded) + { + var recentlyAddedAmountToTake = AmountToTake / 2; + tv = await Api.RecentlyAddedShows(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, server.AdministratorId, server.FullUri); + if (tv.TotalRecordCount > recentlyAddedAmountToTake) + { + tv.TotalRecordCount = recentlyAddedAmountToTake; + } + } + else + { + tv = await Api.GetAllShows(server.ApiKey, parentId, 0, AmountToTake, server.AdministratorId, server.FullUri); + } var totalTv = tv.TotalRecordCount; - var processed = 1; + var processed = 0; while (processed < totalTv) { foreach (var tvShow in tv.Items) { - try + processed++; + if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) { + _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); + continue; + } - processed++; - if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) - { - _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); - continue; - } - - var existingTv = await _repo.GetByEmbyId(tvShow.Id); - if (existingTv == null) - { - _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); - mediaToAdd.Add(new EmbyContent - { - TvDbId = tvShow.ProviderIds?.Tvdb, - ImdbId = tvShow.ProviderIds?.Imdb, - TheMovieDbId = tvShow.ProviderIds?.Tmdb, - Title = tvShow.Name, - Type = EmbyMediaType.Series, - EmbyId = tvShow.Id, - Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), - AddedAt = DateTime.UtcNow - }); - } - else + var existingTv = await _repo.GetByEmbyId(tvShow.Id); + if (existingTv == null) + { + _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); + mediaToAdd.Add(new EmbyContent { - _logger.LogDebug("We already have TV Show {0}", tvShow.Name); - } - + TvDbId = tvShow.ProviderIds?.Tvdb, + ImdbId = tvShow.ProviderIds?.Imdb, + TheMovieDbId = tvShow.ProviderIds?.Tmdb, + Title = tvShow.Name, + Type = EmbyMediaType.Series, + EmbyId = tvShow.Id, + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), + AddedAt = DateTime.UtcNow + }); } - catch (Exception) + else { - - throw; + _logger.LogDebug("We already have TV Show {0}", tvShow.Name); } } // Get the next batch - tv = await Api.GetAllShows(server.ApiKey, parentId, processed, 200, server.AdministratorId, server.FullUri); + if (!recentlyAdded) + { + tv = await Api.GetAllShows(server.ApiKey, parentId, processed, AmountToTake, server.AdministratorId, server.FullUri); + } await _repo.AddRange(mediaToAdd); mediaToAdd.Clear(); } @@ -171,11 +189,25 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.AddRange(mediaToAdd); } - private async Task ProcessMovies(EmbyServers server, string parentId = default) + private async Task ProcessMovies(EmbyServers server, bool recentlyAdded, string parentId = default) { - var movies = await Api.GetAllMovies(server.ApiKey, parentId, 0, 200, server.AdministratorId, server.FullUri); + EmbyItemContainer movies; + if (recentlyAdded) + { + var recentlyAddedAmountToTake = AmountToTake / 2; + movies = await Api.RecentlyAddedMovies(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, server.AdministratorId, 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.GetAllMovies(server.ApiKey, parentId, 0, AmountToTake, server.AdministratorId, server.FullUri); + } var totalCount = movies.TotalRecordCount; - var processed = 1; + var processed = 0; var mediaToAdd = new HashSet(); while (processed < totalCount) { @@ -189,22 +221,24 @@ namespace Ombi.Schedule.Jobs.Emby { await ProcessMovies(item, mediaToAdd, server); } - - processed++; } else { - processed++; // Regular movie await ProcessMovies(movie, mediaToAdd, server); } + + processed++; } // Get the next batch - movies = await Api.GetAllMovies(server.ApiKey, parentId, processed, 200, server.AdministratorId, server.FullUri); + // Recently Added should never be checked as the TotalRecords should equal the amount to take + if (!recentlyAdded) + { + movies = await Api.GetAllMovies(server.ApiKey, parentId, processed, AmountToTake, server.AdministratorId, server.FullUri); + } await _repo.AddRange(mediaToAdd); mediaToAdd.Clear(); - } } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index 1a55da6c9..c1ca48ef7 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -40,6 +40,8 @@ using Ombi.Store.Entities; using Ombi.Store.Repository; using Quartz; using Ombi.Schedule.Jobs.Ombi; +using Ombi.Api.Emby.Models; +using Ombi.Api.Emby.Models.Media.Tv; namespace Ombi.Schedule.Jobs.Emby { @@ -61,13 +63,22 @@ namespace Ombi.Schedule.Jobs.Emby private readonly IEmbyContentRepository _repo; private readonly IHubContext _notification; + private const int AmountToTake = 100; + private IEmbyApi Api { get; set; } - public async Task Execute(IJobExecutionContext job) + public async Task Execute(IJobExecutionContext context) { + JobDataMap dataMap = context.MergedJobDataMap; + var recentlyAddedSearch = false; + if (dataMap.TryGetValue(JobDataKeys.EmbyRecentlyAddedSearch, out var recentlyAddedObj)) + { + recentlyAddedSearch = Convert.ToBoolean(recentlyAddedObj); + } + var settings = await _settings.GetSettingsAsync(); - + Api = _apiFactory.CreateClient(settings); await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Emby Episode Sync Started"); @@ -79,12 +90,12 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var tvParentIdFilter in tvLibsToFilter) { _logger.LogInformation($"Scanning Lib for episodes '{tvParentIdFilter.Title}'"); - await CacheEpisodes(server, tvParentIdFilter.Key); + await CacheEpisodes(server, recentlyAddedSearch, tvParentIdFilter.Key); } } else { - await CacheEpisodes(server, string.Empty); + await CacheEpisodes(server, recentlyAddedSearch, string.Empty); } } @@ -94,11 +105,24 @@ namespace Ombi.Schedule.Jobs.Emby await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System"); } - private async Task CacheEpisodes(EmbyServers server, string parentIdFilter) + private async Task CacheEpisodes(EmbyServers server, bool recentlyAdded, string parentIdFilter) { - var allEpisodes = await Api.GetAllEpisodes(server.ApiKey, parentIdFilter, 0, 200, server.AdministratorId, server.FullUri); + EmbyItemContainer allEpisodes; + if (recentlyAdded) + { + var recentlyAddedAmountToTake = AmountToTake; + allEpisodes = await Api.RecentlyAddedEpisodes(server.ApiKey, parentIdFilter, 0, recentlyAddedAmountToTake, server.AdministratorId, server.FullUri); + if (allEpisodes.TotalRecordCount > recentlyAddedAmountToTake) + { + allEpisodes.TotalRecordCount = recentlyAddedAmountToTake; + } + } + else + { + allEpisodes = await Api.GetAllEpisodes(server.ApiKey, parentIdFilter, 0, AmountToTake, server.AdministratorId, server.FullUri); + } var total = allEpisodes.TotalRecordCount; - var processed = 1; + var processed = 0; var epToAdd = new HashSet(); while (processed < total) { @@ -163,7 +187,10 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.AddRange(epToAdd); epToAdd.Clear(); - allEpisodes = await Api.GetAllEpisodes(server.ApiKey, parentIdFilter, processed, 200, server.AdministratorId, server.FullUri); + if (!recentlyAdded) + { + allEpisodes = await Api.GetAllEpisodes(server.ApiKey, parentIdFilter, processed, AmountToTake, server.AdministratorId, server.FullUri); + } } if (epToAdd.Any()) diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs index 2a950fcf4..e8de12bd2 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs @@ -118,7 +118,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin var mediaToAdd = new HashSet(); var tv = await Api.GetAllShows(server.ApiKey, parentId, 0, 200, server.AdministratorId, server.FullUri); var totalTv = tv.TotalRecordCount; - var processed = 1; + var processed = 0; while (processed < totalTv) { foreach (var tvShow in tv.Items) @@ -177,7 +177,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin { var movies = await Api.GetAllMovies(server.ApiKey, parentId, 0, 200, server.AdministratorId, server.FullUri); var totalCount = movies.TotalRecordCount; - var processed = 1; + var processed = 0; var mediaToAdd = new HashSet(); while (processed < totalCount) { diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs index 60f500d19..27e50c9b7 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs @@ -98,7 +98,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin { var allEpisodes = await Api.GetAllEpisodes(server.ApiKey, parentIdFilter, 0, 200, server.AdministratorId, server.FullUri); var total = allEpisodes.TotalRecordCount; - var processed = 1; + var processed = 0; var epToAdd = new HashSet(); while (processed < total) { diff --git a/src/Ombi.Schedule/Jobs/Ombi/AutoDeleteRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/AutoDeleteRequests.cs index 6625a3bbb..a2a7e4ed1 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/AutoDeleteRequests.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/AutoDeleteRequests.cs @@ -16,14 +16,16 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ISettingsService _ombiSettings; private readonly IMovieRequestRepository _movieRequests; private readonly ITvRequestRepository _tvRequestRepository; + private readonly IMusicRequestRepository _musicRequestRepository; private readonly ILogger _logger; public AutoDeleteRequests(ISettingsService ombiSettings, IMovieRequestRepository movieRequest, - ILogger logger, ITvRequestRepository tvRequestRepository) + ILogger logger, ITvRequestRepository tvRequestRepository, IMusicRequestRepository musicRequestRepository) { _ombiSettings = ombiSettings; _movieRequests = movieRequest; _tvRequestRepository = tvRequestRepository; + _musicRequestRepository = _musicRequestRepository; _logger = logger; } @@ -37,6 +39,7 @@ namespace Ombi.Schedule.Jobs.Ombi var date = DateTime.UtcNow.AddDays(-settings.AutoDeleteAfterDays).Date; await ProcessMovieRequests(date); await ProcessTvRequests(date); + await ProcessMusicRequests(date); } private async Task ProcessMovieRequests(DateTime date) @@ -66,6 +69,20 @@ namespace Ombi.Schedule.Jobs.Ombi await _tvRequestRepository.DeleteRange(parentRequests); } + private async Task ProcessMusicRequests(DateTime date) + { + var requestsToDelete = await _musicRequestRepository.GetAll().Where(x => x.Available && x.MarkedAsAvailable.HasValue && x.MarkedAsAvailable.Value < date).ToListAsync(); + + _logger.LogInformation($"Deleting {requestsToDelete.Count} music requests that have now been scheduled for deletion, All available requests before {date::MM/dd/yyyy} will be deleted"); + foreach (var r in requestsToDelete) + { + _logger.LogInformation($"Deleting music title {r.Title} as it was approved on {r.MarkedAsApproved:MM/dd/yyyy hh:mm tt}"); + } + + await _musicRequestRepository.DeleteRange(requestsToDelete); + + } + private bool _disposed; protected virtual void Dispose(bool disposing) diff --git a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs index c2cf42441..4489a045e 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs @@ -321,7 +321,9 @@ namespace Ombi.Schedule.Jobs.Ombi public async Task DownloadAsync(string requestUri, string filename) { Logger.LogDebug(LoggingEvents.Updater, "Starting the DownloadAsync"); +#pragma warning disable SYSLIB0014 // Type or member is obsolete using (var client = new WebClient()) +#pragma warning restore SYSLIB0014 // Type or member is obsolete { await client.DownloadFileTaskAsync(requestUri, filename); } diff --git a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs index cf13fb037..834c45883 100644 --- a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs @@ -108,7 +108,7 @@ namespace Ombi.Schedule.Jobs.Sonarr foreach (var s in ids) { - if (!s.Monitored || s.EpisodeFileCount == 0) // We have files + if (!s.Monitored && s.EpisodeFileCount == 0) // We have files { continue; } diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index 51cf46f82..ed1a7d9a1 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -11,10 +11,9 @@ - - + diff --git a/src/Ombi.Schedule/OmbiScheduler.cs b/src/Ombi.Schedule/OmbiScheduler.cs index 6c54895d6..8e45be57c 100644 --- a/src/Ombi.Schedule/OmbiScheduler.cs +++ b/src/Ombi.Schedule/OmbiScheduler.cs @@ -96,6 +96,7 @@ namespace Ombi.Schedule private static async Task AddEmby(JobSettings s) { await OmbiQuartz.Instance.AddJob(nameof(IEmbyContentSync), "Emby", JobSettingsHelper.EmbyContent(s)); + await OmbiQuartz.Instance.AddJob(nameof(IEmbyContentSync) + "RecentlyAdded", "Emby", JobSettingsHelper.EmbyRecentlyAddedSync(s), new Dictionary { { JobDataKeys.EmbyRecentlyAddedSearch, "true" } }); await OmbiQuartz.Instance.AddJob(nameof(IEmbyEpisodeSync), "Emby", null); await OmbiQuartz.Instance.AddJob(nameof(IEmbyAvaliabilityChecker), "Emby", null); await OmbiQuartz.Instance.AddJob(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s)); diff --git a/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj b/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj index d30b780c6..5f93b4b68 100644 --- a/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj +++ b/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj @@ -1,6 +1,6 @@  - net5.0 + net6.0 false @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 377970b59..b39c123e2 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs index 1f2155331..c8406493f 100644 --- a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs @@ -12,6 +12,7 @@ public bool RecentlyAddedPage { get; set; } public bool UseCustomPage { get; set; } public bool HideAvailableFromDiscover { get; set; } + public string Favicon { get; set; } public string AddToUrl(string part) { diff --git a/src/Ombi.Settings/Settings/Models/JobSettings.cs b/src/Ombi.Settings/Settings/Models/JobSettings.cs index 18bde6774..ac7ab3bdc 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettings.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettings.cs @@ -3,6 +3,7 @@ public class JobSettings : Settings { public string EmbyContentSync { get; set; } + public string EmbyRecentlyAddedSync { get; set; } public string JellyfinContentSync { get; set; } public string SonarrSync { get; set; } public string RadarrSync { get; set; } diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index fef0792d3..e580e1977 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -1,5 +1,4 @@ -using System; -using Ombi.Helpers; +using Ombi.Helpers; using Quartz; namespace Ombi.Settings.Settings.Models @@ -18,7 +17,12 @@ namespace Ombi.Settings.Settings.Models public static string EmbyContent(JobSettings s) { - return ValidateCron(Get(s.EmbyContentSync, Cron.Hourly(5))); + return ValidateCron(Get(s.EmbyContentSync, Cron.Daily(2))); + } + + public static string EmbyRecentlyAddedSync(JobSettings s) + { + return ValidateCron(Get(s.EmbyRecentlyAddedSync, Cron.Hourly(30))); } public static string JellyfinContent(JobSettings s) diff --git a/src/Ombi.Store/Entities/Requests/Issues.cs b/src/Ombi.Store/Entities/Requests/Issues.cs index 082261a15..ea64365be 100644 --- a/src/Ombi.Store/Entities/Requests/Issues.cs +++ b/src/Ombi.Store/Entities/Requests/Issues.cs @@ -25,6 +25,8 @@ namespace Ombi.Store.Entities.Requests public string UserReportedId { get; set; } public OmbiUser UserReported { get; set; } public List Comments { get; set; } + [NotMapped] + public string PosterPath { get; set; } } public enum IssueStatus diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 99e12530f..f35da548f 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -12,14 +12,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/src/Ombi.Test.Common/Ombi.Test.Common.csproj b/src/Ombi.Test.Common/Ombi.Test.Common.csproj index deeccf52b..71b05f59c 100644 --- a/src/Ombi.Test.Common/Ombi.Test.Common.csproj +++ b/src/Ombi.Test.Common/Ombi.Test.Common.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 8.0 Debug;Release;NonUiBuild diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 9153b12d1..c38756445 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 false @@ -9,12 +9,12 @@ - + - + diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index 6d0859cef..eadfb3a3a 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -31,6 +31,7 @@ namespace Ombi.Api.TheMovieDb Task GetTVInfo(string themoviedbid, string langCode = "en"); Task> SearchByActor(string searchTerm, string langCode); Task GetActorMovieCredits(int actorId, string langCode); + Task GetActorTvCredits(int actorId, string langCode); Task> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken); Task> DiscoverMovies(string langCode, int keywordId); Task GetFullMovieInfo(int movieId, CancellationToken cancellationToken, string langCode); diff --git a/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj b/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj index 6311e948c..41ac19686 100644 --- a/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj +++ b/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Ombi.Api.TheMovieDb 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index aa46b0c8e..0391c184a 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -139,6 +139,16 @@ namespace Ombi.Api.TheMovieDb var result = await Api.Request(request); return result; } + + public async Task GetActorTvCredits(int actorId, string langCode) + { + var request = new Request($"person/{actorId}/tv_credits", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + + var result = await Api.Request(request); + return result; + } public async Task> SearchTv(string searchTerm, string year = default) { diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index 6c74dbe74..d31229b62 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - net5.0 + net6.0 3.0.0.0 3.0.0.0 @@ -13,14 +13,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Ombi/.vscode/settings.json b/src/Ombi/.vscode/settings.json index 89d2dbd93..f8ea48b8f 100644 --- a/src/Ombi/.vscode/settings.json +++ b/src/Ombi/.vscode/settings.json @@ -10,13 +10,17 @@ "cSpell.words": [ "usermanagement" ], - "discord.enabled": true, "conventionalCommits.scopes": [ "discover", "request-limits", "notifications", "settings", "user-management", - "newsletter" + "newsletter", + "mass-email", + "issues", + "emby", + "availability-rules", + "details" ] } diff --git a/src/Ombi/ClientApp/angular.json b/src/Ombi/ClientApp/angular.json index 76749756d..066a75be5 100644 --- a/src/Ombi/ClientApp/angular.json +++ b/src/Ombi/ClientApp/angular.json @@ -34,11 +34,8 @@ "node_modules/@fortawesome/fontawesome-free/scss/brands.scss", "node_modules/primeng/resources/primeng.min.css", "node_modules/primeicons/primeicons.css", - "node_modules/please-wait/src/please-wait.scss", "node_modules/@fullcalendar/core/main.min.css", - "node_modules/@fullcalendar/daygrid/main.min.css", - "node_modules/spinkit/scss/spinners/11-folding-cube.scss", - "node_modules/spinkit/scss/spinkit.scss" + "node_modules/@fullcalendar/daygrid/main.min.css" ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", @@ -82,7 +79,13 @@ "replace": "src/environments/environment.ts", "with": "src/environments/environment.hmr.ts" } - ] + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true } } }, diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index c68ed3782..7ee4f64e5 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -52,13 +52,11 @@ "ngx-infinite-scroll": "^9.0.0", "ngx-moment": "^3.0.1", "ngx-order-pipe": "^2.1.1", - "please-wait": "^0.0.5", "popper.js": "^1.14.3", "primeicons": "^4.1.0", "primeng": "^12.2.0", "rxjs": "^7.4.0", "sass-recursive-map-merge": "^1.0.1", - "spinkit": "^1.2.5", "store": "^2.0.12", "ts-md5": "^1.2.7", "tslib": "^1.10.0", diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index cbfcf0af0..2c543606b 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { OverlayContainer } from '@angular/cdk/overlay'; +import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, OnInit, HostBinding, Inject } from "@angular/core"; import { NavigationStart, Router } from "@angular/router"; @@ -34,6 +34,7 @@ export class AppComponent implements OnInit { public userName: string; public userEmail: string; public accessToken: string; + public favicon: string; private hubConnected: boolean; @@ -88,9 +89,15 @@ export class AppComponent implements OnInit { this.customizationFacade.settings$().subscribe(x => { this.customizationSettings = x; if (this.customizationSettings && this.customizationSettings.applicationName) { - this.applicationName = this.customizationSettings.applicationName; - this.document.getElementsByTagName('title')[0].innerText = this.applicationName; + this.applicationName = this.customizationSettings.applicationName; + this.document.getElementsByTagName('title')[0].innerText = this.applicationName; } + + if (this.customizationSettings && this.customizationSettings.favicon) { + this.favicon = this.customizationSettings.favicon; + this.document.getElementById('favicon').setAttribute('href', this.favicon); + } + if (this.customizationSettings && this.customizationSettings.customCss) { var dom = this.document.getElementsByTagName('head')[0]; var css = document.createElement("style"); diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index b4e78087c..082ce80d2 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -65,6 +65,9 @@ import { TooltipModule } from "primeng/tooltip"; import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor"; import { environment } from "../environments/environment"; +import { MatPaginatorIntl } from "@angular/material/paginator"; +import { TranslateService } from "@ngx-translate/core"; +import { MatPaginatorI18n } from "./localization/MatPaginatorI18n"; const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, @@ -212,6 +215,10 @@ export function JwtTokenGetter() { useClass: UnauthorizedInterceptor, multi: true }, + { + provide: MatPaginatorIntl, deps: [TranslateService], + useFactory: (translateService: TranslateService) => new MatPaginatorI18n(translateService).getPaginatorIntl() + }, ], bootstrap: [AppComponent], }) diff --git a/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.html b/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.html index a7f6a7847..67f243503 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.ts b/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.ts index 8cdb90436..290042d87 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/actor/discover-actor.component.ts @@ -1,10 +1,12 @@ import { Component } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { SearchV2Service } from "../../../services"; -import { IActorCredits } from "../../../interfaces/ISearchTvResultV2"; +import { IActorCredits, IActorCast } from "../../../interfaces/ISearchTvResultV2"; import { IDiscoverCardResult } from "../../interfaces"; import { RequestType } from "../../../interfaces"; import { AuthService } from "../../../auth/auth.service"; +import { forkJoin } from "rxjs"; +import { isEqual } from "lodash"; @Component({ templateUrl: "./discover-actor.component.html", @@ -12,7 +14,6 @@ import { AuthService } from "../../../auth/auth.service"; }) export class DiscoverActorComponent { public actorId: number; - public actorCredits: IActorCredits; public loadingFlag: boolean; public isAdmin: boolean; @@ -24,24 +25,32 @@ export class DiscoverActorComponent { this.route.params.subscribe((params: any) => { this.actorId = params.actorId; this.isAdmin = this.auth.isAdmin(); - this.loading(); - this.searchService.getMoviesByActor(this.actorId).subscribe(res => { - this.actorCredits = res; - this.createModel(); - }); + this.search(); }); } - private createModel() { - this.finishLoading(); + private search() { this.discoverResults = []; - this.actorCredits.cast.forEach(m => { + this.loading(); + + forkJoin([ + this.searchService.getMoviesByActor(this.actorId), + this.searchService.getTvByActor(this.actorId) + ]).subscribe(([movie, tv]) => { + this.pushDiscoverResults(movie.cast, RequestType.movie); + this.pushDiscoverResults(tv.cast, RequestType.tvShow); + this.finishLoading(); + }); + } + + pushDiscoverResults(cast: IActorCast[], type: RequestType) { + cast.forEach(m => { this.discoverResults.push({ available: false, posterPath: m.poster_path ? `https://image.tmdb.org/t/p/w300/${m.poster_path}` : "../../../images/default_movie_poster.png", requested: false, title: m.title, - type: RequestType.movie, + type: type, id: m.id, url: null, rating: 0, diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index 1bd2546bc..4de883256 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -1,14 +1,15 @@ -import { Component, OnInit, Input } from "@angular/core"; -import { IDiscoverCardResult } from "../../interfaces"; -import { RequestType } from "../../../interfaces"; +import { Component, Input, OnInit } from "@angular/core"; import { MessageService, RequestService, SearchV2Service } from "../../../services"; -import { TranslateService } from "@ngx-translate/core"; -import { MatDialog } from "@angular/material/dialog"; -import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; -import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; -import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; + import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component"; import { DiscoverType } from "../carousel-list/carousel-list.component"; +import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; +import { IDiscoverCardResult } from "../../interfaces"; +import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; +import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; +import { MatDialog } from "@angular/material/dialog"; +import { RequestType } from "../../../interfaces"; +import { TranslateService } from "@ngx-translate/core"; @Component({ selector: "discover-card", @@ -168,6 +169,7 @@ export class DiscoverCardComponent implements OnInit { this.result.url = "http://www.imdb.com/title/" + updated.imdbId + "/"; this.result.available = updated.available; this.result.requested = updated.requested; + this.result.approved = updated.approved; this.result.rating = updated.voteAverage; this.result.overview = updated.overview; this.result.imdbid = updated.imdbId; diff --git a/src/Ombi/ClientApp/src/app/errors/not-found.component.ts b/src/Ombi/ClientApp/src/app/errors/not-found.component.ts index f102d0449..25269ecd1 100644 --- a/src/Ombi/ClientApp/src/app/errors/not-found.component.ts +++ b/src/Ombi/ClientApp/src/app/errors/not-found.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; @Component({ - template: "

Page not found

", + template: "

{{ 'ErrorPages.NotFound' | translate }}

", }) export class PageNotFoundComponent { } diff --git a/src/Ombi/ClientApp/src/app/interfaces/IIssues.ts b/src/Ombi/ClientApp/src/app/interfaces/IIssues.ts index 10de2a596..aac30c1e4 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IIssues.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IIssues.ts @@ -14,6 +14,7 @@ export interface IIssues { comments: IIssueComments[]; requestId: number | undefined; userReported: IUser | undefined; + posterPath: string; } export enum IssueStatus { diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index 919e6a271..c7990215f 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -1,4 +1,4 @@ -import { ISettings } from "./ICommon"; +import { ISettings } from "./ICommon"; import { RequestLimitType } from "."; export interface IExternalSettings extends ISettings { @@ -193,6 +193,7 @@ export interface ICustomizationSettings extends ISettings { recentlyAddedPage: boolean; useCustomPage: boolean; hideAvailableFromDiscover: boolean; + favicon: string; } export interface IJobSettings { @@ -212,6 +213,7 @@ export interface IJobSettings { retryRequests: string; mediaDatabaseRefresh: string; autoDeleteRequests: string; + embyRecentlyAddedSync: string; } export interface IIssueSettings extends ISettings { diff --git a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts index 2db5a5de2..97882e783 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts @@ -121,6 +121,7 @@ export interface IMassEmailModel { subject: string; body: string; users: IUser[]; + bcc: boolean; } export interface INotificationPreferences { diff --git a/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html b/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html index 01ddaf75e..74cc041d3 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html +++ b/src/Ombi/ClientApp/src/app/issues/components/details-group/details-group.component.html @@ -2,6 +2,7 @@ {{issue.subject}} {{'Issues.UserOnDate' | translate: { user: issue.userReported?.userName, date: issue.createdDate | amLocal | amUserLocale | amDateFormat: 'LL' } }} + {{issue.issueCategory.value}}

@@ -10,8 +11,8 @@ -

here is ignored
- +
+ diff --git a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html index bc0e8a794..42a84171b 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html +++ b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.html @@ -1,6 +1,6 @@
-

Issues for {{details.title}}

+

{{ 'Issues.IssuesForTitle' | translate: { title: details.title} }}

{{'Issues.Requested' | translate}} diff --git a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.scss b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.scss index d6dcd67de..77d4223ab 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.scss +++ b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.scss @@ -1,9 +1,5 @@ @import "~styles/variables.scss"; -::ng-deep .mat-card { - background: $ombi-background-primary-accent; -} - .top-spacing { margin-top:2%; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts index 4003acd40..12ceb422e 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts +++ b/src/Ombi/ClientApp/src/app/issues/components/details/details.component.ts @@ -1,13 +1,13 @@ -import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core"; -import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from "@angular/router"; -import { TranslateService } from "@ngx-translate/core"; -import { AuthService } from "../../../auth/auth.service"; -import { IIssues, IIssueSettings, IIssuesSummary, IssueStatus, RequestType } from "../../../interfaces"; +import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core"; +import { IIssueSettings, IIssues, IIssuesSummary, IssueStatus, RequestType } from "../../../interfaces"; import { IssuesService, NotificationService, SettingsService } from "../../../services"; -import { IssuesV2Service } from "../../../services/issuesv2.service"; -import { IssueChatComponent } from "../issue-chat/issue-chat.component"; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { AuthService } from "../../../auth/auth.service"; +import { IssueChatComponent } from "../issue-chat/issue-chat.component"; +import { IssuesV2Service } from "../../../services/issuesv2.service"; +import { TranslateService } from "@ngx-translate/core"; export interface IssuesDetailsGroupData { issues: IIssues[]; @@ -77,15 +77,15 @@ export class IssuesDetailsComponent implements OnInit { const firstIssue = this.details.issues[0]; switch(firstIssue.requestType) { case RequestType.movie: - this.router.navigate(['/details/movie/', firstIssue.providerId]); + this.router.navigate(['/details/movie/', this.providerId]); return; case RequestType.album: - this.router.navigate(['/details/artist/', firstIssue.providerId]); + this.router.navigate(['/details/artist/', this.providerId]); return; case RequestType.tvShow: - this.router.navigate(['/details/tv/', firstIssue.providerId]); + this.router.navigate(['/details/tv/', this.providerId]); return; } } diff --git a/src/Ombi/ClientApp/src/app/issues/issueDetails.component.ts b/src/Ombi/ClientApp/src/app/issues/issueDetails.component.ts index 1e30f932c..70eb69aee 100644 --- a/src/Ombi/ClientApp/src/app/issues/issueDetails.component.ts +++ b/src/Ombi/ClientApp/src/app/issues/issueDetails.component.ts @@ -70,6 +70,7 @@ export class IssueDetailsComponent implements OnInit { requestId: x.requestId, providerId: x.providerId, userReported: x.userReported, + posterPath: undefined, // Poster Path is not stored in the db, will always be undefined }; this.setBackground(x); }); diff --git a/src/Ombi/ClientApp/src/app/localization/MatPaginatorI18n.ts b/src/Ombi/ClientApp/src/app/localization/MatPaginatorI18n.ts new file mode 100644 index 000000000..71a86b7bd --- /dev/null +++ b/src/Ombi/ClientApp/src/app/localization/MatPaginatorI18n.ts @@ -0,0 +1,34 @@ +import { MatPaginatorIntl } from '@angular/material/paginator'; +import { TranslateService } from '@ngx-translate/core'; + +export class MatPaginatorI18n { + + constructor(private translate: TranslateService) { } + + getPaginatorIntl(): MatPaginatorIntl { + const paginatorIntl = new MatPaginatorIntl(); + paginatorIntl.itemsPerPageLabel = this.translate.instant('Paginator.itemsPerPageLabel'); + paginatorIntl.nextPageLabel = this.translate.instant('Paginator.nextPageLabel'); + paginatorIntl.previousPageLabel = this.translate.instant('Paginator.previousPageLabel'); + paginatorIntl.firstPageLabel = this.translate.instant('Paginator.firstPageLabel'); + paginatorIntl.lastPageLabel = this.translate.instant('Paginator.lastPageLabel'); + paginatorIntl.getRangeLabel = this.getRangeLabel.bind(this); + return paginatorIntl; + } + + private getRangeLabel(page: number, pageSize: number, length: number): string { + if (length == 0 || pageSize == 0) { + return this.translate.instant('Paginator.rangePageLabel1', { length }); + } + + length = Math.max(length, 0); + + const startIndex = page * pageSize; + + // If the start index exceeds the list length, do not try and fix the end index to the end. + const endIndex = + startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize; + + return this.translate.instant('Paginator.rangePageLabel2', { startIndex: startIndex + 1, endIndex, length }); + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index 592758c9f..235e31a8e 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -177,6 +177,7 @@ export class LoginComponent implements OnDestroy, OnInit { if (this.oAuthWindow) { this.oAuthWindow.close(); } + this.oAuthWindow = window.open( window.location.toString(), "_blank", @@ -214,6 +215,11 @@ export class LoginComponent implements OnDestroy, OnInit { } public getPinResult(pinId: number) { + if (this.oAuthWindow.closed) { + if (this.pinTimer) { + clearInterval(this.pinTimer); + } + } this.authService.oAuth(pinId).subscribe( (x) => { if (x.access_token) { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/index.ts b/src/Ombi/ClientApp/src/app/media-details/components/index.ts index 6799a8f74..eadc8d635 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/index.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/index.ts @@ -1,26 +1,26 @@ -import { MovieDetailsComponent } from "./movie/movie-details.component"; -import { YoutubeTrailerComponent } from "./shared/youtube-trailer.component"; -import { TvDetailsComponent } from "./tv/tv-details.component"; -import { MovieInformationPanelComponent } from "./movie/panels/movie-information-panel.component"; -import { TvInformationPanelComponent } from "./tv/panels/tv-information-panel/tv-information-panel.component"; -import { TopBannerComponent } from "./shared/top-banner/top-banner.component"; -import { SocialIconsComponent } from "./shared/social-icons/social-icons.component"; -import { MediaPosterComponent } from "./shared/media-poster/media-poster.component"; -import { CastCarouselComponent } from "./shared/cast-carousel/cast-carousel.component"; -import { DenyDialogComponent } from "./shared/deny-dialog/deny-dialog.component"; -import { TvRequestsPanelComponent } from "./tv/panels/tv-requests/tv-requests-panel.component"; -import { MovieAdvancedOptionsComponent } from "./movie/panels/movie-advanced-options/movie-advanced-options.component"; -import { SearchService, RequestService, RadarrService, IssuesService, SonarrService } from "../../services"; -import { RequestServiceV2 } from "../../services/requestV2.service"; -import { NewIssueComponent } from "./shared/new-issue/new-issue.component"; +import { IssuesService, RadarrService, RequestService, SearchService, SonarrService } from "../../services"; + import { ArtistDetailsComponent } from "./artist/artist-details.component"; import { ArtistInformationPanel } from "./artist/panels/artist-information-panel/artist-information-panel.component"; import { ArtistReleasePanel } from "./artist/panels/artist-release-panel/artist-release-panel.component"; +import { CastCarouselComponent } from "./shared/cast-carousel/cast-carousel.component"; +import { DenyDialogComponent } from "./shared/deny-dialog/deny-dialog.component"; import { IssuesPanelComponent } from "./shared/issues-panel/issues-panel.component"; -import { TvAdvancedOptionsComponent } from "./tv/panels/tv-advanced-options/tv-advanced-options.component"; +import { MediaPosterComponent } from "./shared/media-poster/media-poster.component"; +import { MovieAdvancedOptionsComponent } from "./movie/panels/movie-advanced-options/movie-advanced-options.component"; +import { MovieDetailsComponent } from "./movie/movie-details.component"; +import { MovieInformationPanelComponent } from "./movie/panels/movie-information-panel.component"; +import { NewIssueComponent } from "./shared/new-issue/new-issue.component"; import { RequestBehalfComponent } from "./shared/request-behalf/request-behalf.component"; +import { RequestServiceV2 } from "../../services/requestV2.service"; +import { SocialIconsComponent } from "./shared/social-icons/social-icons.component"; +import { TopBannerComponent } from "./shared/top-banner/top-banner.component"; +import { TvAdvancedOptionsComponent } from "./tv/panels/tv-advanced-options/tv-advanced-options.component"; +import { TvDetailsComponent } from "./tv/tv-details.component"; +import { TvInformationPanelComponent } from "./tv/panels/tv-information-panel/tv-information-panel.component"; import { TvRequestGridComponent } from "./tv/panels/tv-request-grid/tv-request-grid.component"; -import { DetailsGroupComponent } from "../../issues/components/details-group/details-group.component"; +import { TvRequestsPanelComponent } from "./tv/panels/tv-requests/tv-requests-panel.component"; +import { YoutubeTrailerComponent } from "./shared/youtube-trailer.component"; export const components: any[] = [ MovieDetailsComponent, diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html index 4e088bd69..2e807547c 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html @@ -72,6 +72,12 @@ {{'Common.Request' | translate }} + + + + + + @@ -131,7 +135,7 @@
- +
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts index 7b1c98ba8..845dac993 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts @@ -84,7 +84,7 @@ export class TvDetailsComponent implements OnInit { public async issue() { const dialogRef = this.dialog.open(NewIssueComponent, { width: '500px', - data: { requestId: this.tvRequest ? this.tv.requestId : null, requestType: RequestType.tvShow, providerId: this.tv.theTvDbId, title: this.tv.title } + data: { requestId: this.tvRequest ? this.tv.requestId : null, requestType: RequestType.tvShow, providerId: this.tv.id, title: this.tv.title, posterPath: this.tv.images.original } }); } @@ -125,6 +125,10 @@ export class TvDetailsComponent implements OnInit { } } + public allEpisodesRequested(): boolean { + return this.tv.seasonRequests.every(e => e.episodes.every(x => x.approved)); + } + private loadAdvancedInfo() { const profile = this.sonarrService.getQualityProfilesWithoutSettings(); const folders = this.sonarrService.getRootFoldersWithoutSettings(); diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss index 3b9fd4a15..119035062 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss @@ -323,8 +323,7 @@ } .media-row .mat-raised-button{ - padding:2px 1.5em;; - width:170px; + padding:2px 1.5em; margin-top:10px; margin-left:10px; } diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.html index 88d942be2..977b55a17 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.html @@ -60,7 +60,7 @@ - + diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.ts index 633ddf1c2..d0529e018 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/albums-grid/albums-grid.component.ts @@ -25,6 +25,8 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit { public defaultSort: string = "requestedDate"; public defaultOrder: string = "desc"; public currentFilter: RequestFilterType = RequestFilterType.All; + public manageOwnRequests: boolean; + public userName: string; public RequestFilter = RequestFilterType; @@ -42,10 +44,12 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit { constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, private auth: AuthService, private storageService: StorageService) { + this.userName = auth.claims().name; } public ngOnInit() { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + this.manageOwnRequests = this.auth.hasRole("ManageOwnRequests") const defaultCount = this.storageService.get(this.storageKeyGridCount); const defaultSort = this.storageService.get(this.storageKey); @@ -117,16 +121,17 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit { public openOptions(request: IAlbumRequest) { const filter = () => { - this.dataSource = this.dataSource.filter((req) => { - return req.id !== request.id; - }) + this.dataSource = this.dataSource.filter((req) => { + return req.id !== request.id; + }); }; const onChange = () => { this.ref.detectChanges(); }; - this.onOpenOptions.emit({ request: request, filter: filter, onChange: onChange }); + const data = { request: request, filter: filter, onChange: onChange, manageOwnRequests: this.manageOwnRequests, isAdmin: this.isAdmin }; + this.onOpenOptions.emit(data); } public switchFilter(type: RequestFilterType) { diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html index 0e6e31521..61baa3eaa 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.html @@ -76,7 +76,7 @@ - + diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts index c033238b1..111c299c5 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/movies-grid/movies-grid.component.ts @@ -1,7 +1,7 @@ import { AfterViewInit, ChangeDetectorRef, Component, EventEmitter, OnInit, Output, ViewChild } from "@angular/core"; import { IMovieRequests, IRequestEngineResult, IRequestsViewModel } from "../../../interfaces"; import { NotificationService, RequestService } from "../../../services"; -import { Observable, forkJoin, merge, of as observableOf } from 'rxjs'; +import { Observable, combineLatest, forkJoin, merge, of as observableOf } from 'rxjs'; import { catchError, map, startWith, switchMap } from 'rxjs/operators'; import { AuthService } from "../../../auth/auth.service"; @@ -31,6 +31,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { public defaultOrder: string = "desc"; public currentFilter: RequestFilterType = RequestFilterType.All; public selection = new SelectionModel(true, []); + public userName: string; public RequestFilter = RequestFilterType; @@ -46,10 +47,11 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { @ViewChild(MatSort) sort: MatSort; constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, - private auth: AuthService, private storageService: StorageService, - private requestServiceV1: RequestService, private notification: NotificationService, - private translateService: TranslateService) { + private auth: AuthService, private storageService: StorageService, + private requestServiceV1: RequestService, private notification: NotificationService, + private translateService: TranslateService) { + this.userName = auth.claims().name; } public ngOnInit() { @@ -162,16 +164,16 @@ export class MoviesGridComponent implements OnInit, AfterViewInit { if (this.selection.isEmpty()) { return; } - let tasks = new Array(); + let tasks = new Array>(); this.selection.selected.forEach((selected) => { tasks.push(this.requestServiceV1.removeMovieRequestAsync(selected.id)); }); - await Promise.all(tasks); - - this.notification.success(this.translateService.instant('Requests.RequestPanel.Deleted')) - this.selection.clear(); - this.ngAfterViewInit(); + combineLatest(tasks).subscribe(() => { + this.notification.success(this.translateService.instant('Requests.RequestPanel.Deleted')) + this.selection.clear(); + this.ngAfterViewInit(); + }); } public bulkApprove() { diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts index 625efe31c..aa98943ca 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts @@ -1,8 +1,10 @@ import { Component, Inject } from '@angular/core'; import { MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef } from '@angular/material/bottom-sheet'; -import { RequestService } from '../../../services'; -import { RequestType } from '../../../interfaces'; +import { MessageService, RequestService } from '../../../services'; +import { IRequestEngineResult, RequestType } from '../../../interfaces'; import { UpdateType } from '../../models/UpdateType'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; @Component({ selector: 'request-options', @@ -13,21 +15,31 @@ export class RequestOptionsComponent { public RequestType = RequestType; constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: any, - private requestService: RequestService, private bottomSheetRef: MatBottomSheetRef) { } + private requestService: RequestService, + private messageService: MessageService, + private bottomSheetRef: MatBottomSheetRef, + private translate: TranslateService) { } public async delete() { + var request: Observable; if (this.data.type === RequestType.movie) { - await this.requestService.removeMovieRequestAsync(this.data.id); + request = this.requestService.removeMovieRequestAsync(this.data.id); } if (this.data.type === RequestType.tvShow) { - await this.requestService.deleteChild(this.data.id).toPromise(); + request = this.requestService.deleteChild(this.data.id); } if (this.data.type === RequestType.album) { - await this.requestService.removeAlbumRequest(this.data.id).toPromise(); + request = this.requestService.removeAlbumRequest(this.data.id); } - - this.bottomSheetRef.dismiss({type: UpdateType.Delete}); - return; + request.subscribe(result => { + if (result.result) { + this.messageService.send(this.translate.instant("Requests.SuccessfullyDeleted")); + this.bottomSheetRef.dismiss({type: UpdateType.Delete}); + return; + } else { + this.messageService.sendRequestEngineResultError(result); + } + }); } public async approve() { diff --git a/src/Ombi/ClientApp/src/app/requests/movierequests.component.html b/src/Ombi/ClientApp/src/app/requests/movierequests.component.html index 03c932fd8..4e7ad3628 100644 --- a/src/Ombi/ClientApp/src/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/src/app/requests/movierequests.component.html @@ -1,6 +1,6 @@
-
+
+ Get it on Google Play + Get it on App Store +

News

diff --git a/src/Ombi/ClientApp/src/app/settings/about/about.component.scss b/src/Ombi/ClientApp/src/app/settings/about/about.component.scss index daeccd03a..55d4a9765 100644 --- a/src/Ombi/ClientApp/src/app/settings/about/about.component.scss +++ b/src/Ombi/ClientApp/src/app/settings/about/about.component.scss @@ -1,4 +1,6 @@ -.mat-table { +@import "~styles/variables.scss"; + +.mat-table { display: block; } @@ -37,4 +39,13 @@ white-space: nowrap; vertical-align: baseline; border-radius: 0.25rem; - } \ No newline at end of file + } + + .container-alert { + margin-left: 3%; + margin-right: 3%; + color: white; + background-color: $ombi-background-accent; + border-color: $warn; + } + diff --git a/src/Ombi/ClientApp/src/app/settings/about/about.component.ts b/src/Ombi/ClientApp/src/app/settings/about/about.component.ts index 688669c01..fc10c5181 100644 --- a/src/Ombi/ClientApp/src/app/settings/about/about.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/about/about.component.ts @@ -1,10 +1,12 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, Inject, OnInit } from "@angular/core"; +import { HubService, SettingsService, SystemService } from "../../services"; import { IAbout, IUpdateModel } from "../../interfaces/ISettings"; -import { SettingsService, HubService, SystemService } from "../../services"; + import { IConnectedUser } from "../../interfaces"; -import { UpdateService } from "../../services/update.service"; import { MatDialog } from "@angular/material/dialog"; import { UpdateDialogComponent } from "./update-dialog.component"; +import { UpdateService } from "../../services/update.service"; +import { APP_BASE_HREF } from "@angular/common"; @Component({ templateUrl: "./about.component.html", @@ -16,6 +18,16 @@ export class AboutComponent implements OnInit { public newUpdate: boolean; public connectedUsers: IConnectedUser[]; public newsHtml: string; + public appstoreImage: string; + + public get usingSqliteDatabase() { + if (this.about.ombiDatabaseType.toLowerCase() === 'sqlite' + || this.about.externalDatabaseType.toLowerCase() === 'sqlite' + || this.about.settingsDatabaseType.toLowerCase() === 'sqlite') { + return true; + } + return false; + } private update: IUpdateModel; @@ -23,9 +35,15 @@ export class AboutComponent implements OnInit { private readonly jobService: UpdateService, private readonly hubService: HubService, private readonly systemService: SystemService, - private readonly dialog: MatDialog) { } + private readonly dialog: MatDialog, + @Inject(APP_BASE_HREF) private readonly href:string) { } public async ngOnInit() { + this.appstoreImage = "../../../images/appstore.svg"; + const base = this.href; + if (base) { + this.appstoreImage = "../../.." + base + "/images/appstore.svg"; + } this.settingsService.about().subscribe(x => this.about = x); this.newsHtml = await this.systemService.getNews().toPromise(); diff --git a/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html b/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html index b2168bb72..4c8bcecb6 100644 --- a/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html +++ b/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html @@ -34,6 +34,15 @@
+ + The favicon url should be an externally accesible URL. Leave blank for default. + +
+ + Custom Favicon + + +
Hide Available Content On The Discover Page diff --git a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html index 14aaada64..6787fb4c9 100644 --- a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html +++ b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html @@ -105,12 +105,29 @@
+
+
+ +
+
+
+ +
+
+ +
+
@@ -118,19 +135,8 @@
-
-
-
- -
-
-
- -
-
+ +
diff --git a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.ts b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.ts index b50175f61..69e31741d 100644 --- a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.ts @@ -93,6 +93,14 @@ export class EmbyComponent implements OnInit { }); } + public runRecentlyAddedCacher(): void { + this.jobService.runEmbyRecentlyAddedCacher().subscribe(x => { + if (x) { + this.notificationService.success("Triggered the Emby Recently Added Sync"); + } + }); + } + public clearDataAndResync(): void { this.jobService.clearMediaserverData().subscribe(x => { if (x) { diff --git a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html index 7e25704fa..e15a88b7f 100644 --- a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html @@ -86,6 +86,14 @@ +
+ + Emby Recently Added Sync + + The Emby Recently Added Sync is required + +
+
Jellyfin Sync diff --git a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts index 83508e752..47d05776b 100644 --- a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts @@ -1,7 +1,6 @@ import { Component, OnInit } from "@angular/core"; - import { FormBuilder, FormGroup, Validators } from "@angular/forms"; -import { NotificationService, SettingsService, JobService } from "../../services"; +import { JobService, NotificationService, SettingsService } from "../../services"; @Component({ templateUrl: "./jobs.component.html", @@ -36,7 +35,8 @@ export class JobsComponent implements OnInit { issuesPurge: [x.issuesPurge, Validators.required], retryRequests: [x.retryRequests, Validators.required], mediaDatabaseRefresh: [x.mediaDatabaseRefresh, Validators.required], - autoDeleteRequests: [x.autoDeleteRequests, Validators.required] + autoDeleteRequests: [x.autoDeleteRequests, Validators.required], + embyRecentlyAddedSync: [x.embyRecentlyAddedSync, Validators.required], }); }); } diff --git a/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.html b/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.html index 49890ac6e..fb6ab0ad0 100644 --- a/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.html +++ b/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.html @@ -3,15 +3,15 @@
Mass Email - +
Hey! We need a subject!
-
- +
+
@@ -20,7 +20,14 @@ May appear differently on email clients
+
+ This will send out the Mass email BCC'ing all of the selected users rather than sending individual messages +
+ BCC +
+
+
@@ -28,23 +35,21 @@
- - + +
-
- - -
+
+ Select All +
-
- - -
+
+ {{u.user.userName}} ({{u.user.emailAddress}}) +
- +
diff --git a/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.ts b/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.ts index 17beb5405..1ed2fe995 100644 --- a/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/massemail/massemail.component.ts @@ -12,6 +12,7 @@ export class MassEmailComponent implements OnInit { public users: IMassEmailUserModel[] = []; public message: string; public subject: string; + public bcc: boolean; public missingSubject = false; @@ -26,17 +27,19 @@ export class MassEmailComponent implements OnInit { public ngOnInit(): void { this.identityService.getUsers().subscribe(x => { x.forEach(u => { - this.users.push({ - user: u, - selected: false, - }); + if (u.emailAddress) { + this.users.push({ + user: u, + selected: false, + }); + } }); }); this.settingsService.getEmailSettingsEnabled().subscribe(x => this.emailEnabled = x); } - public selectAllUsers() { - this.users.forEach(u => u.selected = !u.selected); + public selectAllUsers(event: any) { + this.users.forEach(u => u.selected = event.checked); } public send() { @@ -44,10 +47,10 @@ export class MassEmailComponent implements OnInit { this.missingSubject = true; return; } - if(!this.emailEnabled) { - this.notification.error("You have not yet setup your email notifications, do that first!"); - return; - } + // if(!this.emailEnabled) { + // this.notification.error("You have not yet setup your email notifications, do that first!"); + // return; + // } this.missingSubject = false; // Where(x => x.selected).Select(x => x.user) const selectedUsers = this.users.filter(u => { @@ -63,6 +66,7 @@ export class MassEmailComponent implements OnInit { users: selectedUsers, subject: this.subject, body: this.message, + bcc: this.bcc, }; this.notification.info("Sending","Sending mass email... Please wait"); this.notificationMessageService.sendMassEmail(model).subscribe(x => { diff --git a/src/Ombi/ClientApp/src/app/settings/notifications/emailnotification.component.html b/src/Ombi/ClientApp/src/app/settings/notifications/emailnotification.component.html index 33aa3a06c..5c24d21f2 100644 --- a/src/Ombi/ClientApp/src/app/settings/notifications/emailnotification.component.html +++ b/src/Ombi/ClientApp/src/app/settings/notifications/emailnotification.component.html @@ -61,7 +61,7 @@
Admin Email - + Admin Email is required diff --git a/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts b/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts index dd702eae4..9e787b406 100644 --- a/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts @@ -62,7 +62,6 @@ export class RadarrComponent implements OnInit { { name: "Announced", value: "Announced" }, { name: "In Cinemas", value: "InCinemas" }, { name: "Physical / Web", value: "Released" }, - { name: "PreDb", value: "PreDb" }, ]; } diff --git a/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html index 201c10a9f..39e87c48b 100644 --- a/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html +++ b/src/Ombi/ClientApp/src/app/shared/chat-box/chat-box.component.html @@ -1,7 +1,7 @@
-

Users

+

{{ "Issues.UserManagement" | translate }}

{{user}}

@@ -19,7 +19,7 @@
- - + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html b/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html index 4e5bf394a..b28f7de02 100644 --- a/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html +++ b/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html @@ -10,7 +10,7 @@ cancel cancel cancel -

Unsubscribed!

+

{{ 'UserPreferences.Unsubscribed' | translate }}

\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts index 9b9e8bdfd..c772b86aa 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts @@ -66,10 +66,22 @@ export class UserManagementUserComponent implements OnInit { } else { this.identityService.getNotificationPreferences().subscribe(x => this.notificationPreferences = x); } - this.sonarrService.getQualityProfilesWithoutSettings().subscribe(x => this.sonarrQualities = x); - this.sonarrService.getRootFoldersWithoutSettings().subscribe(x => this.sonarrRootFolders = x); - this.radarrService.getQualityProfilesFromSettings().subscribe(x => this.radarrQualities = x); - this.radarrService.getRootFoldersFromSettings().subscribe(x => this.radarrRootFolders = x); + this.sonarrService.getQualityProfilesWithoutSettings().subscribe(x => { + this.sonarrQualities = x; + this.sonarrQualities.unshift({id: 0, name: "None"}); + }); + this.sonarrService.getRootFoldersWithoutSettings().subscribe(x => { + this.sonarrRootFolders = x; + this.sonarrRootFolders.unshift({id: 0, path: "None"}); + }); + this.radarrService.getQualityProfilesFromSettings().subscribe(x => { + this.radarrQualities = x; + this.radarrQualities.unshift({id: 0, name: "None"}); + }); + this.radarrService.getRootFoldersFromSettings().subscribe(x => { + this.radarrRootFolders = x; + this.radarrRootFolders.unshift({id: 0, path: "None"}); + }); this.identityService.getUserAccessToken(this.userId).subscribe(x => this.accessToken = x); @@ -179,6 +191,7 @@ export class UserManagementUserComponent implements OnInit { return; } + this.identityService.updateUser(this.user).subscribe(x => { if (x.successful) { this.identityService.updateNotificationPreferences(this.notificationPreferences).subscribe(); diff --git a/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.ts b/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.ts index 849977b55..d1a9c59eb 100644 --- a/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.ts +++ b/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.ts @@ -1,11 +1,12 @@ import { AfterViewInit, Component, OnInit, ViewChild } from "@angular/core"; -import { Router } from "@angular/router"; +import { IdentityService, NotificationService, SettingsService } from "../../services"; + +import { CustomizationFacade } from "../../state/customization/customization.facade"; import { ICreateWizardUser } from "../../interfaces"; -import { IdentityService, NotificationService } from "../../services"; import { IOmbiConfigModel } from "../models/OmbiConfigModel"; -import { WizardService } from "../services/wizard.service"; import { MatHorizontalStepper } from'@angular/material/stepper'; -import { StepperSelectionEvent } from "@angular/cdk/stepper"; +import { Router } from "@angular/router"; +import { WizardService } from "../services/wizard.service"; @Component({ templateUrl: "./welcome.component.html", @@ -18,7 +19,8 @@ export class WelcomeComponent implements OnInit { public config: IOmbiConfigModel; constructor(private router: Router, private identityService: IdentityService, - private notificationService: NotificationService, private WizardService: WizardService) { } + private notificationService: NotificationService, private WizardService: WizardService, + private settingsService: SettingsService, private customizationFacade: CustomizationFacade) { } public ngOnInit(): void { this.localUser = { @@ -34,20 +36,40 @@ export class WelcomeComponent implements OnInit { } public createUser() { - this.WizardService.addOmbiConfig(this.config).subscribe(config => { - if(config != null) { - this.identityService.createWizardUser(this.localUser).subscribe(x => { - if (x.result) { - // save the config - this.router.navigate(["login"]); + if (this.config.applicationUrl) { + this.settingsService.verifyUrl(this.config.applicationUrl).subscribe(x => { + if (!x) { + this.notificationService.error(`The URL "${this.config.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`); + this.stepper.selectedIndex = 3; + return; + } + this.saveConfig(); + }); } else { - if (x.errors.length > 0) { - this.notificationService.error(x.errors[0]); - this.stepper.previous(); - } + this.saveConfig(); } - }); } - }, configErr => this.notificationService.error(configErr)); + + private saveConfig() { + this.WizardService.addOmbiConfig(this.config).subscribe({ + next: (config) => { + if(config != null) { + this.identityService.createWizardUser(this.localUser).subscribe(x => { + if (x.result) { + this.customizationFacade.loadCustomziationSettings().subscribe(); + // save the config + this.router.navigate(["login"]); + } else { + if (x.errors.length > 0) { + this.notificationService.error(x.errors[0]); + this.stepper.previous(); + } + } + }); + } + }, + error: (configErr) => this.notificationService.error(configErr) + }); } + } diff --git a/src/Ombi/ClientApp/src/index.html b/src/Ombi/ClientApp/src/index.html index e9c1ed486..4f7786647 100644 --- a/src/Ombi/ClientApp/src/index.html +++ b/src/Ombi/ClientApp/src/index.html @@ -1,12 +1,12 @@ - + - + diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index 6585ff2f9..5cff8378d 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -6191,11 +6191,6 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" -please-wait@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/please-wait/-/please-wait-0.0.5.tgz#67098ce6260e92e0809e2d3b7c23f1d167dad960" - integrity sha1-ZwmM5iYOkuCAni07fCPx0Wfa2WA= - pngjs@^3.3.0: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" @@ -7700,11 +7695,6 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -spinkit@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/spinkit/-/spinkit-1.2.5.tgz#90f9f466a20e8e39ef24da959c1e611c2a30dd54" - integrity sha1-kPn0ZqIOjjnvJNqVnB5hHCow3VQ= - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" diff --git a/src/Ombi/Controllers/V1/IssuesController.cs b/src/Ombi/Controllers/V1/IssuesController.cs index 5222cf4e7..2828b5ef2 100644 --- a/src/Ombi/Controllers/V1/IssuesController.cs +++ b/src/Ombi/Controllers/V1/IssuesController.cs @@ -244,9 +244,9 @@ namespace Ombi.Controllers.V1 var isAdmin = await _userManager.IsInRoleAsync(user, OmbiRoles.Admin) || user.IsSystemUser; AddIssueNotificationSubstitutes(notificationModel, issue, user.UserName, user.UserAlias); - notificationModel.Substitutes.Add("NewIssueComment", comment.Comment); - notificationModel.Substitutes.Add("IssueId", comment.IssueId.ToString()); - notificationModel.Substitutes.Add("AdminComment", isAdmin.ToString()); + notificationModel.Substitutes.Add(NotificationSubstitues.NewIssueComment, comment.Comment); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueId, comment.IssueId.ToString()); + notificationModel.Substitutes.Add(NotificationSubstitues.AdminComment, isAdmin.ToString()); if (isAdmin) { @@ -331,14 +331,15 @@ namespace Ombi.Controllers.V1 private static void AddIssueNotificationSubstitutes(NotificationOptions notificationModel, Issues issue, string issueReportedUsername, string alias) { - notificationModel.Substitutes.Add("Title", issue.Title); - notificationModel.Substitutes.Add("IssueDescription", issue.Description); - notificationModel.Substitutes.Add("IssueCategory", issue.IssueCategory?.Value); - notificationModel.Substitutes.Add("IssueStatus", issue.Status.ToString()); - notificationModel.Substitutes.Add("IssueSubject", issue.Subject); - notificationModel.Substitutes.Add("IssueUser", issueReportedUsername); - notificationModel.Substitutes.Add("IssueUserAlias", alias); - notificationModel.Substitutes.Add("RequestType", notificationModel.RequestType.ToString()); + notificationModel.Substitutes.Add(NotificationSubstitues.Title, issue.Title); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueDescription, issue.Description); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueCategory, issue.IssueCategory?.Value); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueStatus, issue.Status.ToString()); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueSubject, issue.Subject); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueUser, issueReportedUsername); + notificationModel.Substitutes.Add(NotificationSubstitues.IssueUserAlias, alias); + notificationModel.Substitutes.Add(NotificationSubstitues.RequestType, notificationModel.RequestType.ToString()); + notificationModel.Substitutes.Add(NotificationSubstitues.PosterPath, issue.PosterPath); } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/V1/JobController.cs b/src/Ombi/Controllers/V1/JobController.cs index 2dd694619..442866762 100644 --- a/src/Ombi/Controllers/V1/JobController.cs +++ b/src/Ombi/Controllers/V1/JobController.cs @@ -118,9 +118,9 @@ namespace Ombi.Controllers.V1 /// /// [HttpPost("plexcontentcacher")] - public bool StartPlexContentCacher() + public async Task StartPlexContentCacher() { - OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IPlexContentSync), "Plex"), new JobDataMap(new Dictionary { { "recentlyAddedSearch", "false" } })); + await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IPlexContentSync), "Plex"), new JobDataMap(new Dictionary { { "recentlyAddedSearch", "false" } })); return true; } @@ -129,9 +129,9 @@ namespace Ombi.Controllers.V1 /// /// [HttpPost("clearmediaserverdata")] - public bool ClearMediaServerData() + public async Task ClearMediaServerData() { - OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IMediaDatabaseRefresh), "System")); + await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IMediaDatabaseRefresh), "System")); return true; } @@ -140,9 +140,9 @@ namespace Ombi.Controllers.V1 /// /// [HttpPost("plexrecentlyadded")] - public bool StartRecentlyAdded() + public async Task StartRecentlyAdded() { - OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IPlexContentSync) + "RecentlyAdded", "Plex"), new JobDataMap(new Dictionary { { "recentlyAddedSearch", "true" } })); + await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IPlexContentSync) + "RecentlyAdded", "Plex"), new JobDataMap(new Dictionary { { "recentlyAddedSearch", "true" } })); return true; } @@ -153,7 +153,18 @@ namespace Ombi.Controllers.V1 [HttpPost("embycontentcacher")] public async Task StartEmbyContentCacher() { - await OmbiQuartz.TriggerJob(nameof(IEmbyContentSync), "Emby"); + await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyContentSync), "Emby"), new JobDataMap(new Dictionary { { JobDataKeys.EmbyRecentlyAddedSearch, "false" } })); + return true; + } + + /// + /// Runs a smaller version of the content cacher + /// + /// + [HttpPost("embyrecentlyadded")] + public async Task StartEmbyRecentlyAdded() + { + await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyContentSync) + "RecentlyAdded", "Emby"), new JobDataMap(new Dictionary { { JobDataKeys.EmbyRecentlyAddedSearch, "true" } })); return true; } diff --git a/src/Ombi/Controllers/V1/MusicRequestController.cs b/src/Ombi/Controllers/V1/MusicRequestController.cs index df9d2f406..289c7cca1 100644 --- a/src/Ombi/Controllers/V1/MusicRequestController.cs +++ b/src/Ombi/Controllers/V1/MusicRequestController.cs @@ -113,9 +113,9 @@ namespace Ombi.Controllers.V1 /// [HttpDelete("{requestId:int}")] [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] - public async Task DeleteRequest(int requestId) + public async Task DeleteRequest(int requestId) { - await _engine.RemoveAlbumRequest(requestId); + return await _engine.RemoveAlbumRequest(requestId); } /// diff --git a/src/Ombi/Controllers/V1/RequestController.cs b/src/Ombi/Controllers/V1/RequestController.cs index 961969b6f..cc686b506 100644 --- a/src/Ombi/Controllers/V1/RequestController.cs +++ b/src/Ombi/Controllers/V1/RequestController.cs @@ -128,9 +128,9 @@ namespace Ombi.Controllers.V1 /// [HttpDelete("movie/{requestId:int}")] [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] - public async Task DeleteRequest(int requestId) + public async Task DeleteRequest(int requestId) { - await MovieRequestEngine.RemoveMovieRequest(requestId); + return await MovieRequestEngine.RemoveMovieRequest(requestId); } /// @@ -324,7 +324,7 @@ namespace Ombi.Controllers.V1 /// The request identifier. /// [HttpDelete("tv/{requestId:int}")] - [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] + [Authorize(Roles = "Admin,PowerUser")] public async Task DeleteTvRequest(int requestId) { await TvRequestEngine.RemoveTvRequest(requestId); @@ -437,10 +437,9 @@ namespace Ombi.Controllers.V1 /// [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] [HttpDelete("tv/child/{requestId:int}")] - public async Task DeleteChildRequest(int requestId) + public async Task DeleteChildRequest(int requestId) { - await TvRequestEngine.RemoveTvChild(requestId); - return true; + return await TvRequestEngine.RemoveTvChild(requestId); } diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index 9e7343472..fcd4bb1a4 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -334,6 +334,7 @@ namespace Ombi.Controllers.V1 [ApiExplorerSettings(IgnoreApi = true)] [HttpPost("customization/urlverify")] + [AllowAnonymous] public bool VerifyUrl([FromBody]UrlVerifyModel url) { return Uri.TryCreate(url.Url, UriKind.Absolute, out var __); @@ -618,6 +619,7 @@ namespace Ombi.Controllers.V1 j.RetryRequests = j.RetryRequests.HasValue() ? j.RetryRequests : JobSettingsHelper.ResendFailedRequests(j); j.MediaDatabaseRefresh = j.MediaDatabaseRefresh.HasValue() ? j.MediaDatabaseRefresh : JobSettingsHelper.MediaDatabaseRefresh(j); j.AutoDeleteRequests = j.AutoDeleteRequests.HasValue() ? j.AutoDeleteRequests : JobSettingsHelper.AutoDeleteRequests(j); + j.EmbyRecentlyAddedSync = j.EmbyRecentlyAddedSync.HasValue() ? j.EmbyRecentlyAddedSync : JobSettingsHelper.EmbyRecentlyAddedSync(j); return j; } diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index 0b58244b3..ac5905c33 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -52,7 +52,7 @@ namespace Ombi.Controllers.V2 [HttpPost("multi/{searchTerm}")] public async Task> MultiSearch(string searchTerm, [FromBody] MultiSearchFilter filter) { - return await _multiSearchEngine.MultiSearch(searchTerm, filter, Request.HttpContext.RequestAborted); + return await _multiSearchEngine.MultiSearch(Uri.UnescapeDataString(searchTerm), filter, Request.HttpContext.RequestAborted); } /// @@ -395,6 +395,19 @@ namespace Ombi.Controllers.V2 { return await _movieEngineV2.GetMoviesByActor(actorId, null); } + + /// + /// Returns all the tv shows that is by the actor id + /// + /// TheMovieDb Actor ID + /// + [HttpGet("actor/{actorId}/tv")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public async Task GetTvByActor(int actorId) + { + return await _tvEngineV2.GetTvByActor(actorId, null); + } [HttpGet("artist/{artistId}")] diff --git a/src/Ombi/Extensions/DatabaseExtensions.cs b/src/Ombi/Extensions/DatabaseExtensions.cs index 70bd8c70e..a96011f1c 100644 --- a/src/Ombi/Extensions/DatabaseExtensions.cs +++ b/src/Ombi/Extensions/DatabaseExtensions.cs @@ -120,9 +120,9 @@ namespace Ombi.Extensions public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config) { - options.UseMySql(config.ConnectionString, b => + options.UseMySql(config.ConnectionString, ServerVersion.AutoDetect(config.ConnectionString), b => { - b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); + //b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations? b.EnableRetryOnFailure(); }); } diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 063dc65db..1f10c1058 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -1,6 +1,6 @@  - net5.0 + net6.0 win10-x64;win10-x86;osx-x64;linux-x64;linux-arm;linux-arm64; false Latest @@ -62,27 +62,27 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - + + - + - - + + diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 7f7a875d1..ff7723703 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -68,7 +68,9 @@ namespace Ombi var services = new ServiceCollection(); services.ConfigureDatabases(null); +#pragma warning disable ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' using var provider = services.BuildServiceProvider(); +#pragma warning restore ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' var settingsDb = provider.GetRequiredService(); var ombiDb = provider.GetRequiredService(); diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 95fd6447f..a37c1d230 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -109,7 +109,7 @@ namespace Ombi .AllowCredentials(); })); - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest); + services.AddMvc(); services.AddSignalR(); services.AddSpaStaticFiles(configuration => configuration.RootPath = "ClientApp/dist"); diff --git a/src/Ombi/Views/Shared/_Layout.cshtml b/src/Ombi/Views/Shared/_Layout.cshtml index 5cee9c275..3cd9ccc56 100644 --- a/src/Ombi/Views/Shared/_Layout.cshtml +++ b/src/Ombi/Views/Shared/_Layout.cshtml @@ -23,6 +23,12 @@ appName = "Ombi"; } + var favicon = customization.Favicon; + if (string.IsNullOrEmpty(favicon)) + { + favicon = "images/favicon/favicon.ico"; + } + } diff --git a/src/Ombi/wwwroot/translations/bg.json b/src/Ombi/wwwroot/translations/bg.json index 01a16bcce..9242cd30a 100644 --- a/src/Ombi/wwwroot/translations/bg.json +++ b/src/Ombi/wwwroot/translations/bg.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Медийният сървър в момента е извън линия.", "CheckPageForUpdates": "Проверете тази страница за последни новини на сайта." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Открийте", "Search": "Търсене", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Избраните елементи са одобрени успешно" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Проблеми", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Нерешени проблеми", "InProgressTitle": "Проблеми в процес на изпълнение", "ResolvedTitle": "Решени проблеми", @@ -255,6 +268,7 @@ "Delete": "Изтриване на проблем", "DeletedIssue": "Проблемът е изтрит", "Chat": "Чат", + "EnterYourMessage": "Enter Your Message", "Requested": "Заявено", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Отказано", + "Trailers": "Trailers", "RecommendationsTitle": "Препоръчани", "SimilarTitle": "Подобни", "VideosTitle": "Видеоклипове", @@ -291,9 +306,9 @@ "ViewCollection": "Преглед на колекцията", "NotEnoughInfo": "За съжаление все още няма достатъчно информация за това предаване!", "AdvancedOptions": "Разширени настройки", - "AutoApproveOptions": "Можете да конфигурирате заявката си тук. След като бъде създадена, тя ще бъде изпратена до вашето записващо приложение (DVR) и ще бъде автоматично одобрена! Моля, обърнете внимание, че тази стъпка не е задължителна и можете да натиснете бутона Заявка за да я пропуснете!", - "AutoApproveOptionsTv": "Можете да конфигурирате заявката си тук. След като бъде създадена, тя ще бъде изпратена до вашето записващо приложение (DVR) и ще бъде автоматично одобрена! Ако заявката вече е обработена от Sonarr, програмата няма да промени основната папка или профила на качеството ако сте ги задали. Моля, обърнете внимание, че тази стъпка не е задължителна и можете да натиснете бутона Заявка за да я пропуснете!", - "AutoApproveOptionsTvShort": "Можете да конфигурирате заявката си тук. След като бъде създадена, тя ще бъде изпратена до вашето записващо приложение (DVR)! Ако заявката вече е обработена от Sonarr, програмата няма да промени основната папка или профила на качеството ако сте ги задали. Моля, обърнете внимание, че тази стъпка не е задължителна и можете да натиснете бутона Заявка за да я пропуснете!", + "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": "Изберете профил на качеството", "RootFolderSelect": "Изберете основна папка", "LanguageProfileSelect": "Изберете езиков профил", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Моля, изберете потребител", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Повтаряне на заявка", @@ -382,7 +399,7 @@ "DarkMode": "Тъмен режим", "Updated": "Обновяването е успешно", "StreamingCountry": "Страна на поточно предаване", - "StreamingCountryDescription": "Това е кодът на държавата, за който ще показваме поточна информация. Ако сте в САЩ, моля изберете САЩ и ще имате поточна информация, свързана със САЩ.", + "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": "Това е езикът, на който искате да се показва интерфейсът на Ombi.", "MobileQRCode": "Мобилен QR код", "LegacyApp": "Стартирайте старото приложение", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/cs.json b/src/Ombi/wwwroot/translations/cs.json new file mode 100644 index 000000000..b3234f471 --- /dev/null +++ b/src/Ombi/wwwroot/translations/cs.json @@ -0,0 +1,435 @@ +{ + "Login": { + "SignInButton": "Přihlásit se", + "UsernamePlaceholder": "Uživatelské jméno", + "PasswordPlaceholder": "Heslo", + "RememberMe": "Zapamatovat si mě", + "SignInWith": "Přihlásit se pomocí {{appName}}", + "SignInWithPlex": "Přihlásit se přes Plex", + "ForgottenPassword": "Zapomněli jste heslo?", + "Errors": { + "IncorrectCredentials": "Nesprávné uživatelské jméno nebo heslo" + } + }, + "Common": { + "ContinueButton": "Pokračovat", + "Available": "Dostupný", + "Approved": "Schváleno", + "Pending": "Čeká na vyřízení", + "PartiallyAvailable": "Částečně dostupné", + "Monitored": "Monitorováno", + "NotAvailable": "Není k dispozici", + "ProcessingRequest": "Zpracovávání žádosti", + "PendingApproval": "Čeká na schválení", + "RequestDenied": "Žádost zamítnuta", + "NotRequested": "Nevyžádáno", + "Requested": "Vyžádáno", + "Search": "Hledat", + "Request": "Žádost", + "Denied": "Zamítnuto", + "Approve": "Schválit", + "PartlyAvailable": "Částečně k dispozici", + "ViewDetails": "View Details", + "Errors": { + "Validation": "Please check your entered values" + }, + "Cancel": "Cancel", + "Submit": "Submit", + "Update": "Update", + "tvShow": "TV Show", + "movie": "Movie", + "album": "Album" + }, + "PasswordReset": { + "EmailAddressPlaceholder": "Email Address", + "ResetPasswordButton": "Reset Password" + }, + "LandingPage": { + "OnlineHeading": "Currently Online", + "OnlineParagraph": "The media server is currently online", + "PartiallyOnlineHeading": "Partially Online", + "PartiallyOnlineParagraph": "The media server is partially online.", + "MultipleServersUnavailable": "There are {{serversUnavailable}} servers offline out of {{totalServers}}.", + "SingleServerUnavailable": "There is {{serversUnavailable}} server offline out of {{totalServers}}.", + "OfflineHeading": "Currently Offline", + "OfflineParagraph": "The media server is currently offline.", + "CheckPageForUpdates": "Check this page for continuous site updates." + }, + "ErrorPages": { + "NotFound": "Page not found" + }, + "NavigationBar": { + "Discover": "Discover", + "Search": "Search", + "Requests": "Requests", + "UserManagement": "Users", + "Issues": "Issues", + "Vote": "Vote", + "Donate": "Donate!", + "DonateLibraryMaintainer": "Donate to Library Maintainer", + "DonateTooltip": "This is how I convince my wife to let me spend my spare time developing Ombi 😁", + "UpdateAvailableTooltip": "Update Available!", + "Settings": "Settings", + "Welcome": "Welcome {{username}}", + "UpdateDetails": "Update Details", + "Logout": "Logout", + "OpenMobileApp": "Open Mobile App", + "RecentlyAdded": "Recently Added", + "ChangeTheme": "Change Theme", + "Calendar": "Calendar", + "UserPreferences": "Preferences", + "FeatureSuggestion": "Features", + "FeatureSuggestionTooltip": "Have a great new idea? Suggest it here!", + "Filter": { + "Movies": "Movies", + "TvShows": "TV Shows", + "Music": "Music", + "People": "People" + }, + "MorningWelcome": "Good morning!", + "AfternoonWelcome": "Good afternoon!", + "EveningWelcome": "Good evening!" + }, + "Search": { + "Title": "Search", + "Paragraph": "Want to watch something that is not currently available? No problem, just search for it below and request it!", + "MoviesTab": "Movies", + "TvTab": "TV Shows", + "MusicTab": "Music", + "AdvancedSearch": "You can fill in any of the below to discover new media. All of the results are sorted by popularity", + "AdvancedSearchHeader": "Advanced Search", + "Suggestions": "Suggestions", + "NoResults": "Sorry, we didn't find any results!", + "DigitalDate": "Digital Release: {{date}}", + "TheatricalRelease": "Theatrical Release: {{date}}", + "ViewOnPlex": "Play On Plex", + "ViewOnEmby": "Play On Emby", + "ViewOnJellyfin": "Play On Jellyfin", + "RequestAdded": "Request for {{title}} has been added successfully", + "Similar": "Similar", + "Refine": "Refine", + "SearchBarPlaceholder": "Type Here to Search", + "Movies": { + "PopularMovies": "Popular Movies", + "UpcomingMovies": "Upcoming Movies", + "TopRatedMovies": "Top Rated Movies", + "NowPlayingMovies": "Now Playing Movies", + "HomePage": "Home Page", + "Trailer": "Trailer" + }, + "TvShows": { + "Popular": "Popular", + "Trending": "Trending", + "MostWatched": "Most Watched", + "MostAnticipated": "Most Anticipated", + "Results": "Results", + "AirDate": "Air Date:", + "AllSeasons": "All Seasons", + "FirstSeason": "First Season", + "LatestSeason": "Latest Season", + "Select": "Select ...", + "SubmitRequest": "Submit Request", + "Season": "Season {{seasonNumber}}", + "SelectAllInSeason": "Select All in Season {{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" + }, + "Requests": { + "Title": "Requests", + "Paragraph": "Below you can see yours and all other requests, as well as their download and approval status.", + "MoviesTab": "Movies", + "ArtistName": "Artist", + "AlbumName": "Album Name", + "TvTab": "TV Shows", + "MusicTab": "Music", + "RequestedBy": "Requested By", + "Status": "Status", + "RequestStatus": "Request status", + "Denied": " Denied:", + "TheatricalRelease": "Theatrical Release: {{date}}", + "ReleaseDate": "Released: {{date}}", + "TheatricalReleaseSort": "Theatrical Release", + "DigitalRelease": "Digital Release: {{date}}", + "RequestDate": "Request Date", + "QualityOverride": "Quality Override:", + "RootFolderOverride": "Root Folder Override:", + "ChangeRootFolder": "Root Folder", + "ChangeQualityProfile": "Quality Profile", + "MarkUnavailable": "Mark Unavailable", + "MarkAvailable": "Mark Available", + "Remove": "Remove", + "Deny": "Deny", + "DenyReason": "Deny Reason", + "DeniedReason": "Denied Reason", + "Season": "Season", + "GridTitle": "Title", + "AirDate": "Air Date", + "GridStatus": "Status", + "ReportIssue": "Report Issue", + "Filter": "Filter", + "Sort": "Sort", + "SeasonNumberHeading": "Season: {seasonNumber}", + "SortTitleAsc": "Title ▲", + "SortTitleDesc": "Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc": "Status ▲", + "SortStatusDesc": "Status ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} requests remaining", + "NextDays": "Another request will be added in {{time}} days", + "NextHours": "Another request will be added in {{time}} hours", + "NextMinutes": "Another request will be added in {{time}} minutes", + "NextMinute": "Another request will be added in {{time}} minute" + }, + "AllRequests": "All Requests", + "PendingRequests": "Pending Requests", + "ProcessingRequests": "Processing Requests", + "AvailableRequests": "Available Requests", + "DeniedRequests": "Denied Requests", + "RequestsToDisplay": "Requests to display", + "RequestsTitle": "Title", + "Details": "Details", + "Options": "Options", + "RequestPanel": { + "Delete": "Delete Request", + "Approve": "Approve Request", + "ChangeAvailability": "Mark Available", + "Deleted": "Successfully deleted selected items", + "Approved": "Successfully approved selected items" + }, + "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!", + "RequestAddedSuccessfully": "Request for {{title}} has been added successfully", + "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!" + }, + "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}}" + }, + "Issues": { + "Title": "Issues", + "IssuesForTitle": "Issues for {{title}}", + "PendingTitle": "Pending Issues", + "InProgressTitle": "In Progress Issues", + "ResolvedTitle": "Resolved Issues", + "ColumnTitle": "Title", + "Count": "Count", + "Category": "Category", + "Status": "Status", + "Details": "Details", + "Description": "Description", + "NoComments": "No Comments!", + "MarkInProgress": "Mark In Progress", + "MarkResolved": "Mark Resolved", + "SendMessageButton": "Send", + "Subject": "Subject", + "Comments": "Comments", + "WriteMessagePlaceholder": "Write your message here...", + "ReportedBy": "Reported By", + "IssueDialog": { + "Title": "Report an issue", + "DescriptionPlaceholder": "Please describe the issue", + "TitlePlaceholder": "Short title of your issue", + "SelectCategory": "Select Category", + "IssueCreated": "Issue has been created" + }, + "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", + "Requested": "Requested", + "UserOnDate": "{{user}} on {{date}}" + }, + "Filter": { + "ClearFilter": "Clear Filter", + "FilterHeaderAvailability": "Availability", + "FilterHeaderRequestStatus": "Status", + "Approved": "Approved", + "PendingApproval": "Pending Approval", + "WatchProviders": "Watch Providers", + "Keywords": "Keywords" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} remaining", + "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", + "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", + "TvDue": "TV: {{date}}", + "MovieDue": "Movie: {{date}}", + "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" + }, + "MediaDetails": { + "Denied": "Denied", + "Trailers": "Trailers", + "RecommendationsTitle": "Recommendations", + "SimilarTitle": "Similar", + "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", + "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" + }, + "Seasons": "Seasons:", + "Episodes": "Episodes:", + "Availability": "Availability:", + "RequestStatus": "Request Status", + "Quality": "Quality:", + "RootFolderOverride": "Root Folder Override:", + "QualityOverride": "Quality Override:", + "Network": "Network:", + "GenresLabel": "Genres:", + "Genres": "Genres", + "FirstAired": "First Aired:", + "TheatricalRelease": "Release:", + "DigitalRelease": "Digital Release:", + "Votes": "Votes:", + "Runtime": "Runtime:", + "Minutes": "{{runtime}} Minutes", + "Revenue": "Revenue:", + "Budget": "Budget:", + "Keywords": "Keywords/Tags:", + "Casts": { + "CastTitle": "Cast" + }, + "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!", + "SeasonNumber": "Season {{number}}" + }, + "SonarrConfiguration": "Sonarr Configuration", + "RadarrConfiguration": "Radarr Configuration", + "RequestOnBehalf": "Request on behalf of", + "PleaseSelectUser": "Please select a user", + "StreamingOn": "Streaming On:", + "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", + "RequestDate": "Request Date:", + "DeniedReason": "Denied Reason:", + "ReProcessRequest": "Re-Process Request", + "Music": { + "Type": "Type:", + "Country": "Country:", + "StartDate": "Start Date:", + "EndDate": "EndDate:" + } + }, + "Discovery": { + "PopularTab": "Popular", + "TrendingTab": "Trending", + "UpcomingTab": "Upcoming", + "SeasonalTab": "Seasonal", + "RecentlyRequestedTab": "Recently Requested", + "Movies": "Movies", + "Combined": "Combined", + "Tv": "TV", + "CardDetails": { + "Availability": "Availability", + "Studio": "Studio", + "Network": "Network", + "UnknownNetwork": "Unknown", + "RequestStatus": "Request Status", + "Director": "Director", + "InCinemas": "In Cinemas", + "FirstAired": "First Aired", + "Writer": "Writer", + "ExecProducer": "Exec Producer" + }, + "NoSearch": "Sorry, nothing matches your search!" + }, + "UserPreferences": { + "Welcome": "Welcome {{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", + "EmailAddress": "Email Address", + "NewPassword": "New Password", + "NewPasswordConfirm": "New Password Confirm", + "Security": "Security", + "Profile": "Profile", + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" + }, + "UserTypeLabel": { + "1": "Local User", + "2": "Plex User", + "3": "Emby User", + "4": "Emby Connect User", + "5": "Jellyfin User" + }, + "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}}" + } +} diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index 6515ab2d7..1598df272 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Medieserveren er offline.", "CheckPageForUpdates": "Tjek denne side for løbende opdateringer." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Discover", "Search": "Søg", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Problemer", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Afventende problemer", "InProgressTitle": "Igangværende probemer", "ResolvedTitle": "Løste problemer", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Afvist", + "Trailers": "Trailers", "RecommendationsTitle": "Recommendations", "SimilarTitle": "Lignende", "VideosTitle": "Videos", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index ce334e480..9ed748ac5 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Der Mediaserver ist derzeit offline.", "CheckPageForUpdates": "Überprüfe diese Seite für kontinuierliche Website-Updates." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Entdecken", "Search": "Suche", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Probleme", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Ausstehende Probleme", "InProgressTitle": "Probleme in bearbeitung", "ResolvedTitle": "Behobene Probleme", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Abgelehnt", + "Trailers": "Trailers", "RecommendationsTitle": "Empfehlungen", "SimilarTitle": "Ähnliche", "VideosTitle": "Videos", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index ad9ee1ba2..a6d9cb25a 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -55,6 +55,9 @@ "OfflineParagraph": "The media server is currently offline.", "CheckPageForUpdates": "Check this page for continuous site updates." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Discover", "Search": "Search", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded":"You have exceeded your Movie request quota!", "TvRequestQuotaExceeded":"You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded":"You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Issues", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Pending Issues", "InProgressTitle": "In Progress Issues", "ResolvedTitle": "Resolved Issues", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat":"Chat", + "EnterYourMessage":"Enter Your Message", "Requested":"Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Denied", + "Trailers": "Trailers", "RecommendationsTitle": "Recommendations", "SimilarTitle": "Similar", "VideosTitle": "Videos", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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":"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", @@ -317,7 +332,8 @@ "RootFolderOverride":"Root Folder Override:", "QualityOverride":"Quality Override:", "Network":"Network:", - "Genres":"Genres:", + "GenresLabel":"Genres:", + "Genres":"Genres", "FirstAired":"First Aired:", "TheatricalRelease":"Release:", "DigitalRelease":"Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,6 +422,15 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index 0a61ca8af..8bf684664 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -55,6 +55,9 @@ "OfflineParagraph": "El servidor de medios está fuera de línea.", "CheckPageForUpdates": "Consulta esta página para ver las últimas novedades." }, + "ErrorPages": { + "NotFound": "Página no encontrada" + }, "NavigationBar": { "Discover": "Descubre", "Search": "Buscar", @@ -131,6 +134,9 @@ }, "AdvancedSearchInstructions": "Por favor, elija el tipo de medio que está buscando:", "YearOfRelease": "Año de lanzamiento", + "SearchGenre": "Buscar por género", + "SearchKeyword": "Buscar palabra clave", + "SearchProvider": "Proveedor de búsqueda", "KeywordSearchingDisclaimer": "Por favor, ten en cuenta que la búsqueda de palabras clave es poco confiable debido a la inconsistencia en la base de datos de TheMovieDb" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Los elementos seleccionados ha sido aprobados correctamente" }, "SuccessfullyApproved": "Se ha aprobado con éxito", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "La solicitud está disponible", "NowUnavailable": "La solicitud no está disponible", "SuccessfullyReprocessed": "La solicitud ha sido procesada correctamente", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Problemas", + "IssuesForTitle": "Problemas de {{title}}", "PendingTitle": "Problemas pendientes", "InProgressTitle": "Problemas en curso", "ResolvedTitle": "Problemas resueltos", @@ -255,6 +268,7 @@ "Delete": "Borrar problema", "DeletedIssue": "El problema ha sido borrado", "Chat": "Chat", + "EnterYourMessage": "Ingrese Su Mensaje", "Requested": "Solicitado", "UserOnDate": "{{user}} en {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Denegado", + "Trailers": "Trailer", "RecommendationsTitle": "Recomendaciones", "SimilarTitle": "Similar", "VideosTitle": "Vídeos", @@ -291,9 +306,9 @@ "ViewCollection": "Ver Colección", "NotEnoughInfo": "Desafortunadamente todavía no hay suficiente información sobre este programa!", "AdvancedOptions": "Opciones Avanzadas", - "AutoApproveOptions": "Puede configurar la solicitud aquí. Una vez solicitado, se enviará a su aplicación DVR y se aprobará automáticamente. Tenga en cuenta que esto es opcional, presione \"Preguntar\" para saltar.", - "AutoApproveOptionsTv": "You can configure the request here, once requested it will be send 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 send 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": "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": "Seleccione un Perfil de Calidad", "RootFolderSelect": "Seleccione una Carpeta Raíz", "LanguageProfileSelect": "Select A Language Profile", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Géneros:", + "Genres": "Géneros", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Solicitado por {{user}} el {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "DarkMode": "Modo Oscuro", "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "No suscrito!" }, "UserTypeLabel": { "1": "Usuario local", @@ -404,5 +422,14 @@ "3": "Usuario de Emby", "4": "Emby Connect User", "5": "Usuario de Jellyfin" + }, + "Paginator": { + "itemsPerPageLabel": "Elementos por página:", + "nextPageLabel": "Página siguiente", + "previousPageLabel": "Página anterior", + "firstPageLabel": "Primera página", + "lastPageLabel": "Última página", + "rangePageLabel1": "0 de {{length}}", + "rangePageLabel2": "{{startIndex}} – {{endIndex}} de {{length}}" } } diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index e3f6ba8bb..298a87aba 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -4,8 +4,8 @@ "UsernamePlaceholder": "Nom d’utilisateur", "PasswordPlaceholder": "Mot de passe", "RememberMe": "Se souvenir de moi", - "SignInWith": "Se Connecter avec {{appName}}", - "SignInWithPlex": "Se Connecter avec Plex", + "SignInWith": "Se connecter avec {{appName}}", + "SignInWithPlex": "Se connecter avec Plex", "ForgottenPassword": "Mot de passe oublié ?", "Errors": { "IncorrectCredentials": "Nom d'utilisateur ou mot de passe incorrect" @@ -31,7 +31,7 @@ "PartlyAvailable": "Partiellement disponible", "ViewDetails": "Voir les détails", "Errors": { - "Validation": "Veuillez vérifier les valeurs entrées" + "Validation": "Veuillez vérifier les valeurs saisies" }, "Cancel": "Annuler", "Submit": "Envoyer", @@ -55,6 +55,9 @@ "OfflineParagraph": "Le serveur média est actuellement hors-ligne.", "CheckPageForUpdates": "Consultez cette page pour voir les mises à jour du site." }, + "ErrorPages": { + "NotFound": "Page non trouvée" + }, "NavigationBar": { "Discover": "Découvrir", "Search": "Rechercher", @@ -62,14 +65,14 @@ "UserManagement": "Utilisateurs", "Issues": "Problèmes", "Vote": "Vote", - "Donate": "Faire un don !", + "Donate": "Faire un don", "DonateLibraryMaintainer": "Faire un don au mainteneur de la bibliothèque", "DonateTooltip": "C’est pour convaincre ma femme de me laisser passer mon temps libre à développer Ombi 😁", "UpdateAvailableTooltip": "Mise à jour disponible !", "Settings": "Paramètres", "Welcome": "Bienvenue {{username}}", "UpdateDetails": "Mettre à jour les informations", - "Logout": "Déconnexion", + "Logout": "Se déconnecter", "OpenMobileApp": "Ouvrir l'application", "RecentlyAdded": "Ajouts récents", "ChangeTheme": "Changer de thème", @@ -89,7 +92,7 @@ }, "Search": { "Title": "Rechercher", - "Paragraph": "Vous voulez regarder quelque chose qui n'est pas disponible actuellement ? Pas de problème, recherchez-le ci-dessous et demandez-le !", + "Paragraph": "Vous voulez regarder quelque chose qui n'est pas disponible actuellement ? Pas de problème, recherchez-le en bas et demandez-le !", "MoviesTab": "Films", "TvTab": "Séries", "MusicTab": "Musique", @@ -118,9 +121,9 @@ "Popular": "Populaire", "Trending": "Tendances", "MostWatched": "Les plus visionnées", - "MostAnticipated": "Les plus attendus", + "MostAnticipated": "Les plus attendues", "Results": "Résultats", - "AirDate": "Date de diffusion:", + "AirDate": "Date de diffusion :", "AllSeasons": "Toutes les saisons", "FirstSeason": "Première saison", "LatestSeason": "Dernière saison", @@ -131,6 +134,9 @@ }, "AdvancedSearchInstructions": "Veuillez choisir le type de média que vous recherchez :", "YearOfRelease": "Année de sortie", + "SearchGenre": "Rechercher un genre", + "SearchKeyword": "Rechercher un mot-clé", + "SearchProvider": "Rechercher un fournisseur", "KeywordSearchingDisclaimer": "Veuillez noter que la recherche de mots-clés n'est pas très fiable en raison de données incohérentes dans TheMovieDb" }, "Requests": { @@ -145,17 +151,17 @@ "Status": "Statut", "RequestStatus": "Statut de la demande", "Denied": " Refusé :", - "TheatricalRelease": "Sortie en salle: {{date}}", + "TheatricalRelease": "Sortie en salle : {{date}}", "ReleaseDate": "Sortie : {{date}}", "TheatricalReleaseSort": "Sortie en salle", - "DigitalRelease": "Sortie numérique: {{date}}", + "DigitalRelease": "Sortie numérique : {{date}}", "RequestDate": "Date de la demande", "QualityOverride": "Remplacement de la qualité :", "RootFolderOverride": "Remplacement du répertoire racine :", "ChangeRootFolder": "Répertoire racine", "ChangeQualityProfile": "Profil de qualité", - "MarkUnavailable": "Marquer comme non disponible", - "MarkAvailable": "Marquer comme disponible", + "MarkUnavailable": "Marquer indisponible", + "MarkAvailable": "Marquer disponible", "Remove": "Supprimer", "Deny": "Refuser", "DenyReason": "Motif de refus", @@ -198,6 +204,7 @@ "Approved": "Éléments sélectionnés approuvés avec succès" }, "SuccessfullyApproved": "Approuvée avec succès", + "SuccessfullyDeleted": "Demande supprimée avec succès", "NowAvailable": "La demande est maintenant disponible", "NowUnavailable": "La demande est maintenant indisponible", "SuccessfullyReprocessed": "La demande a été retraitée avec succès", @@ -215,14 +222,20 @@ "ChildRequestDoesNotExist": "La demande enfant n'existe pas", "NoPermissionsRequestMovie": "Vous n'avez pas la permission de demander un film", "NoPermissionsRequestTV": "Vous n'avez pas la permission de demander une série TV", - "NoPermissionsRequestAlbum": "Vous n'avez pas la permission de demander un album", - "MovieRequestQuotaExceeded": "Vous avez dépassé votre quota de demande de films !", - "TvRequestQuotaExceeded": "Vous avez dépassé votre quota de demande d'épisodes !", - "AlbumRequestQuotaExceeded": "Vous avez dépassé votre quota de demande d'albums !" - } + "NoPermissionsRequestAlbum": "Vous n'avez pas le droit de demander un album", + "MovieRequestQuotaExceeded": "Vous avez dépassé votre quota de demandes de films !", + "TvRequestQuotaExceeded": "Vous avez dépassé votre quota de demandes d'épisodes !", + "AlbumRequestQuotaExceeded": "Vous avez dépassé votre quota de demandes d'albums !" + }, + "Notify": "Notifier", + "RemoveNotification": "Supprimer les notifications", + "SuccessfulNotify": "Vous allez maintenant être notifié pour {{title}}", + "SuccessfulUnNotify": "Vous ne serez plus notifié pour {{title}}", + "CouldntNotify": "Impossible de notifier {{title}}" }, "Issues": { "Title": "Problèmes", + "IssuesForTitle": "Problèmes pour {{title}}", "PendingTitle": "Problèmes en attente", "InProgressTitle": "Problèmes en cours", "ResolvedTitle": "Problèmes résolus", @@ -236,7 +249,7 @@ "MarkInProgress": "Marquer en cours de traitement", "MarkResolved": "Marquer comme résolu", "SendMessageButton": "Envoyer", - "Subject": "Sujet", + "Subject": "Objet", "Comments": "Commentaires", "WriteMessagePlaceholder": "Écrivez votre message ici...", "ReportedBy": "Signalé par", @@ -255,6 +268,7 @@ "Delete": "Supprimer le problème", "DeletedIssue": "Problème supprimé", "Chat": "Discuter", + "EnterYourMessage": "Saisissez votre message", "Requested": "Demandé", "UserOnDate": "{{user}} le {{date}}" }, @@ -268,9 +282,9 @@ "Keywords": "Mots-clés" }, "UserManagment": { - "TvRemaining": "Séries : {{remaining}}/{{total}} restant(s)", - "MovieRemaining": "Films : {{remaining}}/{{total}} restant(s)", - "MusicRemaining": "Musique : {{remaining}}/{{total}} restant(s)", + "TvRemaining": "Séries : {{remaining}}/{{total}} restante(s)", + "MovieRemaining": "Films : {{remaining}}/{{total}} restante(s)", + "MusicRemaining": "Musique : {{remaining}}/{{total}} restante(s)", "TvDue": "Séries : {{date}}", "MovieDue": "Film : {{date}}", "MusicDue": "Musique : {{date}}" @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Refusé", + "Trailers": "Bandes-annonces", "RecommendationsTitle": "Recommandations", "SimilarTitle": "Similaires", "VideosTitle": "Vidéos", @@ -300,13 +315,13 @@ "Status": "Statut :", "StatusValues": { "Rumored": "Rumeur", - "Planned": "Planifié", + "Planned": "Prévu", "In Production": "En Production", "Post Production": "Post-production", "Released": "Sorti", "Running": "En cours", "Returning Series": "Série en cours", - "Ended": "Finie", + "Ended": "Terminée", "Canceled": "Annulée" }, "Seasons": "Saisons :", @@ -314,19 +329,20 @@ "Availability": "Disponibilité :", "RequestStatus": "Statut de la Demande", "Quality": "Qualité :", - "RootFolderOverride": "Remplacement du répertoire racine :", - "QualityOverride": "Remplacement de la qualité :", + "RootFolderOverride": "Répertoire racine remplacé :", + "QualityOverride": "Qualité remplacée :", "Network": "Diffuseur :", - "Genres": "Genres :", + "GenresLabel": "Genres :", + "Genres": "Genres", "FirstAired": "Première diffusion :", - "TheatricalRelease": "Sortie :", + "TheatricalRelease": "Sortie en salle :", "DigitalRelease": "Sortie numérique :", "Votes": "Votes :", "Runtime": "Durée :", "Minutes": "{{runtime}} Minutes", "Revenue": "Recettes :", "Budget": "Budget :", - "Keywords": "Mots-clés / Tags :", + "Keywords": "Mots-clés/Tags :", "Casts": { "CastTitle": "Casting" }, @@ -343,6 +359,7 @@ "PleaseSelectUser": "Veuillez sélectionner un utilisateur", "StreamingOn": "En streaming sur :", "RequestedBy": "Demandé par :", + "RequestedByOn": "Demandé par {{user}} le {{date}}", "RequestDate": "Date de la demande :", "DeniedReason": "Motif de refus :", "ReProcessRequest": "Refaire une demande", @@ -367,23 +384,23 @@ "Studio": "Studio", "Network": "Diffuseur", "UnknownNetwork": "Inconnu", - "RequestStatus": "Statut de la Demande", + "RequestStatus": "Statut de la demande", "Director": "Réalisateur", - "InCinemas": "En Salles", - "FirstAired": "Première Diffusion", + "InCinemas": "En salles", + "FirstAired": "Première diffusion", "Writer": "Scénariste", - "ExecProducer": "Producteur Exécutif" + "ExecProducer": "Producteur exécutif" }, "NoSearch": "Désolé, rien ne correspond à votre recherche !" }, "UserPreferences": { "Welcome": "Bienvenue {{username}} !", "OmbiLanguage": "Langue", - "DarkMode": "Mode Sombre", + "DarkMode": "Mode sombre", "Updated": "Mise à jour réussie", "StreamingCountry": "Pays de diffusion", - "StreamingCountryDescription": "C'est le code du pays pour lequel nous afficherons des informations de diffusion. Si vous êtes en France, veuillez sélectionner la France et vous aurez des informations de streaming en relation avec la France.", - "LanguageDescription": "C'est la langue dans laquelle vous souhaitez que l'interface Ombi soit affichée.", + "StreamingCountryDescription": "C'est le code du pays dont nous afficherons les informations de diffusion. Si vous êtes en France, veuillez sélectionner la France et vous aurez des informations de streaming pour la France.", + "LanguageDescription": "C'est la langue dans laquelle vous souhaitez que le site d'Ombi soit affichée.", "MobileQRCode": "QR Code mobile", "LegacyApp": "Lancer l'ancienne application", "NoQrCode": "Veuillez contacter votre administrateur pour activer les QR codes", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "Confirmation du nouveau mot de passe", "Security": "Sécurité", "Profile": "Profil", - "UpdatedYourInformation": "Informations mises à jour" + "UpdatedYourInformation": "Informations mises à jour", + "Unsubscribed": "Désabonné !" }, "UserTypeLabel": { "1": "Utilisateur local", @@ -404,5 +422,14 @@ "3": "Utilisateur Emby", "4": "Utilisateur Emby Connect", "5": "Utilisateur Jellyfin" + }, + "Paginator": { + "itemsPerPageLabel": "Éléments par page :", + "nextPageLabel": "Page suivante", + "previousPageLabel": "Page précédente", + "firstPageLabel": "Première page", + "lastPageLabel": "Dernière page", + "rangePageLabel1": "0 sur {{length}}", + "rangePageLabel2": "{{startIndex}} – {{endIndex}} sur {{length}}" } } diff --git a/src/Ombi/wwwroot/translations/hu.json b/src/Ombi/wwwroot/translations/hu.json index ecae7d9ff..7797e1318 100644 --- a/src/Ombi/wwwroot/translations/hu.json +++ b/src/Ombi/wwwroot/translations/hu.json @@ -55,11 +55,14 @@ "OfflineParagraph": "A médiaszerver jelenleg nem elérhető.", "CheckPageForUpdates": "Látogasd meg ezt az oldalt a frissítésekhez." }, + "ErrorPages": { + "NotFound": "Az oldal nem található" + }, "NavigationBar": { "Discover": "Felfedezés", "Search": "Keresés", "Requests": "Kérések", - "UserManagement": "Felhasználók kezelése", + "UserManagement": "Felhasználók", "Issues": "Problémák", "Vote": "Szavazás", "Donate": "Adakozás!", @@ -75,7 +78,7 @@ "ChangeTheme": "Téma váltása", "Calendar": "Naptár", "UserPreferences": "Beállítások", - "FeatureSuggestion": "Feature Suggestion", + "FeatureSuggestion": "Funkciójavaslás", "FeatureSuggestionTooltip": "Van egy jó ötleted? Oszd meg itt!", "Filter": { "Movies": "Filmek", @@ -131,7 +134,10 @@ }, "AdvancedSearchInstructions": "Kérlek válaszd ki, milyen típusú médiát keresel:", "YearOfRelease": "Megjelenés éve", - "KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb" + "SearchGenre": "Műfaj keresése", + "SearchKeyword": "Keresés kulcsszóra", + "SearchProvider": "Keresési szolgáltató", + "KeywordSearchingDisclaimer": "Megjegyzés: A kulcsszó keresés funkció nem megbízható a TheMovieDb adatainak inkonzisztenciája miatt" }, "Requests": { "Title": "Kérések", @@ -159,7 +165,7 @@ "Remove": "Törlés", "Deny": "Elutasítás", "DenyReason": "Elutasítási ok", - "DeniedReason": "Denied Reason", + "DeniedReason": "Elutasítás oka", "Season": "Évad", "GridTitle": "Cím", "AirDate": "Bemutató", @@ -194,40 +200,47 @@ "Delete": "Kérés törlése", "Approve": "Kérés elfogadása", "ChangeAvailability": "Elérhetőnek jelölés", - "Deleted": "Successfully deleted selected items", - "Approved": "Successfully approved selected items" + "Deleted": "A kijelölt elemek törlésre kerultek", + "Approved": "A kijelölt elemek jóváhagyásra kerultek" }, - "SuccessfullyApproved": "Successfully Approved", - "NowAvailable": "Request is now available", - "NowUnavailable": "Request is now unavailable", - "SuccessfullyReprocessed": "Successfully Re-processed the request", + "SuccessfullyApproved": "Sikeresen jóváhagyva", + "SuccessfullyDeleted": "Kérés sikeresen törölve", + "NowAvailable": "Kérés elérhető", + "NowUnavailable": "Kérés nem elérhető", + "SuccessfullyReprocessed": "Kérés sikeresen újra-feldolgozva", "DeniedRequest": "Elutasított kérés", - "RequestCollection": "Request Collection", - "CollectionSuccesfullyAdded": "The collection {{name}} has been successfully added!", - "NeedToSelectEpisodes": "You need to select some episodes!", - "RequestAddedSuccessfully": "Request for {{title}} has been added successfully", + "RequestCollection": "Kérés gyűjtemény", + "CollectionSuccesfullyAdded": "A(z) {{name}} gyűjtemény sikeresen hozzáadva!", + "NeedToSelectEpisodes": "Ki kell választani néhány epizódot!", + "RequestAddedSuccessfully": "Kérés sikeresen leadva erre: {{title}}", "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": "Ez már kérve lett", + "EpisodesAlreadyRequested": "Már vannak kért epizódok ehhez a sorozathoz", + "NoPermissionsOnBehalf": "Nincs jogosultságod más felhasználók nevében kérni!", + "NoPermissions": "Nincs megfelelő jogosultságod!", + "RequestDoesNotExist": "A kérés nem létezik", + "ChildRequestDoesNotExist": "Gyerek kérés nem létezik", + "NoPermissionsRequestMovie": "Nincs jogosultságod filmet kérni", + "NoPermissionsRequestTV": "Nincs jogosultságod sorozatot kérni", + "NoPermissionsRequestAlbum": "Nincs jogosultságod albumot kérni", + "MovieRequestQuotaExceeded": "Túllépted a megszabott film kérési kereted!", + "TvRequestQuotaExceeded": "Túllépted a megszabott epizód kérési kereted!", + "AlbumRequestQuotaExceeded": "Túllépted a megszabott album kérési kereted!" + }, + "Notify": "Értesítés", + "RemoveNotification": "Értesítések törlése", + "SuccessfulNotify": "Mostantól értesítve leszel erről: {{title}}", + "SuccessfulUnNotify": "Mostantól nem leszel értesítve erről: {{title}}", + "CouldntNotify": "Nem lehetett értesíteni erről: {{title}}" }, "Issues": { "Title": "Problémák", + "IssuesForTitle": "{{title}} problémái", "PendingTitle": "Várakozó problémák", "InProgressTitle": "Folyamatban lévő problémák", "ResolvedTitle": "Megoldott problémák", "ColumnTitle": "Cím", - "Count": "Count", + "Count": "Mennyiség", "Category": "Kategória", "Status": "Állapot", "Details": "Részletek", @@ -247,16 +260,17 @@ "SelectCategory": "Válassz kategóriát", "IssueCreated": "Hibajegy létrehozva" }, - "Outstanding": "There are outstanding issues", + "Outstanding": "Lezáratlan problémák vannak", "ResolvedDate": "Megoldás dátuma", - "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", + "CreatedDate": "Létrejött", + "MarkedAsResolved": "Ez a hibajegy megoldottnak lett jelölve!", + "MarkedAsInProgress": "Ez a hibajegy folyamatban lévőnek lett jelölve!", + "Delete": "Hibajegy törlése", + "DeletedIssue": "Hibajegy törölve lett", "Chat": "Csevegő", - "Requested": "Requested", - "UserOnDate": "{{user}} on {{date}}" + "EnterYourMessage": "Írd be az üzeneted", + "Requested": "Kérve", + "UserOnDate": "{{user}} ekkor: {{date}}" }, "Filter": { "ClearFilter": "Szűrő törlése", @@ -265,7 +279,7 @@ "Approved": "Jóváhagyva", "PendingApproval": "Jóváhagyásra vár", "WatchProviders": "Watch Providers", - "Keywords": "Keywords" + "Keywords": "Kulcsszavak" }, "UserManagment": { "TvRemaining": "TV: {{remaining}}/{{total}} maradt", @@ -281,7 +295,8 @@ }, "MediaDetails": { "Denied": "Megtagadva", - "RecommendationsTitle": "Recommendations", + "Trailers": "Előzetesek", + "RecommendationsTitle": "Ajánlások", "SimilarTitle": "Hasonló", "VideosTitle": "Videók", "AlbumsTitle": "Albumok", @@ -289,22 +304,22 @@ "ClearSelection": "Kijelölés törlése", "RequestSelectedAlbums": "Kiválasztott albumok kérése", "ViewCollection": "Gyűjtemény megnézése", - "NotEnoughInfo": "Unfortunately there is not enough information about this show yet!", + "NotEnoughInfo": "Sajnos nincs elég információ erről a műsorról!", "AdvancedOptions": "Részletes beállítások", - "AutoApproveOptions": "You can configure the request here, once requested it will be send 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 send 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 send 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": "Itt állíthatod be a kérésed részleteit. A kérés elküldése után a DVR applikációba kerül, ahol automatikusan elfogadásra fog kerülni! Megjegyzés: A beállítások nem kötelezőek, a kihagyásukhoz nyomd meg a Kérés gombot!", + "AutoApproveOptionsTv": "Itt állíthatod be a kérésed részleteit. A kérés elküldése után a DVR applikációba kerül, ahol automatikusan elfogadásra fog kerülni! Ha a kérés már megtalálható a Sonarrban, nem fogjuk megváltoztatni a gyökérkönyvtárat vagy a minőségi profilt, ha beállítod! Megjegyzés: A beállítások nem kötelezőek, a kihagyásukhoz nyomd meg a Kérés gombot!", + "AutoApproveOptionsTvShort": "Itt állíthatod be a kérésed részleteit. A kérés elküldése után a DVR applikációba kerül! Ha a kérés már megtalálható a Sonarrban, nem fogjuk megváltoztatni a gyökérkönyvtárat vagy a minőségi profilt, ha beállítod! Megjegyzés: A beállítások nem kötelezőek, a kihagyásukhoz nyomd meg a Kérés gombot!", "QualityProfilesSelect": "Válassz ki egy minőségprofilt", "RootFolderSelect": "Válassz ki egy gyökérmappát", "LanguageProfileSelect": "Válassz ki egy nyelvi profilt", "Status": "Állapot:", "StatusValues": { - "Rumored": "Rumored", - "Planned": "Planned", - "In Production": "In Production", - "Post Production": "Post Production", - "Released": "Released", - "Running": "Running", + "Rumored": "Híresztelt", + "Planned": "Tervezett", + "In Production": "Gyártás alatt", + "Post Production": "Utómunka alatt", + "Released": "Megjelent", + "Running": "Folyamatban", "Returning Series": "Visszatérő sorozatok", "Ended": "Befejezett", "Canceled": "Megszakított" @@ -316,14 +331,15 @@ "Quality": "Minőség:", "RootFolderOverride": "Gyökér mappa felülírása:", "QualityOverride": "Minőség felülírása:", - "Network": "Network:", - "Genres": "Műfaj:", + "Network": "Hálózat:", + "GenresLabel": "Műfajok:", + "Genres": "Műfajok", "FirstAired": "Első sugárzás ideje:", - "TheatricalRelease": "Release:", - "DigitalRelease": "Digital Release:", - "Votes": "Votes:", - "Runtime": "Runtime:", - "Minutes": "{{runtime}} Minutes", + "TheatricalRelease": "Kiadás:", + "DigitalRelease": "Digitális megjelenés:", + "Votes": "Szavazatok:", + "Runtime": "Hossz:", + "Minutes": "{{runtime}} perc", "Revenue": "Bevétel:", "Budget": "Költségvetés:", "Keywords": "Kulcsszavak/Címkék:", @@ -334,43 +350,44 @@ "AllSeasonsTooltip": "Ezzel kérni fogja a sorozat összes évadát", "FirstSeasonTooltip": "Ezzel kérni fogja a sorozat első évadát", "LatestSeasonTooltip": "Ezzel kérni fogja a sorozat legutóbbi évadát", - "NoEpisodes": "There unfortunately is no episode data for this show yet!", + "NoEpisodes": "Sajnos még nincs epizód adat ehhez a sorozathoz!", "SeasonNumber": "{{number}}. évad" }, "SonarrConfiguration": "Sonarr konfiguráció", "RadarrConfiguration": "Radarr konfiguráció", "RequestOnBehalf": "Kérés más nevében", "PleaseSelectUser": "Válassz egy felhasználót", - "StreamingOn": "Streaming On:", + "StreamingOn": "Streaming csatorna:", "RequestedBy": "Kérte:", + "RequestedByOn": "Kérte: {{user}} ekkor: {{date}}", "RequestDate": "Kérés ideje:", "DeniedReason": "Elutasítás oka:", - "ReProcessRequest": "Re-Process Request", + "ReProcessRequest": "Kérés újrafeldolgozása", "Music": { - "Type": "Type:", - "Country": "Country:", - "StartDate": "Start Date:", - "EndDate": "EndDate:" + "Type": "Típus:", + "Country": "Ország:", + "StartDate": "Kezdés dátuma:", + "EndDate": "Befejezés dátuma:" } }, "Discovery": { "PopularTab": "Népszerű", "TrendingTab": "Felkapott", - "UpcomingTab": "Upcoming", - "SeasonalTab": "Seasonal", - "RecentlyRequestedTab": "Recently Requested", + "UpcomingTab": "Közelgő", + "SeasonalTab": "Évadok", + "RecentlyRequestedTab": "Kérések az utóbbi időben", "Movies": "Filmek", - "Combined": "Combined", + "Combined": "Kombinált", "Tv": "TV", "CardDetails": { "Availability": "Elérhetőség", "Studio": "Stúdió", - "Network": "Network", + "Network": "Hálózat", "UnknownNetwork": "Ismeretlen", "RequestStatus": "Kérés állapota", "Director": "Rendezte", "InCinemas": "A mozikban", - "FirstAired": "First Aired", + "FirstAired": "Első sugárzás ideje", "Writer": "Írta", "ExecProducer": "Gyártásvezető" }, @@ -381,28 +398,38 @@ "OmbiLanguage": "Nyelv", "DarkMode": "Sötét mód", "Updated": "Sikeresen frissítve", - "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 have US related streaming information.", - "LanguageDescription": "This is the language you would like the Ombi interface to be displayed in.", + "StreamingCountry": "Streaming ország", + "StreamingCountryDescription": "Ez az országkód, amire streaming információt jelenítünk meg. Amennyiben az Egyesült Államokban vagy, válaszd az US kódot az amerikai streaming információhoz.", + "LanguageDescription": "Ez a nyelv, amiben látni szeretnéd az Ombi felületét.", "MobileQRCode": "Mobil QR kód", - "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", + "LegacyApp": "Eredeti applikáció indítása", + "NoQrCode": "A QR kód aktiválásához kérjük vegye fel a kapcsolatot az adminisztrátorával", + "UserType": "Felhasználó típusa:", + "ChangeDetails": "Részletek megváltoztatása", + "NeedCurrentPassword": "Az itteni változtatásokhoz a jelenlegi jelszavad szükséges", "CurrentPassword": "Jelenlegi jelszó", "EmailAddress": "E-mail cím", "NewPassword": "Új jelszó", "NewPasswordConfirm": "Új jelszó megerősítése", - "Security": "Security", + "Security": "Biztonság", "Profile": "Profil", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Az információid frissítésre kerültek", + "Unsubscribed": "Leiratkozva!" }, "UserTypeLabel": { "1": "Helyi felhasználó", "2": "Plex felhasználó", "3": "Emby felhasználó", - "4": "Emby Connect User", + "4": "Emby Connect felhasználó", "5": "Jellyfin felhasználó" + }, + "Paginator": { + "itemsPerPageLabel": "Elemek oldalanként:", + "nextPageLabel": "Következő oldal", + "previousPageLabel": "Előző oldal", + "firstPageLabel": "Első oldal", + "lastPageLabel": "Utolsó oldal", + "rangePageLabel1": "0 ennyiből: {{length}}", + "rangePageLabel2": "{{startIndex}} - {{endIndex}} ennyiből: {{length}}" } } diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 74feb48a0..d1c910b66 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -14,8 +14,8 @@ "Common": { "ContinueButton": "Continua", "Available": "Disponibile", - "Approved": "Approved", - "Pending": "Pending", + "Approved": "Approvato", + "Pending": "In Sospeso", "PartiallyAvailable": "Parzialmente disponibile", "Monitored": "Monitorato", "NotAvailable": "Non disponibile", @@ -24,7 +24,7 @@ "RequestDenied": "Richiesta negata", "NotRequested": "Non richiesta", "Requested": "Richiesta", - "Search": "Search", + "Search": "Cerca", "Request": "Richiesta", "Denied": "Negata", "Approve": "Approva", @@ -35,9 +35,9 @@ }, "Cancel": "Annulla", "Submit": "Invia", - "Update": "Update", - "tvShow": "TV Show", - "movie": "Movie", + "Update": "Aggiorna", + "tvShow": "Serie TV", + "movie": "Film", "album": "Album" }, "PasswordReset": { @@ -55,6 +55,9 @@ "OfflineParagraph": "Il server dei media è attualmente offline.", "CheckPageForUpdates": "Controlla questa pagina per aggiornamenti continui del sito." }, + "ErrorPages": { + "NotFound": "Pagina non trovata" + }, "NavigationBar": { "Discover": "Scopri", "Search": "Cerca", @@ -93,15 +96,15 @@ "MoviesTab": "Film", "TvTab": "Serie TV", "MusicTab": "Musica", - "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": "Puoi compilare uno dei seguenti per scoprire nuovi media. Tutti i risultati sono ordinati per popolarità", + "AdvancedSearchHeader": "Ricerca Avanzata", "Suggestions": "Suggerimenti", "NoResults": "Spiacenti, non abbiamo trovato nulla!", "DigitalDate": "Uscita in digitale: {{date}}", "TheatricalRelease": "Uscita nei cinema: {{date}}", "ViewOnPlex": "Guarda su Plex", "ViewOnEmby": "Guarda su Emby", - "ViewOnJellyfin": "Play On Jellyfin", + "ViewOnJellyfin": "Riproduci Su Jellyfin", "RequestAdded": "La richiesta per {{title}} è stata aggiunta con successo", "Similar": "Simili", "Refine": "Affina", @@ -129,9 +132,12 @@ "Season": "Stagione {{seasonNumber}}", "SelectAllInSeason": "Seleziona tutto nella stagione {{seasonNumber}}" }, - "AdvancedSearchInstructions": "Please choose what type of media you are searching for:", - "YearOfRelease": "Year of Release", - "KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb" + "AdvancedSearchInstructions": "Sei pregato di scegliere che tipo di media stai cercando:", + "YearOfRelease": "Anno di Rilascio", + "SearchGenre": "Cerca per genere", + "SearchKeyword": "Termini di ricerca", + "SearchProvider": "Provider di ricerca", + "KeywordSearchingDisclaimer": "Sei pregato di notare che la Ricerca per Parola chiave è molto a fortuna a causa di dati non coerenti nel TheMovieDb" }, "Requests": { "Title": "Richieste", @@ -159,7 +165,7 @@ "Remove": "Rimuovi", "Deny": "Nega", "DenyReason": "Nega Motivo", - "DeniedReason": "Denied Reason", + "DeniedReason": "Motivo di Negazione", "Season": "Stagione", "GridTitle": "Titolo", "AirDate": "Data di trasmissione", @@ -194,40 +200,47 @@ "Delete": "Elimina Richiesta", "Approve": "Approva Richiesta", "ChangeAvailability": "Segna come Disponibile", - "Deleted": "Successfully deleted selected items", - "Approved": "Successfully approved selected items" + "Deleted": "Elementi selezionati eliminati correttamente", + "Approved": "Elementi selezionati approvati correttamente" }, - "SuccessfullyApproved": "Successfully Approved", - "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!", - "RequestAddedSuccessfully": "Request for {{title}} has been added successfully", + "SuccessfullyApproved": "Approvato Correttamente", + "SuccessfullyDeleted": "Richiesta eliminata correttamente", + "NowAvailable": "Richiesta ora disponibile", + "NowUnavailable": "Richiesta ora non disponibile", + "SuccessfullyReprocessed": "Richiesta ri-elaborata correttamente", + "DeniedRequest": "Richiesta Negata", + "RequestCollection": "Richiedi Raccolta", + "CollectionSuccesfullyAdded": "La raccolta {{name}} è stata aggiunta correttamente!", + "NeedToSelectEpisodes": "Devi selezionare degli episodi!", + "RequestAddedSuccessfully": "La richiesta per {{title}} è stata aggiunta correttamente", "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": "Questo è già stato richiesto", + "EpisodesAlreadyRequested": "Abbiamo già degli episodi richiesti da questa serie", + "NoPermissionsOnBehalf": "Non hai le autorizzazioni corrette per richiedere per conto degli utenti!", + "NoPermissions": "Non hai le autorizzazioni corrette!", + "RequestDoesNotExist": "La richiesta non esiste", + "ChildRequestDoesNotExist": "La Richiesta per Bambini non esiste", + "NoPermissionsRequestMovie": "Non hai le autorizzazioni per Richiedere un Film", + "NoPermissionsRequestTV": "Non hai le autorizzazioni per Richiedere una Serie TV", + "NoPermissionsRequestAlbum": "Non hai le autorizzazioni per Richiedere un Album", + "MovieRequestQuotaExceeded": "Hai superato la tua quota di richieste di Film!", + "TvRequestQuotaExceeded": "Hai superato la tua quota di richieste di Episodi!", + "AlbumRequestQuotaExceeded": "Hai superato la tua quota di richieste di Album!" + }, + "Notify": "Notifica", + "RemoveNotification": "Rimuovi Notifiche", + "SuccessfulNotify": "Sarai notificato per il titolo {{title}}", + "SuccessfulUnNotify": "Non sarai più notificato per il titolo {{title}}", + "CouldntNotify": "Impossibile notificare il titolo {{title}}" }, "Issues": { "Title": "Problemi", + "IssuesForTitle": "Problemi per {{title}}", "PendingTitle": "Problemi In Attesa", "InProgressTitle": "Problemi In Corso", "ResolvedTitle": "Problemi Risolti", "ColumnTitle": "Titolo", - "Count": "Count", + "Count": "Conteggio", "Category": "Categoria", "Status": "Stato", "Details": "Dettagli", @@ -255,8 +268,9 @@ "Delete": "Elimina problema", "DeletedIssue": "Il problema è stato eliminato", "Chat": "Chat", - "Requested": "Requested", - "UserOnDate": "{{user}} on {{date}}" + "EnterYourMessage": "Scrivi qualcosa...", + "Requested": "Richiesto", + "UserOnDate": "{{user}} il {{date}}" }, "Filter": { "ClearFilter": "Rimuovi filtro", @@ -264,8 +278,8 @@ "FilterHeaderRequestStatus": "Stato", "Approved": "Approvato", "PendingApproval": "In attesa di approvazione", - "WatchProviders": "Watch Providers", - "Keywords": "Keywords" + "WatchProviders": "Guarda i Fornitori", + "Keywords": "Parole Chiave" }, "UserManagment": { "TvRemaining": "TV: {{remaining}}/{{total}} rimanenti", @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Rifiutato", + "Trailers": "Trailer", "RecommendationsTitle": "Raccomandazioni", "SimilarTitle": "Simili", "VideosTitle": "Video", @@ -291,42 +306,43 @@ "ViewCollection": "Visualizza Raccolta", "NotEnoughInfo": "Sfortunatamente ancora non ci sono abbastanza informazioni su questo show!", "AdvancedOptions": "Opzioni Avanzate", - "AutoApproveOptions": "You can configure the request here, once requested it will be send 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 send 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 send 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": "Puoi configurare qui la tua richiesta, che verrà inviata alla tua applicazione DVR e automaticamente approvata! La configurazione è facoltativa, basta premere Richiedi per saltarla!", + "AutoApproveOptionsTv": "Puoi configurare qui la tua richiesta, che verrà inviata alla tua applicazione DVR e automaticamente approvata! Se la richiesta è già presente in Sonarr, la cartella radice o il profilo qualità non verrà cambiato! La configurazione è facoltativa, basta premere Richiedi per saltarla!", + "AutoApproveOptionsTvShort": "Puoi configurare qui la tua richiesta, che verrà inviata alla tua applicazione DVR! Se la richiesta è già presente in Sonarr, la cartella radice o il profilo qualità non verrà cambiato! La configurazione è facoltativa, basta premere Richiedi per saltarla!", "QualityProfilesSelect": "Seleziona Un Profilo di Qualità", "RootFolderSelect": "Seleziona Una Cartella di Root", - "LanguageProfileSelect": "Select A Language Profile", - "Status": "Status:", + "LanguageProfileSelect": "Seleziona Un Profilo della Lingua", + "Status": "Stato:", "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": "Rumor", + "Planned": "Pianificato", + "In Production": "In Produzione", + "Post Production": "Post Produzione", + "Released": "Rilasciato", + "Running": "In Corso", + "Returning Series": "Serie Rinnovate", + "Ended": "Terminata", + "Canceled": "Annullata" }, - "Seasons": "Seasons:", - "Episodes": "Episodes:", - "Availability": "Availability:", + "Seasons": "Stagioni:", + "Episodes": "Episodi:", + "Availability": "Disponibilità:", "RequestStatus": "Stato Richiesta", - "Quality": "Quality:", - "RootFolderOverride": "Root Folder Override:", - "QualityOverride": "Quality Override:", - "Network": "Network:", - "Genres": "Genres:", - "FirstAired": "First Aired:", - "TheatricalRelease": "Release:", - "DigitalRelease": "Digital Release:", - "Votes": "Votes:", - "Runtime": "Runtime:", + "Quality": "Qualità:", + "RootFolderOverride": "Sovrascrizione Cartella di Root:", + "QualityOverride": "Sovrascrizione Qualità:", + "Network": "Rete:", + "GenresLabel": "Generi:", + "Genres": "Generi", + "FirstAired": "Prima Trasmissione:", + "TheatricalRelease": "Uscita:", + "DigitalRelease": "Uscita Digitale:", + "Votes": "Voti:", + "Runtime": "Durata:", "Minutes": "{{runtime}} Minuti", - "Revenue": "Revenue:", + "Revenue": "Entrate:", "Budget": "Budget:", - "Keywords": "Keywords/Tags:", + "Keywords": "Parole Chiave/Tag:", "Casts": { "CastTitle": "Trasmetti" }, @@ -334,31 +350,32 @@ "AllSeasonsTooltip": "Questo richiederà ogni stagione per questo show", "FirstSeasonTooltip": "Questo richiederà solo la Prima Stagione per questo show", "LatestSeasonTooltip": "Questo richiederà solo l'Ultima Stagione per questo show", - "NoEpisodes": "There unfortunately is no episode data for this show yet!", - "SeasonNumber": "Season {{number}}" + "NoEpisodes": "Sfortunatamente, non c'è ancora alcun dato dell'episodio per questa serie!", + "SeasonNumber": "Stagione {{number}}" }, - "SonarrConfiguration": "Sonarr Configuration", - "RadarrConfiguration": "Radarr Configuration", - "RequestOnBehalf": "Request on behalf of", - "PleaseSelectUser": "Please select a user", - "StreamingOn": "Streaming On:", - "RequestedBy": "Requested By:", - "RequestDate": "Request Date:", - "DeniedReason": "Denied Reason:", - "ReProcessRequest": "Re-Process Request", + "SonarrConfiguration": "Configurazione di Sonarr", + "RadarrConfiguration": "Configurazione di Radarr", + "RequestOnBehalf": "Richiesta per conto di", + "PleaseSelectUser": "Sei pregato di selezionare un utente", + "StreamingOn": "In Streaming Su:", + "RequestedBy": "Richiesto Da:", + "RequestedByOn": "Richiesto da {{user}} il {{date}}", + "RequestDate": "Data di Richiesta:", + "DeniedReason": "Motivo di Negazione:", + "ReProcessRequest": "Ri-Elabora Richiesta", "Music": { - "Type": "Type:", - "Country": "Country:", - "StartDate": "Start Date:", - "EndDate": "EndDate:" + "Type": "Tipo:", + "Country": "Paese:", + "StartDate": "Data Iniziale:", + "EndDate": "Data Finale:" } }, "Discovery": { "PopularTab": "Popolare", "TrendingTab": "Tendenze", "UpcomingTab": "In arrivo", - "SeasonalTab": "Seasonal", - "RecentlyRequestedTab": "Recently Requested", + "SeasonalTab": "Stagionale", + "RecentlyRequestedTab": "Richiesto Recentemente", "Movies": "Film", "Combined": "Combinato", "Tv": "TV", @@ -374,35 +391,45 @@ "Writer": "Scrittore", "ExecProducer": "Produttore Esecutivo" }, - "NoSearch": "Sorry, nothing matches your search!" + "NoSearch": "Spiacenti, nessun risultato corrisponde alla tua ricerca!" }, "UserPreferences": { "Welcome": "Benvenuto {{username}}!", "OmbiLanguage": "Lingua", "DarkMode": "Modalità Scura", - "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 have 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", - "EmailAddress": "Email Address", - "NewPassword": "New Password", - "NewPasswordConfirm": "New Password Confirm", - "Security": "Security", - "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "Updated": "Aggiornate Correttamente", + "StreamingCountry": "Paese di Streaming", + "StreamingCountryDescription": "Questo è il codice paese per cui mostreremo le informazioni di streaming. Se sei negli USA, selezionando US riceverai le informazioni di streaming correlate agli USA.", + "LanguageDescription": "Questa è la lingua in cui vorresti fosse visualizzata l'interfaccia di Ombi.", + "MobileQRCode": "Codice QR Mobile", + "LegacyApp": "Avvia l'App Legacy", + "NoQrCode": "Sei pregato di contattare il tuo amministratore per abilitare i codici QR", + "UserType": "Tipo di Utente:", + "ChangeDetails": "Modifica i Dettagli", + "NeedCurrentPassword": "Necessiti la tua password corrente per effettuare qualsiasi modifica qui", + "CurrentPassword": "Password Corrente", + "EmailAddress": "Indirizzo Email", + "NewPassword": "Nuova Password", + "NewPasswordConfirm": "Conferma Nuova Password", + "Security": "Sicurezza", + "Profile": "Profilo", + "UpdatedYourInformation": "Le tue informazioni sono state aggiornate", + "Unsubscribed": "Disiscritto!" }, "UserTypeLabel": { - "1": "Local User", - "2": "Plex User", - "3": "Emby User", - "4": "Emby Connect User", - "5": "Jellyfin User" + "1": "Utente Locale", + "2": "Utente di Plex", + "3": "Utente di Emby", + "4": "Connetti Utente di Emby", + "5": "Utente di Jellyfin" + }, + "Paginator": { + "itemsPerPageLabel": "Righe per pagina:", + "nextPageLabel": "Pagina successiva", + "previousPageLabel": "Pagina precedente", + "firstPageLabel": "Prima pagina", + "lastPageLabel": "Ultima pagina", + "rangePageLabel1": "0 su {{length}}", + "rangePageLabel2": "{{startIndex}} – {{endIndex}} su {{length}}" } } diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index da6ce4cc5..801f48479 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -55,6 +55,9 @@ "OfflineParagraph": "De mediaserver is momenteel offline.", "CheckPageForUpdates": "Controleer deze pagina voor updates." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Ontdekken", "Search": "Zoeken", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Problemen", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Onopgeloste Problemen", "InProgressTitle": "Problemen in Behandeling", "ResolvedTitle": "Opgeloste Problemen", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Afgewezen", + "Trailers": "Trailers", "RecommendationsTitle": "Aanbevelingen", "SimilarTitle": "Vergelijkbaar", "VideosTitle": "Video's", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index c889a4f8f..41e027c37 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Medieserveren er for øyeblikket utilgjengelig.", "CheckPageForUpdates": "Sjekk denne siden for kontinuerlige oppdateringer." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Discover", "Search": "Søk", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Mangler", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Ventende løsninger", "InProgressTitle": "Mangler under behandling", "ResolvedTitle": "Løste mangler", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Avslått", + "Trailers": "Trailers", "RecommendationsTitle": "Recommendations", "SimilarTitle": "Lignende", "VideosTitle": "Videos", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/pl.json b/src/Ombi/wwwroot/translations/pl.json index 3affb1ef9..e1a2ad62e 100644 --- a/src/Ombi/wwwroot/translations/pl.json +++ b/src/Ombi/wwwroot/translations/pl.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Serwer multimediów jest aktualnie offline.", "CheckPageForUpdates": "Tutaj znajdziesz aktualizacje dotyczące tej strony." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Odkryj", "Search": "Szukaj", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Problemy", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Oczekujące problemy", "InProgressTitle": "Problemy w trakcie", "ResolvedTitle": "Problemy rozwiązane", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Odrzucone", + "Trailers": "Trailers", "RecommendationsTitle": "Rekomendacje", "SimilarTitle": "Podobne", "VideosTitle": "Wideo", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/pt.json b/src/Ombi/wwwroot/translations/pt.json index cd2a3d80a..ea1967e78 100644 --- a/src/Ombi/wwwroot/translations/pt.json +++ b/src/Ombi/wwwroot/translations/pt.json @@ -14,8 +14,8 @@ "Common": { "ContinueButton": "Continuar", "Available": "Disponível", - "Approved": "Approved", - "Pending": "Pending", + "Approved": "Aprovado", + "Pending": "Pendente", "PartiallyAvailable": "Parcialmente Disponível", "Monitored": "Monitorado", "NotAvailable": "Não Disponível", @@ -24,7 +24,7 @@ "RequestDenied": "Solicitação Negada", "NotRequested": "Não Solicitado", "Requested": "Solicitado", - "Search": "Search", + "Search": "Buscar", "Request": "Solicitar", "Denied": "Negado", "Approve": "Aprovar", @@ -35,10 +35,10 @@ }, "Cancel": "Cancelar", "Submit": "Submeter", - "Update": "Update", - "tvShow": "TV Show", - "movie": "Movie", - "album": "Album" + "Update": "Atualizar", + "tvShow": "Series", + "movie": "Filmes", + "album": "Álbum" }, "PasswordReset": { "EmailAddressPlaceholder": "Endereço de e-mail", @@ -55,8 +55,11 @@ "OfflineParagraph": "O servidor de mídia está atualmente offline.", "CheckPageForUpdates": "Verifique esta página para acompanhar as atualizações do site." }, + "ErrorPages": { + "NotFound": "Pagina não encontrada" + }, "NavigationBar": { - "Discover": "Discover", + "Discover": "Explorar", "Search": "Pesquisar", "Requests": "Solicitações", "UserManagement": "User Management", @@ -64,7 +67,7 @@ "Vote": "Votar", "Donate": "Doações!", "DonateLibraryMaintainer": "Doar para o Dono da Biblioteca", - "DonateTooltip": "This is how I convince my wife to let me spend my spare time developing Ombi 😁", + "DonateTooltip": "É assim que convenço minha esposa a me deixar passar o meu tempo livre desenvolvendo o Ombi 😁", "UpdateAvailableTooltip": "Atualização Disponível!", "Settings": "Configurações", "Welcome": "Bem-vindo(a), {{username}}", @@ -72,20 +75,20 @@ "Logout": "Desconectar", "OpenMobileApp": "Abrir Aplicativo do Celular", "RecentlyAdded": "Recentemente Adicionado", - "ChangeTheme": "Change Theme", - "Calendar": "Calendar", - "UserPreferences": "Preferences", + "ChangeTheme": "Trocar Tema", + "Calendar": "Calendário", + "UserPreferences": "Preferências", "FeatureSuggestion": "Feature Suggestion", - "FeatureSuggestionTooltip": "Have a great new idea? Suggest it here!", + "FeatureSuggestionTooltip": "Teve uma ótima idéia? Sugira aqui!", "Filter": { - "Movies": "Movies", - "TvShows": "TV Shows", - "Music": "Music", - "People": "People" + "Movies": "Filmes", + "TvShows": "Séries", + "Music": "Músicas", + "People": "Pessoas" }, - "MorningWelcome": "Good morning!", - "AfternoonWelcome": "Good afternoon!", - "EveningWelcome": "Good evening!" + "MorningWelcome": "Bom dia!", + "AfternoonWelcome": "Boa tarde!", + "EveningWelcome": "Boa noite!" }, "Search": { "Title": "Pesquisar", @@ -93,15 +96,15 @@ "MoviesTab": "Filmes", "TvTab": "Séries", "MusicTab": "Músicas", - "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": "Você pode preencher qualquer um dos abaixo para descobrir novas mídias. Todos os resultados são classificados por popularidade", + "AdvancedSearchHeader": "Pesquisa avançada", "Suggestions": "Sugestões", "NoResults": "Desculpe, não encontramos nenhum resultado!", "DigitalDate": "Lançamento Digital: {{date}}", "TheatricalRelease": "Lançamento nos Cinemas: {{date}}", "ViewOnPlex": "View On Plex", "ViewOnEmby": "View On Emby", - "ViewOnJellyfin": "Play On Jellyfin", + "ViewOnJellyfin": "Assistir no Jellyfin", "RequestAdded": "O pedido de {{title}} foi adicionado com sucesso", "Similar": "Semelhantes", "Refine": "Corrigir", @@ -126,30 +129,33 @@ "LatestSeason": "Última Temporada", "Select": "Selecionar...", "SubmitRequest": "Enviar solicitação", - "Season": "Season {{seasonNumber}}", + "Season": "Temporada: {{seasonNumber}}", "SelectAllInSeason": "Selecione Tudo na Temporada {{seasonNumber}}" }, - "AdvancedSearchInstructions": "Please choose what type of media you are searching for:", - "YearOfRelease": "Year of Release", - "KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb" + "AdvancedSearchInstructions": "Por favor, escolha o tipo de mídia que você está procurando:", + "YearOfRelease": "Ano de lançamento", + "SearchGenre": "Pesquisa por gênero", + "SearchKeyword": "Pesquisar palavras-chave", + "SearchProvider": "Provedores de pesquisa", + "KeywordSearchingDisclaimer": "Por favor, note que a busca por palavra-chave é muito acertada e perdida devido aos dados inconsistentes no TheMovieDb" }, "Requests": { "Title": "Solicitações", "Paragraph": "Abaixo, você pode ver o seu e todos os outros pedidos, bem como o seu download e status de aprovação.", "MoviesTab": "Filmes", - "ArtistName": "Artist", - "AlbumName": "Album Name", + "ArtistName": "Artistas", + "AlbumName": "Nome do álbum", "TvTab": "Séries", "MusicTab": "Músicas", - "RequestedBy": "Requested By", + "RequestedBy": "Solicitado por", "Status": "Status", - "RequestStatus": "Request status", + "RequestStatus": "Status da solicitação", "Denied": " Negados:", "TheatricalRelease": "Lançamento nos Cinemas: {{date}}", "ReleaseDate": "Lançado: {{date}}", "TheatricalReleaseSort": "Lançamento nos Cinemas", "DigitalRelease": "Lançamento Digital: {{date}}", - "RequestDate": "Request Date", + "RequestDate": "Data da Solicitação", "QualityOverride": "Substituição de Qualidade:", "RootFolderOverride": "Substituição da Pasta Raiz:", "ChangeRootFolder": "Pasta Raiz", @@ -158,9 +164,9 @@ "MarkAvailable": "Marcar como Disponível", "Remove": "Remover", "Deny": "Negar", - "DenyReason": "Deny Reason", - "DeniedReason": "Denied Reason", - "Season": "Season", + "DenyReason": "Razão da rejeição", + "DeniedReason": "Razão da rejeição", + "Season": "Temporada", "GridTitle": "Título", "AirDate": "AirDate", "GridStatus": "Status", @@ -181,23 +187,24 @@ "NextMinutes": "Outra solicitação será adicionada em {{time}} minutos", "NextMinute": "Outra solicitação será adicionada em {{time}} minuto" }, - "AllRequests": "All Requests", - "PendingRequests": "Pending Requests", - "ProcessingRequests": "Processing Requests", - "AvailableRequests": "Available Requests", - "DeniedRequests": "Denied Requests", - "RequestsToDisplay": "Requests to display", - "RequestsTitle": "Title", - "Details": "Details", - "Options": "Options", + "AllRequests": "Todas solicitações", + "PendingRequests": "Solicitações pendentes", + "ProcessingRequests": "Solicitações em andamento", + "AvailableRequests": "Solicitação Disponíveis", + "DeniedRequests": "Solicitações Negadas", + "RequestsToDisplay": "Itens por página", + "RequestsTitle": "Título", + "Details": "Detalhes", + "Options": "Opções", "RequestPanel": { - "Delete": "Delete Request", + "Delete": "Apagar Solicitação", "Approve": "Approve Request", "ChangeAvailability": "Mark Available", "Deleted": "Successfully deleted selected items", "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Problemas", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Problemas pendentes", "InProgressTitle": "Problemas em resolução", "ResolvedTitle": "Problemas Resolvidos", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,19 +295,20 @@ }, "MediaDetails": { "Denied": "Denied", + "Trailers": "Trailers", "RecommendationsTitle": "Recommendations", "SimilarTitle": "Similar", - "VideosTitle": "Videos", - "AlbumsTitle": "Albums", - "RequestAllAlbums": "Request All Albums", - "ClearSelection": "Clear Selection", + "VideosTitle": "Vídeos", + "AlbumsTitle": "Álbuns", + "RequestAllAlbums": "Solicitar Todos Álbuns", + "ClearSelection": "Limpar Seleção", "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/ru.json b/src/Ombi/wwwroot/translations/ru.json index 3b0659f2d..0f57f9c31 100644 --- a/src/Ombi/wwwroot/translations/ru.json +++ b/src/Ombi/wwwroot/translations/ru.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Медиа-сервер в настоящее время не в сети.", "CheckPageForUpdates": "Проверьте эту страницу для получения последних новостей сайта." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Discover", "Search": "Поиск", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "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}}" }, "Issues": { "Title": "Проблемы", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Проблемы в ожидании", "InProgressTitle": "Проблемы в процессе", "ResolvedTitle": "Решенные проблемы", @@ -255,6 +268,7 @@ "Delete": "Delete issue", "DeletedIssue": "Issue has been deleted", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Отказано", + "Trailers": "Trailers", "RecommendationsTitle": "Recommendations", "SimilarTitle": "Похожие", "VideosTitle": "Videos", @@ -291,9 +306,9 @@ "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 send 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 send 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 send 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": "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", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/sk.json b/src/Ombi/wwwroot/translations/sk.json index 774445f95..1e0ae25cf 100644 --- a/src/Ombi/wwwroot/translations/sk.json +++ b/src/Ombi/wwwroot/translations/sk.json @@ -14,8 +14,8 @@ "Common": { "ContinueButton": "Pokračovať", "Available": "Dostupné", - "Approved": "Approved", - "Pending": "Pending", + "Approved": "Schválené", + "Pending": "Čakajúce", "PartiallyAvailable": "Čiastočne dostupné", "Monitored": "Sledované", "NotAvailable": "Nie je k dispozícii", @@ -24,7 +24,7 @@ "RequestDenied": "Požiadavka zamietnutá", "NotRequested": "Nepožiadané", "Requested": "Požiadané", - "Search": "Search", + "Search": "Hľadať", "Request": "Požiadať", "Denied": "Zamietnuté", "Approve": "Schválené", @@ -35,10 +35,10 @@ }, "Cancel": "Zrušiť", "Submit": "Odoslať", - "Update": "Update", - "tvShow": "TV Show", - "movie": "Movie", - "album": "Album" + "Update": "Aktualizovať", + "tvShow": "Seriály", + "movie": "Filmy", + "album": "Albumy" }, "PasswordReset": { "EmailAddressPlaceholder": "Emailová adresa", @@ -55,6 +55,9 @@ "OfflineParagraph": "Mediálny server je momentálne offline.", "CheckPageForUpdates": "Prezrite túto stránku pre aktualizácie." }, + "ErrorPages": { + "NotFound": "Stránka sa nenašla" + }, "NavigationBar": { "Discover": "Objaviť", "Search": "Hľadať", @@ -93,15 +96,15 @@ "MoviesTab": "Filmy", "TvTab": "Seriály", "MusicTab": "Hudba", - "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": "Ak chcete objaviť nové médiá, môžete vyplniť ktorýkoľvek z nižšie uvedených údajov. Všetky výsledky sú zoradené podľa obľúbenosti", + "AdvancedSearchHeader": "Rozšírené vyhľadávanie", "Suggestions": "Návrhy", "NoResults": "Ľutujeme, nenašli sme žiadne výsledky!", "DigitalDate": "Online vydanie: {{date}}", "TheatricalRelease": "Kino vydanie: {{date}}", "ViewOnPlex": "Zobraziť na Plex", "ViewOnEmby": "Zobraziť na Emby", - "ViewOnJellyfin": "Play On Jellyfin", + "ViewOnJellyfin": "Prehrať cez Jellyfin", "RequestAdded": "Žiadosť o {{title}} bola úspešne pridaná", "Similar": "Podobné", "Refine": "Filtrovať", @@ -129,9 +132,12 @@ "Season": "Séria {{seasonNumber}}", "SelectAllInSeason": "Vybrať všetko v danej sérii {{seasonNumber}}" }, - "AdvancedSearchInstructions": "Please choose what type of media you are searching for:", - "YearOfRelease": "Year of Release", - "KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb" + "AdvancedSearchInstructions": "Vyberte, aký typ médií hľadáte:", + "YearOfRelease": "Rok vydania", + "SearchGenre": "Hľadať podľa žánru", + "SearchKeyword": "Hľadať kľúčové slovo", + "SearchProvider": "Vyhľadávanie poskytovateľa", + "KeywordSearchingDisclaimer": "Upozorňujeme, že vyhľadávanie podľa kľúčových slov je kvôli nekonzistentným údajom v databáze TheMovieDb veľmi nepresné" }, "Requests": { "Title": "Požiadavky", @@ -159,7 +165,7 @@ "Remove": "Odstrániť", "Deny": "Odmietnuť", "DenyReason": "Odmietnuť dôvod", - "DeniedReason": "Denied Reason", + "DeniedReason": "Odmietnuť dôvod", "Season": "Séria", "GridTitle": "Názov", "AirDate": "Dátum vysielania", @@ -186,7 +192,7 @@ "ProcessingRequests": "Spracovávané požiadavky", "AvailableRequests": "Dostupné požiadavky", "DeniedRequests": "Odmietnuté požiadavky", - "RequestsToDisplay": "Requests to display", + "RequestsToDisplay": "Požiadavky na zobrazenie", "RequestsTitle": "Názov", "Details": "Podrobnosti", "Options": "Možnosti", @@ -194,40 +200,47 @@ "Delete": "Odstrániž požiadavku", "Approve": "Schváliť žiadosť", "ChangeAvailability": "Označiť k dispozícií", - "Deleted": "Successfully deleted selected items", - "Approved": "Successfully approved selected items" + "Deleted": "Úspešne odstránené vybrané položky", + "Approved": "Úspešne schválené vybrané položky" }, - "SuccessfullyApproved": "Successfully Approved", - "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!", - "RequestAddedSuccessfully": "Request for {{title}} has been added successfully", + "SuccessfullyApproved": "Úspešne schválené", + "SuccessfullyDeleted": "Žiadosť bola úspešne vymazaná", + "NowAvailable": "Žiadosť je teraz k dispozícii", + "NowUnavailable": "Žiadosť je teraz nedostupná", + "SuccessfullyReprocessed": "Úspešné opätovné spracovanie požiadavky", + "DeniedRequest": "Zamietnutá žiadosť", + "RequestCollection": "Zbierka žiadostí", + "CollectionSuccesfullyAdded": "Kolekcia {{name}} bola úspešne pridaná!", + "NeedToSelectEpisodes": "Musíte si vybrať nejaké epizódy!", + "RequestAddedSuccessfully": "Žiadosť o {{title}} bola úspešne pridaná", "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": "Táto požiadavka už bola zadaná", + "EpisodesAlreadyRequested": "Už máme požiadavky na epizódy z tejto série", + "NoPermissionsOnBehalf": "Nemáte správne oprávnenie žiadať v mene používateľov!", + "NoPermissions": "Nemáte potrebné oprávnenie!", + "RequestDoesNotExist": "Žiadosť neexistuje", + "ChildRequestDoesNotExist": "Žiadosť neexistuje", + "NoPermissionsRequestMovie": "Nemáte oprávnenia na vyžiadanie filmu", + "NoPermissionsRequestTV": "Nemáte oprávnenia na vyžiadanie TV seriálu", + "NoPermissionsRequestAlbum": "Nemáte oprávnenia na vyžiadanie albumu", + "MovieRequestQuotaExceeded": "Prekročili ste limit požiadaviek na filmy!", + "TvRequestQuotaExceeded": "Prekročili ste limit požiadaviek na epizódy!", + "AlbumRequestQuotaExceeded": "Prekročili ste limit požiadaviek na albumy!" + }, + "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}}" }, "Issues": { "Title": "Problémy", + "IssuesForTitle": "Problémy pre {{title}}", "PendingTitle": "Nevyriešené problémy", "InProgressTitle": "Riešené problémy", "ResolvedTitle": "Vyiešené problémy", "ColumnTitle": "Názov", - "Count": "Count", + "Count": "Počet", "Category": "Kategória", "Status": "Stav", "Details": "Podrobnosti", @@ -247,7 +260,7 @@ "SelectCategory": "Vybrať kategóriu", "IssueCreated": "Problém bol nahlásený" }, - "Outstanding": "There are outstanding issues", + "Outstanding": "Existujú nevyriešené problémy", "ResolvedDate": "Dátum vyriešenia", "CreatedDate": "Nastolený", "MarkedAsResolved": "Tento problém bol označený ako vyriešený!", @@ -255,8 +268,9 @@ "Delete": "Odstrániť problém", "DeletedIssue": "Problém bol odstránený", "Chat": "Chat", - "Requested": "Requested", - "UserOnDate": "{{user}} on {{date}}" + "EnterYourMessage": "Zadaj správu", + "Requested": "Požiadané", + "UserOnDate": "{{user}} dňa {{date}}" }, "Filter": { "ClearFilter": "Vymazať filter", @@ -264,8 +278,8 @@ "FilterHeaderRequestStatus": "Stav", "Approved": "Schválené", "PendingApproval": "Čaká na schválenie", - "WatchProviders": "Watch Providers", - "Keywords": "Keywords" + "WatchProviders": "Sledovanie poskytovateľov", + "Keywords": "Kľúčové slová" }, "UserManagment": { "TvRemaining": "Seriál: {{remaining}}/{{total}} zostávajúce", @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Zamietnuté", + "Trailers": "Upútavka", "RecommendationsTitle": "Odporúčania", "SimilarTitle": "Podobné", "VideosTitle": "Videá", @@ -291,42 +306,43 @@ "ViewCollection": "Pozrieť zbierku", "NotEnoughInfo": "Bohužiaľ, o tomto seriáli nie je k dispozícií dostatok informácií!", "AdvancedOptions": "Pokročilé možnosti", - "AutoApproveOptions": "You can configure the request here, once requested it will be send 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 send 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 send 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": "Požiadavku môžete nakonfigurovať tu, po jej vyžiadaní sa odošle do aplikácie DVR a bude automaticky schválená! Upozorňujeme, že je to voliteľné, pre preskočenie stačí stlačiť Žiadosť!", + "AutoApproveOptionsTv": "Požiadavku môžete nakonfigurovať tu, po jej vyžiadaní sa odošle do aplikácie DVR a bude automaticky schválená! Ak je žiadosť už v Sonarr, nezmeníme koreňový priečinok ani profil kvality, ak ho už nastavíte! Upozorňujeme, že je to voliteľné, stačí stlačiť Žiadosť na preskočenie!", + "AutoApproveOptionsTvShort": "Požiadavku môžete nakonfigurovať tu, po jej vyžiadaní bude odoslaná do vašej aplikácie DVR! Ak je požiadavka už v Sonarr, nezmeníme koreňový priečinok ani profil kvality, ak ho už nastavíte! Upozorňujeme, že je to voliteľné, stačí stlačiť Žiadosť na preskočenie!", "QualityProfilesSelect": "Výber profilu kvality", "RootFolderSelect": "Výber koreňového priečinka", - "LanguageProfileSelect": "Select A Language Profile", - "Status": "Status:", + "LanguageProfileSelect": "Vyberte jazykový profil", + "Status": "Stav:", "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": "Nepotvrdený", + "Planned": "Plánované", + "In Production": "V produkcii", + "Post Production": "V post-produkcii", + "Released": "Vydané", + "Running": "Premietané", + "Returning Series": "Vracajúca sa séria", + "Ended": "Ukončené", + "Canceled": "Zrušené" }, - "Seasons": "Seasons:", - "Episodes": "Episodes:", - "Availability": "Availability:", + "Seasons": "Séria:", + "Episodes": "Epizódy:", + "Availability": "Dostupnosť:", "RequestStatus": "Požiadať stav", - "Quality": "Quality:", - "RootFolderOverride": "Root Folder Override:", - "QualityOverride": "Quality Override:", - "Network": "Network:", - "Genres": "Genres:", - "FirstAired": "First Aired:", - "TheatricalRelease": "Release:", - "DigitalRelease": "Digital Release:", - "Votes": "Votes:", - "Runtime": "Runtime:", + "Quality": "Kvalita:", + "RootFolderOverride": "Prepísanie koreňového priečinku:", + "QualityOverride": "Prepísanie kvality:", + "Network": "Sieť:", + "GenresLabel": "Žánre:", + "Genres": "Žánre", + "FirstAired": "Prvýkrát odvysielané:", + "TheatricalRelease": "Vydané:", + "DigitalRelease": "Digitálne vydanie:", + "Votes": "Hlasované:", + "Runtime": "Dĺžka:", "Minutes": "{{runtime}} minút", - "Revenue": "Revenue:", - "Budget": "Budget:", - "Keywords": "Keywords/Tags:", + "Revenue": "Výnos:", + "Budget": "Rozpočet:", + "Keywords": "Kľúčové slová/Tagy:", "Casts": { "CastTitle": "Obsadenie" }, @@ -334,31 +350,32 @@ "AllSeasonsTooltip": "Požiadať všetky sezóny série.", "FirstSeasonTooltip": "Požiadať iba prvú sezónu série.", "LatestSeasonTooltip": "Požiadať iba poslednú sezónu série", - "NoEpisodes": "There unfortunately is no episode data for this show yet!", - "SeasonNumber": "Season {{number}}" + "NoEpisodes": "Pre tento seriál zatiaľ nie sú k dispozícii žiadne údaje o epizódach!", + "SeasonNumber": "Séria {{number}}" }, - "SonarrConfiguration": "Sonarr Configuration", - "RadarrConfiguration": "Radarr Configuration", - "RequestOnBehalf": "Request on behalf of", - "PleaseSelectUser": "Please select a user", - "StreamingOn": "Streaming On:", - "RequestedBy": "Requested By:", - "RequestDate": "Request Date:", - "DeniedReason": "Denied Reason:", - "ReProcessRequest": "Re-Process Request", + "SonarrConfiguration": "Nastavenie Sonarr", + "RadarrConfiguration": "Nastavenie Radarr", + "RequestOnBehalf": "Požiadavka v mene", + "PleaseSelectUser": "Prosím vyberte uživatela", + "StreamingOn": "Streamovanie cez:", + "RequestedBy": "Vyžiadané od:", + "RequestedByOn": "Vyžiadané od {{user}} dňa {{date}}", + "RequestDate": "Dátum požiadavky:", + "DeniedReason": "Dôvod zamietnutia:", + "ReProcessRequest": "Žiadosť o opätovné spracovanie", "Music": { - "Type": "Type:", - "Country": "Country:", - "StartDate": "Start Date:", - "EndDate": "EndDate:" + "Type": "Typ:", + "Country": "Krajina:", + "StartDate": "Dátum začatia:", + "EndDate": "Dátum ukončenia:" } }, "Discovery": { "PopularTab": "Populárne", "TrendingTab": "Trendy", "UpcomingTab": "Čoskoro", - "SeasonalTab": "Seasonal", - "RecentlyRequestedTab": "Recently Requested", + "SeasonalTab": "Sezónne", + "RecentlyRequestedTab": "Nedávno vyžiadané", "Movies": "Filmy", "Combined": "Kombinované", "Tv": "Seriály", @@ -374,35 +391,45 @@ "Writer": "Autor", "ExecProducer": "Výkonný producent" }, - "NoSearch": "Sorry, nothing matches your search!" + "NoSearch": "Ospravedlňujeme sa, nič sa nezhoduje vášmu vyhľadávaniu!" }, "UserPreferences": { "Welcome": "Vitaj {{username}}!", "OmbiLanguage": "Jazyk", "DarkMode": "Tmavý režim", - "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 have 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", - "EmailAddress": "Email Address", - "NewPassword": "New Password", - "NewPasswordConfirm": "New Password Confirm", - "Security": "Security", - "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "Updated": "Úspešne aktualizované", + "StreamingCountry": "Krajina streamovania", + "StreamingCountryDescription": "Toto je kód krajiny, pre ktorú budeme zobrazovať informácie o vysielaní. Ak sa nachádzate v USA, vyberte USA a zobrazia sa vám informácie o streamovaní týkajúce sa USA.", + "LanguageDescription": "Toto je jazyk, v ktorom by ste chceli zobrazovať rozhranie Ombi.", + "MobileQRCode": "Mobilný QR kód", + "LegacyApp": "Spustite Legacy App", + "NoQrCode": "Ak chcete povoliť QR kódy, obráťte sa na svojho administratora", + "UserType": "Typ používateľa:", + "ChangeDetails": "Zmeniť podrobnosti", + "NeedCurrentPassword": "Na vykonanie akýchkoľvek zmien tu potrebujete svoje aktuálne heslo", + "CurrentPassword": "Súčasné heslo", + "EmailAddress": "Emailová adresa", + "NewPassword": "Nové heslo", + "NewPasswordConfirm": "Potvrdenie nového hesla", + "Security": "Zabezpečenie", + "Profile": "Profil", + "UpdatedYourInformation": "Aktualizovať informácie", + "Unsubscribed": "Odhlásené z odberu!" }, "UserTypeLabel": { - "1": "Local User", - "2": "Plex User", - "3": "Emby User", - "4": "Emby Connect User", - "5": "Jellyfin User" + "1": "Miestny použivateľ", + "2": "Používateľ Plex", + "3": "Používateľ Emby", + "4": "Používateľ Emby Connect", + "5": "Používateľ Jellyfin" + }, + "Paginator": { + "itemsPerPageLabel": "Položiek na stránku:", + "nextPageLabel": "Ďalšia stránka", + "previousPageLabel": "Predchádzajúca stránka", + "firstPageLabel": "Prvá stránka", + "lastPageLabel": "Posledná stránka", + "rangePageLabel1": "0 z {{length}}", + "rangePageLabel2": "{{startIndex}} – {{endIndex}} z {{length}}" } } diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index a77e67ee5..8532930be 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -55,6 +55,9 @@ "OfflineParagraph": "Medieservern är för närvarande offline.", "CheckPageForUpdates": "Håll utkik här för uppdateringar på denna sida." }, + "ErrorPages": { + "NotFound": "Page not found" + }, "NavigationBar": { "Discover": "Upptäck", "Search": "Sök", @@ -131,6 +134,9 @@ }, "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" }, "Requests": { @@ -198,6 +204,7 @@ "Approved": "Successfully approved selected items" }, "SuccessfullyApproved": "Successfully Approved", + "SuccessfullyDeleted": "Request successfully deleted", "NowAvailable": "Request is now available", "NowUnavailable": "Request is now unavailable", "SuccessfullyReprocessed": "Successfully Re-processed the request", @@ -219,10 +226,16 @@ "MovieRequestQuotaExceeded": "You have exceeded your Movie request quota!", "TvRequestQuotaExceeded": "You have exceeded your Episode request quota!", "AlbumRequestQuotaExceeded": "You have exceeded your Album request quota!" - } + }, + "Notify": "Avisering", + "RemoveNotification": "Ta bort avisering", + "SuccessfulNotify": "Du kommer nu att aviseras för titel {{title}}", + "SuccessfulUnNotify": "Du kommer inte längre att aviseras för titel {{title}}", + "CouldntNotify": "Kunde inte avisera för {{title}}" }, "Issues": { "Title": "Problem", + "IssuesForTitle": "Issues for {{title}}", "PendingTitle": "Väntande problem", "InProgressTitle": "Pågående problem", "ResolvedTitle": "Lösta problem", @@ -255,6 +268,7 @@ "Delete": "Ta bort problem", "DeletedIssue": "Problemet har tagits bort", "Chat": "Chat", + "EnterYourMessage": "Enter Your Message", "Requested": "Requested", "UserOnDate": "{{user}} on {{date}}" }, @@ -281,6 +295,7 @@ }, "MediaDetails": { "Denied": "Nekad", + "Trailers": "Trailers", "RecommendationsTitle": "Rekommendationer", "SimilarTitle": "Liknande", "VideosTitle": "Videor", @@ -291,9 +306,9 @@ "ViewCollection": "Visa samling", "NotEnoughInfo": "Tyvärr finns det inte tillräckligt med information om denna serie ännu!", "AdvancedOptions": "Avancerade inställningar", - "AutoApproveOptions": "You can configure the request here, once requested it will be send 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 send 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 send 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": "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": "Välj en kvalitetsprofil", "RootFolderSelect": "Välj en rotmapp", "LanguageProfileSelect": "Select A Language Profile", @@ -317,7 +332,8 @@ "RootFolderOverride": "Root Folder Override:", "QualityOverride": "Quality Override:", "Network": "Network:", - "Genres": "Genres:", + "GenresLabel": "Genres:", + "Genres": "Genres", "FirstAired": "First Aired:", "TheatricalRelease": "Release:", "DigitalRelease": "Digital Release:", @@ -343,6 +359,7 @@ "PleaseSelectUser": "Please select a user", "StreamingOn": "Streaming On:", "RequestedBy": "Requested By:", + "RequestedByOn": "Requested By {{user}} on {{date}}", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", "ReProcessRequest": "Re-Process Request", @@ -382,7 +399,7 @@ "DarkMode": "Mörkt läge", "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 have US related streaming information.", + "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", @@ -396,7 +413,8 @@ "NewPasswordConfirm": "New Password Confirm", "Security": "Security", "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "UpdatedYourInformation": "Updated your information", + "Unsubscribed": "Unsubscribed!" }, "UserTypeLabel": { "1": "Local User", @@ -404,5 +422,14 @@ "3": "Emby User", "4": "Emby Connect User", "5": "Jellyfin User" + }, + "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}}" } } diff --git a/src/Ombi/wwwroot/translations/zh.json b/src/Ombi/wwwroot/translations/zh.json index 8e173c559..453ef7418 100644 --- a/src/Ombi/wwwroot/translations/zh.json +++ b/src/Ombi/wwwroot/translations/zh.json @@ -1,408 +1,435 @@ { "Login": { - "SignInButton": "登入", - "UsernamePlaceholder": "帳戶名稱", - "PasswordPlaceholder": "密碼", - "RememberMe": "記住我", - "SignInWith": "Sign in with {{appName}}", - "SignInWithPlex": "Sign in with Plex", - "ForgottenPassword": "忘記密碼了嗎?", + "SignInButton": "登录", + "UsernamePlaceholder": "用户名", + "PasswordPlaceholder": "密码", + "RememberMe": "记住我", + "SignInWith": "使用 {{appName}} 登录", + "SignInWithPlex": "使用 Plex 登录", + "ForgottenPassword": "忘记密码?", "Errors": { - "IncorrectCredentials": "用戶名或密碼錯誤" + "IncorrectCredentials": "用户名或密码错误" } }, "Common": { - "ContinueButton": "繼續", - "Available": "已收錄", - "Approved": "Approved", - "Pending": "Pending", - "PartiallyAvailable": "部分已收錄", - "Monitored": "監控中", - "NotAvailable": "未收錄", - "ProcessingRequest": "處理請求", + "ContinueButton": "继续", + "Available": "可播放", + "Approved": "已批准", + "Pending": "待审核", + "PartiallyAvailable": "部分可播放", + "Monitored": "监控中", + "NotAvailable": "不可播放", + "ProcessingRequest": "处理中请求", "PendingApproval": "等待批准", - "RequestDenied": "請求被拒絕", - "NotRequested": "未申請", - "Requested": "已申請", - "Search": "Search", - "Request": "請求內容", - "Denied": "拒絕", - "Approve": "確認", - "PartlyAvailable": "部分已收錄", - "ViewDetails": "View Details", + "RequestDenied": "请求被拒绝", + "NotRequested": "未请求", + "Requested": "已请求", + "Search": "搜索", + "Request": "请求", + "Denied": "已拒绝", + "Approve": "批准", + "PartlyAvailable": "部分可播放", + "ViewDetails": "查看详情", "Errors": { - "Validation": "請檢查您輸入的數值" + "Validation": "请检查您输入的值" }, - "Cancel": "Cancel", - "Submit": "Submit", - "Update": "Update", - "tvShow": "TV Show", - "movie": "Movie", - "album": "Album" + "Cancel": "取消", + "Submit": "提交", + "Update": "更新", + "tvShow": "电视节目", + "movie": "电影", + "album": "专辑" }, "PasswordReset": { - "EmailAddressPlaceholder": "電郵地址", - "ResetPasswordButton": "重設密碼" + "EmailAddressPlaceholder": "邮箱", + "ResetPasswordButton": "重置密码" }, "LandingPage": { - "OnlineHeading": "上線中", - "OnlineParagraph": "服務器當前離線。", - "PartiallyOnlineHeading": "部分上線", - "PartiallyOnlineParagraph": "媒體服務器部分在綫。", - "MultipleServersUnavailable": "{{totalServers}} 中有{{serversUnavailable}} 個服務器離綫。", - "SingleServerUnavailable": "在{{totalServers}} 服务器中,有{{serversUnavailable}} 服務器離綫。", - "OfflineHeading": "離線中", - "OfflineParagraph": "服務器當前離線。", - "CheckPageForUpdates": "檢查此頁面以獲取持續的站點更新。" + "OnlineHeading": "当前在线", + "OnlineParagraph": "媒体服务器当前在线", + "PartiallyOnlineHeading": "部分在线", + "PartiallyOnlineParagraph": "媒体服务器部分在线", + "MultipleServersUnavailable": "{{totalServers}} 个服务器中有{{serversUnavailable}} 个已离线", + "SingleServerUnavailable": "{{totalServers}} 个服务器中有{{serversUnavailable}} 个已离线", + "OfflineHeading": "当前离线", + "OfflineParagraph": "媒体服务器当前离线", + "CheckPageForUpdates": "检查此页面以获取站点连续更新。" + }, + "ErrorPages": { + "NotFound": "找不到页面" }, "NavigationBar": { - "Discover": "Discover", + "Discover": "发现", "Search": "搜索", - "Requests": "請求", - "UserManagement": "Users", - "Issues": "問題", + "Requests": "请求", + "UserManagement": "用户", + "Issues": "问题", "Vote": "投票", - "Donate": "贊助", - "DonateLibraryMaintainer": "捐贈給維護者", - "DonateTooltip": "This is how I convince my wife to let me spend my spare time developing Ombi 😁", - "UpdateAvailableTooltip": "有可用的更新", - "Settings": "設定", - "Welcome": "歡迎您 {{username}}", - "UpdateDetails": "更新詳細資料", + "Donate": "捐助", + "DonateLibraryMaintainer": "捐赠给维护者", + "DonateTooltip": "这是我说服我妻子让我继续利用业余时间开发Ombi的途径 😁", + "UpdateAvailableTooltip": "有可用更新!", + "Settings": "设置", + "Welcome": "{{username}},欢迎您", + "UpdateDetails": "更新详请", "Logout": "登出", - "OpenMobileApp": "打開手機應用程序", - "RecentlyAdded": "最近新增", - "ChangeTheme": "Change Theme", - "Calendar": "Calendar", - "UserPreferences": "Preferences", - "FeatureSuggestion": "Features", - "FeatureSuggestionTooltip": "Have a great new idea? Suggest it here!", + "OpenMobileApp": "打开移动应用", + "RecentlyAdded": "最近添加", + "ChangeTheme": "更改主题", + "Calendar": "日历", + "UserPreferences": "偏好设置", + "FeatureSuggestion": "功能", + "FeatureSuggestionTooltip": "有一个很棒的新想法吗?在这里建议!", "Filter": { - "Movies": "Movies", - "TvShows": "TV Shows", - "Music": "Music", - "People": "People" + "Movies": "电影", + "TvShows": "电视节目", + "Music": "音乐", + "People": "人员" }, - "MorningWelcome": "Good morning!", - "AfternoonWelcome": "Good afternoon!", - "EveningWelcome": "Good evening!" + "MorningWelcome": "早上好!", + "AfternoonWelcome": "下午好!", + "EveningWelcome": "晚上好!" }, "Search": { "Title": "搜索", - "Paragraph": "想觀看目前無法觀看的內容嗎?沒問題,只需在下面搜索並請求它!", - "MoviesTab": "電影", - "TvTab": "電視節目", - "MusicTab": "音樂", - "AdvancedSearch": "You can fill in any of the below to discover new media. All of the results are sorted by popularity", - "AdvancedSearchHeader": "Advanced Search", - "Suggestions": "建議", - "NoResults": "抱歉,我們沒有找到任何結果!", - "DigitalDate": "數字發行:{{date}}", - "TheatricalRelease": "劇場版:{{date}}", - "ViewOnPlex": "Play On Plex", - "ViewOnEmby": "Play On Emby", - "ViewOnJellyfin": "Play On Jellyfin", - "RequestAdded": "已成功添加對 {{title}} 的請求", + "Paragraph": "想要观看当前无法观看的内容吗?没问题,只需在下方搜索并请求!", + "MoviesTab": "电影", + "TvTab": "电视节目", + "MusicTab": "音乐", + "AdvancedSearch": "您可以填写以下任何一项来发现新媒体,所有结果都按受欢迎程度排序。", + "AdvancedSearchHeader": "高级搜索", + "Suggestions": "建议", + "NoResults": "对不起,我们没有找到任何结果。", + "DigitalDate": "数字版发行:{{date}}", + "TheatricalRelease": "剧场版发行:{{date}}", + "ViewOnPlex": "在Plex上播放", + "ViewOnEmby": "在 Emby上播放", + "ViewOnJellyfin": "在 Jellyfin上播放", + "RequestAdded": "{{title}} 的请求已成功添加", "Similar": "相似", - "Refine": "精簡", - "SearchBarPlaceholder": "在這裡輸入以搜尋", + "Refine": "精选", + "SearchBarPlaceholder": "在这里输入搜索内容", "Movies": { - "PopularMovies": "熱門電影", - "UpcomingMovies": "即將上映的電影", - "TopRatedMovies": "评级最高的电影", - "NowPlayingMovies": "正在上映的電影", - "HomePage": "首頁", - "Trailer": "預告片" + "PopularMovies": "热门电影", + "UpcomingMovies": "即将上映的电影", + "TopRatedMovies": "高评分的电影", + "NowPlayingMovies": "正在播放的电影", + "HomePage": "主页", + "Trailer": "预告片" }, "TvShows": { - "Popular": "流行的", - "Trending": "趨勢", - "MostWatched": "最受矚目", + "Popular": "热门", + "Trending": "趋势", + "MostWatched": "观看次数最多", "MostAnticipated": "最期待", - "Results": "結果", + "Results": "结果", "AirDate": "播出日期:", - "AllSeasons": "全季", + "AllSeasons": "所有季", "FirstSeason": "第一季", - "LatestSeason": "最新一季", - "Select": "選擇...", - "SubmitRequest": "送出請求", - "Season": "Season {{seasonNumber}}", - "SelectAllInSeason": "選擇第 {{seasonNumber}} 季中的全部" + "LatestSeason": "最新季", + "Select": "请选择...", + "SubmitRequest": "提交请求", + "Season": "第{{seasonNumber}} 季", + "SelectAllInSeason": "在所有季中选择第{{seasonNumber}} 季" }, - "AdvancedSearchInstructions": "Please choose what type of media you are searching for:", - "YearOfRelease": "Year of Release", - "KeywordSearchingDisclaimer": "Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb" + "AdvancedSearchInstructions": "请选择您需要搜索的媒体类型:", + "YearOfRelease": "发行年份", + "SearchGenre": "按类型搜索", + "SearchKeyword": "按关键字搜索", + "SearchProvider": "按提供者搜索", + "KeywordSearchingDisclaimer": "请注意,由于TheMovieDb中的数据不一致,关键字搜索方式非常容易出错" }, "Requests": { - "Title": "請求", - "Paragraph": "您可以在下方看到您的請求和所有其他請求,以及它們的下載和批准狀態。", - "MoviesTab": "電影", - "ArtistName": "Artist", - "AlbumName": "Album Name", - "TvTab": "電視節目", - "MusicTab": "音樂", - "RequestedBy": "Requested By", - "Status": "Status", - "RequestStatus": "Request status", - "Denied": " 拒絕:", - "TheatricalRelease": "劇場版:{{date}}", - "ReleaseDate": "發佈日期:{{date}}", - "TheatricalReleaseSort": "劇場版", - "DigitalRelease": "數字發行:{{date}}", - "RequestDate": "Request Date", - "QualityOverride": "質量覆蓋:", - "RootFolderOverride": "根文件夾覆蓋:", - "ChangeRootFolder": "根文件夹", - "ChangeQualityProfile": "質量設置", - "MarkUnavailable": "標記未收錄", - "MarkAvailable": "標記已收錄", - "Remove": "移除", - "Deny": "拒絕", - "DenyReason": "Deny Reason", - "DeniedReason": "Denied Reason", - "Season": "Season", - "GridTitle": "標題", - "AirDate": "Air Date", - "GridStatus": "狀態", - "ReportIssue": "回報問題", - "Filter": "篩選", - "Sort": "分類", - "SeasonNumberHeading": "季: {seasonNumber}", - "SortTitleAsc": "標題 ▲", - "SortTitleDesc": "標題 ▼", + "Title": "请求", + "Paragraph": "您可以在下方看到您的和所有其他人的请求,以及他们的下载和批准状态", + "MoviesTab": "电影", + "ArtistName": "艺术家", + "AlbumName": "专辑名称", + "TvTab": "电视节目", + "MusicTab": "音乐", + "RequestedBy": "请求者", + "Status": "发行状态", + "RequestStatus": "申请状态", + "Denied": "已拒绝:", + "TheatricalRelease": "剧场版发行:{{date}}", + "ReleaseDate": "已发行: {{date}}", + "TheatricalReleaseSort": "剧场版发行", + "DigitalRelease": "数字版发行:{{date}}", + "RequestDate": "请求日期", + "QualityOverride": "质量覆盖:", + "RootFolderOverride": "根目录覆盖:", + "ChangeRootFolder": "根目录", + "ChangeQualityProfile": "画质配置文件", + "MarkUnavailable": "标记为不可用", + "MarkAvailable": "标记为可用", + "Remove": "删除", + "Deny": "拒绝", + "DenyReason": "拒绝原因", + "DeniedReason": "被拒绝原因", + "Season": "季", + "GridTitle": "标题", + "AirDate": "播出日期:", + "GridStatus": "状态", + "ReportIssue": "反馈问题", + "Filter": "筛选", + "Sort": "排序", + "SeasonNumberHeading": "第{seasonNumber} 季", + "SortTitleAsc": "标题 ▲", + "SortTitleDesc": "标题 ▼", "SortRequestDateAsc": "请求日期 ▲", "SortRequestDateDesc": "请求日期 ▼", - "SortStatusAsc": "狀態 ▲", - "SortStatusDesc": "狀態 ▼", + "SortStatusAsc": "状态 ▲", + "SortStatusDesc": "状态 ▼", "Remaining": { - "Quota": "{{remaining}}/{{total}} 個剩餘請求", - "NextDays": "將在 {{time}} 天內添加另一個請求", - "NextHours": "將在 {{time}} 小時內添加另一個請求", - "NextMinutes": "將在 {{time}} 分鐘內添加另一個請求", - "NextMinute": "將在 {{time}} 分鐘內添加另一個請求" + "Quota": "{{remaining}}/{{total}} 个请求剩余", + "NextDays": "将在{{time}} 天内添加另一个请求", + "NextHours": "将在{{time}} 小时后添加另一个请求", + "NextMinutes": "将在{{time}} 分钟内添加另一个请求", + "NextMinute": "将在{{time}} 分钟内添加另一个请求" }, - "AllRequests": "All Requests", - "PendingRequests": "Pending Requests", - "ProcessingRequests": "Processing Requests", - "AvailableRequests": "Available Requests", - "DeniedRequests": "Denied Requests", - "RequestsToDisplay": "Requests to display", - "RequestsTitle": "Title", - "Details": "Details", - "Options": "Options", + "AllRequests": "所有请求", + "PendingRequests": "待处理请求", + "ProcessingRequests": "处理中请求", + "AvailableRequests": "可用请求", + "DeniedRequests": "已拒绝请求", + "RequestsToDisplay": "显示请求", + "RequestsTitle": "标题", + "Details": "详情", + "Options": "选项", "RequestPanel": { - "Delete": "Delete Request", - "Approve": "Approve Request", - "ChangeAvailability": "Mark Available", - "Deleted": "Successfully deleted selected items", - "Approved": "Successfully approved selected items" + "Delete": "删除请求", + "Approve": "批准请求", + "ChangeAvailability": "标记为可用", + "Deleted": "所选项目已删除", + "Approved": "所选项目已批准" }, - "SuccessfullyApproved": "Successfully Approved", - "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!", - "RequestAddedSuccessfully": "Request for {{title}} has been added successfully", + "SuccessfullyApproved": "批准成功", + "SuccessfullyDeleted": "删除请求成功", + "NowAvailable": "请求现在可观看", + "NowUnavailable": "请求现在不可观看", + "SuccessfullyReprocessed": "重新处理请求成功", + "DeniedRequest": "已拒绝请求", + "RequestCollection": "请求合集", + "CollectionSuccesfullyAdded": "合集 {{name}} 已成功添加!", + "NeedToSelectEpisodes": "您需要选择集数!", + "RequestAddedSuccessfully": "{{title}} 的请求已成功添加", "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": "请求已经存在", + "EpisodesAlreadyRequested": "已存在这个系列的剧集请求", + "NoPermissionsOnBehalf": "您没有代表用户请求的正确权限!", + "NoPermissions": "您没有正确的权限!", + "RequestDoesNotExist": "请求不存在", + "ChildRequestDoesNotExist": "子请求不存在", + "NoPermissionsRequestMovie": "您没有请求电影的权限", + "NoPermissionsRequestTV": "您没有请求电视节目的权限", + "NoPermissionsRequestAlbum": "您没有请求专辑的权限", + "MovieRequestQuotaExceeded": "您的电影请求已超出配额!", + "TvRequestQuotaExceeded": "您的剧集请求已超出配额!", + "AlbumRequestQuotaExceeded": "您的专辑请求已超出配额!" + }, + "Notify": "通知", + "RemoveNotification": "删除通知", + "SuccessfulNotify": "您将收到标题为 {{title}} 的通知", + "SuccessfulUnNotify": "您将不再收到标题为 {{title}} 的通知", + "CouldntNotify": "无法通知标题 {{title}}" }, "Issues": { - "Title": "問題", - "PendingTitle": "未決問題", - "InProgressTitle": "進行中的問題", - "ResolvedTitle": "已解決的問題", - "ColumnTitle": "標題", - "Count": "Count", - "Category": "分類", - "Status": "狀態", - "Details": "詳細資料", - "Description": "描述信息", - "NoComments": "暫無評論!", - "MarkInProgress": "標記為進行中", - "MarkResolved": "標記為已解決", - "SendMessageButton": "發送", - "Subject": "主題", - "Comments": "評論", - "WriteMessagePlaceholder": "在這裡撰寫訊息...", - "ReportedBy": "報告者:", + "Title": "问题", + "IssuesForTitle": "{{title}} 的问题", + "PendingTitle": "待处理的问题", + "InProgressTitle": "正在处理的问题", + "ResolvedTitle": "已解决的问题", + "ColumnTitle": "标题", + "Count": "总计", + "Category": "类别", + "Status": "状态", + "Details": "详情", + "Description": "描述", + "NoComments": "暂无评论", + "MarkInProgress": "标记为进行中", + "MarkResolved": "标记为已解决", + "SendMessageButton": "发送", + "Subject": "主题", + "Comments": "评论", + "WriteMessagePlaceholder": "在这里写下您的留言", + "ReportedBy": "报告人", "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": "反馈问题", + "DescriptionPlaceholder": "请描述您的问题", + "TitlePlaceholder": "简短标题", + "SelectCategory": "选择分类", + "IssueCreated": "问题已创建" }, - "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", - "Requested": "Requested", - "UserOnDate": "{{user}} on {{date}}" + "Outstanding": "未解决的问题", + "ResolvedDate": "解决日期", + "CreatedDate": "提出日期", + "MarkedAsResolved": "此问题已被标记为已解决!", + "MarkedAsInProgress": "此问题已被标记为正在处理中!", + "Delete": "删除问题", + "DeletedIssue": "问题已被删除", + "Chat": "聊天", + "EnterYourMessage": "输入您的消息", + "Requested": "已请求", + "UserOnDate": "{{user}} 在 {{date}}" }, "Filter": { - "ClearFilter": "清除篩選條件", + "ClearFilter": "清除筛选", "FilterHeaderAvailability": "可用性", - "FilterHeaderRequestStatus": "狀態", - "Approved": "已通過審核", - "PendingApproval": "待決審核", - "WatchProviders": "Watch Providers", - "Keywords": "Keywords" + "FilterHeaderRequestStatus": "状态", + "Approved": "已批准", + "PendingApproval": "待批准", + "WatchProviders": "观看提供商", + "Keywords": "关键字" }, "UserManagment": { - "TvRemaining": "電視:{{remaining}}/{{total}} 剩餘", - "MovieRemaining": "電影:{{remaining}}/{{total}} 剩餘", - "MusicRemaining": "音樂:{{remaining}}/{{total}} 剩餘", - "TvDue": "電視:{{date}}", - "MovieDue": "電影:{{date}}", - "MusicDue": "音樂:{{date}}" + "TvRemaining": "电视节目:{{remaining}}/{{total}} 剩余", + "MovieRemaining": "电影: {{remaining}}/{{total}} 剩余", + "MusicRemaining": "音乐: {{remaining}}/{{total}} 剩余", + "TvDue": "电视节目:{{date}}", + "MovieDue": "电影: {{date}}", + "MusicDue": "音乐: {{date}}" }, "Votes": { "CompletedVotesTab": "已投票", "VotesTab": "需要投票" }, "MediaDetails": { - "Denied": "Denied", - "RecommendationsTitle": "Recommendations", - "SimilarTitle": "Similar", - "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 send 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 send 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 send 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", - "Status": "Status:", + "Denied": "已拒绝", + "Trailers": "预告片", + "RecommendationsTitle": "推荐", + "SimilarTitle": "相似", + "VideosTitle": "视频", + "AlbumsTitle": "专辑", + "RequestAllAlbums": "请求所有专辑", + "ClearSelection": "清除已选择", + "RequestSelectedAlbums": "请求选中的专辑", + "ViewCollection": "查看合集", + "NotEnoughInfo": "很遗憾,这个节目还没有足够的信息!", + "AdvancedOptions": "高级选项", + "AutoApproveOptions": "您可以在这里配置请求,一旦请求它将被发送到您的DVR应用程序并将被自动批准! 请注意,这是可选的,只需按请求跳过!", + "AutoApproveOptionsTv": "您可以在这里配置请求,一旦请求它将被发送到您的DVR应用程序并将被自动批准! 如果请求已经在 Sonarr 中并且您设置了它,我们将不会更改根目录或画质配置文件! 请注意,这是可选的,只需按请求跳过!", + "AutoApproveOptionsTvShort": "您可以在这里配置请求,一旦请求它将被发送到您的DVR应用程序! 如果请求已经在 Sonarr 中并且您设置了它,我们将不会更改根目录或画质配置文件! 请注意,这是可选的,只需按请求跳过!", + "QualityProfilesSelect": "请选择画质配置文件", + "RootFolderSelect": "请选择根目录", + "LanguageProfileSelect": "请选择语言配置文件", + "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": "有想法", + "Planned": "计划中", + "In Production": "制作中", + "Post Production": "后期处理", + "Released": "已发布", + "Running": "运行中", + "Returning Series": "回归系列", + "Ended": "已结束", + "Canceled": "已取消" }, - "Seasons": "Seasons:", - "Episodes": "Episodes:", - "Availability": "Availability:", - "RequestStatus": "Request Status", - "Quality": "Quality:", - "RootFolderOverride": "Root Folder Override:", - "QualityOverride": "Quality Override:", - "Network": "Network:", - "Genres": "Genres:", - "FirstAired": "First Aired:", - "TheatricalRelease": "Release:", - "DigitalRelease": "Digital Release:", - "Votes": "Votes:", - "Runtime": "Runtime:", - "Minutes": "{{runtime}} Minutes", - "Revenue": "Revenue:", - "Budget": "Budget:", - "Keywords": "Keywords/Tags:", + "Seasons": "季:", + "Episodes": "集:", + "Availability": "可用性:", + "RequestStatus": "请求状态", + "Quality": "画质:", + "RootFolderOverride": "根目录覆盖:", + "QualityOverride": "质量覆盖:", + "Network": "网络:", + "GenresLabel": "流派", + "Genres": "流派", + "FirstAired": "首播", + "TheatricalRelease": "发行日:", + "DigitalRelease": "数字版发行:", + "Votes": "投票:", + "Runtime": "运行时间:", + "Minutes": "{{runtime}} 分钟", + "Revenue": "收入:", + "Budget": "预算:", + "Keywords": "关键词/标签:", "Casts": { - "CastTitle": "Cast" + "CastTitle": "演员" }, "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!", - "SeasonNumber": "Season {{number}}" + "AllSeasonsTooltip": "请求这个节目的每一季", + "FirstSeasonTooltip": "请求这个节目的第一季", + "LatestSeasonTooltip": "只请求这个节目的最新季", + "NoEpisodes": "很遗憾,目前还没有该节目的剧集数据!", + "SeasonNumber": "第{{number}} 季" }, - "SonarrConfiguration": "Sonarr Configuration", - "RadarrConfiguration": "Radarr Configuration", - "RequestOnBehalf": "Request on behalf of", - "PleaseSelectUser": "Please select a user", - "StreamingOn": "Streaming On:", - "RequestedBy": "Requested By:", - "RequestDate": "Request Date:", - "DeniedReason": "Denied Reason:", - "ReProcessRequest": "Re-Process Request", + "SonarrConfiguration": "Sonarr 配置", + "RadarrConfiguration": "Radarr 配置", + "RequestOnBehalf": "代表请求", + "PleaseSelectUser": "请选择用户", + "StreamingOn": "流媒体:", + "RequestedBy": "请求者:", + "RequestedByOn": "由 {{user}} 在 {{date}} 时请求", + "RequestDate": "请求日期:", + "DeniedReason": "被拒绝原因:", + "ReProcessRequest": "重新处理请求", "Music": { - "Type": "Type:", - "Country": "Country:", - "StartDate": "Start Date:", - "EndDate": "EndDate:" + "Type": "类型:", + "Country": "国家:", + "StartDate": "开始日期:", + "EndDate": "结束日期:" } }, "Discovery": { - "PopularTab": "Popular", - "TrendingTab": "Trending", - "UpcomingTab": "Upcoming", - "SeasonalTab": "Seasonal", - "RecentlyRequestedTab": "Recently Requested", - "Movies": "Movies", - "Combined": "Combined", - "Tv": "TV", + "PopularTab": "热门", + "TrendingTab": "趋势", + "UpcomingTab": "即将上映", + "SeasonalTab": "按季的", + "RecentlyRequestedTab": "最近请求", + "Movies": "电影", + "Combined": "混合", + "Tv": "电视节目", "CardDetails": { - "Availability": "Availability", - "Studio": "Studio", - "Network": "Network", - "UnknownNetwork": "Unknown", - "RequestStatus": "Request Status", - "Director": "Director", - "InCinemas": "In Cinemas", - "FirstAired": "First Aired", - "Writer": "Writer", - "ExecProducer": "Exec Producer" + "Availability": "可用性", + "Studio": "工作室", + "Network": "网络", + "UnknownNetwork": "未知", + "RequestStatus": "请求状态", + "Director": "导演", + "InCinemas": "影院放映", + "FirstAired": "首播", + "Writer": "编剧", + "ExecProducer": "制作发行商" }, - "NoSearch": "Sorry, nothing matches your search!" + "NoSearch": "抱歉,没有符合您搜索条件的内容!" }, "UserPreferences": { - "Welcome": "Welcome {{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 have 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", - "EmailAddress": "Email Address", - "NewPassword": "New Password", - "NewPasswordConfirm": "New Password Confirm", - "Security": "Security", - "Profile": "Profile", - "UpdatedYourInformation": "Updated your information" + "Welcome": "{{username}},欢迎您!", + "OmbiLanguage": "语言", + "DarkMode": "深色模式", + "Updated": "更新成功", + "StreamingCountry": "流媒体国家", + "StreamingCountryDescription": "这是我们将显示流媒体信息的国家代码。 如果您在美国,请选择美国并获取美国相关的流媒体信息。", + "LanguageDescription": "您希望Ombi界面显示的语言", + "MobileQRCode": "手机二维码", + "LegacyApp": "启动旧版应用程序", + "NoQrCode": "请联系管理员以启用二维码", + "UserType": "用户类型:", + "ChangeDetails": "变更详情", + "NeedCurrentPassword": "您需要输入当前密码才能进行更改", + "CurrentPassword": "当前密码", + "EmailAddress": "邮箱地址", + "NewPassword": "新密码", + "NewPasswordConfirm": "确认新密码", + "Security": "安全选项", + "Profile": "用户信息", + "UpdatedYourInformation": "您的用户信息已更新", + "Unsubscribed": "取消订阅!" }, "UserTypeLabel": { - "1": "Local User", - "2": "Plex User", - "3": "Emby User", - "4": "Emby Connect User", - "5": "Jellyfin User" + "1": "本地用户", + "2": "Plex用户", + "3": "Emby用户", + "4": "Emby Connect用户", + "5": "Jellyfin用户" + }, + "Paginator": { + "itemsPerPageLabel": "每页显示项目数:", + "nextPageLabel": "下一页", + "previousPageLabel": "上一页", + "firstPageLabel": "转到第一页", + "lastPageLabel": "转到最后一页", + "rangePageLabel1": "{{length}} 中的第0项", + "rangePageLabel2": "{{length}} 中的第{{startIndex}} 到{{endIndex}} 项" } } diff --git a/tests/cypress/tests/requests/requests.spec.ts b/tests/cypress/tests/requests/requests.spec.ts index 23c5ccb09..fc1a7489c 100644 --- a/tests/cypress/tests/requests/requests.spec.ts +++ b/tests/cypress/tests/requests/requests.spec.ts @@ -39,7 +39,7 @@ describe("Requests Tests", () => { row.optionsDelete.click(); cy.wait('@deleteRequest').then((intercept) => { - expect(intercept.response.body).is.true; + expect(intercept.response.body.result).is.true; }) row.title.should('not.exist'); diff --git a/tests/cypress/tests/search/search.spec.ts b/tests/cypress/tests/search/search.spec.ts index 6bcef1712..a8aa3deb5 100644 --- a/tests/cypress/tests/search/search.spec.ts +++ b/tests/cypress/tests/search/search.spec.ts @@ -101,7 +101,7 @@ describe("Search Tests", () => { it("No Movie results, enabling Tv filter we get results", () => { Page.navbar.searchFilter.applyFilter(false, true, false); - Page.visit("It's always sunny in Philadelphia"); + Page.visit("Dexter New Blood"); cy.wait('@searchResponse'); Page.noSearchResultMessage.should('exist'); @@ -110,11 +110,11 @@ describe("Search Tests", () => { Page.navbar.searchFilter.tvToggle.click(); cy.wait('@searchResponse'); - const card = Page.getCard('2710', false); + const card = Page.getCard('131927', false); card.topLevelCard.realHover(); - card.title.should('have.text', "It's Always Sunny in Philadelphia"); - card.overview.contains('Irish pub'); + card.title.should('have.text', "Dexter: New Blood"); + card.overview.contains('Iron Lake'); card.requestType.contains('TV Show'); card.requestButton.should('exist'); }); diff --git a/version.json b/version.json index 3f41f3df2..069458b57 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.3.3" + "version": "4.10.1" } \ No newline at end of file