From bb781f2787824514f50b02dee589e79ce787e66f Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 5 Aug 2022 20:19:00 +0100 Subject: [PATCH] develop to master (#4718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove dead code * Localize TV requests messages on TV details page * Transform buttons with link into anchors * Sonarr sync: stop using seasonpass API * chore(release): :rocket: v4.16.13 * Fix requests when 4k available and 4k disabled Fixes #4610 * chore(release): :rocket: v4.16.14 * Hide subscribe button when request is available * Hide subscribe button when request is denied * Add Title to Partially Available Message If the Title of the show is not menitoned it can be unclear what Episodes are now available. * Better error message when test email fails due to missing recipient * feat(discover): Add original language filter * chore(release): :rocket: v4.16.15 * fix(4616): :bug: fixed mandatory fields * chore: :busts_in_silhouette: Updated Contributors [skip ci] * chore(release): :rocket: v4.16.16 * added test results into the PR pipeline * chore(release): :rocket: v4.16.17 * Add information about cache refresh * Update pr.yml [skip ci] * Update pr.yml [skip ci] * Update pr.yml [skip ci] * chore(release): :rocket: v4.17.0 * feat(discover): Add new trending source experimental feature * fix(settings): Allow toggling features when there are more than one * fix(discover): Fix new trending feature detection * fix(discover): Fix cache mix up * refactor(discover): Move movie trending feature toggle to backend * feat(discover): Default trending source to new logic * chore(release): :rocket: v4.18.0 * feat(sync): Detect reidentified movies in Emby and Jellyfin * feat(sync): Detect reidentified series in Emby and Jellyfin * Fix sync log criticity * Update pr.yml [skip ci] * Update label.yml [skip ci] * Fix formatting * Update pr.yml [skip ci] * Update label.yml [skip ci] * chore: :busts_in_silhouette: Updated Contributors [skip ci] * chore(release): :rocket: v4.19.0 * refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series * refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie * chore(release): :rocket: v4.19.1 * feat(discover): Show more relevant shows in upcoming TV * chore(release): :rocket: v4.20.0 * fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes * perf(sync): Emby+Jellyfin - use a more reliable filter to missing items * fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci] * fix: added media type tag to media type text (#4638) [skip ci] * fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648) [skip ci] * fix: Missing Poster broken link fix (#4637) [skip ci] * 🌐 Translations Update (#4622) [skip ci] * Update launch.json (#4650) [skip ci] * fix: Improve Swagger documentation (#4652) * Upgrade Swashbuckle dependency * Document /token response * Add support for Newtonsoft annotations in Swagger * Remove unecessary ActionResult [skip ci] * fix(API): Fix pagination in some edge cases (#4649) [skip ci] * 🌐 Translations Update (#4655) * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633) * fixed touch not working on carousels * fixed touch not working * Movie details component fixes Fixed recommendations and similar not changing the data on the component by calling the init function again on param change Moved the ngif results > 0 to the mat-expansion panel to avoid rendering the entire element if it doesn't have any results instead of having an empty panel. * removed unused line, added scroll to top on init * updated recommendation refresh implementation Changed the implementation to use the router instead in order to reload the component instead of just reloading the data. This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page. * chore: :busts_in_silhouette: Updated Contributors [skip ci] * chore(release): :rocket: v4.20.1 [skip ci] * fix: :bug: Fixed the Request on Behalf of having blanks (#4667) * chore(release): :rocket: v4.20.2 [skip ci] * fix(plex): 🐛 Fixed an issue with the Plex Sync * chore(release): :rocket: v4.20.3 [skip ci] * fix (technical): Improved some of the date time parsing handling * fix: fixed build * chore(release): :rocket: v4.20.4 [skip ci] * feat: Upgrade to Angular14 (#4668) * refactor: :fire: removed angular-bootstrap-md dependancy * chore: update tsconfig * yeah * ng14 upgrade * refactor: migration changes * fix: fixed CLI * test: Fixed automation * chore: :busts_in_silhouette: Updated Contributors [skip ci] * perf: stop populating obsolete subscribe fields (#4625) * chore(release): :rocket: v4.21.0 [skip ci] * fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685 * chore(release): :rocket: v4.21.1 [skip ci] * 🌐 Translations Update (#4683) * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix: Landing and Login page improvements (#4690) * chore(release): :rocket: v4.21.2 [skip ci] * feat(discover): ✨ Added infinite scroll on advanced search results * feat(discover): :sparkles: Added infinite scroll on advanced search results * chore(release): :rocket: v4.22.0 [skip ci] * 🌐 Translations Update (#4694) * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(discover): :bug: Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699) * chore(release): :rocket: v4.22.1 [skip ci] * fix: fixed an issue where I broke images for some users * chore(release): :rocket: v4.22.2 [skip ci] * ci(Mergify): configuration update (#4701) Signed-off-by: Jamie [skip ci] * fix: Override Sonarr V3 Profiles endpoint (#4678) * Override Sonarr V3 Profiles endpoint [skip ci] * fix(4K) :4K request fixes (#4702) * GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status. * Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled. * Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001). * Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K requests. * chore: :busts_in_silhouette: Updated Contributors [skip ci] * chore(release): :rocket: v4.22.3 [skip ci] * chore: Storybook (#4700) [skip ci] * chore: Translations [skip ci] * 🌐 Translations Update (#4704) * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] * fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713) * build: Run automation tests in docker (#4715) [skip ci] * fix: fixed trakt image not loading when base url present (#4711) [skip ci] * fix: :bug: Fixed missing externals (#4712) * chore: :busts_in_silhouette: Updated Contributors [skip ci] * chore(release): :rocket: v4.22.4 [skip ci] * test: fixed automationt tests [skip ci] Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com> Co-authored-by: Conventional Changelog Action Co-authored-by: Teifun2 Co-authored-by: contrib-readme-bot Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com> Co-authored-by: echel0n Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com> Co-authored-by: Igor Borges Co-authored-by: Lucane Co-authored-by: mkgeeky --- .github/workflows/chromatic.yml | 47 + .github/workflows/cypress.yml | 40 +- .github/workflows/label.yml | 2 + .github/workflows/pr.yml | 8 +- .gitignore | 1 + .mergify.yml | 7 + CHANGELOG.md | 301 +- README.md | 130 +- src/.dockerignore | 285 + src/Ombi.Api.Emby/EmbyApi.cs | 8 +- src/Ombi.Api.Jellyfin/JellyfinApi.cs | 6 +- src/Ombi.Api.Plex/Models/Metadata.cs | 1 + .../Models/SickRageEpisodeStatus.cs | 2 +- src/Ombi.Api.Sonarr/SonarrApi.cs | 2 +- src/Ombi.Api.Sonarr/SonarrV3Api.cs | 9 + src/Ombi.Core/Engine/MovieRequestEngine.cs | 12 +- src/Ombi.Core/Engine/MovieSearchEngine.cs | 28 +- src/Ombi.Core/Engine/MusicRequestEngine.cs | 5 +- src/Ombi.Core/Engine/TvRequestEngine.cs | 5 +- src/Ombi.Core/Engine/TvSearchEngine.cs | 16 +- src/Ombi.Core/Engine/V2/IMultiSearchEngine.cs | 2 + .../Engine/V2/MovieSearchEngineV2.cs | 64 +- src/Ombi.Core/Engine/V2/MultiSearchEngine.cs | 5 + src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 30 +- src/Ombi.Core/Helpers/TvShowRequestBuilder.cs | 11 +- .../Helpers/TvShowRequestBuilderV2.cs | 12 +- .../Models/Search/SearchViewModel.cs | 1 + src/Ombi.Core/Senders/MusicSender.cs | 2 +- src/Ombi.Core/Senders/TvSender.cs | 79 +- .../PagnationHelperTests.cs | 2 + src/Ombi.Helpers/PaginationHelper.cs | 2 +- src/Ombi.I18n/Resources/Texts.de.resx | 6 +- src/Ombi.I18n/Resources/Texts.pl.resx | 14 +- .../Jobs/Emby/EmbyAvaliabilityChecker.cs | 11 +- .../Jobs/Emby/EmbyContentSync.cs | 55 +- .../Jobs/Emby/EmbyEpisodeSync.cs | 36 +- .../Jellyfin/JellyfinAvaliabilityChecker.cs | 11 +- .../Jobs/Jellyfin/JellyfinContentSync.cs | 56 +- .../Jobs/Jellyfin/JellyfinEpisodeSync.cs | 35 +- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 45 +- .../Models/External/TheMovieDbSettings.cs | 2 + .../Settings/Models/FeatureSettings.cs | 1 + src/Ombi.Store/Context/OmbiContext.cs | 2 +- .../Repository/EmbyContentRepository.cs | 7 + .../IMediaServerContentRepository.cs | 1 + .../Repository/JellyfinContentRepository.cs | 7 + .../Repository/MediaServerRepository.cs | 1 + .../Repository/PlexContentRepository.cs | 7 + src/Ombi.TheMovieDbApi/IMovieDbApi.cs | 7 +- src/Ombi.TheMovieDbApi/Models/Language.cs | 14 + src/Ombi.TheMovieDbApi/TheMovieDbApi.cs | 81 +- src/Ombi/.vscode/launch.json | 4 +- src/Ombi/ClientApp/.storybook/main.js | 19 + .../ClientApp/.storybook/preview-body.html | 9 + src/Ombi/ClientApp/.storybook/preview.js | 14 + src/Ombi/ClientApp/.storybook/tsconfig.json | 24 + src/Ombi/ClientApp/.storybook/typings.d.ts | 4 + src/Ombi/ClientApp/angular.json | 5 +- src/Ombi/ClientApp/documentation.json | 18 + src/Ombi/ClientApp/package.json | 65 +- src/Ombi/ClientApp/src/app/app.module.ts | 9 +- .../image-background.component.html | 6 + .../image-background.component.scss | 26 + .../image-background.component.ts | 43 + .../app/components/image/image.component.html | 1 + .../image/image.component.stories.ts | 73 + .../app/components/image/image.component.ts | 57 + .../ClientApp/src/app/components/index.ts | 2 + .../ClientApp/src/app/components/modules.ts | 3 + .../app/custompage/custompage.component.ts | 6 +- .../card/discover-card.component.html | 7 +- .../card/discover-card.component.scss | 2 +- .../card/discover-card.component.ts | 13 + .../carousel-list/carousel-list.component.ts | 1 + .../search-results.component.html | 8 +- .../search-results.component.ts | 28 +- .../src/app/discover/discover.module.ts | 2 + .../ClientApp/src/app/interfaces/IImages.ts | 4 + .../ClientApp/src/app/interfaces/IMovieDb.ts | 5 + .../ClientApp/src/app/interfaces/ISettings.ts | 3 +- .../src/app/issues/issuestable.component.html | 2 +- .../landingpage/landingpage.component.html | 4 +- .../landingpage/landingpage.component.scss | 9 - .../app/landingpage/landingpage.component.ts | 36 +- .../src/app/login/login.component.html | 5 +- .../src/app/login/login.component.scss | 17 - .../src/app/login/login.component.ts | 37 +- .../app/login/resetpassword.component.html | 5 +- .../src/app/login/resetpassword.component.ts | 21 +- .../login/tokenresetpassword.component.html | 20 +- .../app/login/tokenresetpassword.component.ts | 18 +- .../movie/movie-details.component.html | 27 +- .../movie/movie-details.component.ts | 54 +- .../movie-information-panel.component.html | 50 +- .../cast-carousel.component.html | 4 +- .../cast-carousel.component.scss | 4 +- .../media-poster/media-poster.component.html | 4 +- .../request-behalf.component.ts | 17 +- .../social-icons/social-icons.component.html | 2 +- .../social-icons/social-icons.component.scss | 2 + .../social-icons.component.stories.ts | 67 + .../social-icons/social-icons.component.ts | 20 +- .../top-banner/top-banner.component.html | 1 - .../tv-information-panel.component.html | 2 +- .../tv-request-grid.component.ts | 66 +- .../tv-requests-panel.component.ts | 24 +- .../components/tv/tv-details.component.html | 14 +- .../components/tv/tv-details.component.ts | 10 +- .../app/media-details/media-details.module.ts | 2 + .../src/app/my-nav/nav-search.component.ts | 6 +- .../albums-grid/albums-grid.component.html | 2 +- .../movies-grid/movies-grid.component.html | 6 +- .../components/tv-grid/tv-grid.component.html | 2 +- .../src/app/search/moviesearch.component.html | 198 - .../src/app/search/moviesearch.component.ts | 275 - .../app/search/moviesearchgrid.component.html | 149 - .../app/search/moviesearchgrid.component.ts | 164 - .../search/music/albumsearch.component.html | 116 - .../app/search/music/albumsearch.component.ts | 91 - .../search/music/artistsearch.component.html | 65 - .../search/music/artistsearch.component.ts | 27 - .../search/music/musicsearch.component.html | 52 - .../app/search/music/musicsearch.component.ts | 149 - .../src/app/search/search.component.html | 35 - .../src/app/search/search.component.scss | 34 - .../src/app/search/search.component.ts | 47 - .../ClientApp/src/app/search/search.module.ts | 60 - .../search/seriesinformation.component.html | 71 - .../search/seriesinformation.component.scss | 25 - .../app/search/seriesinformation.component.ts | 98 - .../src/app/search/tvsearch.component.html | 185 - .../src/app/search/tvsearch.component.ts | 238 - .../services/helpers/validation.service.ts | 8 +- .../src/app/services/image.service.ts | 13 +- .../src/app/services/message.service.ts | 14 +- .../src/app/services/searchV2.service.ts | 8 +- .../services/signlarnotification.service.ts | 4 +- .../authentication.component.ts | 8 +- .../couchpotato/couchpotato.component.ts | 16 +- .../app/settings/dognzb/dognzb.component.ts | 8 +- .../src/app/settings/emby/emby.component.ts | 4 +- .../settings/features/features.component.html | 4 +- .../app/settings/issues/issues.component.ts | 8 +- .../settings/jellyfin/jellyfin.component.ts | 4 +- .../src/app/settings/jobs/jobs.component.ts | 8 +- .../app/settings/lidarr/lidarr.component.ts | 16 +- .../notifications/cloudmobile.coponent.ts | 10 +- .../notifications/discord.component.ts | 10 +- .../emailnotification.component.ts | 10 +- .../notifications/gotify.component.ts | 10 +- .../notifications/mattermost.component.ts | 10 +- .../notifications/mobile.component.ts | 10 +- .../notifications/pushbullet.component.ts | 10 +- .../notifications/pushover.component.ts | 10 +- .../settings/notifications/slack.component.ts | 10 +- .../notifications/telegram.component.ts | 10 +- .../notifications/twilio/twilio.component.ts | 8 +- .../twilio/whatsapp.component.ts | 6 +- .../notifications/webhook.component.ts | 10 +- .../src/app/settings/ombi/ombi.component.ts | 8 +- .../src/app/settings/plex/plex.component.ts | 4 +- .../components/radarr-form.component.ts | 18 +- .../app/settings/radarr/radarr.component.html | 2 + .../app/settings/radarr/radarr.component.ts | 28 +- .../settings/sickrage/sickrage.component.ts | 10 +- .../app/settings/sonarr/sonarr.component.ts | 16 +- .../themoviedb/themoviedb.component.html | 12 +- .../themoviedb/themoviedb.component.ts | 61 +- .../app/settings/update/update.component.ts | 8 +- .../src/app/settings/vote/vote.component.ts | 8 +- .../admin-request-dialog.component.ts | 8 +- .../advanced-search-dialog-data.service.ts | 22 + .../advanced-search-dialog.component.html | 1 - .../advanced-search-dialog.component.ts | 10 +- .../genre-select/genre-select.component.ts | 6 +- .../keyword-search.component.ts | 6 +- .../watch-providers-select.component.ts | 6 +- .../user-preference.component.ts | 6 +- .../usermanagement.component.html | 4 +- .../src/app/wizard/plex/plex.component.html | 2 +- .../app/wizard/welcome/welcome.component.html | 12 +- .../app/wizard/welcome/welcome.component.ts | 6 +- src/Ombi/ClientApp/src/styles/Styles.scss | 4 + src/Ombi/ClientApp/src/tsconfig.json | 36 + src/Ombi/ClientApp/src/typings/globals.d.ts | 2 +- .../{src/tsconfig.app.json => tsconfig.json} | 2 +- src/Ombi/ClientApp/yarn.lock | 11193 ++++++++++++++-- .../V1/External/TesterController.cs | 11 +- src/Ombi/Controllers/V1/ImagesController.cs | 104 +- src/Ombi/Controllers/V1/TokenController.cs | 18 +- src/Ombi/Controllers/V2/SearchController.cs | 13 +- src/Ombi/Extensions/StartupExtensions.cs | 2 + src/Ombi/Ombi.csproj | 3 +- src/Ombi/wwwroot/translations/bg.json | 9 +- src/Ombi/wwwroot/translations/cs.json | 9 +- src/Ombi/wwwroot/translations/da.json | 9 +- src/Ombi/wwwroot/translations/de.json | 27 +- src/Ombi/wwwroot/translations/en.json | 71 +- src/Ombi/wwwroot/translations/es.json | 9 +- src/Ombi/wwwroot/translations/fr.json | 9 +- src/Ombi/wwwroot/translations/hu.json | 9 +- src/Ombi/wwwroot/translations/it.json | 9 +- src/Ombi/wwwroot/translations/nl.json | 9 +- src/Ombi/wwwroot/translations/no.json | 9 +- src/Ombi/wwwroot/translations/pl.json | 9 +- src/Ombi/wwwroot/translations/pt-BR.json | 41 +- src/Ombi/wwwroot/translations/pt.json | 9 +- src/Ombi/wwwroot/translations/ru.json | 9 +- src/Ombi/wwwroot/translations/sk.json | 9 +- src/Ombi/wwwroot/translations/sv.json | 9 +- src/Ombi/wwwroot/translations/zh-TW.json | 9 +- src/Ombi/wwwroot/translations/zh.json | 17 +- src/dockerfile | 76 + tests/cypress.json | 2 +- tests/cypress/config/regression.json | 2 +- .../fixtures/api/v1/tv-search-extra-info.json | 4 +- .../page-objects/shared/DiscoverCard.ts | 2 +- .../details/tv/tvdetails-buttons.spec.ts | 5 +- tests/cypress/tests/search/search.spec.ts | 2 +- version.json | 2 +- 220 files changed, 12420 insertions(+), 4489 deletions(-) create mode 100644 .github/workflows/chromatic.yml create mode 100644 .mergify.yml create mode 100644 src/.dockerignore create mode 100644 src/Ombi.TheMovieDbApi/Models/Language.cs create mode 100644 src/Ombi/ClientApp/.storybook/main.js create mode 100644 src/Ombi/ClientApp/.storybook/preview-body.html create mode 100644 src/Ombi/ClientApp/.storybook/preview.js create mode 100644 src/Ombi/ClientApp/.storybook/tsconfig.json create mode 100644 src/Ombi/ClientApp/.storybook/typings.d.ts create mode 100644 src/Ombi/ClientApp/documentation.json create mode 100644 src/Ombi/ClientApp/src/app/components/image-background/image-background.component.html create mode 100644 src/Ombi/ClientApp/src/app/components/image-background/image-background.component.scss create mode 100644 src/Ombi/ClientApp/src/app/components/image-background/image-background.component.ts create mode 100644 src/Ombi/ClientApp/src/app/components/image/image.component.html create mode 100644 src/Ombi/ClientApp/src/app/components/image/image.component.stories.ts create mode 100644 src/Ombi/ClientApp/src/app/components/image/image.component.ts create mode 100644 src/Ombi/ClientApp/src/app/components/index.ts create mode 100644 src/Ombi/ClientApp/src/app/components/modules.ts create mode 100644 src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.stories.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/moviesearch.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/moviesearch.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/moviesearchgrid.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/moviesearchgrid.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/music/albumsearch.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/music/albumsearch.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/music/artistsearch.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/music/artistsearch.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/music/musicsearch.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/music/musicsearch.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/search.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/search.component.scss delete mode 100644 src/Ombi/ClientApp/src/app/search/search.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/search.module.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/seriesinformation.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/seriesinformation.component.scss delete mode 100644 src/Ombi/ClientApp/src/app/search/seriesinformation.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/search/tvsearch.component.html delete mode 100644 src/Ombi/ClientApp/src/app/search/tvsearch.component.ts create mode 100644 src/Ombi/ClientApp/src/tsconfig.json rename src/Ombi/ClientApp/{src/tsconfig.app.json => tsconfig.json} (95%) create mode 100644 src/dockerfile diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 000000000..07f8a1a2b --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,47 @@ +name: 'Chromatic' + +# Event for the workflow +on: + push: + workflow_dispatch: + +# List of jobs +jobs: + storybook-build: + # Operating System + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: NodeModules Cache + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: node_modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + working-directory: ./src/Ombi/ClientApp + run: yarn + + - name: Publish to Chromatic + if: github.ref != 'refs/heads/master' + uses: chromaui/action@v1 + with: + projectToken: 7c47e1a1a4bd + exitZeroOnChanges: true + workingDir: ./src/Ombi/ClientApp + buildScriptName: storybookbuild + exitOnceUploaded: true + + - name: Publish to Chromatic and auto accept changes + if: github.ref == 'refs/heads/develop' + uses: chromaui/action@v1 + with: + projectToken: 7c47e1a1a4bd + autoAcceptChanges: true # 👈 Option to accept all changes + workingDir: ./src/Ombi/ClientApp + buildScriptName: storybookbuild + exitOnceUploaded: true diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 2e382eee3..2dc51179e 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -34,15 +34,29 @@ jobs: - name: Install Frontend Deps run: yarn --cwd ./src/Ombi/ClientApp install - - name: Install Automation Deps - run: yarn --cwd ./tests install - - - name: Start Backend - run: | - nohup dotnet run --project ./src/Ombi -- --host http://*:3577 & - - name: Start Frontend - run: | - nohup yarn --cwd ./src/Ombi/ClientApp start & + - name: Build Frontend + run: yarn --cwd ./src/Ombi/ClientApp build + + - name: Build Docker Image + run: docker build -t ombi src/ + + - name: Run Docker Image + run: nohup docker run --rm -p 5000:5000 ombi & + + - name: Sleep for server to start + run: sleep 20 + + # - name: Start Frontend + # run: | + # nohup yarn --cwd ./src/Ombi/ClientApp start & + + # - name: Install Automation Deps + # run: yarn --cwd ./tests install + + # - name: Start Backend + # run: | + # nohup dotnet run --project ./src/Ombi -- --host http://*:3577 & + - name: Cypress Tests uses: cypress-io/github-action@v2.8.2 with: @@ -50,9 +64,9 @@ jobs: browser: chrome headless: true working-directory: tests - wait-on: http://localhost:3577/ - # 7 minutes - wait-on-timeout: 420 + wait-on: http://localhost:5000/ + # 10 minutes + wait-on-timeout: 600 env: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 7c724a62a..402c5ffd0 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -8,6 +8,8 @@ name: Labeler on: [pull_request] +permissions: write-all + jobs: label: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 961871f8a..7465635cc 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -5,6 +5,11 @@ on: types: [opened, synchronize, reopened] workflow_dispatch: +permissions: + pull-requests: write + issues: write + repository-projects: write + jobs: build-ui: runs-on: ubuntu-latest @@ -28,6 +33,7 @@ jobs: unit-test: runs-on: ubuntu-latest + steps: - uses: actions/checkout@v2 - uses: actions/setup-dotnet@v1 @@ -45,7 +51,7 @@ jobs: - name: Run Unit Tests run: | cd src - dotnet test --logger trx --results-directory "TestResults" + dotnet test --configuration "Release" --logger "trx;LogFileName=test-results.trx" analysis: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 845eda385..cae15017a 100644 --- a/.gitignore +++ b/.gitignore @@ -251,3 +251,4 @@ _Pvt_Extensions /src/Ombi/databases.json /src/Ombi/healthchecksdb /src/Ombi/ClientApp/package-lock.json +/src/Ombi.Core/Properties/launchSettings.json diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 000000000..fac2166e3 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,7 @@ +pull_request_rules: + - name: Automatic merge on approval + conditions: + - "#approved-reviews-by>=1" + actions: + merge: + method: merge diff --git a/CHANGELOG.md b/CHANGELOG.md index 2915d98bb..cce336ad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,360 +1,363 @@ -## [4.16.12](https://github.com/Ombi-app/Ombi/compare/v4.16.11...v4.16.12) (2022-04-19) - - - -## [4.16.11](https://github.com/Ombi-app/Ombi/compare/v4.16.10...v4.16.11) (2022-04-14) +## [4.22.4](https://github.com/Ombi-app/Ombi/compare/v4.22.3...v4.22.4) (2022-08-04) ### Bug Fixes -* Set the default job for the watchlist import to hourly instead of daily ([75906af](https://github.com/Ombi-app/Ombi/commit/75906af0adee3e3c68d825c3aaa8f7b918461b1f)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([0e8a64b](https://github.com/Ombi-app/Ombi/commit/0e8a64b8ca00d210fbe843ac2c3f6af218d80cbc)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([7b0ad61](https://github.com/Ombi-app/Ombi/commit/7b0ad61bfcff3986b33180dc64022cba7ea8eefb)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([4fc2c1f](https://github.com/Ombi-app/Ombi/commit/4fc2c1f24534085a783a3d5791f5533b68272153)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([76ab733](https://github.com/Ombi-app/Ombi/commit/76ab733b91791e4d93d184f3c7d0779c6a388695)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([06e4cef](https://github.com/Ombi-app/Ombi/commit/06e4cefa7b4e55b860da9a64f461f6ec8fa17367)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([c12d89d](https://github.com/Ombi-app/Ombi/commit/c12d89d6781a337520977ad285f8d08c93f434dd)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([bc0c2f6](https://github.com/Ombi-app/Ombi/commit/bc0c2f622e34fb5a2711039d9ed7aad34f982b15)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([e4b00e6](https://github.com/Ombi-app/Ombi/commit/e4b00e6b3468bd9389eeb02fc6ad7daf27abc3b3)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d1998d3](https://github.com/Ombi-app/Ombi/commit/d1998d326f999a38586d0a351a20c5448df95842)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([bee4ccb](https://github.com/Ombi-app/Ombi/commit/bee4ccb804594e7385b1fbdc9fe2ef5c42e0d21f)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([80233ed](https://github.com/Ombi-app/Ombi/commit/80233ed560cc976e83570d0655c3472f20171fb3)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([8a78adc](https://github.com/Ombi-app/Ombi/commit/8a78adc9bb62f277f2b213dcb3847ed6d0089fcb)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d04c60a](https://github.com/Ombi-app/Ombi/commit/d04c60aa5909b47ba6bffa6f66b03079cbd43521)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([92a785e](https://github.com/Ombi-app/Ombi/commit/92a785e736fa4b72a45270da2d0f4661df433078)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([634982d](https://github.com/Ombi-app/Ombi/commit/634982df2661cefab5ea9f5163fe04a005cc0171)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([b404baa](https://github.com/Ombi-app/Ombi/commit/b404baad6d0aeaa1561701e0db8db4e78613a364)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d14f11e](https://github.com/Ombi-app/Ombi/commit/d14f11e0eb20ab0a68e765ee77968b3b3e54e995)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([7cf64f9](https://github.com/Ombi-app/Ombi/commit/7cf64f909d78908edaabeffb8a39a7d02e73fe7e)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([0c9e1ec](https://github.com/Ombi-app/Ombi/commit/0c9e1ec090827080cc8f7393e5e91456ff37d691)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([3b0b730](https://github.com/Ombi-app/Ombi/commit/3b0b730cb02efe24f6d4026e5fdb20d37e495119)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([6ed1a03](https://github.com/Ombi-app/Ombi/commit/6ed1a03b7ff4077f09ea9e13394b18b0d138f4c3)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([2941acd](https://github.com/Ombi-app/Ombi/commit/2941acd3b2ec74a5e6aeea275ab5a39d2653f37f)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([c075a1a](https://github.com/Ombi-app/Ombi/commit/c075a1a66784d975eaf60f2dfbbcbe048f2f63d7)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([76bd81c](https://github.com/Ombi-app/Ombi/commit/76bd81c3ca55a98c6ec944a838dc01294a6193a6)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([0d38275](https://github.com/Ombi-app/Ombi/commit/0d3827507e002bcf58f673e97ffcc3bd25dcf337)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([5c99601](https://github.com/Ombi-app/Ombi/commit/5c99601b07aec1a65d0186a4c4327440811e64c6)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([01546a0](https://github.com/Ombi-app/Ombi/commit/01546a0f7f86379528b486463246ef9bdfb9033e)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d7fea78](https://github.com/Ombi-app/Ombi/commit/d7fea7843aaaab7ddff8dc31ca6d2a9117471dcc)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([1a6b95d](https://github.com/Ombi-app/Ombi/commit/1a6b95d45c220310213b8d811272a63f0f6ff42b)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([fa10174](https://github.com/Ombi-app/Ombi/commit/fa1017422c4efd4b0897871bd3c671151774d7c3)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([0c31e62](https://github.com/Ombi-app/Ombi/commit/0c31e628df376aac6d56ae67c7c705a9a4a7c080)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([6399643](https://github.com/Ombi-app/Ombi/commit/63996437a02fe10ffae6822ffa15369bec0a6b36)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([5826e2d](https://github.com/Ombi-app/Ombi/commit/5826e2d9a1c3f1210a87fa270dc0c81bac32944a)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d434514](https://github.com/Ombi-app/Ombi/commit/d43451405be489254d7cdc7755d5f516a1e495a5)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([0b9596d](https://github.com/Ombi-app/Ombi/commit/0b9596d807178f5e071113ec0347868ec7f0960b)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([8c4c0b2](https://github.com/Ombi-app/Ombi/commit/8c4c0b262978c1303767af360d802c4b4c2b4d24)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([289ab77](https://github.com/Ombi-app/Ombi/commit/289ab77b0e04aae235b6f6cebc86e0a8d1f0cf2b)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([30e3417](https://github.com/Ombi-app/Ombi/commit/30e3417285a4eed18d429d7776f0e74096e834c0)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([6c0a5da](https://github.com/Ombi-app/Ombi/commit/6c0a5dadd4b8f37760252eb0fe7f88908f55506d)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d5bf969](https://github.com/Ombi-app/Ombi/commit/d5bf9692ce1fc0ccfe7beca6dd200c78be177bdc)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([8a9e7ea](https://github.com/Ombi-app/Ombi/commit/8a9e7ea588aefbcd73ed82625887e3614e1703ea)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([01047a3](https://github.com/Ombi-app/Ombi/commit/01047a3fd67153f3ff16f860d2c7b50213e8d9b2)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([698a23f](https://github.com/Ombi-app/Ombi/commit/698a23fb83f323cdd1dd57cb49803079d44214a7)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([24eb842](https://github.com/Ombi-app/Ombi/commit/24eb842fc4424f7bcc3ec2949d7f5472492e96f6)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([ac8b16a](https://github.com/Ombi-app/Ombi/commit/ac8b16a3051ad71dbd54a8973c7dd847b564a515)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([f428ce6](https://github.com/Ombi-app/Ombi/commit/f428ce6a700c081437703839bc84d2f2b1138bcc)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([94b16df](https://github.com/Ombi-app/Ombi/commit/94b16dfe09bf1d2cd6286777d74eb5d4496abbbb)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([4881775](https://github.com/Ombi-app/Ombi/commit/4881775eda69a8f136ce0d8fbbf970e3d0406dc9)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([8297db9](https://github.com/Ombi-app/Ombi/commit/8297db91e85da308bde6fb09ad78347dee063630)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([d1152ab](https://github.com/Ombi-app/Ombi/commit/d1152ab7674243daa528c524c0cdc87d81ad49c9)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([eb2788b](https://github.com/Ombi-app/Ombi/commit/eb2788b761b55c487a59a049427ca08f6c10e836)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([21a794c](https://github.com/Ombi-app/Ombi/commit/21a794cbc0a5fa735ca0347c8f7f1ac04a487fbc)) +* :bug: Fixed missing externals ([#4712](https://github.com/Ombi-app/Ombi/issues/4712)) ([fcc1eaa](https://github.com/Ombi-app/Ombi/commit/fcc1eaaa377683dcdc81d62a2a688fb0c4490c7b)) +* fixed trakt image not loading when base url present ([#4711](https://github.com/Ombi-app/Ombi/issues/4711)) ([f102dcf](https://github.com/Ombi-app/Ombi/commit/f102dcf751c2eb62ebfe30f9f8e4b2ad863c3b0d)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4713](https://github.com/Ombi-app/Ombi/issues/4713)) ([ff142b0](https://github.com/Ombi-app/Ombi/commit/ff142b09abbb2f9540387284222552e6e12639fe)) -## [4.10.2](https://github.com/Ombi-app/Ombi/compare/v4.10.1...v4.10.2) (2022-01-22) +## [4.22.3](https://github.com/Ombi-app/Ombi/compare/v4.22.2...v4.22.3) (2022-07-28) +### Bug Fixes -## [4.16.10](https://github.com/Ombi-app/Ombi/compare/v4.16.9...v4.16.10) (2022-04-13) +* Override Sonarr V3 Profiles endpoint ([#4678](https://github.com/Ombi-app/Ombi/issues/4678)) ([875da95](https://github.com/Ombi-app/Ombi/commit/875da959f353119b05138d68ee6d32a49e14b91e)) -## [4.16.9](https://github.com/Ombi-app/Ombi/compare/v4.16.8...v4.16.9) (2022-04-13) +## [4.22.2](https://github.com/Ombi-app/Ombi/compare/v4.22.1...v4.22.2) (2022-07-25) ### Bug Fixes -* **plex-watchlist:** Only request the latest season when importing from the watchlist ([77a47ff](https://github.com/Ombi-app/Ombi/commit/77a47ff157c6c5feafe3f2a29a3fcba8df4fdfef)) +* fixed an issue where I broke images for some users ([81ddc85](https://github.com/Ombi-app/Ombi/commit/81ddc8553b9094c3f6843b036daebb2eb9262e00)) -## [4.16.8](https://github.com/Ombi-app/Ombi/compare/v4.16.7...v4.16.8) (2022-04-13) +## [4.22.1](https://github.com/Ombi-app/Ombi/compare/v4.22.0...v4.22.1) (2022-07-25) ### Bug Fixes -* **availability:** Fixed an issue where we wouldn't mark a available 4k movie as available (when 4K request feature is disabled) ([b492699](https://github.com/Ombi-app/Ombi/commit/b49269961d4830a530e3054976a47f519524948b)) +* **discover:** :bug: Created new Image component to handle 429's from TMDB ([#4698](https://github.com/Ombi-app/Ombi/issues/4698)) and fixed [#4635](https://github.com/Ombi-app/Ombi/issues/4635) ([#4699](https://github.com/Ombi-app/Ombi/issues/4699)) ([f22d3da](https://github.com/Ombi-app/Ombi/commit/f22d3da765799365455b919027f7563e52b347c3)) -## [4.16.7](https://github.com/Ombi-app/Ombi/compare/v4.16.6...v4.16.7) (2022-04-12) +# [4.22.0](https://github.com/Ombi-app/Ombi/compare/v4.21.2...v4.22.0) (2022-07-22) +### Features -## [4.16.6](https://github.com/Ombi-app/Ombi/compare/v4.16.5...v4.16.6) (2022-04-11) +* **discover:** ✨ Added infinite scroll on advanced search results ([898bc89](https://github.com/Ombi-app/Ombi/commit/898bc89fa78245c1f3de9481f6c724f087a16e39)) -## [4.16.5](https://github.com/Ombi-app/Ombi/compare/v4.16.4...v4.16.5) (2022-04-08) +## [4.21.2](https://github.com/Ombi-app/Ombi/compare/v4.21.1...v4.21.2) (2022-07-22) ### Bug Fixes -* **watchlist:** actually fixed it this time... ([d962a32](https://github.com/Ombi-app/Ombi/commit/d962a3211eca29520662ddce962676e3aea17ec5)) +* Landing and Login page improvements ([#4690](https://github.com/Ombi-app/Ombi/issues/4690)) ([6d423b5](https://github.com/Ombi-app/Ombi/commit/6d423b5447c52c5e59d8d2bd92a23b47468eb736)) -## [4.16.4](https://github.com/Ombi-app/Ombi/compare/v4.16.3...v4.16.4) (2022-04-08) +## [4.21.1](https://github.com/Ombi-app/Ombi/compare/v4.21.0...v4.21.1) (2022-07-11) +### Bug Fixes -## [4.16.3](https://github.com/Ombi-app/Ombi/compare/v4.16.2...v4.16.3) (2022-04-08) +* **images:** Retry images with a backoff when we get a Too Many requests from TheMovieDb [#4685](https://github.com/Ombi-app/Ombi/issues/4685) ([3f1f35d](https://github.com/Ombi-app/Ombi/commit/3f1f35df3164db6739691cdda8f925c296239791)) -### Bug Fixes -* **plex-watchlist:** :bug: Fixed the issue where the watchlist didn't work for users logging in via OAuth ([6398f6a](https://github.com/Ombi-app/Ombi/commit/6398f6a4f7755281ebeac537e3ff623df5cfa0f3)) +# [4.21.0](https://github.com/Ombi-app/Ombi/compare/v4.20.4...v4.21.0) (2022-06-22) +### Features -## [4.16.2](https://github.com/Ombi-app/Ombi/compare/v4.16.1...v4.16.2) (2022-04-07) +* Upgrade to Angular14 ([#4668](https://github.com/Ombi-app/Ombi/issues/4668)) ([b9d55a4](https://github.com/Ombi-app/Ombi/commit/b9d55a469b412558cbf67c1e25db7fdda5964cd8)) -### Bug Fixes +### Performance Improvements -* **wizard:** Fixed an issue when using Plex OAuth it could fail setting up ([b743cf4](https://github.com/Ombi-app/Ombi/commit/b743cf4fafa7341ad1b163276f006d7ab0e9dcff)) +* stop populating obsolete subscribe fields ([#4625](https://github.com/Ombi-app/Ombi/issues/4625)) ([9a73463](https://github.com/Ombi-app/Ombi/commit/9a734637665f671b17c2bb440d93b35a891c142b)) -## [4.16.1](https://github.com/Ombi-app/Ombi/compare/v4.16.0...v4.16.1) (2022-04-07) +## [4.20.4](https://github.com/Ombi-app/Ombi/compare/v4.20.3...v4.20.4) (2022-06-15) +### Bug Fixes -# [4.16.0](https://github.com/Ombi-app/Ombi/compare/v4.15.6...v4.16.0) (2022-04-07) +* fixed build ([f877921](https://github.com/Ombi-app/Ombi/commit/f8779219146051ea74f8b6408658ff7975afb88b)) -## [4.15.6](https://github.com/Ombi-app/Ombi/compare/v4.15.5...v4.15.6) (2022-04-07) +## [4.20.3](https://github.com/Ombi-app/Ombi/compare/v4.20.2...v4.20.3) (2022-06-05) ### Bug Fixes -* **radarr:** Fixed an issue where we couldn't sync radarr content [#4577](https://github.com/Ombi-app/Ombi/issues/4577) ([a5355a3](https://github.com/Ombi-app/Ombi/commit/a5355a3023e6900c4dd1b0da4722d7596c03907f)) +* **plex:** 🐛 Fixed an issue with the Plex Sync ([ab1a11a](https://github.com/Ombi-app/Ombi/commit/ab1a11af78efbe9d37bd55aa80a640796c138a98)) -## [4.15.5](https://github.com/Ombi-app/Ombi/compare/v4.15.4...v4.15.5) (2022-04-06) +## [4.20.2](https://github.com/Ombi-app/Ombi/compare/v4.20.1...v4.20.2) (2022-06-03) +### Bug Fixes -## [4.15.4](https://github.com/Ombi-app/Ombi/compare/v4.15.3...v4.15.4) (2022-03-29) +* :bug: Fixed the Request on Behalf of having blanks ([#4667](https://github.com/Ombi-app/Ombi/issues/4667)) ([7dd9b1c](https://github.com/Ombi-app/Ombi/commit/7dd9b1cac07f571dd35b362544e4fe0226c4b817)) -## [4.15.3](https://github.com/Ombi-app/Ombi/compare/v4.15.2...v4.15.3) (2022-03-24) +## [4.20.1](https://github.com/Ombi-app/Ombi/compare/v4.20.0...v4.20.1) (2022-05-27) +### Bug Fixes -## [4.15.2](https://github.com/Ombi-app/Ombi/compare/v4.15.1...v4.15.2) (2022-03-23) +* added media type tag to media type text ([#4638](https://github.com/Ombi-app/Ombi/issues/4638)) ([fe501d3](https://github.com/Ombi-app/Ombi/commit/fe501d34a0c36ac9f000b107eca49dbc6694d006)) +* **API:** Fix pagination in some edge cases ([#4649](https://github.com/Ombi-app/Ombi/issues/4649)) ([a70bf8f](https://github.com/Ombi-app/Ombi/commit/a70bf8f46c76d74c9dfdf908c53bd9955ca0a35d)) +* **discover:** Carousel touch not working when scrolling page and recommendations and similar movie navigation ([#4633](https://github.com/Ombi-app/Ombi/issues/4633)) ([d5ef1d5](https://github.com/Ombi-app/Ombi/commit/d5ef1d53e5f77d19dba8b8059c80b538a3e14f2a)) +* Improve Swagger documentation ([#4652](https://github.com/Ombi-app/Ombi/issues/4652)) ([181892b](https://github.com/Ombi-app/Ombi/commit/181892bcfe88e6d76febf49ef57745d04552d08e)) +* Missing Poster broken link fix ([#4637](https://github.com/Ombi-app/Ombi/issues/4637)) ([4070f0d](https://github.com/Ombi-app/Ombi/commit/4070f0d093b1c92487a1c80cabad8283a9650f51)) +* **sickrage:** Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present ([#4648](https://github.com/Ombi-app/Ombi/issues/4648)) ([6d16442](https://github.com/Ombi-app/Ombi/commit/6d16442d4d714920367df065a3ced42b729f4233)) +* **sync:** Emby+Jellyfin - sync multi-episode files of 3+ episodes ([bd8fd89](https://github.com/Ombi-app/Ombi/commit/bd8fd890554c9d85d6da4d2cee813e82ce698e52)) -### Bug Fixes -* **metadata:** improved the metadata job to also lookup the media in Plex to see if it has any more uptodate metadata ([83d1a15](https://github.com/Ombi-app/Ombi/commit/83d1a15cc9d0ee91be73bd91c4672cf1bcf2728a)) +# [4.20.0](https://github.com/Ombi-app/Ombi/compare/v4.19.1...v4.20.0) (2022-04-28) +### Features -## [4.15.1](https://github.com/Ombi-app/Ombi/compare/v4.15.0...v4.15.1) (2022-03-18) +* **discover:** Show more relevant shows in upcoming TV ([8357819](https://github.com/Ombi-app/Ombi/commit/8357819b53b8c675c0b246d7006b5a778bdba33f)) -### Bug Fixes -* **mediaserver:** fixed an issue where we were not detecting available content correctly [#4542](https://github.com/Ombi-app/Ombi/issues/4542) ([9cdd6f4](https://github.com/Ombi-app/Ombi/commit/9cdd6f41cdab8825a984905c089611409c53c753)) +## [4.19.1](https://github.com/Ombi-app/Ombi/compare/v4.19.0...v4.19.1) (2022-04-27) -# [4.15.0](https://github.com/Ombi-app/Ombi/compare/v4.14.4...v4.15.0) (2022-03-17) +# [4.19.0](https://github.com/Ombi-app/Ombi/compare/v4.18.0...v4.19.0) (2022-04-27) -### Bug Fixes +### Features -* **jellyfin:** :bug: Fixed an issue where Jellyfin content was showing the Play on Emby button ([18b167d](https://github.com/Ombi-app/Ombi/commit/18b167d16a3d682b5060ee36dedbbb069bef09de)), closes [#4542](https://github.com/Ombi-app/Ombi/issues/4542) +* **sync:** Detect reidentified movies in Emby and Jellyfin ([5938077](https://github.com/Ombi-app/Ombi/commit/5938077d82a5357f79c07b218b3986557a5816e8)) +* **sync:** Detect reidentified series in Emby and Jellyfin ([9096e91](https://github.com/Ombi-app/Ombi/commit/9096e91d55d268819bce22831f8a8b27f2a1776b)) -## [4.14.4](https://github.com/Ombi-app/Ombi/compare/v4.14.3...v4.14.4) (2022-03-10) +# [4.18.0](https://github.com/Ombi-app/Ombi/compare/v4.17.0...v4.18.0) (2022-04-26) ### Bug Fixes -* :bug: Fixed the Request On Behalf autocomplete not filtering correctly ([a8ba2f3](https://github.com/Ombi-app/Ombi/commit/a8ba2f3544a1c01c57f217c4036a277ab0e67a09)), closes [#4539](https://github.com/Ombi-app/Ombi/issues/4539) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([356c742](https://github.com/Ombi-app/Ombi/commit/356c7424e0ce8c1c5063b04bc6ed9b809f214d65)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([6fcaecf](https://github.com/Ombi-app/Ombi/commit/6fcaecf80b766f2d43ac7082d74364238e1638b7)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([132f4d4](https://github.com/Ombi-app/Ombi/commit/132f4d4e609b7fb7e37f38ee2f395926e2911abe)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([f292006](https://github.com/Ombi-app/Ombi/commit/f292006a08894a8d0ba899c8c6e9fe863e558dda)) +* **discover:** Fix cache mix up ([03d9422](https://github.com/Ombi-app/Ombi/commit/03d94220c7eaafb50c6c80a6ed1150794b873ac3)) +* **discover:** Fix new trending feature detection ([6794b88](https://github.com/Ombi-app/Ombi/commit/6794b887f6544fb41528bdd9728b7824b65e47ee)) +* **settings:** Allow toggling features when there are more than one ([a373359](https://github.com/Ombi-app/Ombi/commit/a373359ae8e6bad42b558a6e01a8ff2840d3bbaa)) +### Features -## [4.14.3](https://github.com/Ombi-app/Ombi/compare/v4.14.2...v4.14.3) (2022-03-06) +* **discover:** Add new trending source experimental feature ([1a0823c](https://github.com/Ombi-app/Ombi/commit/1a0823ca80559417c67323aaeaa1ef5243e98031)) +* **discover:** Default trending source to new logic ([4f12939](https://github.com/Ombi-app/Ombi/commit/4f12939e22020a67a5ee75e2907923faea136e8d)) -### Bug Fixes -* **availability:** :bug: Fixed an issue where with 4k content, we could repeat notifications ([f9ebc1c](https://github.com/Ombi-app/Ombi/commit/f9ebc1cc2e13c7cd335121cd86295b10eda529ba)) +# [4.17.0](https://github.com/Ombi-app/Ombi/compare/v4.16.17...v4.17.0) (2022-04-25) +### Features -## [4.14.2](https://github.com/Ombi-app/Ombi/compare/v4.14.1...v4.14.2) (2022-03-05) +* **discover:** Add original language filter ([ef7ec86](https://github.com/Ombi-app/Ombi/commit/ef7ec861d8aede2a4817752c990617f583805391)) -### Bug Fixes -* **Sonarr:** :bug: Fixed an issue where some seasons were not being monitored correctly in sonarr ([60cfd41](https://github.com/Ombi-app/Ombi/commit/60cfd41f68e9006555c1a419dcff1aaa24b3e09f)), closes [#4506](https://github.com/Ombi-app/Ombi/issues/4506) +## [4.16.17](https://github.com/Ombi-app/Ombi/compare/v4.16.16...v4.16.17) (2022-04-25) -## [4.14.1](https://github.com/Ombi-app/Ombi/compare/v4.14.0...v4.14.1) (2022-03-03) +## [4.16.16](https://github.com/Ombi-app/Ombi/compare/v4.16.15...v4.16.16) (2022-04-25) +### Bug Fixes -# [4.14.0](https://github.com/Ombi-app/Ombi/compare/v4.13.2...v4.14.0) (2022-03-02) +* **4616:** :bug: fixed mandatory fields ([d8f2260](https://github.com/Ombi-app/Ombi/commit/d8f2260c7ae3ed48386743b7adbd06e284487034)) -## [4.13.2](https://github.com/Ombi-app/Ombi/compare/v4.13.1...v4.13.2) (2022-03-01) +## [4.16.15](https://github.com/Ombi-app/Ombi/compare/v4.16.14...v4.16.15) (2022-04-24) -### Bug Fixes -* **requests:** :bug: Fixed an issue where you couldn't approve movies from the request list ([1611ef9](https://github.com/Ombi-app/Ombi/commit/1611ef9198befbb7a4db50a4f0953e50f29a788f)) +## [4.16.14](https://github.com/Ombi-app/Ombi/compare/v4.16.13...v4.16.14) (2022-04-19) -## [4.13.1](https://github.com/Ombi-app/Ombi/compare/v4.13.0...v4.13.1) (2022-03-01) +## [4.16.13](https://github.com/Ombi-app/Ombi/compare/v4.16.12...v4.16.13) (2022-04-19) -### Bug Fixes -* **details:** :bug: Fixed the missing Play on Media server button for 4k content [#4529](https://github.com/Ombi-app/Ombi/issues/4529) ([68600f3](https://github.com/Ombi-app/Ombi/commit/68600f3b45376e12dd2ef263d81ca4040c84cbca)) -* **discover:** :bug: Fixed the issue where there was an option on the discover to request 4k shows (that's not supported currently) ([dcfd688](https://github.com/Ombi-app/Ombi/commit/dcfd688c8d2337e55fa9c6c33b7c3e80fc560cda)) -* **requests:** :bug: Fixed the issue where we could no longer approve TV Requests from the requests list ([19fe4e3](https://github.com/Ombi-app/Ombi/commit/19fe4e342efe5578c26ab8ba7ee2f2e64bbc9418)) -* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4526](https://github.com/Ombi-app/Ombi/issues/4526)) ([7e9f54f](https://github.com/Ombi-app/Ombi/commit/7e9f54fc80a09c938184e6be40ce5f49ce9673ef)) +## [4.16.12](https://github.com/Ombi-app/Ombi/compare/v4.16.11...v4.16.12) (2022-04-19) -# [4.13.0](https://github.com/Ombi-app/Ombi/compare/v4.12.7...v4.13.0) (2022-02-25) +## [4.16.11](https://github.com/Ombi-app/Ombi/compare/v4.16.10...v4.16.11) (2022-04-14) ### Bug Fixes -* **4k:** Hide 'Has 4K Request' column list if 4k feature is disabled ([#4521](https://github.com/Ombi-app/Ombi/issues/4521)) ([a9a6067](https://github.com/Ombi-app/Ombi/commit/a9a60678e74d22fa7ba34051a2645db86b600b4a)) -* **issues:** Fix label ID in chatbox page ([#4520](https://github.com/Ombi-app/Ombi/issues/4520)) ([76882ad](https://github.com/Ombi-app/Ombi/commit/76882adf231f92e1cdd396239933c13467c112b3)) -* **localisation:** Localize request types in notifications ([#4516](https://github.com/Ombi-app/Ombi/issues/4516)) ([e09435d](https://github.com/Ombi-app/Ombi/commit/e09435da455b12fc429f129372de31e0654da797)) -* **notifications:** Remove generic admin email in favour of admins' email ([#4519](https://github.com/Ombi-app/Ombi/issues/4519)) ([b90fc5f](https://github.com/Ombi-app/Ombi/commit/b90fc5fea771a83e6cf576c71a307066efd59ea4)) -* **tv:** Display TV show as requested if all episodes are requested ([#4518](https://github.com/Ombi-app/Ombi/issues/4518)) ([2ed8c48](https://github.com/Ombi-app/Ombi/commit/2ed8c48d128a69f0d144c5d332286dbf3b0bdf28)) +* Set the default job for the watchlist import to hourly instead of daily ([75906af](https://github.com/Ombi-app/Ombi/commit/75906af0adee3e3c68d825c3aaa8f7b918461b1f)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([0e8a64b](https://github.com/Ombi-app/Ombi/commit/0e8a64b8ca00d210fbe843ac2c3f6af218d80cbc)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([7b0ad61](https://github.com/Ombi-app/Ombi/commit/7b0ad61bfcff3986b33180dc64022cba7ea8eefb)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([4fc2c1f](https://github.com/Ombi-app/Ombi/commit/4fc2c1f24534085a783a3d5791f5533b68272153)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([76ab733](https://github.com/Ombi-app/Ombi/commit/76ab733b91791e4d93d184f3c7d0779c6a388695)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([06e4cef](https://github.com/Ombi-app/Ombi/commit/06e4cefa7b4e55b860da9a64f461f6ec8fa17367)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([c12d89d](https://github.com/Ombi-app/Ombi/commit/c12d89d6781a337520977ad285f8d08c93f434dd)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([bc0c2f6](https://github.com/Ombi-app/Ombi/commit/bc0c2f622e34fb5a2711039d9ed7aad34f982b15)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([e4b00e6](https://github.com/Ombi-app/Ombi/commit/e4b00e6b3468bd9389eeb02fc6ad7daf27abc3b3)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d1998d3](https://github.com/Ombi-app/Ombi/commit/d1998d326f999a38586d0a351a20c5448df95842)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([bee4ccb](https://github.com/Ombi-app/Ombi/commit/bee4ccb804594e7385b1fbdc9fe2ef5c42e0d21f)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([80233ed](https://github.com/Ombi-app/Ombi/commit/80233ed560cc976e83570d0655c3472f20171fb3)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([8a78adc](https://github.com/Ombi-app/Ombi/commit/8a78adc9bb62f277f2b213dcb3847ed6d0089fcb)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d04c60a](https://github.com/Ombi-app/Ombi/commit/d04c60aa5909b47ba6bffa6f66b03079cbd43521)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([92a785e](https://github.com/Ombi-app/Ombi/commit/92a785e736fa4b72a45270da2d0f4661df433078)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([634982d](https://github.com/Ombi-app/Ombi/commit/634982df2661cefab5ea9f5163fe04a005cc0171)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([b404baa](https://github.com/Ombi-app/Ombi/commit/b404baad6d0aeaa1561701e0db8db4e78613a364)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d14f11e](https://github.com/Ombi-app/Ombi/commit/d14f11e0eb20ab0a68e765ee77968b3b3e54e995)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([7cf64f9](https://github.com/Ombi-app/Ombi/commit/7cf64f909d78908edaabeffb8a39a7d02e73fe7e)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([0c9e1ec](https://github.com/Ombi-app/Ombi/commit/0c9e1ec090827080cc8f7393e5e91456ff37d691)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([3b0b730](https://github.com/Ombi-app/Ombi/commit/3b0b730cb02efe24f6d4026e5fdb20d37e495119)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([6ed1a03](https://github.com/Ombi-app/Ombi/commit/6ed1a03b7ff4077f09ea9e13394b18b0d138f4c3)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([2941acd](https://github.com/Ombi-app/Ombi/commit/2941acd3b2ec74a5e6aeea275ab5a39d2653f37f)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([c075a1a](https://github.com/Ombi-app/Ombi/commit/c075a1a66784d975eaf60f2dfbbcbe048f2f63d7)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([76bd81c](https://github.com/Ombi-app/Ombi/commit/76bd81c3ca55a98c6ec944a838dc01294a6193a6)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([0d38275](https://github.com/Ombi-app/Ombi/commit/0d3827507e002bcf58f673e97ffcc3bd25dcf337)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([5c99601](https://github.com/Ombi-app/Ombi/commit/5c99601b07aec1a65d0186a4c4327440811e64c6)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([01546a0](https://github.com/Ombi-app/Ombi/commit/01546a0f7f86379528b486463246ef9bdfb9033e)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d7fea78](https://github.com/Ombi-app/Ombi/commit/d7fea7843aaaab7ddff8dc31ca6d2a9117471dcc)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([1a6b95d](https://github.com/Ombi-app/Ombi/commit/1a6b95d45c220310213b8d811272a63f0f6ff42b)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([fa10174](https://github.com/Ombi-app/Ombi/commit/fa1017422c4efd4b0897871bd3c671151774d7c3)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([0c31e62](https://github.com/Ombi-app/Ombi/commit/0c31e628df376aac6d56ae67c7c705a9a4a7c080)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([6399643](https://github.com/Ombi-app/Ombi/commit/63996437a02fe10ffae6822ffa15369bec0a6b36)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([5826e2d](https://github.com/Ombi-app/Ombi/commit/5826e2d9a1c3f1210a87fa270dc0c81bac32944a)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d434514](https://github.com/Ombi-app/Ombi/commit/d43451405be489254d7cdc7755d5f516a1e495a5)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([0b9596d](https://github.com/Ombi-app/Ombi/commit/0b9596d807178f5e071113ec0347868ec7f0960b)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([8c4c0b2](https://github.com/Ombi-app/Ombi/commit/8c4c0b262978c1303767af360d802c4b4c2b4d24)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([289ab77](https://github.com/Ombi-app/Ombi/commit/289ab77b0e04aae235b6f6cebc86e0a8d1f0cf2b)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([30e3417](https://github.com/Ombi-app/Ombi/commit/30e3417285a4eed18d429d7776f0e74096e834c0)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([6c0a5da](https://github.com/Ombi-app/Ombi/commit/6c0a5dadd4b8f37760252eb0fe7f88908f55506d)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d5bf969](https://github.com/Ombi-app/Ombi/commit/d5bf9692ce1fc0ccfe7beca6dd200c78be177bdc)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([8a9e7ea](https://github.com/Ombi-app/Ombi/commit/8a9e7ea588aefbcd73ed82625887e3614e1703ea)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([01047a3](https://github.com/Ombi-app/Ombi/commit/01047a3fd67153f3ff16f860d2c7b50213e8d9b2)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([698a23f](https://github.com/Ombi-app/Ombi/commit/698a23fb83f323cdd1dd57cb49803079d44214a7)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([24eb842](https://github.com/Ombi-app/Ombi/commit/24eb842fc4424f7bcc3ec2949d7f5472492e96f6)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([ac8b16a](https://github.com/Ombi-app/Ombi/commit/ac8b16a3051ad71dbd54a8973c7dd847b564a515)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([f428ce6](https://github.com/Ombi-app/Ombi/commit/f428ce6a700c081437703839bc84d2f2b1138bcc)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([94b16df](https://github.com/Ombi-app/Ombi/commit/94b16dfe09bf1d2cd6286777d74eb5d4496abbbb)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([4881775](https://github.com/Ombi-app/Ombi/commit/4881775eda69a8f136ce0d8fbbf970e3d0406dc9)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([8297db9](https://github.com/Ombi-app/Ombi/commit/8297db91e85da308bde6fb09ad78347dee063630)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([d1152ab](https://github.com/Ombi-app/Ombi/commit/d1152ab7674243daa528c524c0cdc87d81ad49c9)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([eb2788b](https://github.com/Ombi-app/Ombi/commit/eb2788b761b55c487a59a049427ca08f6c10e836)) +* **translations:** 🌐 New translations from Crowdin [skip ci] ([21a794c](https://github.com/Ombi-app/Ombi/commit/21a794cbc0a5fa735ca0347c8f7f1ac04a487fbc)) -### Features -* **email-notifications:** Add a link to Ombi details page in email notifications ([#4517](https://github.com/Ombi-app/Ombi/issues/4517)) ([a3e97b3](https://github.com/Ombi-app/Ombi/commit/a3e97b31e2298d95e7deebd71268095b8ed5e9dc)) -* **media-details:** Add Trakt to social icons ([#4522](https://github.com/Ombi-app/Ombi/issues/4522)) ([d6ae79c](https://github.com/Ombi-app/Ombi/commit/d6ae79ce9eddbd5b7b888ab1b9f7e342d9d9ff9e)) +## [4.10.2](https://github.com/Ombi-app/Ombi/compare/v4.10.1...v4.10.2) (2022-01-22) -## [4.12.7](https://github.com/Ombi-app/Ombi/compare/v4.12.6...v4.12.7) (2022-02-23) +## [4.16.10](https://github.com/Ombi-app/Ombi/compare/v4.16.9...v4.16.10) (2022-04-13) -## [4.12.6](https://github.com/Ombi-app/Ombi/compare/v4.12.5...v4.12.6) (2022-02-22) +## [4.16.9](https://github.com/Ombi-app/Ombi/compare/v4.16.8...v4.16.9) (2022-04-13) ### Bug Fixes -* **emby/jellyfin:** :bug: Fixed another issue where we were not correctly displaying the correct status' for movies ([5c0556e](https://github.com/Ombi-app/Ombi/commit/5c0556e6f44b8997a611f3a4d8e9e4e05d08bd13)) -* **mediaserver:** fixed some more issues in the media server sync and availability checks ([f3ea979](https://github.com/Ombi-app/Ombi/commit/f3ea979b8bd77842780ce8e6928b16237dd779cf)) +* **plex-watchlist:** Only request the latest season when importing from the watchlist ([77a47ff](https://github.com/Ombi-app/Ombi/commit/77a47ff157c6c5feafe3f2a29a3fcba8df4fdfef)) -## [4.12.5](https://github.com/Ombi-app/Ombi/compare/v4.12.4...v4.12.5) (2022-02-21) +## [4.16.8](https://github.com/Ombi-app/Ombi/compare/v4.16.7...v4.16.8) (2022-04-13) ### Bug Fixes -* **emby:** :bug: Fixed the emby content sync [#4513](https://github.com/Ombi-app/Ombi/issues/4513) ([2927504](https://github.com/Ombi-app/Ombi/commit/2927504f0e0b4e7251e69b44e0e30c7ec9519980)) -* **emby:** :bug: Fixed the emby content sync [#4513](https://github.com/Ombi-app/Ombi/issues/4513) ([bd441cb](https://github.com/Ombi-app/Ombi/commit/bd441cb54fd77d6befb03fae321dc36c29f0de2e)) +* **availability:** Fixed an issue where we wouldn't mark a available 4k movie as available (when 4K request feature is disabled) ([b492699](https://github.com/Ombi-app/Ombi/commit/b49269961d4830a530e3054976a47f519524948b)) -## [4.12.4](https://github.com/Ombi-app/Ombi/compare/v4.12.3...v4.12.4) (2022-02-17) +## [4.16.7](https://github.com/Ombi-app/Ombi/compare/v4.16.6...v4.16.7) (2022-04-12) -## [4.12.3](https://github.com/Ombi-app/Ombi/compare/v4.12.2...v4.12.3) (2022-02-16) +## [4.16.6](https://github.com/Ombi-app/Ombi/compare/v4.16.5...v4.16.6) (2022-04-11) -## [4.12.2](https://github.com/Ombi-app/Ombi/compare/v4.12.1...v4.12.2) (2022-02-16) +## [4.16.5](https://github.com/Ombi-app/Ombi/compare/v4.16.4...v4.16.5) (2022-04-08) ### Bug Fixes -* **requests:** :bug: Fixed the approve 4k option on the requests list not working as expected ([c0189da](https://github.com/Ombi-app/Ombi/commit/c0189dad478ea375beda61ba3bee3f029a39b8e5)) +* **watchlist:** actually fixed it this time... ([d962a32](https://github.com/Ombi-app/Ombi/commit/d962a3211eca29520662ddce962676e3aea17ec5)) + + + +## [4.16.4](https://github.com/Ombi-app/Ombi/compare/v4.16.3...v4.16.4) (2022-04-08) -## [4.12.1](https://github.com/Ombi-app/Ombi/compare/v4.12.0...v4.12.1) (2022-02-16) +## [4.16.3](https://github.com/Ombi-app/Ombi/compare/v4.16.2...v4.16.3) (2022-04-08) ### Bug Fixes -* **requests:** :bug: Fixed the issue where Approving a 4K Request wouldn't send it to the correct 4K radarr instance ([87cb990](https://github.com/Ombi-app/Ombi/commit/87cb9903db30e1dead25ee8c5ea34305eb084a03)), closes [#4509](https://github.com/Ombi-app/Ombi/issues/4509) +* **plex-watchlist:** :bug: Fixed the issue where the watchlist didn't work for users logging in via OAuth ([6398f6a](https://github.com/Ombi-app/Ombi/commit/6398f6a4f7755281ebeac537e3ff623df5cfa0f3)) -# [4.12.0](https://github.com/Ombi-app/Ombi/compare/v4.11.8...v4.12.0) (2022-02-14) +## [4.16.2](https://github.com/Ombi-app/Ombi/compare/v4.16.1...v4.16.2) (2022-04-07) -### Features +### Bug Fixes -* **radarr:** 4K Requests and Radarr 4K support ([ba88848](https://github.com/Ombi-app/Ombi/commit/ba88848866b0a9dedb1e79b55c4d81a0fd453843)) +* **wizard:** Fixed an issue when using Plex OAuth it could fail setting up ([b743cf4](https://github.com/Ombi-app/Ombi/commit/b743cf4fafa7341ad1b163276f006d7ab0e9dcff)) -## [4.11.8](https://github.com/Ombi-app/Ombi/compare/v4.11.7...v4.11.8) (2022-02-13) +## [4.16.1](https://github.com/Ombi-app/Ombi/compare/v4.16.0...v4.16.1) (2022-04-07) -### Bug Fixes -* **settings:** :bug: Fixed an issue where we were not displaying the excluded keyworks correctly in the TheMovieDbSettings page ([d3b3316](https://github.com/Ombi-app/Ombi/commit/d3b3316cbac18356b2f6b0912a3deb2c183e6534)) +# [4.16.0](https://github.com/Ombi-app/Ombi/compare/v4.15.6...v4.16.0) (2022-04-07) -## [4.11.7](https://github.com/Ombi-app/Ombi/compare/v4.11.6...v4.11.7) (2022-02-12) +## [4.15.6](https://github.com/Ombi-app/Ombi/compare/v4.15.5...v4.15.6) (2022-04-07) ### Bug Fixes -* **notifications:** :bug: This is a fix for some of the duplicate notification issues [#3825](https://github.com/Ombi-app/Ombi/issues/3825) ([22bb422](https://github.com/Ombi-app/Ombi/commit/22bb4226ead2d62e8c2c2c05be47d7da621402e2)) +* **radarr:** Fixed an issue where we couldn't sync radarr content [#4577](https://github.com/Ombi-app/Ombi/issues/4577) ([a5355a3](https://github.com/Ombi-app/Ombi/commit/a5355a3023e6900c4dd1b0da4722d7596c03907f)) + +## [4.15.5](https://github.com/Ombi-app/Ombi/compare/v4.15.4...v4.15.5) (2022-04-06) -## [4.11.6](https://github.com/Ombi-app/Ombi/compare/v4.11.5...v4.11.6) (2022-02-10) -### Bug Fixes +## [4.15.4](https://github.com/Ombi-app/Ombi/compare/v4.15.3...v4.15.4) (2022-03-29) + + -* **plex:** Fixed an issue where in a rare case we couldn't sync the data [#4502](https://github.com/Ombi-app/Ombi/issues/4502) ([191318d](https://github.com/Ombi-app/Ombi/commit/191318ddad5a8148422955bf928f1c49b890e3eb)) +## [4.15.3](https://github.com/Ombi-app/Ombi/compare/v4.15.2...v4.15.3) (2022-03-24) -## [4.11.5](https://github.com/Ombi-app/Ombi/compare/v4.11.4...v4.11.5) (2022-02-05) +## [4.15.2](https://github.com/Ombi-app/Ombi/compare/v4.15.1...v4.15.2) (2022-03-23) ### Bug Fixes -* **sonarr:** Fixed where requesting all seasons would only mark the latest as monitored [#4496](https://github.com/Ombi-app/Ombi/issues/4496) ([cfb85c2](https://github.com/Ombi-app/Ombi/commit/cfb85c23d77626b9ec1d99a6cf76497c438d0338)) +* **metadata:** improved the metadata job to also lookup the media in Plex to see if it has any more uptodate metadata ([83d1a15](https://github.com/Ombi-app/Ombi/commit/83d1a15cc9d0ee91be73bd91c4672cf1bcf2728a)) diff --git a/README.md b/README.md index 32552817e..c8fde6e73 100644 --- a/README.md +++ b/README.md @@ -100,17 +100,17 @@ Here are some of the features Ombi has: - - anojht + + sephrat
- Anojh Thayaparan + Sephrat
- - sephrat + + anojht
- Sephrat + Anojh Thayaparan
@@ -222,10 +222,10 @@ Here are some of the features Ombi has: - - steffokeffo + + grimsan55
- Steffokeffo + Stefan
@@ -286,6 +286,13 @@ Here are some of the features Ombi has: + + + dr3am37 +
+ Dr3amer +
+ mhann @@ -320,15 +327,15 @@ Here are some of the features Ombi has:
Austin Jackson
- + + D34DC3N73R
D34DC3N73R
- - + pooley182 @@ -363,15 +370,15 @@ Here are some of the features Ombi has:
Jeffrey Peters
- + + MariusSchiffer
Marius Schiffer
- - + Qstick @@ -406,15 +413,15 @@ Here are some of the features Ombi has:
Abe Kline
- + + XanderStrike
Alexander Standke
- - + Alasano @@ -449,15 +456,15 @@ Here are some of the features Ombi has:
Chris Lees
- + + cdemi
Christopher Demicoli
- - + Codehhh @@ -492,15 +499,15 @@ Here are some of the features Ombi has:
Devin Buhl
- + + elisspace
Eli
- - + Fish2 @@ -515,6 +522,13 @@ Here are some of the features Ombi has: Haries Ramdhani + + + comigor +
+ Igor Borges +
+ ImgBotApp @@ -528,7 +542,8 @@ Here are some of the features Ombi has:
Jacob Pyke
- + + jamesmacwhite @@ -542,8 +557,7 @@ Here are some of the features Ombi has:
Joe Groocock
- - + errorhandler @@ -571,7 +585,8 @@ Here are some of the features Ombi has:
Kris Klosterman
- + + kmlucy @@ -585,8 +600,14 @@ Here are some of the features Ombi has:
Lightkeeper
- - + + + + Lucane +
+ Lucane +
+ devbymadde @@ -594,13 +615,21 @@ Here are some of the features Ombi has: Madeleine Schönemann + + + marleypowell +
+ Marley +
+ mattmattmatt
Matt
- + + LMaxence @@ -628,8 +657,7 @@ Here are some of the features Ombi has:
Qiming Chen
- - + randallbruder @@ -643,7 +671,8 @@ Here are some of the features Ombi has:
Rob Gökemeijer
- + + sambartik @@ -665,21 +694,28 @@ Here are some of the features Ombi has: Shoghi + + + Teifun2 +
+ Teifun2 +
+ thomasvt1
Thomas Van Tilburg
- - + Tim-Trott
Tim Trott
- + + tombomb @@ -714,15 +750,15 @@ Here are some of the features Ombi has:
Tim OBrien
- - + x-limitless-x
Blake Drumm
- + + camjac251 @@ -744,6 +780,13 @@ Here are some of the features Ombi has: Dorian ALKOUM + + + echel0n +
+ Echel0n +
+ m4tta @@ -766,6 +809,13 @@ Here are some of the features Ombi has: Patrick Weber + + + mkgeeky +
+ Mkgeeky +
+ sir-marv diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 000000000..252736d92 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1,285 @@ +**/bin/ +**/obj/ +**/.angular/ +**/node_modules/ +.gitignore +.git/ + + +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ +Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/ +tools/Cake.CoreCLR +.vscode +tools +.dotnet +Dockerfile + +# .env file contains default environment variables for docker +.env +.git/ \ No newline at end of file diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index a1791494d..e9e5f0fca 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -106,7 +106,7 @@ namespace Ombi.Api.Emby request.AddQueryString("Fields", "ProviderIds,Overview"); - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("IsMissing", "False"); return await Api.Request>(request); } @@ -180,7 +180,7 @@ namespace Ombi.Api.Emby request.AddQueryString("ParentId", parentIdFilder); } - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("IsMissing", "False"); AddHeaders(request, apiKey); @@ -207,7 +207,7 @@ namespace Ombi.Api.Emby request.AddQueryString("IncludeItemTypes", type); request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("IsMissing", "False"); AddHeaders(request, apiKey); @@ -229,7 +229,7 @@ namespace Ombi.Api.Emby request.AddQueryString("ParentId", parentIdFilder); } - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("isMissing", "False"); AddHeaders(request, apiKey); diff --git a/src/Ombi.Api.Jellyfin/JellyfinApi.cs b/src/Ombi.Api.Jellyfin/JellyfinApi.cs index f6afb7912..0604cb984 100644 --- a/src/Ombi.Api.Jellyfin/JellyfinApi.cs +++ b/src/Ombi.Api.Jellyfin/JellyfinApi.cs @@ -82,7 +82,7 @@ namespace Ombi.Api.Jellyfin request.AddQueryString("Fields", "ProviderIds,Overview"); - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("isMissing", "False"); return await Api.Request>(request); } @@ -143,7 +143,7 @@ namespace Ombi.Api.Jellyfin request.AddQueryString("IncludeItemTypes", type); request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("isMissing", "False"); AddHeaders(request, apiKey); @@ -165,7 +165,7 @@ namespace Ombi.Api.Jellyfin request.AddQueryString("ParentId", parentIdFilder); } - request.AddQueryString("IsVirtualItem", "False"); + request.AddQueryString("isMissing", "False"); AddHeaders(request, apiKey); diff --git a/src/Ombi.Api.Plex/Models/Metadata.cs b/src/Ombi.Api.Plex/Models/Metadata.cs index 3481e9b57..97cab391b 100644 --- a/src/Ombi.Api.Plex/Models/Metadata.cs +++ b/src/Ombi.Api.Plex/Models/Metadata.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Newtonsoft.Json; namespace Ombi.Api.Plex.Models { diff --git a/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs b/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs index 4f131d5e3..9142e2b42 100644 --- a/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs +++ b/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs @@ -9,7 +9,7 @@ public class SickRageEpisodeSetStatus { - public Data data { get; set; } + public Data[] data { get; set; } public string message { get; set; } public string result { get; set; } } diff --git a/src/Ombi.Api.Sonarr/SonarrApi.cs b/src/Ombi.Api.Sonarr/SonarrApi.cs index d2df50cd9..50e4f04ff 100644 --- a/src/Ombi.Api.Sonarr/SonarrApi.cs +++ b/src/Ombi.Api.Sonarr/SonarrApi.cs @@ -19,7 +19,7 @@ namespace Ombi.Api.Sonarr protected IApi Api { get; } protected virtual string ApiBaseUrl => "/api/"; - public async Task> GetProfiles(string apiKey, string baseUrl) + public virtual async Task> GetProfiles(string apiKey, string baseUrl) { var request = new Request($"{ApiBaseUrl}profile", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); diff --git a/src/Ombi.Api.Sonarr/SonarrV3Api.cs b/src/Ombi.Api.Sonarr/SonarrV3Api.cs index 64377ee4a..71c230f35 100644 --- a/src/Ombi.Api.Sonarr/SonarrV3Api.cs +++ b/src/Ombi.Api.Sonarr/SonarrV3Api.cs @@ -1,6 +1,8 @@ using System.Net.Http; using System.Collections.Generic; using System.Threading.Tasks; + +using Ombi.Api.Sonarr.Models; using Ombi.Api.Sonarr.Models.V3; namespace Ombi.Api.Sonarr @@ -21,5 +23,12 @@ namespace Ombi.Api.Sonarr return await Api.Request>(request); } + + public override async Task> GetProfiles(string apiKey, string baseUrl) + { + var request = new Request($"{ApiBaseUrl}qualityprofile", baseUrl, HttpMethod.Get); + request.AddHeader("X-Api-Key", apiKey); + return await Api.Request>(request); + } } } diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index cb4cb4ba6..e5114677b 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -136,6 +136,7 @@ namespace Ombi.Core.Engine Status = movieInfo.Status, RequestedDate = model.Is4kRequest ? DateTime.MinValue : DateTime.Now, Approved = false, + Approved4K = false, RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id, Background = movieInfo.BackdropPath, LangCode = model.LanguageCode, @@ -151,7 +152,7 @@ namespace Ombi.Core.Engine var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); requestModel.DigitalReleaseDate = usDates?.ReleaseDate ?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; - + var ruleResults = (await RunRequestRules(requestModel)).ToList(); var ruleResultInError = ruleResults.Find(x => !x.Success); if (ruleResultInError != null) @@ -163,7 +164,7 @@ namespace Ombi.Core.Engine }; } - if (requestModel.Approved) // The rules have auto approved this + if (requestModel.Approved || requestModel.Approved4K) // The rules have auto approved this { var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf, isExisting, is4kRequest); if (requestEngineResult.Result) @@ -333,7 +334,7 @@ namespace Ombi.Core.Engine allRequests = allRequests.Where(x => (x.RequestedDate != DateTime.MinValue && x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value)) || - (x.Has4KRequest && x.Approved4K && !x.Available && (!x.Denied.HasValue || !x.Denied.Value)) + (x.Has4KRequest && x.Approved4K && !x.Available4K && (!x.Denied4K.HasValue || !x.Denied4K.Value)) ); break; case RequestStatus.Available: @@ -533,7 +534,10 @@ namespace Ombi.Core.Engine } else { - x.ShowSubscribe = true; + if (!x.Available && !x.Available4K && (!x.Denied ?? true) && (!x.Denied4K ?? true)) + { + x.ShowSubscribe = true; + } var hasSub = sub.FirstOrDefault(r => r.RequestId == x.Id); x.Subscribed = hasSub != null; } diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs index 682a999eb..ae24e275a 100644 --- a/src/Ombi.Core/Engine/MovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs @@ -16,7 +16,6 @@ using Ombi.Store.Repository; using System; using System.Collections.Generic; using System.Linq; -using System.Security.Principal; using System.Threading.Tasks; namespace Ombi.Core.Engine @@ -161,7 +160,7 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAddAsync(CacheKeys.UpcomingMovies, async () => { var langCode = await DefaultLanguageCode(null); - return await MovieApi.Upcoming(langCode); + return await MovieApi.UpcomingMovies(langCode); }, DateTimeOffset.Now.AddHours(12)); if (result != null) { @@ -216,34 +215,9 @@ namespace Ombi.Core.Engine await RunSearchRules(viewMovie); - // This requires the rules to be run first to populate the RequestId property - await CheckForSubscription(viewMovie); - return viewMovie; } - private async Task CheckForSubscription(SearchMovieViewModel viewModel) - { - // Check if this user requested it - var user = await GetUser(); - if (user == null) - { - return; - } - var request = await RequestService.MovieRequestService.GetAll() - .AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id); - if (request || viewModel.Available) - { - viewModel.ShowSubscribe = false; - } - else - { - viewModel.ShowSubscribe = true; - var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => s.UserId == user.Id - && s.RequestId == viewModel.RequestId && s.RequestType == RequestType.Movie); - viewModel.Subscribed = sub != null; - } - } private async Task ProcessSingleMovie(MovieDbSearchResult movie) { diff --git a/src/Ombi.Core/Engine/MusicRequestEngine.cs b/src/Ombi.Core/Engine/MusicRequestEngine.cs index 640d2ade3..67caa81a2 100644 --- a/src/Ombi.Core/Engine/MusicRequestEngine.cs +++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs @@ -270,7 +270,10 @@ namespace Ombi.Core.Engine } else { - x.ShowSubscribe = true; + if (!x.Available && (!x.Denied ?? false)) + { + x.ShowSubscribe = true; + } var hasSub = sub.FirstOrDefault(r => r.RequestId == x.Id); x.Subscribed = hasSub != null; } diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 5664d276f..8ccc6d17e 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -886,7 +886,10 @@ namespace Ombi.Core.Engine } else { - x.ShowSubscribe = true; + if (!x.Available && (!x.Denied ?? true)) + { + x.ShowSubscribe = true; + } var result = relevantSubs.FirstOrDefault(s => s.RequestId == x.Id); x.Subscribed = result != null; } diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs index 2747f262a..ac2ed9979 100644 --- a/src/Ombi.Core/Engine/TvSearchEngine.cs +++ b/src/Ombi.Core/Engine/TvSearchEngine.cs @@ -106,11 +106,17 @@ namespace Ombi.Core.Engine SeasonNumber = e.season, Episodes = new List() }; + var hasAirDate = e.airstamp.HasValue(); + var airDate = DateTime.MinValue; + if (hasAirDate) + { + hasAirDate = DateTime.TryParse(e.airdate, out airDate); + } newSeason.Episodes.Add(new EpisodeRequests { Url = e.url.ToHttpsUrl(), Title = e.name, - AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue, + AirDate = airDate, EpisodeNumber = e.number, }); @@ -118,12 +124,18 @@ namespace Ombi.Core.Engine } else { + var hasAirDate = e.airstamp.HasValue(); + var airDate = DateTime.MinValue; + if (hasAirDate) + { + hasAirDate = DateTime.TryParse(e.airdate, out airDate); + } // We already have the season, so just add the episode season.Episodes.Add(new EpisodeRequests { Url = e.url.ToHttpsUrl(), Title = e.name, - AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue, + AirDate = airDate, EpisodeNumber = e.number, }); } diff --git a/src/Ombi.Core/Engine/V2/IMultiSearchEngine.cs b/src/Ombi.Core/Engine/V2/IMultiSearchEngine.cs index 7ad8c509a..7666db6fe 100644 --- a/src/Ombi.Core/Engine/V2/IMultiSearchEngine.cs +++ b/src/Ombi.Core/Engine/V2/IMultiSearchEngine.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Models.Search.V2; +using Ombi.TheMovieDbApi.Models; // Due to conflicting Genre models in // Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models @@ -14,5 +15,6 @@ namespace Ombi.Core.Engine.V2 { Task> MultiSearch(string searchTerm, MultiSearchFilter filter, CancellationToken cancellationToken); Task> GetGenres(string media, CancellationToken requestAborted); + Task> GetLanguages(CancellationToken requestAborted); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index 3acd7d1f0..044d79969 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -11,6 +11,7 @@ using Ombi.Core.Models.Search; using Ombi.Core.Models.Search.V2; using Ombi.Core.Models.UI; using Ombi.Core.Rule.Interfaces; +using Ombi.Core.Services; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models; @@ -31,7 +32,8 @@ namespace Ombi.Core.Engine.V2 { public MovieSearchEngineV2(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper, ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s, IRepository sub, - ISettingsService customizationSettings, IMovieRequestEngine movieRequestEngine, IHttpClientFactory httpClientFactory) + ISettingsService customizationSettings, IMovieRequestEngine movieRequestEngine, IHttpClientFactory httpClientFactory, + IFeatureService feature) : base(identity, service, r, um, mem, s, sub) { MovieApi = movApi; @@ -40,6 +42,7 @@ namespace Ombi.Core.Engine.V2 _customizationSettings = customizationSettings; _movieRequestEngine = movieRequestEngine; _client = httpClientFactory.CreateClient(); + _feature = feature; } private IMovieDbApi MovieApi { get; } @@ -48,6 +51,7 @@ namespace Ombi.Core.Engine.V2 private readonly ISettingsService _customizationSettings; private readonly IMovieRequestEngine _movieRequestEngine; private readonly HttpClient _client; + private readonly IFeatureService _feature; public async Task GetFullMovieInformation(int theMovieDbId, CancellationToken cancellationToken, string langCode = null) { @@ -148,15 +152,15 @@ namespace Ombi.Core.Engine.V2 { var langCode = await DefaultLanguageCode(null); - //var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); + var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); var results = new List(); - //foreach (var pagesToLoad in pages) - //{ - var apiResult = await MovieApi.AdvancedSearch(model, cancellationToken); - //results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); - //} - return await TransformMovieResultsToResponse(apiResult); + foreach (var pagesToLoad in pages) + { + var apiResult = await MovieApi.AdvancedSearch(model, pagesToLoad.Page, cancellationToken); + results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); + } + return await TransformMovieResultsToResponse(results); } /// @@ -196,14 +200,19 @@ namespace Ombi.Core.Engine.V2 public async Task> NowPlayingMovies(int currentPosition, int amountToLoad) { var langCode = await DefaultLanguageCode(null); + var isOldTrendingSourceEnabled = await _feature.FeatureEnabled(FeatureNames.OldTrendingSource); var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); var results = new List(); foreach (var pagesToLoad in pages) { + var search = () => (isOldTrendingSourceEnabled) ? + MovieApi.NowPlaying(langCode, pagesToLoad.Page) + : MovieApi.TrendingMovies(langCode, pagesToLoad.Page); + var apiResult = await Cache.GetOrAddAsync(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode, - () => MovieApi.NowPlaying(langCode, pagesToLoad.Page), DateTimeOffset.Now.AddHours(12)); + search, DateTimeOffset.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } return await TransformMovieResultsToResponse(results); @@ -278,7 +287,7 @@ namespace Ombi.Core.Engine.V2 var result = await Cache.GetOrAddAsync(CacheKeys.UpcomingMovies, async () => { var langCode = await DefaultLanguageCode(null); - return await MovieApi.Upcoming(langCode); + return await MovieApi.UpcomingMovies(langCode); }, DateTimeOffset.Now.AddHours(12)); if (result != null) { @@ -298,7 +307,7 @@ namespace Ombi.Core.Engine.V2 foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAddAsync(nameof(UpcomingMovies) + pagesToLoad.Page + langCode, - () => MovieApi.Upcoming(langCode, pagesToLoad.Page), DateTimeOffset.Now.AddHours(12)); + () => MovieApi.UpcomingMovies(langCode, pagesToLoad.Page), DateTimeOffset.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } return await TransformMovieResultsToResponse(results); @@ -393,8 +402,6 @@ namespace Ombi.Core.Engine.V2 await RunSearchRules(viewMovie); - // This requires the rules to be run first to populate the RequestId property - await CheckForSubscription(viewMovie); var mapped = Mapper.Map(movie); mapped.Available = viewMovie.Available; @@ -406,8 +413,6 @@ namespace Ombi.Core.Engine.V2 mapped.PlexUrl = viewMovie.PlexUrl; mapped.EmbyUrl = viewMovie.EmbyUrl; mapped.JellyfinUrl = viewMovie.JellyfinUrl; - mapped.Subscribed = viewMovie.Subscribed; - mapped.ShowSubscribe = viewMovie.ShowSubscribe; mapped.DigitalReleaseDate = viewMovie.DigitalReleaseDate; mapped.RequestedDate4k = viewMovie.RequestedDate4k; mapped.Approved4K = viewMovie.Approved4K; @@ -429,8 +434,6 @@ namespace Ombi.Core.Engine.V2 var mappedMovie = Mapper.Map(movie); await RunSearchRules(mappedMovie); - // This requires the rules to be run first to populate the RequestId property - await CheckForSubscription(mappedMovie); var mapped = Mapper.Map(movie); mapped.Available = movie.Available; @@ -440,8 +443,6 @@ namespace Ombi.Core.Engine.V2 mapped.PlexUrl = movie.PlexUrl; mapped.EmbyUrl = movie.EmbyUrl; mapped.JellyfinUrl = movie.JellyfinUrl; - mapped.Subscribed = movie.Subscribed; - mapped.ShowSubscribe = movie.ShowSubscribe; mapped.ReleaseDate = movie.ReleaseDate; } return viewMovie; @@ -470,34 +471,9 @@ namespace Ombi.Core.Engine.V2 await RunSearchRules(viewMovie); - // This requires the rules to be run first to populate the RequestId property - await CheckForSubscription(viewMovie); - return viewMovie; } - private async Task CheckForSubscription(SearchViewModel viewModel) - { - // Check if this user requested it - var user = await GetUser(); - if (user == null) - { - return; - } - var request = await RequestService.MovieRequestService.GetAll() - .AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id); - if (request) - { - viewModel.ShowSubscribe = false; - } - else - { - viewModel.ShowSubscribe = true; - var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => s.UserId == user.Id - && s.RequestId == viewModel.RequestId && s.RequestType == RequestType.Movie); - viewModel.Subscribed = sub != null; - } - } public async Task GetMovieInfoByImdbId(string imdbId, CancellationToken cancellationToken) { diff --git a/src/Ombi.Core/Engine/V2/MultiSearchEngine.cs b/src/Ombi.Core/Engine/V2/MultiSearchEngine.cs index 362a79e43..3f96f1598 100644 --- a/src/Ombi.Core/Engine/V2/MultiSearchEngine.cs +++ b/src/Ombi.Core/Engine/V2/MultiSearchEngine.cs @@ -17,6 +17,7 @@ using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Ombi.TheMovieDbApi.Models; // Due to conflicting Genre models in // Ombi.TheMovieDbApi.Models and Ombi.Api.TheMovieDb.Models @@ -124,5 +125,9 @@ namespace Ombi.Core.Engine.V2 var lang = await DefaultLanguageCode(null); return await _movieDbApi.GetGenres(media, cancellationToken, lang); } + public async Task> GetLanguages(CancellationToken cancellationToken) + { + return await _movieDbApi.GetLanguages(cancellationToken); + } } } diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 344ced813..ee21db8dd 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -26,6 +26,7 @@ using System.Diagnostics; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Models.UI; using Ombi.Core.Helpers; +using Ombi.Core.Services; namespace Ombi.Core.Engine.V2 { @@ -37,10 +38,12 @@ namespace Ombi.Core.Engine.V2 private readonly IMovieDbApi _movieApi; private readonly ISettingsService _customization; private readonly ITvRequestEngine _requestEngine; + private readonly IFeatureService _feature; public TvSearchEngineV2(ICurrentUser identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService s, - IRepository sub, IMovieDbApi movieApi, ISettingsService customization, ITvRequestEngine requestEngine) + IRepository sub, IMovieDbApi movieApi, ISettingsService customization, ITvRequestEngine requestEngine, + IFeatureService feature) : base(identity, service, r, um, memCache, s, sub) { _tvMaze = tvMaze; @@ -49,6 +52,7 @@ namespace Ombi.Core.Engine.V2 _movieApi = movieApi; _customization = customization; _requestEngine = requestEngine; + _feature = feature; } @@ -132,15 +136,19 @@ namespace Ombi.Core.Engine.V2 } public async Task> Trending(int currentlyLoaded, int amountToLoad) - { + { var langCode = await DefaultLanguageCode(null); + var isOldTrendingSourceEnabled = await _feature.FeatureEnabled(FeatureNames.OldTrendingSource); var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit); var results = new List(); foreach (var pagesToLoad in pages) { + var search = ( async () => (isOldTrendingSourceEnabled) ? + await _movieApi.TopRatedTv(langCode, pagesToLoad.Page) + : await _movieApi.TrendingTv(langCode, pagesToLoad.Page)); var apiResult = await Cache.GetOrAddAsync(nameof(Trending) + langCode + pagesToLoad.Page, - async () => await _movieApi.TopRatedTv(langCode, pagesToLoad.Page), DateTimeOffset.Now.AddHours(12)); + search, DateTimeOffset.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } @@ -264,11 +272,17 @@ namespace Ombi.Core.Engine.V2 Overview = tvSeason.overview, Episodes = new List() }; + var hasAirDate = episode.air_date.HasValue(); + var airDate = DateTime.MinValue; + if (hasAirDate) + { + DateTime.TryParse(episode.air_date, out airDate); + } newSeason.Episodes.Add(new EpisodeRequests { //Url = episode...ToHttpsUrl(), Title = episode.name, - AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + AirDate = airDate, EpisodeNumber = episode.episode_number, }); @@ -276,12 +290,18 @@ namespace Ombi.Core.Engine.V2 } else { + var hasAirDate = episode.air_date.HasValue(); + var airDate = DateTime.MinValue; + if (hasAirDate) + { + DateTime.TryParse(episode.air_date, out airDate); + } // We already have the season, so just add the episode season.Episodes.Add(new EpisodeRequests { //Url = e.url.ToHttpsUrl(), Title = episode.name, - AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + AirDate = airDate, EpisodeNumber = episode.episode_number, }); } diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index fc66d197d..5fe378465 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -261,9 +261,16 @@ namespace Ombi.Core.Helpers return this; } - private DateTime FormatDate(string date) + private static DateTime FormatDate(string date) { - return string.IsNullOrEmpty(date) ? DateTime.MinValue : DateTime.Parse(date); + if (date.HasValue()) + { + if (DateTime.TryParse(date, out var d)) + { + return d; + } + } + return DateTime.MinValue; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs index 4053109da..5bb4e61fd 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs @@ -9,6 +9,7 @@ using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository.Requests; using System.Threading; +using Ombi.Helpers; namespace Ombi.Core.Helpers { @@ -255,9 +256,16 @@ namespace Ombi.Core.Helpers return this; } - private DateTime FormatDate(string date) + private static DateTime FormatDate(string date) { - return string.IsNullOrEmpty(date) ? DateTime.MinValue : DateTime.Parse(date); + if (date.HasValue()) + { + if (DateTime.TryParse(date, out var d)) + { + return d; + } + } + return DateTime.MinValue; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchViewModel.cs b/src/Ombi.Core/Models/Search/SearchViewModel.cs index e1a2f2850..4dda0444e 100644 --- a/src/Ombi.Core/Models/Search/SearchViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchViewModel.cs @@ -36,6 +36,7 @@ namespace Ombi.Core.Models.Search [Obsolete("Use request service instead")] public bool Subscribed { get; set; } [NotMapped] + [Obsolete("Use request service instead")] public bool ShowSubscribe { get; set; } } } diff --git a/src/Ombi.Core/Senders/MusicSender.cs b/src/Ombi.Core/Senders/MusicSender.cs index 6390578dd..260b008fe 100644 --- a/src/Ombi.Core/Senders/MusicSender.cs +++ b/src/Ombi.Core/Senders/MusicSender.cs @@ -70,7 +70,7 @@ namespace Ombi.Core.Senders } - return new SenderResult { Success = false, Sent = false, Message = "Something went wrong!" }; + return new SenderResult { Success = false, Sent = false }; } private async Task SendToLidarr(AlbumRequest model, LidarrSettings settings) diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 3676d05ee..22f6aadcd 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -133,8 +133,7 @@ namespace Ombi.Core.Senders return new SenderResult { - Success = false, - Message = "Something went wrong!" + Success = false }; } @@ -343,8 +342,6 @@ namespace Ombi.Core.Senders await Task.Delay(500); } - var seriesChanges = false; - foreach (var season in model.SeasonRequests) { foreach (var ep in season.Episodes) @@ -359,72 +356,36 @@ namespace Ombi.Core.Senders } existingSeason = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); - var sonarrEpisodeList = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber).ToList(); - var sonarrEpCount = sonarrEpisodeList.Count; - var ourRequestCount = season.Episodes.Count; - - var ourEpisodes = season.Episodes.Select(x => x.EpisodeNumber).ToList(); - var unairedEpisodes = sonarrEpisodeList.Where(x => x.airDateUtc > DateTime.UtcNow).Select(x => x.episodeNumber).ToList(); - //// Check if we have requested all the latest episodes, if we have then monitor - //// NOTE, not sure if needed since ombi ui displays future episodes anyway... - //ourEpisodes.AddRange(unairedEpisodes); - //var distinctEpisodes = ourEpisodes.Distinct().ToList(); - //var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes); - - if (sonarrEpCount == ourRequestCount /*|| !missingEpisodes.Any()*/) + // Make sure this season is set to monitored + if (!existingSeason.monitored) { - // We have the same amount of requests as all of the episodes in the season. + // We need to monitor it, problem being is all episodes will now be monitored + // So we need to monitor the series but unmonitor every episode existingSeason.monitored = true; - seriesChanges = true; + var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber); + sea.monitored = true; - // We do not need to update the episodes as marking the season as monitored will mark the episodes as monitored. - var seasonToUpdate = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); - seasonToUpdate.monitored = true; // Update by ref - } - else - { - // Make sure this season is set to monitored - if (!existingSeason.monitored) + result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri); + var epToUnmonitored = new List(); + var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member + foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList()) { - // We need to monitor it, problem being is all episodes will now be monitored - // So we need to monitor the series but unmonitor every episode - // Except the episodes that are already monitored before we update the series (we do not want to unmonitored episodes that are monitored beforehand) - existingSeason.monitored = true; - var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber); - sea.monitored = true; - //var previouslyMonitoredEpisodes = sonarrEpList.Where(x => - // x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this - result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri); - var epToUnmonitored = new List(); - var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member - foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList()) - { - //if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber)) - //{ - // // This was previously monitored. - // continue; - //} - ep.monitored = false; - epToUnmonitored.Add(ep); - } - - foreach (var epToUpdate in epToUnmonitored) - { - await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); - } + ep.monitored = false; + epToUnmonitored.Add(ep); } - // Now update the episodes that need updating - foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber)) + + foreach (var epToUpdate in epToUnmonitored) { await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); } } - } - if (seriesChanges) - { - await SonarrApi.SeasonPass(s.ApiKey, s.FullUri, result); + // Now update the episodes that need updating + foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber)) + { + await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); + } } if (!s.AddOnly) diff --git a/src/Ombi.Helpers.Tests/PagnationHelperTests.cs b/src/Ombi.Helpers.Tests/PagnationHelperTests.cs index 4b058c403..44bbfdd8e 100644 --- a/src/Ombi.Helpers.Tests/PagnationHelperTests.cs +++ b/src/Ombi.Helpers.Tests/PagnationHelperTests.cs @@ -83,6 +83,8 @@ namespace Ombi.Helpers.Tests .SetName("PaginationPosition_Load_SecondHalf_FirstPage_FirstHalf_SecondPage"); yield return new TestCaseData(0, 40, 20, new List { new MultiplePagesTestData(1, 20, 0), new MultiplePagesTestData(2, 20, 0) }) .SetName("PaginationPosition_Load_Full_First_And_SecondPage"); + yield return new TestCaseData(40, 40, 20, new List { new MultiplePagesTestData(3, 20, 0), new MultiplePagesTestData(4, 20, 0) }) + .SetName("PaginationPosition_Load_Full_Third_And_ForthPage"); yield return new TestCaseData(35, 15, 20, new List { new MultiplePagesTestData(2, 5, 15), new MultiplePagesTestData(3, 10, 0) }) .SetName("PaginationPosition_Load_EndSecondPage_Beginning_ThirdPage"); yield return new TestCaseData(18, 22, 20, new List { new MultiplePagesTestData(1, 2, 18), new MultiplePagesTestData(2, 20, 0) }) diff --git a/src/Ombi.Helpers/PaginationHelper.cs b/src/Ombi.Helpers/PaginationHelper.cs index 520f56b19..2caafe186 100644 --- a/src/Ombi.Helpers/PaginationHelper.cs +++ b/src/Ombi.Helpers/PaginationHelper.cs @@ -17,7 +17,7 @@ namespace Ombi.Helpers var lastPage = lastItemIndex / maxItemsPerPage + 1; var stopPos = lastItemIndex % maxItemsPerPage + 1; - while (currentlyLoaded > maxItemsPerPage) + while (currentlyLoaded >= maxItemsPerPage) { currentlyLoaded -= maxItemsPerPage; } diff --git a/src/Ombi.I18n/Resources/Texts.de.resx b/src/Ombi.I18n/Resources/Texts.de.resx index a222d02b0..91a4aced4 100644 --- a/src/Ombi.I18n/Resources/Texts.de.resx +++ b/src/Ombi.I18n/Resources/Texts.de.resx @@ -139,7 +139,7 @@ Episoden: - Powered by + Betrieben durch Abbestellen @@ -148,9 +148,9 @@ Album - Movie + Film - TV Show + TV-Serie \ No newline at end of file diff --git a/src/Ombi.I18n/Resources/Texts.pl.resx b/src/Ombi.I18n/Resources/Texts.pl.resx index c8e5e5ee1..f7833ced6 100644 --- a/src/Ombi.I18n/Resources/Texts.pl.resx +++ b/src/Ombi.I18n/Resources/Texts.pl.resx @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Nowy Album + Nowe Albumy - Nowy film + Nowe filmy - Nowy telewizor + Nowe seriale Gatunki: @@ -136,21 +136,21 @@ Sezon: - Odcinków: + Odcinki: Wspierane przez - Wypisać się + Wypisz się Album - Movie + Film - TV Show + Serial \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index 9d88c5907..8e8b75e45 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -7,10 +7,12 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Core; using Ombi.Core.Notifications; +using Ombi.Core.Services; using Ombi.Helpers; using Ombi.Hubs; using Ombi.Notifications.Models; using Ombi.Schedule.Jobs.Ombi; +using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; @@ -21,7 +23,7 @@ namespace Ombi.Schedule.Jobs.Emby public class EmbyAvaliabilityChecker : IEmbyAvaliabilityChecker { public EmbyAvaliabilityChecker(IEmbyContentRepository repo, ITvRequestRepository t, IMovieRequestRepository m, - INotificationHelper n, ILogger log, IHubContext notification) + INotificationHelper n, ILogger log, IHubContext notification, IFeatureService featureService) { _repo = repo; _tvRepo = t; @@ -29,6 +31,7 @@ namespace Ombi.Schedule.Jobs.Emby _notificationService = n; _log = log; _notification = notification; + _featureService = featureService; } private readonly ITvRequestRepository _tvRepo; @@ -37,6 +40,7 @@ namespace Ombi.Schedule.Jobs.Emby private readonly INotificationHelper _notificationService; private readonly ILogger _log; private readonly IHubContext _notification; + private readonly IFeatureService _featureService; public async Task Execute(IJobExecutionContext job) { @@ -53,6 +57,7 @@ namespace Ombi.Schedule.Jobs.Emby private async Task ProcessMovies() { + var feature4kEnabled = await _featureService.FeatureEnabled(FeatureNames.Movie4KRequests); var movies = _movieRepo.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available || (!x.Available4K && x.Has4KRequest)); foreach (var movie in movies) @@ -85,8 +90,8 @@ namespace Ombi.Schedule.Jobs.Emby notify = true; } - // If we have a non-4k versison then mark as available - if (embyContent.Quality != null && !movie.Available) + // If we have a non-4k version or we don't care about versions, then mark as available + if (!movie.Available && ( !feature4kEnabled || embyContent.Quality != null )) { movie.Available = true; movie.MarkedAsAvailable = DateTime.Now; diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index b4d61f93d..27cdf0f3a 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -157,9 +157,20 @@ namespace Ombi.Schedule.Jobs.Emby } var existingTv = await _repo.GetByEmbyId(tvShow.Id); + + if (existingTv != null && + ( existingTv.ImdbId != tvShow.ProviderIds?.Imdb + || existingTv.TheMovieDbId != tvShow.ProviderIds?.Tmdb + || existingTv.TvDbId != tvShow.ProviderIds?.Tvdb)) + { + _logger.LogDebug($"Series '{tvShow.Name}' has different IDs, probably a reidentification."); + await _repo.DeleteTv(existingTv); + existingTv = null; + } + if (existingTv == null) { - _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); + _logger.LogDebug("Adding TV Show {0}", tvShow.Name); mediaToAdd.Add(new EmbyContent { TvDbId = tvShow.ProviderIds?.Tvdb, @@ -265,23 +276,21 @@ namespace Ombi.Schedule.Jobs.Emby return; } _logger.LogDebug($"Adding new movie {movieInfo.Name}"); - - content.Add(new EmbyContent - { - ImdbId = movieInfo.ProviderIds.Imdb, - TheMovieDbId = movieInfo.ProviderIds?.Tmdb, - Title = movieInfo.Name, - Type = MediaType.Movie, - EmbyId = movieInfo.Id, - Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname), - AddedAt = DateTime.UtcNow, - Quality = has4K ? null : quality, - Has4K = has4K - }); + var newMovie = new EmbyContent(); + newMovie.AddedAt = DateTime.UtcNow; + MapEmbyContent(newMovie, movieInfo, server, has4K, quality); + content.Add(newMovie); } else { - if (!quality.Equals(existingMovie?.Quality, StringComparison.InvariantCultureIgnoreCase)) + var movieHasChanged = false; + if (existingMovie.ImdbId != movieInfo.ProviderIds.Imdb || existingMovie.TheMovieDbId != movieInfo.ProviderIds.Tmdb) + { + _logger.LogDebug($"Updating existing movie '{movieInfo.Name}'"); + MapEmbyContent(existingMovie, movieInfo, server, has4K, quality); + movieHasChanged = true; + } + else if (!quality.Equals(existingMovie?.Quality, StringComparison.InvariantCultureIgnoreCase)) { _logger.LogDebug($"We have found another quality for Movie '{movieInfo.Name}', Quality: '{quality}'"); existingMovie.Quality = has4K ? null : quality; @@ -290,6 +299,11 @@ namespace Ombi.Schedule.Jobs.Emby // Probably could refactor here // If a 4k movie comes in (we don't store the quality on 4k) // it will always get updated even know it's not changed + movieHasChanged = true; + } + + if (movieHasChanged) + { toUpdate.Add(existingMovie); } else @@ -300,6 +314,17 @@ namespace Ombi.Schedule.Jobs.Emby } } + private void MapEmbyContent(EmbyContent content, EmbyMovie movieInfo, EmbyServers server, bool has4K, string quality){ + content.ImdbId = movieInfo.ProviderIds.Imdb; + content.TheMovieDbId = movieInfo.ProviderIds?.Tmdb; + content.Title = movieInfo.Name; + content.Type = MediaType.Movie; + content.EmbyId = movieInfo.Id; + content.Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname); + content.Quality = has4K ? null : quality; + content.Has4K = has4K; + } + private bool ValidateSettings(EmbyServers server) { if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey)) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index b3c1ffbab..5293d30b4 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -130,12 +130,6 @@ namespace Ombi.Schedule.Jobs.Emby { processed++; - if (ep.LocationType?.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase) ?? false) - { - // For some reason Emby is not respecting the `IsVirtualItem` field. - continue; - } - // Let's make sure we have the parent request, stop those pesky forign key errors, // Damn me having data integrity var parent = await _repo.GetByEmbyId(ep.SeriesId); @@ -176,18 +170,26 @@ namespace Ombi.Schedule.Jobs.Emby if (ep.IndexNumberEnd.HasValue && ep.IndexNumberEnd.Value != ep.IndexNumber) { - epToAdd.Add(new EmbyEpisode + int episodeNumber = ep.IndexNumber; + do { - EmbyId = ep.Id, - EpisodeNumber = ep.IndexNumberEnd.Value, - SeasonNumber = ep.ParentIndexNumber, - ParentId = ep.SeriesId, - TvDbId = ep.ProviderIds.Tvdb, - TheMovieDbId = ep.ProviderIds.Tmdb, - ImdbId = ep.ProviderIds.Imdb, - Title = ep.Name, - AddedAt = DateTime.UtcNow - }); + _logger.LogDebug($"Multiple-episode file detected. Adding episode ${episodeNumber}"); + episodeNumber++; + epToAdd.Add(new EmbyEpisode + { + EmbyId = ep.Id, + EpisodeNumber = episodeNumber, + SeasonNumber = ep.ParentIndexNumber, + ParentId = ep.SeriesId, + TvDbId = ep.ProviderIds.Tvdb, + TheMovieDbId = ep.ProviderIds.Tmdb, + ImdbId = ep.ProviderIds.Imdb, + Title = ep.Name, + AddedAt = DateTime.UtcNow + }); + + } while (episodeNumber < ep.IndexNumberEnd.Value); + } } } diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs index b1dafa71f..79eb5d6a4 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs @@ -33,9 +33,11 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Core; +using Ombi.Core.Services; using Ombi.Helpers; using Ombi.Hubs; using Ombi.Notifications.Models; +using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; @@ -46,7 +48,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin public class JellyfinAvaliabilityChecker : IJellyfinAvaliabilityChecker { public JellyfinAvaliabilityChecker(IJellyfinContentRepository repo, ITvRequestRepository t, IMovieRequestRepository m, - INotificationHelper n, ILogger log, IHubContext notification) + INotificationHelper n, ILogger log, IHubContext notification, IFeatureService featureService) { _repo = repo; _tvRepo = t; @@ -54,6 +56,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin _notificationService = n; _log = log; _notification = notification; + _featureService = featureService; } private readonly ITvRequestRepository _tvRepo; @@ -62,6 +65,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin private readonly INotificationHelper _notificationService; private readonly ILogger _log; private readonly IHubContext _notification; + private readonly IFeatureService _featureService; public async Task Execute(IJobExecutionContext job) { @@ -78,6 +82,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin private async Task ProcessMovies() { + var feature4kEnabled = await _featureService.FeatureEnabled(FeatureNames.Movie4KRequests); var movies = _movieRepo.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available || (!x.Available4K && x.Has4KRequest)); foreach (var movie in movies) @@ -110,8 +115,8 @@ namespace Ombi.Schedule.Jobs.Jellyfin notify = true; } - // If we have a non-4k versison then mark as available - if (jellyfinContent.Quality != null && !movie.Available) + // If we have a non-4k version or we don't care about versions, then mark as available + if (!movie.Available && ( !feature4kEnabled || jellyfinContent.Quality != null )) { movie.Available = true; movie.MarkedAsAvailable = DateTime.Now; diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs index 060c974e7..5519b3413 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs @@ -132,9 +132,20 @@ namespace Ombi.Schedule.Jobs.Jellyfin } var existingTv = await _repo.GetByJellyfinId(tvShow.Id); + + if (existingTv != null && + ( existingTv.ImdbId != tvShow.ProviderIds?.Imdb + || existingTv.TheMovieDbId != tvShow.ProviderIds?.Tmdb + || existingTv.TvDbId != tvShow.ProviderIds?.Tvdb)) + { + _logger.LogDebug($"Series '{tvShow.Name}' has different IDs, probably a reidentification."); + await _repo.DeleteTv(existingTv); + existingTv = null; + } + if (existingTv == null) { - _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); + _logger.LogDebug("Adding TV Show {0}", tvShow.Name); mediaToAdd.Add(new JellyfinContent { TvDbId = tvShow.ProviderIds?.Tvdb, @@ -230,22 +241,21 @@ namespace Ombi.Schedule.Jobs.Jellyfin return; } _logger.LogDebug($"Adding new movie {movieInfo.Name}"); - content.Add(new JellyfinContent - { - ImdbId = movieInfo.ProviderIds.Imdb, - TheMovieDbId = movieInfo.ProviderIds?.Tmdb, - Title = movieInfo.Name, - Type = MediaType.Movie, - JellyfinId = movieInfo.Id, - Url = JellyfinHelper.GetJellyfinMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname), - AddedAt = DateTime.UtcNow, - Quality = has4K ? null : quality, - Has4K = has4K - }); + var newMovie = new JellyfinContent(); + newMovie.AddedAt = DateTime.UtcNow; + MapJellyfinMovie(newMovie, movieInfo, server, has4K, quality); + content.Add(newMovie);; } else { - if (!quality.Equals(existingMovie?.Quality, StringComparison.InvariantCultureIgnoreCase)) + var movieHasChanged = false; + if (existingMovie.ImdbId != movieInfo.ProviderIds.Imdb || existingMovie.TheMovieDbId != movieInfo.ProviderIds.Tmdb) + { + _logger.LogDebug($"Updating existing movie '{movieInfo.Name}'"); + MapJellyfinMovie(existingMovie, movieInfo, server, has4K, quality); + movieHasChanged = true; + } + else if (!quality.Equals(existingMovie?.Quality, StringComparison.InvariantCultureIgnoreCase)) { _logger.LogDebug($"We have found another quality for Movie '{movieInfo.Name}', Quality: '{quality}'"); existingMovie.Quality = has4K ? null : quality; @@ -255,6 +265,12 @@ namespace Ombi.Schedule.Jobs.Jellyfin // If a 4k movie comes in (we don't store the quality on 4k) // it will always get updated even know it's not changed toUpdate.Add(existingMovie); + movieHasChanged = true; + } + + if (movieHasChanged) + { + toUpdate.Add(existingMovie); } else { @@ -264,6 +280,18 @@ namespace Ombi.Schedule.Jobs.Jellyfin } } + private void MapJellyfinMovie(JellyfinContent content, JellyfinMovie movieInfo, JellyfinServers server, bool has4K, string quality) + { + content.ImdbId = movieInfo.ProviderIds.Imdb; + content.TheMovieDbId = movieInfo.ProviderIds?.Tmdb; + content.Title = movieInfo.Name; + content.Type = MediaType.Movie; + content.JellyfinId = movieInfo.Id; + content.Url = JellyfinHelper.GetJellyfinMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname); + content.Quality = has4K ? null : quality; + content.Has4K = has4K; + } + private bool ValidateSettings(JellyfinServers server) { if (server?.Ip == null || string.IsNullOrEmpty(server?.ApiKey)) diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs index 6e2daa7b3..31b880114 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs @@ -106,12 +106,6 @@ namespace Ombi.Schedule.Jobs.Jellyfin { processed++; - if (ep.LocationType?.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase) ?? false) - { - // For some reason Jellyfin is not respecting the `IsVirtualItem` field. - continue; - } - // Let's make sure we have the parent request, stop those pesky forign key errors, // Damn me having data integrity var parent = await _repo.GetByJellyfinId(ep.SeriesId); @@ -152,18 +146,25 @@ namespace Ombi.Schedule.Jobs.Jellyfin if (ep.IndexNumberEnd.HasValue && ep.IndexNumberEnd.Value != ep.IndexNumber) { - epToAdd.Add(new JellyfinEpisode + int episodeNumber = ep.IndexNumber; + do { - JellyfinId = ep.Id, - EpisodeNumber = ep.IndexNumberEnd.Value, - SeasonNumber = ep.ParentIndexNumber, - ParentId = ep.SeriesId, - TvDbId = ep.ProviderIds.Tvdb, - TheMovieDbId = ep.ProviderIds.Tmdb, - ImdbId = ep.ProviderIds.Imdb, - Title = ep.Name, - AddedAt = DateTime.UtcNow - }); + _logger.LogDebug($"Multiple-episode file detected. Adding episode ${episodeNumber}"); + episodeNumber++; + epToAdd.Add(new JellyfinEpisode + { + JellyfinId = ep.Id, + EpisodeNumber = episodeNumber, + SeasonNumber = ep.ParentIndexNumber, + ParentId = ep.SeriesId, + TvDbId = ep.ProviderIds.Tvdb, + TheMovieDbId = ep.ProviderIds.Tmdb, + ImdbId = ep.ProviderIds.Imdb, + Title = ep.Name, + AddedAt = DateTime.UtcNow + }); + + } while (episodeNumber < ep.IndexNumberEnd.Value); } } } diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index a11a9ff80..0782a796a 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -409,7 +409,9 @@ namespace Ombi.Schedule.Jobs.Ombi private HashSet FilterEpisodes(IEnumerable source, IEnumerable recentlyAdded) { var itemsToReturn = new HashSet(); - foreach (var ep in source.Where(x => x.Series.HasTvDb)) + foreach (var ep in source.Where(x => x.Series.HasTvDb // needed for recentlyAddedLog + && x.Series.HasTheMovieDb // needed to fetch info to publish, this is just in case... + )) { var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) @@ -501,16 +503,11 @@ namespace Ombi.Schedule.Jobs.Ombi foreach (var content in ordered) { int.TryParse(content.TheMovieDbId, out var movieDbId); - if (movieDbId <= 0) - { - _log.LogWarning($"{content.Title} does not have a TMDB ID, it won't be published."); - continue; - } var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId, defaultLanguageCode); var mediaurl = content.Url; if (info == null) { - _log.LogWarning($"TMDB does not know movie {content.Title}, it won't be published."); + _log.LogError($"TMDB does not know movie {content.Title}. This shouldn't happen because our media server knows it as ID '{movieDbId}'."); continue; } try @@ -665,48 +662,18 @@ namespace Ombi.Schedule.Jobs.Ombi var orderedTv = series.OrderByDescending(x => x.AddedAt); foreach (var t in orderedTv) { - if (!t.HasTvDb) - { - // We may need to use themoviedb for the imdbid or their own id to get info - if (t.HasTheMovieDb) - { - int.TryParse(t.TheMovieDbId, out var movieId); - var externals = await _movieApi.GetTvExternals(movieId); - if (externals == null || externals.tvdb_id <= 0) - { - // needed later for recently added log - _log.LogWarning($"{t.Title} has no TVDB ID, it won't be published."); - continue; - } - t.TvDbId = externals.tvdb_id.ToString(); - } - // WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out. - //else if(t.HasImdb) - //{ - // // Check the imdbid - // var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id); - // if (externals?.tv_results == null || externals.tv_results.Length <= 0) - // { - // continue; - // } - // t.TvDbId = externals.tv_results.FirstOrDefault()..ToString(); - //} - - } - try { var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId, languageCode); if (tvInfo == null) { - _log.LogWarning($"TMDB does not know series {t.Title}, it won't be published."); + _log.LogError($"TMDB does not know series {t.Title}. This shouldn't happen because our media server knows it as ID '{t.TheMovieDbId}'."); continue; } if (tvInfo.backdrop_path.HasValue()) { - AddBackgroundInsideTable($"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}"); } else @@ -732,7 +699,7 @@ namespace Ombi.Schedule.Jobs.Ombi } catch (Exception e) { - _log.LogError(e, "Error when processing Plex TV {0}", t.Title); + _log.LogError(e, "Error when processing TV {0}", t.Title); } finally { diff --git a/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs b/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs index c2df1b9b3..cbb0233a0 100644 --- a/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs @@ -11,5 +11,7 @@ namespace Ombi.Core.Settings.Models.External public List ExcludedMovieGenreIds { get; set; } public List ExcludedTvGenreIds { get; set; } + + public List OriginalLanguages { get; set; } } } diff --git a/src/Ombi.Settings/Settings/Models/FeatureSettings.cs b/src/Ombi.Settings/Settings/Models/FeatureSettings.cs index 6a285d422..9d0149e5d 100644 --- a/src/Ombi.Settings/Settings/Models/FeatureSettings.cs +++ b/src/Ombi.Settings/Settings/Models/FeatureSettings.cs @@ -20,5 +20,6 @@ namespace Ombi.Settings.Settings.Models public static class FeatureNames { public const string Movie4KRequests = nameof(Movie4KRequests); + public const string OldTrendingSource = nameof(OldTrendingSource); } } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 0681d9ca0..77298e719 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -212,7 +212,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Your TV request is now partially available! Season {PartiallyAvailableSeasonNumber} Episodes {PartiallyAvailableEpisodeNumbers}!", + Message = "Your TV request for {Title} is now partially available! Season {PartiallyAvailableSeasonNumber} Episodes {PartiallyAvailableEpisodeNumbers}!", Subject = "{ApplicationName}: Partially Available Request!", Agent = agent, Enabled = true, diff --git a/src/Ombi.Store/Repository/EmbyContentRepository.cs b/src/Ombi.Store/Repository/EmbyContentRepository.cs index df88d8292..8f9904ebb 100644 --- a/src/Ombi.Store/Repository/EmbyContentRepository.cs +++ b/src/Ombi.Store/Repository/EmbyContentRepository.cs @@ -102,6 +102,13 @@ namespace Ombi.Store.Repository return InternalSaveChanges(); } + public override async Task DeleteTv(EmbyContent tv) + { + var episodesToDelete = GetAllEpisodes().Cast().Where(x => x.ParentId == tv.EmbyId).ToList(); + Db.EmbyEpisode.RemoveRange(episodesToDelete); + await Delete(tv); + } + public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Emby; } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IMediaServerContentRepository.cs b/src/Ombi.Store/Repository/IMediaServerContentRepository.cs index db4835a16..3f165b7e6 100644 --- a/src/Ombi.Store/Repository/IMediaServerContentRepository.cs +++ b/src/Ombi.Store/Repository/IMediaServerContentRepository.cs @@ -14,6 +14,7 @@ namespace Ombi.Store.Repository IQueryable GetAllEpisodes(); Task Add(IMediaServerEpisode content); Task AddRange(IEnumerable content); + Task DeleteTv(Content tv); void UpdateWithoutSave(IMediaServerContent existingContent); } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/JellyfinContentRepository.cs b/src/Ombi.Store/Repository/JellyfinContentRepository.cs index c3451da62..8dc729a6b 100644 --- a/src/Ombi.Store/Repository/JellyfinContentRepository.cs +++ b/src/Ombi.Store/Repository/JellyfinContentRepository.cs @@ -104,6 +104,13 @@ namespace Ombi.Store.Repository return InternalSaveChanges(); } + public override async Task DeleteTv(JellyfinContent tv) + { + var episodesToDelete = GetAllEpisodes().Cast().Where(x => x.ParentId == tv.JellyfinId).ToList(); + Db.JellyfinEpisode.RemoveRange(episodesToDelete); + await Delete(tv); + } + public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Jellyfin; } } diff --git a/src/Ombi.Store/Repository/MediaServerRepository.cs b/src/Ombi.Store/Repository/MediaServerRepository.cs index c1c7d4ec8..251fe3cea 100644 --- a/src/Ombi.Store/Repository/MediaServerRepository.cs +++ b/src/Ombi.Store/Repository/MediaServerRepository.cs @@ -22,5 +22,6 @@ namespace Ombi.Store.Repository public abstract Task AddRange(IEnumerable content); public abstract void UpdateWithoutSave(IMediaServerContent existingContent); public abstract Task UpdateRange(IEnumerable existingContent); + public abstract Task DeleteTv(T tv); } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/PlexContentRepository.cs b/src/Ombi.Store/Repository/PlexContentRepository.cs index 96dfbe8e1..9f34af6a1 100644 --- a/src/Ombi.Store/Repository/PlexContentRepository.cs +++ b/src/Ombi.Store/Repository/PlexContentRepository.cs @@ -169,5 +169,12 @@ namespace Ombi.Store.Repository Db.PlexServerContent.UpdateRange((IEnumerable)existingContent); return InternalSaveChanges(); } + + public override Task DeleteTv(PlexServerContent tv) + { + // not used for now + // TODO: delete episodes, then delete series + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index dc2e5525c..9b277c091 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -15,14 +15,16 @@ namespace Ombi.Api.TheMovieDb Task GetMovieInformation(int movieId); Task GetMovieInformationWithExtraInfo(int movieId, string langCode = "en"); Task> NowPlaying(string languageCode, int? page = null); + Task> TrendingMovies(string languageCode, int? page = null); Task> PopularMovies(string languageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)); Task> PopularTv(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)); Task> SearchMovie(string searchTerm, int? year, string languageCode); Task> GetMoviesViaKeywords(string keywordId, string langCode, CancellationToken cancellationToken, int? page = null); Task> SearchTv(string searchTerm, string year = default); Task> TopRated(string languageCode, int? page = null); - Task> Upcoming(string languageCode, int? page = null); + Task> UpcomingMovies(string languageCode, int? page = null); Task> TopRatedTv(string languageCode, int? page = null); + Task> TrendingTv(string languageCode, int? page = null); Task> UpcomingTv(string languageCode, int? page = null); Task> SimilarMovies(int movieId, string langCode); Task Find(string externalId, ExternalSource source); @@ -41,7 +43,8 @@ namespace Ombi.Api.TheMovieDb Task GetMovieWatchProviders(int theMoviedbId, CancellationToken token); Task GetTvWatchProviders(int theMoviedbId, CancellationToken token); Task> GetGenres(string media, CancellationToken cancellationToken, string languageCode); + Task> GetLanguages(CancellationToken cancellationToken); Task> SearchWatchProviders(string media, string searchTerm, CancellationToken cancellationToken); - Task> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken); + Task> AdvancedSearch(DiscoverModel model, int page, CancellationToken cancellationToken); } } diff --git a/src/Ombi.TheMovieDbApi/Models/Language.cs b/src/Ombi.TheMovieDbApi/Models/Language.cs new file mode 100644 index 000000000..c6b14a654 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/Language.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; + +namespace Ombi.TheMovieDbApi.Models +{ + public class Language + { + [JsonProperty("iso_639_1")] + public string Id { get; set; } + [JsonProperty("english_name")] + public string EnglishName { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index 335ea7da3..055265701 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -70,11 +70,11 @@ namespace Ombi.Api.TheMovieDb - public async Task> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken) + public async Task> AdvancedSearch(DiscoverModel model, int page, CancellationToken cancellationToken) { var request = new Request($"discover/{model.Type}", BaseUri, HttpMethod.Get); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - if(model.ReleaseYear.HasValue && model.ReleaseYear.Value > 1900) + if (model.ReleaseYear.HasValue && model.ReleaseYear.Value > 1900) { request.FullUri = request.FullUri.AddQueryParameter("year", model.ReleaseYear.Value.ToString()); } @@ -92,6 +92,9 @@ namespace Ombi.Api.TheMovieDb } //request.FullUri = request.FullUri.AddQueryParameter("sort_by", "popularity.desc"); + request.AddQueryString("page", page.ToString()); + + var result = await Api.Request>(request, cancellationToken); return Mapper.Map>(result.results); } @@ -139,7 +142,7 @@ 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); @@ -282,21 +285,38 @@ namespace Ombi.Api.TheMovieDb return Mapper.Map>(result.results); } - public Task> Upcoming(string langCode, int? page = null) + public Task> TrendingMovies(string langCode, int? page = null) { - return Upcoming("movie", langCode, page); + return Trending("movie", langCode, page); } - public Task> UpcomingTv(string langCode, int? page = null) + + public Task> TrendingTv(string langCode, int? page = null) { - return Upcoming("tv", langCode, page); + return Trending("tv", langCode, page); } + private async Task> Trending(string type, string langCode, int? page = null) + { + // https://developers.themoviedb.org/3/trending/get-trending + var timeWindow = "week"; // another option can be 'day' + var request = new Request($"trending/{type}/{timeWindow}", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + + if (page != null) + { + request.AddQueryString("page", page.ToString()); + } + AddRetry(request); + var result = await Api.Request>(request); + return Mapper.Map>(result.results); + } /// /// Maintains filter parity with /movie/upcoming. /// - private async Task> Upcoming(string type, string langCode, int? page = null) + public async Task> UpcomingMovies(string langCode, int? page = null) { - var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get); + var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); request.AddQueryString("language", langCode); @@ -313,7 +333,27 @@ namespace Ombi.Api.TheMovieDb request.AddQueryString("page", page.ToString()); } await AddDiscoverSettings(request); - await AddGenreFilter(request, type); + await AddGenreFilter(request, "movie"); + AddRetry(request); + var result = await Api.Request>(request); + return Mapper.Map>(result.results); + } + public async Task> UpcomingTv(string langCode, int? page = null) + { + var request = new Request($"discover/tv", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + + // Search for shows that will air in the next month + var startDate = DateTime.Today.AddDays(1); + request.AddQueryString($"first_air_date.gte", startDate.ToString("yyyy-MM-dd")); + request.AddQueryString($"first_air_date.lte", startDate.AddDays(30).ToString("yyyy-MM-dd")); + if (page != null) + { + request.AddQueryString("page", page.ToString()); + } + await AddDiscoverSettings(request); + await AddGenreFilter(request, "tv"); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); @@ -376,8 +416,8 @@ namespace Ombi.Api.TheMovieDb request.AddQueryString("language", langCode); request.AddQueryString("sort_by", "vote_average.desc"); - request.AddQueryString("with_keywords", keywordId); - + request.AddQueryString("with_keywords", keywordId); + // `vote_count` consideration isn't explicitly documented, but using only the `sort_by` filter // does not provide the same results as `/movie/top_rated`. This appears to be adequate enough // to filter out extremely high-rated movies due to very little votes @@ -438,6 +478,16 @@ namespace Ombi.Api.TheMovieDb return result.genres ?? new List(); } + public async Task> GetLanguages(CancellationToken cancellationToken) + { + var request = new Request($"/configuration/languages", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + AddRetry(request); + + var result = await Api.Request>(request, cancellationToken); + return result ?? new List(); + } + public Task> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken) { var request = new Request("search/multi", BaseUri, HttpMethod.Get); @@ -472,6 +522,10 @@ namespace Ombi.Api.TheMovieDb { request.AddQueryString("without_keywords", string.Join(",", settings.ExcludedKeywordIds)); } + if (settings.OriginalLanguages?.Any() == true) + { + request.AddQueryString("with_original_language", string.Join("|", settings.OriginalLanguages)); + } } private async Task AddGenreFilter(Request request, string media_type) @@ -479,7 +533,8 @@ namespace Ombi.Api.TheMovieDb var settings = await Settings; List excludedGenres; - switch (media_type) { + switch (media_type) + { case "tv": excludedGenres = settings.ExcludedTvGenreIds; break; diff --git a/src/Ombi/.vscode/launch.json b/src/Ombi/.vscode/launch.json index 642e584c5..9b6838630 100644 --- a/src/Ombi/.vscode/launch.json +++ b/src/Ombi/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/bin/Debug/net5.0/ombi.dll", + "program": "${workspaceFolder}/bin/Debug/net6.0/ombi.dll", "args": ["--host", "http://localhost:3577"], "cwd": "${workspaceFolder}", "stopAtEntry": false, @@ -47,4 +47,4 @@ } }, ] - } \ No newline at end of file + } diff --git a/src/Ombi/ClientApp/.storybook/main.js b/src/Ombi/ClientApp/.storybook/main.js new file mode 100644 index 000000000..3f1f26236 --- /dev/null +++ b/src/Ombi/ClientApp/.storybook/main.js @@ -0,0 +1,19 @@ +module.exports = { + "stories": [ + "../src/**/*.stories.mdx", + "../src/**/*.stories.@(js|jsx|ts|tsx)" + ], + "addons": [ + "@storybook/addon-links", + "@storybook/addon-essentials", + "@storybook/addon-interactions" + ], + "framework": "@storybook/angular", + "core": { + "builder": "@storybook/builder-webpack5" + }, + "staticDirs": [{from :'../../wwwroot/images', to: 'images'}], + "features": { + interactionsDebugger: true, + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/.storybook/preview-body.html b/src/Ombi/ClientApp/.storybook/preview-body.html new file mode 100644 index 000000000..b043609bf --- /dev/null +++ b/src/Ombi/ClientApp/.storybook/preview-body.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/src/Ombi/ClientApp/.storybook/preview.js b/src/Ombi/ClientApp/.storybook/preview.js new file mode 100644 index 000000000..a20381a89 --- /dev/null +++ b/src/Ombi/ClientApp/.storybook/preview.js @@ -0,0 +1,14 @@ +import { setCompodocJson } from "@storybook/addon-docs/angular"; +import docJson from "../documentation.json"; +setCompodocJson(docJson); + +export const parameters = { + actions: { argTypesRegex: "^on[A-Z].*" }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + docs: { inlineStories: true }, +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/.storybook/tsconfig.json b/src/Ombi/ClientApp/.storybook/tsconfig.json new file mode 100644 index 000000000..54887e29c --- /dev/null +++ b/src/Ombi/ClientApp/.storybook/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../src/tsconfig.json", + "compilerOptions": { + "types": [ + "node" + ], + "typeRoots": [ + "../node_modules/@typings" + ], + "allowSyntheticDefaultImports": true + }, + "exclude": [ + "../src/test.ts", + "../src/**/*.spec.ts", + "../projects/**/*.spec.ts" + ], + "include": [ + "../src/**/*", + "../projects/**/*" + ], + "files": [ + "./typings.d.ts" + ] +} diff --git a/src/Ombi/ClientApp/.storybook/typings.d.ts b/src/Ombi/ClientApp/.storybook/typings.d.ts new file mode 100644 index 000000000..f73d61b39 --- /dev/null +++ b/src/Ombi/ClientApp/.storybook/typings.d.ts @@ -0,0 +1,4 @@ +declare module '*.md' { + const content: string; + export default content; +} diff --git a/src/Ombi/ClientApp/angular.json b/src/Ombi/ClientApp/angular.json index 066a75be5..d50a19844 100644 --- a/src/Ombi/ClientApp/angular.json +++ b/src/Ombi/ClientApp/angular.json @@ -19,7 +19,7 @@ "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.app.json", + "tsConfig": "src/tsconfig.json", "assets": [ "src/assets" ], @@ -114,7 +114,7 @@ "builder": "@angular-devkit/build-angular:tslint", "options": { "tsConfig": [ - "src/tsconfig.app.json" + "src/tsconfig.json" ], "exclude": [ "**/node_modules/**" @@ -124,7 +124,6 @@ } } }, - "defaultProject": "ombi", "cli": { "analytics": false } diff --git a/src/Ombi/ClientApp/documentation.json b/src/Ombi/ClientApp/documentation.json new file mode 100644 index 000000000..901f0d264 --- /dev/null +++ b/src/Ombi/ClientApp/documentation.json @@ -0,0 +1,18 @@ +{ + "pipes": [], + "interfaces": [], + "injectables": [], + "guards": [], + "interceptors": [], + "classes": [], + "directives": [], + "components": [], + "modules": [], + "miscellaneous": [], + "routes": [], + "coverage": { + "count": 0, + "status": "low", + "files": [] + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index 80a9842b4..a2271f2bc 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -4,37 +4,40 @@ "scripts": { "ng": "ng", "start": "ng serve --port 3578 --configuration hmr", - "build": "node --max_old_space_size=6144 node_modules/@angular/cli/bin/ng build --prod", - "lint": "ng lint" + "build": "node --max_old_space_size=6144 node_modules/@angular/cli/bin/ng build -c production", + "lint": "ng lint", + "docs:json": "compodoc -p ./tsconfig.json -e json -d .", + "storybook": "start-storybook -p 6006", + "chromatic": "chromatic --exit-zero-on-changes", + "storybookbuild": "yarn build-storybook" }, "private": true, "dependencies": { - "@angular/animations": "^13.2.0", + "@angular/animations": "^14.0.0", "@angular/cdk": "^13.2.0", - "@angular/common": "^13.2.0", - "@angular/compiler": "^13.2.0", - "@angular/core": "^13.2.0", - "@angular/forms": "^13.2.0", - "@angular/localize": "^13.2.0", + "@angular/common": "^14.0.0", + "@angular/compiler": "^14.0.0", + "@angular/core": "^14.0.0", + "@angular/forms": "^14.0.0", + "@angular/localize": "^14.0.0", "@angular/material": "^13.2.0", - "@angular/platform-browser": "^13.2.0", - "@angular/platform-browser-dynamic": "^13.2.0", - "@angular/platform-server": "^13.2.0", - "@angular/router": "^13.2.0", + "@angular/platform-browser": "^14.0.0", + "@angular/platform-browser-dynamic": "^14.0.0", + "@angular/platform-server": "^14.0.0", + "@angular/router": "^14.0.0", "@angularclass/hmr": "^3.0.0", - "@aspnet/signalr": "^1.1.0", "@auth0/angular-jwt": "^5.0.2", "@fortawesome/fontawesome-free": "^6.0.0", "@fullcalendar/core": "^4.2.0", "@fullcalendar/daygrid": "^4.4.0", "@fullcalendar/interaction": "^4.2.0", + "@microsoft/signalr": "^6.0.7", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", "@ngxs/devtools-plugin": "^3.7.3", "@ngxs/store": "^3.7.3", "@types/jquery": "^3.5.13", "@yellowspot/ng-truncate": "^2.0.0", - "angular-bootstrap-md": "^7.5.4", "angular-router-loader": "^0.8.5", "angularx-qrcode": "^13.0.3", "bootstrap": "^4.2.1", @@ -48,34 +51,52 @@ "moment": "^2.29.1", "ng2-cookies": "^1.0.12", "ngx-clipboard": "^12.1.0", - "ngx-infinite-scroll": "^9.0.0", + "ngx-infinite-scroll": "^14.0.0", "ngx-moment": "^3.0.1", "ngx-order-pipe": "^2.2.0", "popper.js": "^1.14.3", "primeicons": "^5.0.0", "primeng": "^13.2.0", + "protractor": "~5.4.0", "rxjs": "^7.5.4", "sass-recursive-map-merge": "^1.0.1", "store": "^2.0.12", "ts-md5": "^1.2.7", + "ts-node": "~5.0.1", "tslib": "^1.10.0", + "tslint": "^5.12.0", "tslint-angular": "^1.1.2", "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~13.2.0", - "@angular/cli": "~13.2.0", - "@angular/compiler-cli": "^13.2.0", - "@angular/language-service": "^13.2.0", + "@angular-devkit/build-angular": "^14.0.0", + "@angular/cli": "^14.0.0", + "@angular/compiler-cli": "^14.0.0", + "@angular/language-service": "^14.0.0", + "@babel/core": "^7.18.9", + "@compodoc/compodoc": "^1.1.19", + "@storybook/addon-actions": "^6.5.9", + "@storybook/addon-essentials": "^6.5.9", + "@storybook/addon-interactions": "^6.5.9", + "@storybook/addon-links": "^6.5.9", + "@storybook/angular": "^6.5.9", + "@storybook/builder-webpack5": "^6.5.9", + "@storybook/jest": "^0.0.10", + "@storybook/manager-webpack5": "^6.5.9", + "@storybook/testing-library": "^0.0.13", "@types/jasmine": "~3.6.7", "@types/jasminewd2": "~2.0.8", - "@types/node": "^16.10.9", + "@types/node": "^16.11.45", + "babel-loader": "^8.2.5", + "chromatic": "^6.7.1", "codelyzer": "^6.0.1", - "typescript": "~4.5.5" + "typescript": "~4.7.3" }, "optionalDependencies": { "protractor": "~5.4.0", "ts-node": "~5.0.1", "tslint": "^5.12.0" - } + }, + "readme": "ERROR: No README data found!", + "_id": "ombi@3.0.0" } diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 735dce36b..24644d1df 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -1,5 +1,4 @@ import { APP_BASE_HREF, CommonModule, PlatformLocation } from "@angular/common"; -import { CardsFreeModule, MDBBootstrapModule, NavbarModule } from "angular-bootstrap-md"; import { CustomPageService, ImageService, RequestService, SettingsService } from "./services"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from "@angular/common/http"; @@ -69,6 +68,7 @@ import { TooltipModule } from "primeng/tooltip"; import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { TranslateService } from "@ngx-translate/core"; import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor"; +import { ImageBackgroundComponent, ImageComponent } from "./components/"; import { environment } from "../environments/environment"; const routes: Routes = [ @@ -131,7 +131,6 @@ export function JwtTokenGetter() { MatSnackBarModule, DialogModule, MatButtonModule, - NavbarModule, MatCardModule, MatTooltipModule, MatMenuModule, @@ -145,11 +144,9 @@ export function JwtTokenGetter() { ConfirmDialogModule, OverlayPanelModule, CommonModule, - CardsFreeModule, OverlayModule, MatCheckboxModule, MatProgressSpinnerModule, - MDBBootstrapModule.forRoot(), JwtModule.forRoot({ config: { tokenGetter: JwtTokenGetter, @@ -170,7 +167,9 @@ export function JwtTokenGetter() { ...environment.production ? [] : [ NgxsReduxDevtoolsPluginModule.forRoot(), - ] + ], + ImageBackgroundComponent, + ImageComponent, ], declarations: [ AppComponent, diff --git a/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.html b/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.html new file mode 100644 index 000000000..97fff3671 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.html @@ -0,0 +1,6 @@ +
+ + +
{{name}}
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.scss b/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.scss new file mode 100644 index 000000000..177a22d01 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.scss @@ -0,0 +1,26 @@ +.login-gradient-bar{ + background: linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.6) 20.0%, rgba(0,0,0,0.6) 80.0%, transparent 60%),transparent; + height:100%; + width:100%; + position: absolute; +} + +.bg { + background-position: center center; + background-repeat: no-repeat; + background-attachment: fixed; + background-size: cover; + height: 100vh; + width: 100vw; + position: fixed; +} + +.poster-desc { + padding-left: 1%; + color: white; + height: 100vh; + width: 100vw; + display: flex; + justify-content: end; + flex-direction: column; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.ts b/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.ts new file mode 100644 index 000000000..ebd228d85 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/image-background/image-background.component.ts @@ -0,0 +1,43 @@ +import { OmbiCommonModules } from "../modules"; +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { DomSanitizer } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { ImageService } from "../../services"; +import { fadeInOutAnimation } from "app/animations/fadeinout"; + +@Component({ + standalone: true, + selector: 'ombi-image-background', + templateUrl: './image-background.component.html', + styleUrls: ['./image-background.component.scss'], + imports: [...OmbiCommonModules, BrowserAnimationsModule], + providers: [ ImageService ], + animations: [ fadeInOutAnimation ], + }) + export class ImageBackgroundComponent implements OnInit, OnDestroy { + + public background: any; + public name: string; + private timer: NodeJS.Timer; + + constructor(private images: ImageService, private sanitizer: DomSanitizer) { } + + public ngOnDestroy(): void { + clearTimeout(this.timer); + } + + public ngOnInit(): void { + this.cycleBackground(); + + this.timer = setInterval(() => { + this.cycleBackground(); + }, 30000); + } + + private cycleBackground() { + this.images.getRandomBackgroundWithInfo().subscribe((x) => { + this.background = this.sanitizer.bypassSecurityTrustStyle("url(" + x.url + ")"); + this.name = x.name; + }); + } + } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/components/image/image.component.html b/src/Ombi/ClientApp/src/app/components/image/image.component.html new file mode 100644 index 000000000..9ee9d7d19 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/image/image.component.html @@ -0,0 +1 @@ + diff --git a/src/Ombi/ClientApp/src/app/components/image/image.component.stories.ts b/src/Ombi/ClientApp/src/app/components/image/image.component.stories.ts new file mode 100644 index 000000000..b6f7b21fc --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/image/image.component.stories.ts @@ -0,0 +1,73 @@ +// also exported from '@storybook/angular' if you can deal with breaking changes in 6.1 +import { APP_BASE_HREF } from '@angular/common'; +import { Story, Meta, moduleMetadata } from '@storybook/angular'; +import { RequestType } from '../../interfaces'; +import { ImageComponent } from './image.component'; + +// More on default export: https://storybook.js.org/docs/angular/writing-stories/introduction#default-export +export default { + title: 'Image Component', + component: ImageComponent, + decorators: [ + moduleMetadata({ + providers: [ + { + provide: APP_BASE_HREF, + useValue: "" + }, + ] + }) + ] +} as Meta; + +// More on component templates: https://storybook.js.org/docs/angular/writing-stories/introduction#using-args +const Template: Story = (args: ImageComponent) => ({ + props: args, +}); + +export const Primary = Template.bind({}); +// More on args: https://storybook.js.org/docs/angular/writing-stories/args +Primary.args = { + src: 'https://ombi.io/img/logo-orange-small.png', + type: RequestType.movie +}; + +export const ClassApplied = Template.bind({}); +ClassApplied.args = { + src: 'https://ombi.io/img/logo-orange-small.png', + type: RequestType.movie, + class: 'test-class' +}; + +export const StyleApplied = Template.bind({}); +StyleApplied.args = { + src: 'https://ombi.io/img/logo-orange-small.png', + type: RequestType.movie, + style: 'background-color: red;' +}; + +export const IdApplied = Template.bind({}); +IdApplied.args = { + src: 'https://ombi.io/img/logo-orange-small.png', + type: RequestType.movie, + id: 'testId123' +}; + +// export const InvalidMovieImage = Template.bind({}); +// InvalidMovieImage.args = { +// src: 'https://httpstat.us/429', +// type: RequestType.movie, +// id: 'testId123' +// }; + +// export const InvalidTvImage = Template.bind({}); +// InvalidTvImage.args = { +// src: 'https://httpstat.us/429', +// type: RequestType.tvShow, +// }; + +// export const InvalidMusicImage = Template.bind({}); +// InvalidMusicImage.args = { +// src: 'https://httpstat.us/429', +// type: RequestType.album, +// }; diff --git a/src/Ombi/ClientApp/src/app/components/image/image.component.ts b/src/Ombi/ClientApp/src/app/components/image/image.component.ts new file mode 100644 index 000000000..57099016a --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/image/image.component.ts @@ -0,0 +1,57 @@ +import { OmbiCommonModules } from "../modules"; +import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, ViewEncapsulation } from "@angular/core"; +import { RequestType } from "../../interfaces"; +import { APP_BASE_HREF } from "@angular/common"; + +@Component({ + standalone: true, + selector: 'ombi-image', + imports: [...OmbiCommonModules], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './image.component.html', + }) + export class ImageComponent { + + @Input() public src: string; + @Input() public type: RequestType; + + // Attributes from the parent + @Input() public class: string; + @Input() public id: string; + @Input() public alt: string; + @Input() public style: string; + + public baseUrl: string = ""; + + public defaultTv = "/images/default_tv_poster.png"; + private defaultMovie = "/images/default_movie_poster.png"; + private defaultMusic = "i/mages/default-music-placeholder.png"; + + constructor (@Inject(APP_BASE_HREF) public href: string) { + if (this.href.length > 1) { + this.baseUrl = this.href; + } + } + + public onError(event: any) { + // set to a placeholder + switch(this.type) { + case RequestType.movie: + event.target.src = this.baseUrl + this.defaultMovie; + break; + case RequestType.tvShow: + event.target.src = this.baseUrl + this.defaultTv; + break; + case RequestType.album: + event.target.src = this.baseUrl + this.defaultMusic; + break; + } + + // Retry the original image + const timeout = setTimeout(() => { + event.target.src = this.src; + clearTimeout(timeout); + }, Math.floor(Math.random() * (7000 - 1000 + 1)) + 1000); + } + } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/components/index.ts b/src/Ombi/ClientApp/src/app/components/index.ts new file mode 100644 index 000000000..092f6504b --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/index.ts @@ -0,0 +1,2 @@ +export * from "./image-background/image-background.component"; +export * from "./image/image.component"; \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/components/modules.ts b/src/Ombi/ClientApp/src/app/components/modules.ts new file mode 100644 index 000000000..1bf5697e6 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/components/modules.ts @@ -0,0 +1,3 @@ +import { CommonModule } from "@angular/common"; + +export const OmbiCommonModules = [ CommonModule ]; \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/custompage/custompage.component.ts b/src/Ombi/ClientApp/src/app/custompage/custompage.component.ts index 61094e793..37e2dce77 100644 --- a/src/Ombi/ClientApp/src/app/custompage/custompage.component.ts +++ b/src/Ombi/ClientApp/src/app/custompage/custompage.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, SecurityContext } from "@angular/core"; -import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { DomSanitizer } from "@angular/platform-browser"; import { AuthService } from "../auth/auth.service"; import { CustomPageService, NotificationService } from "../services"; @@ -10,11 +10,11 @@ import { CustomPageService, NotificationService } from "../services"; }) export class CustomPageComponent implements OnInit { - public form: FormGroup; + public form: UntypedFormGroup; public isEditing: boolean; public isAdmin: boolean; - constructor(private auth: AuthService, private settings: CustomPageService, private fb: FormBuilder, + constructor(private auth: AuthService, private settings: CustomPageService, private fb: UntypedFormBuilder, private notificationService: NotificationService, private sanitizer: DomSanitizer) { } diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html index 9ca607082..a479b5072 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html @@ -2,15 +2,14 @@
-
+
{{ 'Common.' + RequestType[result.type] | translate }}
{{getAvailabilityStatus()}}
- {{result.title}} + \ No newline at end of file +
diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss index 5fe422fd7..6a7911257 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss @@ -83,7 +83,7 @@ small { } -.image { +::ng-deep .image { border-radius: 10px; opacity: 1; display: block; 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 559a962b5..d5557c0b5 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 @@ -169,6 +169,19 @@ export class DiscoverCardComponent implements OnInit { } } + public onImageError(event: any) { + const originalSrc = event.target.src; + + // set to a placeholder + event.target.src = "../../../images/default_movie_poster.png"; + + // Retry the original image + const timeout = setTimeout(() => { + event.target.src = originalSrc; + clearTimeout(timeout); + }, Math.floor(Math.random() * (7000 - 1000 + 1)) + 1000); + } + private getExtraMovieInfo() { if (!this.result.imdbid) { this.searchService.getFullMovieDetails(+this.result.id) diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts index 9617c993e..43cde5036 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts @@ -48,6 +48,7 @@ export class CarouselListComponent implements OnInit { constructor(private searchService: SearchV2Service, private storageService: StorageService, private featureFacade: FeaturesFacade) { + Carousel.prototype.onTouchMove = () => { }, this.responsiveOptions = [ { breakpoint: '4000px', diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html index b1911ceee..97ff038a0 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html @@ -2,7 +2,13 @@
-
+ +
diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index a8544365d..295902927 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -29,6 +29,7 @@ export class DiscoverSearchResultsComponent implements OnInit { public filter: SearchFilter; private isAdvancedSearch: boolean; + private loadPosition: number = 30; constructor(private searchService: SearchV2Service, private route: ActivatedRoute, @@ -65,7 +66,7 @@ export class DiscoverSearchResultsComponent implements OnInit { } }); - if (this.advancedDataService) { + if (this.isAdvancedSearch) { return; } this.loadingFlag = true; @@ -179,6 +180,31 @@ export class DiscoverSearchResultsComponent implements OnInit { }); } + public onScroll() { + console.log("scrolled"); + if (this.advancedDataService) { + this.loadMoreAdvancedSearch(); + return; + } + } + + private loadMoreAdvancedSearch() { + const advancedOptions = this.advancedDataService.getOptions(); + + this.searchService.advancedSearch({ + type: advancedOptions.type == RequestType.movie ? "movie" : "tv", + companies: advancedOptions.companies, + genreIds: advancedOptions.genres, + keywordIds : advancedOptions.keywords, + releaseYear: advancedOptions.releaseYear, + watchProviders: advancedOptions.watchProviders, + }, this.loadPosition, 30).then(x => { + + this.loadPosition += 30; + this.mapAdvancedData(x); + }); + } + private async search() { this.clear(); this.results = await this.searchService diff --git a/src/Ombi/ClientApp/src/app/discover/discover.module.ts b/src/Ombi/ClientApp/src/app/discover/discover.module.ts index 414c881e6..eade4e451 100644 --- a/src/Ombi/ClientApp/src/app/discover/discover.module.ts +++ b/src/Ombi/ClientApp/src/app/discover/discover.module.ts @@ -8,6 +8,7 @@ import { PipeModule } from "../pipes/pipe.module"; import { RouterModule } from "@angular/router"; import { SharedModule } from "../shared/shared.module"; import { SkeletonModule } from 'primeng/skeleton'; +import { ImageComponent } from 'app/components'; @NgModule({ imports: [ @@ -18,6 +19,7 @@ import { SkeletonModule } from 'primeng/skeleton'; MatButtonToggleModule, InfiniteScrollModule, SkeletonModule, + ImageComponent ], declarations: [ ...fromComponents.components diff --git a/src/Ombi/ClientApp/src/app/interfaces/IImages.ts b/src/Ombi/ClientApp/src/app/interfaces/IImages.ts index 0c73df490..baa1a9829 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IImages.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IImages.ts @@ -1,3 +1,7 @@ export interface IImages { url: string; } +export interface IImagesInfo { + url: string; + name: string; +} diff --git a/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts b/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts index f82225434..77352de4b 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts @@ -17,3 +17,8 @@ export interface IDiscoverModel { watchProviders?: number[]; companies?: number[]; } +export interface ILanguage { + iso_639_1 : string; + english_name : string; + name : string; +} diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index 95b1fba00..4594d2a2f 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -339,7 +339,8 @@ export interface ITheMovieDbSettings extends ISettings { showAdultMovies: boolean; excludedKeywordIds: number[]; excludedMovieGenreIds: number[]; - excludedTvGenreIds: number[] + excludedTvGenreIds: number[]; + originalLanguages: string[]; } export interface IUpdateModel diff --git a/src/Ombi/ClientApp/src/app/issues/issuestable.component.html b/src/Ombi/ClientApp/src/app/issues/issuestable.component.html index 8e22d3e51..99b297b60 100644 --- a/src/Ombi/ClientApp/src/app/issues/issuestable.component.html +++ b/src/Ombi/ClientApp/src/app/issues/issuestable.component.html @@ -14,7 +14,7 @@ - +
{{ 'Issues.Details' | translate}} diff --git a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.html b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.html index abad6473f..3b8a46db7 100644 --- a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.html +++ b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.html @@ -1,5 +1,5 @@ -
-
+
+
diff --git a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.scss b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.scss index 7eb4a4b3c..4c6e7ea9a 100644 --- a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.scss +++ b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.scss @@ -26,15 +26,6 @@ div.centered { transform: translate(-50%, -50%); } -div.bg { - background-position: center center; - background-repeat: no-repeat; - background-attachment: fixed; - background-size: cover; - height: 100vh; - width: 100vw; - position: fixed; -} .online{ color:lightgreen; diff --git a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts index 33f8873f8..e2758b146 100644 --- a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts +++ b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts @@ -1,48 +1,34 @@ -import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; -import { Component, OnDestroy, OnInit, Inject } from "@angular/core"; +import { APP_BASE_HREF } from "@angular/common"; +import { Component, OnInit, Inject } from "@angular/core"; import { IMediaServerStatus } from "../interfaces"; import { ICustomizationSettings, ILandingPageSettings } from "../interfaces"; import { LandingPageService } from "../services"; import { SettingsService } from "../services"; -import { DomSanitizer } from "@angular/platform-browser"; -import { ImageService } from "../services"; - -import { fadeInOutAnimation } from "../animations/fadeinout"; import { CustomizationFacade } from "../state/customization"; -import { ThousandShortPipe } from "../pipes/ThousandShortPipe"; @Component({ templateUrl: "./landingpage.component.html", - animations: [fadeInOutAnimation], styleUrls: ["./landingpage.component.scss"], }) -export class LandingPageComponent implements OnDestroy, OnInit { +export class LandingPageComponent implements OnInit { public customizationSettings: ICustomizationSettings; public landingPageSettings: ILandingPageSettings; - public background: any; public mediaServerStatus: IMediaServerStatus; public baseUrl: string; - private timer: any; private href: string; constructor(private settingsService: SettingsService, - private images: ImageService, private sanitizer: DomSanitizer, private landingPageService: LandingPageService, + private landingPageService: LandingPageService, private customizationFacade: CustomizationFacade, @Inject(APP_BASE_HREF) href :string) { this.href = href } public ngOnInit() { this.customizationFacade.settings$().subscribe(x => this.customizationSettings = x); this.settingsService.getLandingPage().subscribe(x => this.landingPageSettings = x); - this.images.getRandomBackground().subscribe(x => { - this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 19%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 79%, transparent 80%), url(" + x.url + ")"); - }); - this.timer = setInterval(() => { - this.cycleBackground(); - }, 30000); const base = this.href; if (base.length > 1) { @@ -53,18 +39,4 @@ export class LandingPageComponent implements OnDestroy, OnInit { this.mediaServerStatus = x; }); } - - public ngOnDestroy() { - clearInterval(this.timer); - } - - public cycleBackground() { - this.images.getRandomBackground().subscribe(x => { - this.background = ""; - }); - this.images.getRandomBackground().subscribe(x => { - this.background = this.sanitizer - .bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")"); - }); - } } diff --git a/src/Ombi/ClientApp/src/app/login/login.component.html b/src/Ombi/ClientApp/src/app/login/login.component.html index c0932999b..71caa80b4 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.html +++ b/src/Ombi/ClientApp/src/app/login/login.component.html @@ -1,7 +1,4 @@ -
- -
+
diff --git a/src/Ombi/ClientApp/src/app/login/login.component.scss b/src/Ombi/ClientApp/src/app/login/login.component.scss index 4ad645dd8..df368ee0c 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.scss +++ b/src/Ombi/ClientApp/src/app/login/login.component.scss @@ -11,23 +11,6 @@ img.center { max-width: 100%; } -.login-gradient-bar{ - background: linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.6) 20.0%, rgba(0,0,0,0.6) 80.0%, transparent 60%),transparent; - height:100%; - width:100%; - position: absolute; -} - -div.bg { - background-position: center center; - background-repeat: no-repeat; - background-attachment: fixed; - background-size: cover; - height: 100vh; - width: 100vw; - position: fixed; -} - .card-container.card { max-width: 500px; padding: 45px 45px; diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index f6f519191..6e8efc00b 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit, Inject } from "@angular/core"; -import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { TranslateService } from "@ngx-translate/core"; @@ -10,25 +10,19 @@ import { PlexTvService } from "../services"; import { SettingsService } from "../services"; import { StatusService } from "../services"; -import { DomSanitizer } from "@angular/platform-browser"; -import { ImageService } from "../services"; - -import { fadeInOutAnimation } from "../animations/fadeinout"; import { StorageService } from "../shared/storage/storage-service"; import { MatSnackBar } from "@angular/material/snack-bar"; import { CustomizationFacade } from "../state/customization"; @Component({ templateUrl: "./login.component.html", - animations: [fadeInOutAnimation], styleUrls: ["./login.component.scss"], }) export class LoginComponent implements OnDestroy, OnInit { - public form: FormGroup; + public form: UntypedFormGroup; public customizationSettings: ICustomizationSettings; public authenticationSettings: IAuthenticationSettings; public plexEnabled: boolean; - public background: any; public landingFlag: boolean; public baseUrl: string; public loginWithOmbi: boolean; @@ -46,7 +40,6 @@ export class LoginComponent implements OnDestroy, OnInit { public get appNameTranslate(): object { return { appName: this.appName }; } - private timer: any; private clientId: string; private errorBody: string; @@ -59,11 +52,9 @@ export class LoginComponent implements OnDestroy, OnInit { private authService: AuthService, private router: Router, private status: StatusService, - private fb: FormBuilder, + private fb: UntypedFormBuilder, private settingsService: SettingsService, private customziationFacade: CustomizationFacade, - private images: ImageService, - private sanitizer: DomSanitizer, private route: ActivatedRoute, @Inject(APP_BASE_HREF) href: string, private translate: TranslateService, @@ -111,14 +102,6 @@ export class LoginComponent implements OnDestroy, OnInit { this.headerAuth(); }); this.settingsService.getClientId().subscribe((x) => (this.clientId = x)); - this.images.getRandomBackground().subscribe((x) => { - this.background = this.sanitizer.bypassSecurityTrustStyle( - "url(" + x.url + ")" - ); - }); - this.timer = setInterval(() => { - this.cycleBackground(); - }, 30000); const base = this.href; if (base.length > 1) { @@ -132,7 +115,7 @@ export class LoginComponent implements OnDestroy, OnInit { .subscribe((x) => (this.errorValidation = x)); } - public onSubmit(form: FormGroup) { + public onSubmit(form: UntypedFormGroup) { if (form.invalid) { this.notify.open(this.errorValidation, "OK", { duration: 300000, @@ -284,18 +267,6 @@ export class LoginComponent implements OnDestroy, OnInit { } public ngOnDestroy() { - clearInterval(this.timer); clearInterval(this.pinTimer); } - - private cycleBackground() { - this.images.getRandomBackground().subscribe((x) => { - this.background = ""; - }); - this.images.getRandomBackground().subscribe((x) => { - this.background = this.sanitizer.bypassSecurityTrustStyle( - "url(" + x.url + ")" - ); - }); - } } diff --git a/src/Ombi/ClientApp/src/app/login/resetpassword.component.html b/src/Ombi/ClientApp/src/app/login/resetpassword.component.html index 9fbeb7eda..201223800 100644 --- a/src/Ombi/ClientApp/src/app/login/resetpassword.component.html +++ b/src/Ombi/ClientApp/src/app/login/resetpassword.component.html @@ -1,8 +1,5 @@  -
- -
+
diff --git a/src/Ombi/ClientApp/src/app/login/resetpassword.component.ts b/src/Ombi/ClientApp/src/app/login/resetpassword.component.ts index cb030b386..bd0376cb1 100644 --- a/src/Ombi/ClientApp/src/app/login/resetpassword.component.ts +++ b/src/Ombi/ClientApp/src/app/login/resetpassword.component.ts @@ -1,30 +1,27 @@ -import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; +import { APP_BASE_HREF } from "@angular/common"; import { Component, OnInit, Inject } from "@angular/core"; -import { FormBuilder, FormGroup, Validators } from "@angular/forms"; -import { DomSanitizer } from "@angular/platform-browser"; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { fadeInOutAnimation } from "../animations/fadeinout"; import { ICustomizationSettings } from "../interfaces"; -import { IdentityService, ImageService, NotificationService, SettingsService } from "../services"; +import { IdentityService, NotificationService, SettingsService } from "../services"; import { CustomizationFacade } from "../state/customization"; @Component({ templateUrl: "./resetpassword.component.html", - animations: [fadeInOutAnimation], styleUrls: ["./login.component.scss"], }) export class ResetPasswordComponent implements OnInit { - public form: FormGroup; + public form: UntypedFormGroup; public customizationSettings: ICustomizationSettings; public emailSettingsEnabled: boolean; public baseUrl: string; - public background: any; private href: string; constructor(private identityService: IdentityService, private notify: NotificationService, - private fb: FormBuilder, private settingsService: SettingsService, @Inject(APP_BASE_HREF) href:string, - private images: ImageService, private sanitizer: DomSanitizer, private customizationFacade: CustomizationFacade) { + private fb: UntypedFormBuilder, private settingsService: SettingsService, @Inject(APP_BASE_HREF) href:string, + private customizationFacade: CustomizationFacade) { this.href = href; this.form = this.fb.group({ email: ["", [Validators.required]], @@ -32,9 +29,7 @@ export class ResetPasswordComponent implements OnInit { } public ngOnInit() { - this.images.getRandomBackground().subscribe(x => { - this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")"); - }); + const base = this.href; if (base.length > 1) { this.baseUrl = base; @@ -43,7 +38,7 @@ export class ResetPasswordComponent implements OnInit { this.settingsService.getEmailSettingsEnabled().subscribe(x => this.emailSettingsEnabled = x); } - public onSubmit(form: FormGroup) { + public onSubmit(form: UntypedFormGroup) { if (this.emailSettingsEnabled) { if (form.invalid) { diff --git a/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.html b/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.html index 6a1c2567f..bd8bc3e38 100644 --- a/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.html +++ b/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.html @@ -1,11 +1,7 @@ - -
- -
+
- +
diff --git a/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.ts b/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.ts index 695a79abb..b7ef08281 100644 --- a/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.ts +++ b/src/Ombi/ClientApp/src/app/login/tokenresetpassword.component.ts @@ -1,10 +1,9 @@ import { ActivatedRoute, Params } from "@angular/router"; import { Component, OnInit } from "@angular/core"; -import { FormBuilder, FormGroup, Validators } from "@angular/forms"; -import { IdentityService, ImageService } from "../services"; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { IdentityService } from "../services"; import { CustomizationFacade } from "../state/customization"; -import { DomSanitizer } from "@angular/platform-browser"; import { ICustomizationSettings } from "../interfaces"; import { IResetPasswordToken } from "../interfaces"; import { NotificationService } from "../services"; @@ -17,15 +16,12 @@ import { Router } from "@angular/router"; }) export class TokenResetPasswordComponent implements OnInit { - public form: FormGroup; + public form: UntypedFormGroup; public customizationSettings: ICustomizationSettings; - public background: any; public baseUrl: string; constructor(private identityService: IdentityService, private router: Router, private route: ActivatedRoute, private notify: NotificationService, - private fb: FormBuilder, private location: PlatformLocation, private images: ImageService, - private sanitizer: DomSanitizer, private customizationFacade: CustomizationFacade, - ) { + private fb: UntypedFormBuilder, private location: PlatformLocation, private customizationFacade: CustomizationFacade) { this.route.queryParams .subscribe((params: Params) => { @@ -39,9 +35,6 @@ export class TokenResetPasswordComponent implements OnInit { } public ngOnInit() { - this.images.getRandomBackground().subscribe(x => { - this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")"); - }); const base = this.location.getBaseHrefFromDOM(); if (base.length > 1) { this.baseUrl = base; @@ -49,7 +42,7 @@ export class TokenResetPasswordComponent implements OnInit { this.customizationFacade.settings$().subscribe(x => this.customizationSettings = x); } - public onSubmit(form: FormGroup) { + public onSubmit(form: UntypedFormGroup) { if (form.invalid) { this.notify.error("Email address is required"); return; @@ -65,6 +58,5 @@ export class TokenResetPasswordComponent implements OnInit { }); } }); - } } 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 2207ea323..20f5d303b 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 @@ -10,9 +10,8 @@ @@ -24,7 +23,7 @@
- +
@@ -107,7 +106,7 @@ - +
- + {{'MediaDetails.RecommendationsTitle' | translate}} -
+ - + {{'MediaDetails.SimilarTitle' | translate}} -
+
\ No newline at end of file +
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index c040fe4e0..8d1e5976b 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -1,6 +1,6 @@ -import { AfterViewInit, Component, OnInit, ViewChild, ViewEncapsulation } from "@angular/core"; +import { Component, OnInit, ViewEncapsulation } from "@angular/core"; import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from "../../../services"; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { DomSanitizer } from "@angular/platform-browser"; import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; import { MatDialog } from "@angular/material/dialog"; @@ -12,7 +12,6 @@ import { NewIssueComponent } from "../shared/new-issue/new-issue.component"; import { TranslateService } from "@ngx-translate/core"; import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component"; import { RequestServiceV2 } from "../../../services/requestV2.service"; -import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; import { firstValueFrom, forkJoin } from "rxjs"; import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component"; import { FeaturesFacade } from "../../../state/features/features.facade"; @@ -32,31 +31,45 @@ export class MovieDetailsComponent implements OnInit{ public issuesEnabled: boolean; public roleName4k = "Request4KMovie"; public is4KEnabled = false; - public requestType = RequestType.movie; - - private theMovidDbId: number; private imdbId: string; + private snapMovieId: string; - constructor(private searchService: SearchV2Service, private route: ActivatedRoute, + + constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private router: Router, private sanitizer: DomSanitizer, private imageService: ImageService, public dialog: MatDialog, private requestService: RequestService, private requestService2: RequestServiceV2, private radarrService: RadarrService, public messageService: MessageService, private auth: AuthService, private settingsState: SettingsStateService, private translate: TranslateService, private featureFacade: FeaturesFacade) { - this.route.params.subscribe(async (params: any) => { - if (typeof params.movieDbId === 'string' || params.movieDbId instanceof String) { - if (params.movieDbId.startsWith("tt")) { - this.imdbId = params.movieDbId; - } - } - this.theMovidDbId = params.movieDbId; + this.snapMovieId = this.route.snapshot.params.movieDbId; + this.route.params.subscribe(async (params: any) => { + if (typeof params.movieDbId === 'string' || params.movieDbId instanceof String) { + if (params.movieDbId.startsWith("tt")) { + this.imdbId = params.movieDbId; + // Check if we user navigated to another movie and if so reload the component + if (this.imdbId !== this.snapMovieId) { + this.reloadComponent() + } + } + } + this.theMovidDbId = params.movieDbId; + // Check if we user navigated to another movie and if so reload the component + if (params.movieDbId !== this.snapMovieId) { + this.reloadComponent() + } }); } - async ngOnInit() { + reloadComponent() { + let currentUrl = this.router.url; + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + this.router.onSameUrlNavigation = 'reload'; + this.router.navigate([currentUrl]); + } + async ngOnInit() { this.is4KEnabled = this.featureFacade.is4kEnabled(); this.issuesEnabled = this.settingsState.getIssue(); this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); @@ -68,6 +81,7 @@ export class MovieDetailsComponent implements OnInit{ if (this.imdbId) { this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => { this.movie = x; + this.checkPoster(); if (this.movie.requestId > 0) { // Load up this request this.hasRequest = true; @@ -78,6 +92,7 @@ export class MovieDetailsComponent implements OnInit{ } else { this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => { this.movie = x; + this.checkPoster(); if (this.movie.requestId > 0) { // Load up this request this.hasRequest = true; @@ -272,7 +287,14 @@ export class MovieDetailsComponent implements OnInit{ } }); } - + private checkPoster() { + if (this.movie.posterPath == null) { + this.movie.posterPath = "../../../images/default_movie_poster.png"; + } + else { + this.movie.posterPath = "https://image.tmdb.org/t/p/w300/" + this.movie.posterPath + }; + } private loadAdvancedInfo() { const profile = this.radarrService.getQualityProfilesFromSettings(); const folders = this.radarrService.getRootFoldersFromSettings(); diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html index 7fd3ce0ea..29d2c6beb 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html @@ -30,28 +30,35 @@ {{ this.movie.status | translateStatus }}
- {{'MediaDetails.Availability' | translate }} - {{'Common.Available' | translate}} - {{'Common.NotAvailable' | translate}} -
-
- {{'MediaDetails.RequestStatus' | translate }} -
{{'Common.Denied' | translate}}
-
{{'Common.ProcessingRequest' | translate}}
-
{{'Common.PendingApproval' | translate}} -
-
{{'Common.NotRequested' | translate}} -
+ {{'MediaDetails.Availability' | translate }} + {{'Common.Available' | translate}} + {{'Common.NotAvailable' | translate}} +
+
+ {{'MediaDetails.RequestStatus' | translate }} +
+
{{'Common.RequestDenied' | translate}}
+
{{'Common.ProcessingRequest' | translate}}
+
{{'Common.PendingApproval' | translate}}
+ +
+
+
{{'Common.RequestDenied4K' | translate}}
+
{{'Common.ProcessingRequest4K' | translate}}
+
{{'Common.PendingApproval4K' | translate}}
+ +
- {{'MediaDetails.RequestedBy' | translate }} - {{request.requestedUser.userAlias}} + {{'MediaDetails.RequestedBy' | translate }} + {{request.requestedUser.userAlias}}
- {{'MediaDetails.RequestDate' | translate }} - {{request.requestedDate | amUserLocale | amDateFormat: 'LL'}} + {{'MediaDetails.RequestDate' | translate }} + {{request.requestedDate4k | amUserLocale | amDateFormat: 'LL'}} + {{request.requestedDate | amUserLocale | amDateFormat: 'LL'}}
@@ -59,9 +66,14 @@ {{RequestSource[request.source]}}
-
- {{'MediaDetails.DeniedReason' | translate }} +
+ {{'MediaDetails.DeniedReason' | translate }} +
{{request.deniedReason}} +
+
+ {{request.deniedReason4K}} +
@@ -72,7 +84,7 @@
{{'MediaDetails.Quality' | translate }}  - {{"4K" | quality}} + {{"4K" | quality}}
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html index 49875514d..e14323a0e 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html @@ -6,10 +6,10 @@