diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs index 7d8b6829c..1d4db3ed6 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs @@ -13,6 +13,7 @@ namespace Ombi.Core.Engine.Interfaces Task> TopRatedMovies(); Task> UpcomingMovies(); Task> NowPlayingMovies(); + Task GetCollection(int collectionId, string langCode = null); Task GetTvDbId(int theMovieDbId); int ResultLimit { get; set; } } diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index d7de75c6e..673958218 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -1,24 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AutoMapper; +using AutoMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Authentication; +using Ombi.Core.Engine.Interfaces; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; +using Ombi.Core.Models.Search.V2; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; +using System; +using System.Collections.Generic; +using System.Linq; using System.Security.Principal; using System.Threading.Tasks; -using Ombi.Core.Engine.Interfaces; -using Ombi.Core.Models.Search.V2; namespace Ombi.Core.Engine.V2 { @@ -46,6 +46,14 @@ namespace Ombi.Core.Engine.V2 return await ProcessSingleMovie(movieInfo); } + public async Task GetCollection(int collectionId, string langCode = null) + { + langCode = await DefaultLanguageCode(langCode); + var collections = await MovieApi.GetCollection(langCode, collectionId); + + return await ProcessCollection(collections); + } + public async Task GetTvDbId(int theMovieDbId) { var result = await MovieApi.GetTvExternals(theMovieDbId); @@ -180,6 +188,30 @@ namespace Ombi.Core.Engine.V2 return mapped; } + + private async Task ProcessCollection(Collections collection) + { + var viewMovie = Mapper.Map(collection); + foreach (var movie in viewMovie.Collection) + { + 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; + mapped.RequestId = movie.RequestId; + mapped.Requested = movie.Requested; + mapped.PlexUrl = movie.PlexUrl; + mapped.EmbyUrl = movie.EmbyUrl; + mapped.Subscribed = movie.Subscribed; + mapped.ShowSubscribe = movie.ShowSubscribe; + } + return viewMovie; + } + private async Task ProcessSingleMovie(SearchMovieViewModel viewMovie) { if (viewMovie.ImdbId.IsNullOrEmpty()) @@ -188,7 +220,7 @@ namespace Ombi.Core.Engine.V2 viewMovie.Id = showInfo.Id; // TheMovieDbId viewMovie.ImdbId = showInfo.ImdbId; } - + viewMovie.TheMovieDbId = viewMovie.Id.ToString(); await RunSearchRules(viewMovie); diff --git a/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs b/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs new file mode 100644 index 000000000..9490ff23f --- /dev/null +++ b/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Ombi.Store.Entities; + +namespace Ombi.Core.Models.Search.V2 +{ + public class MovieCollectionsViewModel + { + public string Name { get; set; } + public string Overview { get; set; } + public List Collection { get; set; } + } + + public class MovieCollection : SearchViewModel + { + public int Id { get; set; } + public string Overview { get; set; } + public string PosterPath { get; set; } + public string Title { get; set; } + + + public override RequestType Type => RequestType.Movie; + } +} \ No newline at end of file diff --git a/src/Ombi.Mapping/Profiles/MovieProfile.cs b/src/Ombi.Mapping/Profiles/MovieProfile.cs index 2c37e91be..7d981a7bc 100644 --- a/src/Ombi.Mapping/Profiles/MovieProfile.cs +++ b/src/Ombi.Mapping/Profiles/MovieProfile.cs @@ -91,6 +91,19 @@ namespace Ombi.Mapping.Profiles CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + + CreateMap() + .ForMember(x => x.Name, o => o.MapFrom(s => s.name)) + .ForMember(x => x.Overview, o => o.MapFrom(s => s.overview)) + .ForMember(x => x.Collection, o => o.MapFrom(s => s.parts)); + + CreateMap() + .ForMember(x => x.Id, o => o.MapFrom(s => s.id)) + .ForMember(x => x.Overview, o => o.MapFrom(s => s.overview)) + .ForMember(x => x.PosterPath, o => o.MapFrom(s => s.poster_path)) + .ForMember(x => x.Title, o => o.MapFrom(s => s.title)); + + CreateMap().ReverseMap(); } } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html index c547721fa..6e9a8b2d5 100644 --- a/src/Ombi/ClientApp/src/app/app.component.html +++ b/src/Ombi/ClientApp/src/app/app.component.html @@ -168,7 +168,7 @@
- diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index 55d8d81bb..7f5fde744 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -27,6 +27,7 @@ export class AppComponent implements OnInit { public userAccessToken: string; public voteEnabled = false; public applicationName: string = "Ombi" + public isAdmin: boolean; private checkedForUpdate: boolean; @@ -61,6 +62,7 @@ export class AppComponent implements OnInit { const theme = localStorage.getItem("theme"); this.onSetTheme(theme); this.user = this.authService.claims(); + this.isAdmin = this.authService.hasRole("admin"); this.settingsService.getCustomization().subscribe(x => { this.customizationSettings = x; diff --git a/src/Ombi/ClientApp/src/app/discover/card/discover-card.component.scss b/src/Ombi/ClientApp/src/app/discover/card/discover-card.component.scss index d0ca84cb8..51ad20b66 100644 --- a/src/Ombi/ClientApp/src/app/discover/card/discover-card.component.scss +++ b/src/Ombi/ClientApp/src/app/discover/card/discover-card.component.scss @@ -6,9 +6,7 @@ $card-background: #2b2b2b; } .dark-card { - background-color: $card-background; border-radius: 8px; - color: white; height: 435px; } diff --git a/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.html b/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.html new file mode 100644 index 000000000..4499bdce5 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.html @@ -0,0 +1,26 @@ +
+ +
+
+ + + +
+
+
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.scss b/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.scss new file mode 100644 index 000000000..728ff23c5 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.scss @@ -0,0 +1,19 @@ +.full-height { + height: 100%; +} + + +.small-middle-container{ + margin: auto; + width: 80%; +} + +.small-padding { + padding-left: 20px; + padding-right: 20px; + margin-bottom: 28px; +} + +.loading-spinner { + margin: 10%; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.ts b/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.ts new file mode 100644 index 000000000..5ee0a485d --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/collections/discover-collections.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; +import { SearchV2Service } from "../../services"; +import { IMovieCollectionsViewModel } from "../../interfaces/ISearchTvResultV2"; + +@Component({ + templateUrl: "./discover-collections.component.html", + styleUrls: ["./discover-collections.component.scss"], +}) +export class DiscoverCollectionsComponent implements OnInit { + + public collectionId: number; + public collection: IMovieCollectionsViewModel; + + constructor(private searchService: SearchV2Service, private route: ActivatedRoute) { + this.route.params.subscribe((params: any) => { + this.collectionId = params.collectionId; + }); + } + + public async ngOnInit() { + this.collection = await this.searchService.getMovieCollections(this.collectionId); + } +} diff --git a/src/Ombi/ClientApp/src/app/discover/discover.module.ts b/src/Ombi/ClientApp/src/app/discover/discover.module.ts index 989423e07..182936124 100644 --- a/src/Ombi/ClientApp/src/app/discover/discover.module.ts +++ b/src/Ombi/ClientApp/src/app/discover/discover.module.ts @@ -10,9 +10,11 @@ import { AuthGuard } from "../auth/auth.guard"; import { PipeModule } from "../pipes/pipe.module"; import { DiscoverCardDetailsComponent } from "./card/discover-card-details.component"; import { MatDialog } from "@angular/material"; +import { DiscoverCollectionsComponent } from "./collections/discover-collections.component"; const routes: Routes = [ { path: "", component: DiscoverComponent, canActivate: [AuthGuard] }, + { path: "collection/:collectionId", component: DiscoverCollectionsComponent, canActivate: [AuthGuard] } ]; @NgModule({ imports: [ @@ -24,6 +26,7 @@ const routes: Routes = [ DiscoverComponent, DiscoverCardComponent, DiscoverCardDetailsComponent, + DiscoverCollectionsComponent, ], entryComponents: [ DiscoverCardDetailsComponent diff --git a/src/Ombi/ClientApp/src/app/interfaces/ICommon.ts b/src/Ombi/ClientApp/src/app/interfaces/ICommon.ts index a117565fd..06f33eb69 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ICommon.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ICommon.ts @@ -31,4 +31,5 @@ export interface INavBar { icon: string; name: string; link: string; + requiresAdmin: boolean; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts index c34fb6c85..586f638e0 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts @@ -1,4 +1,4 @@ -import { INewSeasonRequests } from "./IRequestModel"; +import { INewSeasonRequests, RequestType } from "./IRequestModel"; export interface ISearchTvResultV2 { id: number; @@ -44,6 +44,26 @@ export interface ISearchTvResultV2 { requestId: number; } +export interface IMovieCollectionsViewModel { + name: string; + overview: string; + collection: IMovieCollection[]; +} + +export interface IMovieCollection { + id: number; + overview: string; + posterPath: string; + title: string; + type: RequestType; + + approved: boolean; + requested: boolean; + available: boolean; + plexUrl: string; + embyUrl: string; + imdbId: string; +} export interface INetwork { id: number; 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 9d7b22706..505440d69 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 @@ -64,7 +64,7 @@
- + 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 bf94c89a4..5a6982efd 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 @@ -3,7 +3,7 @@ -
+
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.scss b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.scss new file mode 100644 index 000000000..d9db92e00 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.scss @@ -0,0 +1,7 @@ +@import "~@angular/material/theming"; +@import "~styles/variables.scss"; +.actor-background { + .dark & { + background: $backgroundTint-dark; + } +} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.ts index 203841250..d7baa81f0 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.ts @@ -3,6 +3,7 @@ import { Component, Input } from "@angular/core"; @Component({ selector: "cast-carousel", templateUrl: "./cast-carousel.component.html", + styleUrls: ["./cast-carousel.component.scss"] }) export class CastCarouselComponent { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index ef5e29537..4dbbf416e 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -1,43 +1,43 @@ - + - + - + + matTooltip="Trailer" class="fa fa-youtube-play fa-2x grow-social"> - + - + - + - + + class="fa fa-play-circle fa-2x grow-social"> + class="fa fa-play-circle fa-2x grow-social"> \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss new file mode 100644 index 000000000..1925e4e98 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss @@ -0,0 +1,8 @@ + +.grow-social { + transition: all .2s ease-in-out; +} +.grow-social:hover { + transform: scale(1.1); + color: black; +} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts index 25897b9a1..7b1e69ee3 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts @@ -3,6 +3,7 @@ import { Component, Inject, Input, Output, EventEmitter } from "@angular/core"; @Component({ selector: "social-icons", templateUrl: "./social-icons.component.html", + styleUrls: ["./social-icons.component.scss"] }) export class SocialIconsComponent { diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss index 918deaa07..11a039a62 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss @@ -174,14 +174,6 @@ width: 173px; } -.grow { - transition: all .2s ease-in-out; -} -.grow:hover { - transform: scale(1.1); - color: black; -} - .media-icons { color: mat-color($ombi-app-primary) !important; padding: 1%; diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index 56b363725..8de40bfcc 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -3,11 +3,12 @@ [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="!(isHandset$ | async)"> {{applicationName}} - + {{nav.icon}} {{nav.name | translate}} - + exit_to_app diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index d33b11843..a52f99969 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -19,6 +19,7 @@ export class MyNavComponent implements OnInit { @Input() public showNav: boolean; @Input() public applicationName: string; @Input() public username: string; + @Input() public isAdmin: string; @Output() public logoutClick = new EventEmitter(); @Output() public themeChange = new EventEmitter(); public theme: string; @@ -31,10 +32,10 @@ export class MyNavComponent implements OnInit { } public navItems: INavBar[] = [ - { name: "NavigationBar.Discover", icon: "find_replace", link: "/discover" }, - { name: "NavigationBar.Requests", icon: "list", link: "/requests-list" }, - { name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement" }, - { name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About" }, + { name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false }, + { name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false }, + { name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true }, + { name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true }, ] public logOut() { @@ -57,7 +58,6 @@ export class MyNavComponent implements OnInit { } public getTheme(){ - debugger; return this.theme === 'dark' ? 'active-list-item-dark' : 'active-list-item'; } diff --git a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts index a5ae6fa17..4b98e2d54 100644 --- a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts @@ -1,4 +1,4 @@ -import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; +import { APP_BASE_HREF } from "@angular/common"; import { Injectable, Inject } from "@angular/core"; import { HttpClient } from "@angular/common/http"; @@ -8,8 +8,7 @@ import { IMultiSearchResult, ISearchMovieResult, ISearchTvResult } from "../inte import { ServiceHelpers } from "./service.helpers"; import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2"; -import { promise } from "selenium-webdriver"; -import { ISearchTvResultV2 } from "../interfaces/ISearchTvResultV2"; +import { ISearchTvResultV2, IMovieCollectionsViewModel } from "../interfaces/ISearchTvResultV2"; @Injectable() export class SearchV2Service extends ServiceHelpers { @@ -64,7 +63,12 @@ export class SearchV2Service extends ServiceHelpers { public getTvInfo(tvdbid: number): Promise { return this.http.get(`${this.url}/Tv/${tvdbid}`, { headers: this.headers }).toPromise(); } + public getTvInfoWithMovieDbId(theMovieDbId: number): Promise { return this.http.get(`${this.url}/Tv/moviedb/${theMovieDbId}`, { headers: this.headers }).toPromise(); } + + public getMovieCollections(collectionId: number): Promise { + return this.http.get(`${this.url}/movie/collection/${collectionId}`, { headers: this.headers }).toPromise(); + } } diff --git a/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts b/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts index 4e39f95ee..c3f88fc3a 100644 --- a/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts +++ b/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts @@ -3,9 +3,9 @@ export function getBaseLocation() { let paths: string[] = location.pathname.split('/').splice(1, 1); let basePath: string = (paths && paths[0] ? paths[0] : ""); if(invalidProxies.indexOf(basePath.toUpperCase()) === -1){ - return '/'; + return '/' + basePath; } - return '/' + basePath; + return '/'; } const invalidProxies: string[] = [ diff --git a/src/Ombi/ClientApp/src/styles/primeng-overrides.scss b/src/Ombi/ClientApp/src/styles/primeng-overrides.scss index 101626719..4ccb0765e 100644 --- a/src/Ombi/ClientApp/src/styles/primeng-overrides.scss +++ b/src/Ombi/ClientApp/src/styles/primeng-overrides.scss @@ -9,7 +9,7 @@ .ui-carousel-viewport { border:0 !important; - background-color:$background !important; + background-color:transparent !important; } .ui-carousel .ui-carousel-header { diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index 139deb9f9..4f028bc75 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -60,6 +60,16 @@ namespace Ombi.Controllers.V2 return await _movieEngineV2.GetFullMovieInformation(movieDbId); } + /// + /// Returns basic information about the provided collection + /// + /// The collection id from TheMovieDb + /// + [HttpGet("movie/collection/{collectionId}")] + public async Task GetMovieCollections(int collectionId) + { + return await _movieEngineV2.GetCollection(collectionId); + } /// /// Returns details for a single show