diff --git a/src/Ombi.Api.TvMaze/ITvMazeApi.cs b/src/Ombi.Api.TvMaze/ITvMazeApi.cs index 9a8688568..819051d83 100644 --- a/src/Ombi.Api.TvMaze/ITvMazeApi.cs +++ b/src/Ombi.Api.TvMaze/ITvMazeApi.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Api.TvMaze.Models; +using Ombi.Api.TvMaze.Models.V2; namespace Ombi.Api.TvMaze { @@ -11,5 +12,6 @@ namespace Ombi.Api.TvMaze Task> Search(string searchTerm); Task ShowLookup(int showId); Task ShowLookupByTheTvDbId(int theTvDbId); + Task GetTvFullInformation(int id); } } \ No newline at end of file diff --git a/src/Ombi.Api.TvMaze/Models/V2/FullSearch.cs b/src/Ombi.Api.TvMaze/Models/V2/FullSearch.cs new file mode 100644 index 000000000..77b9c4a28 --- /dev/null +++ b/src/Ombi.Api.TvMaze/Models/V2/FullSearch.cs @@ -0,0 +1,144 @@ +using System; + +namespace Ombi.Api.TvMaze.Models.V2 +{ + public class FullSearch + { + public int id { get; set; } + public string url { get; set; } + public string name { get; set; } + public string type { get; set; } + public string language { get; set; } + public string[] genres { get; set; } + public string status { get; set; } + public int runtime { get; set; } + public string premiered { get; set; } + public string officialSite { get; set; } + public Schedule schedule { get; set; } + public Rating rating { get; set; } + public int weight { get; set; } + public Network network { get; set; } + public object webChannel { get; set; } + public Externals externals { get; set; } + public Image image { get; set; } + public string summary { get; set; } + public int updated { get; set; } + public _Links _links { get; set; } + public _Embedded _embedded { get; set; } + } + + public class Schedule + { + public string time { get; set; } + public string[] days { get; set; } + } + + public class Rating + { + public float average { get; set; } + } + + public class Network + { + public int id { get; set; } + public string name { get; set; } + public Country country { get; set; } + } + + public class Country + { + public string name { get; set; } + public string code { get; set; } + public string timezone { get; set; } + } + + public class Externals + { + public int tvrage { get; set; } + public int thetvdb { get; set; } + public string imdb { get; set; } + } + + public class Image + { + public string medium { get; set; } + public string original { get; set; } + } + + public class _Links + { + public Self self { get; set; } + public Previousepisode previousepisode { get; set; } + } + + public class Self + { + public string href { get; set; } + } + + public class Previousepisode + { + public string href { get; set; } + } + + public class _Embedded + { + public Cast[] cast { get; set; } + public Crew[] crew { get; set; } + public Episode[] episodes { get; set; } + } + + public class Cast + { + public Person person { get; set; } + public Character character { get; set; } + public bool self { get; set; } + public bool voice { get; set; } + } + + public class Person + { + public int id { get; set; } + public string url { get; set; } + public string name { get; set; } + public Country country { get; set; } + public string birthday { get; set; } + public object deathday { get; set; } + public string gender { get; set; } + public Image image { get; set; } + public _Links _links { get; set; } + } + + + public class Character + { + public int id { get; set; } + public string url { get; set; } + public string name { get; set; } + public Image image { get; set; } + public _Links _links { get; set; } + } + + public class Crew + { + public string type { get; set; } + public Person person { get; set; } + } + + public class Episode + { + public int id { get; set; } + public string url { get; set; } + public string name { get; set; } + public int season { get; set; } + public int number { get; set; } + public string airdate { get; set; } + public string airtime { get; set; } + public DateTime airstamp { get; set; } + public int runtime { get; set; } + public Image image { get; set; } + public string summary { get; set; } + public _Links _links { get; set; } + } + +} diff --git a/src/Ombi.Api.TvMaze/TvMazeApi.cs b/src/Ombi.Api.TvMaze/TvMazeApi.cs index 9aa547483..5d761da47 100644 --- a/src/Ombi.Api.TvMaze/TvMazeApi.cs +++ b/src/Ombi.Api.TvMaze/TvMazeApi.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Ombi.Api.TvMaze.Models; +using Ombi.Api.TvMaze.Models.V2; using Ombi.Helpers; namespace Ombi.Api.TvMaze @@ -15,7 +15,6 @@ namespace Ombi.Api.TvMaze { Api = api; Logger = logger; - //Mapper = mapper; } private string Uri = "http://api.tvmaze.com"; private IApi Api { get; } @@ -75,5 +74,17 @@ namespace Ombi.Api.TvMaze return await Api.Request>(request); } + public async Task GetTvFullInformation(int id) + { + var request = new Request($"shows/{id}", Uri, HttpMethod.Get); + + request.AddQueryString("embed[]", "cast"); + request.AddQueryString("embed[]", "crew"); + request.AddQueryString("embed[]", "episodes"); + + request.AddContentHeader("Content-Type", "application/json"); + + return await Api.Request(request); + } } } diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs new file mode 100644 index 000000000..0a18a32bf --- /dev/null +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Ombi.Core.Models.Search.V2; + +namespace Ombi.Core +{ + public interface ITVSearchEngineV2 + { + Task GetShowInformation(int tvdbid); + } +} \ 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 1c77f8607..e3fe312cb 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -12,14 +12,11 @@ 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.Models.Search.V2; -namespace Ombi.Core.Engine +namespace Ombi.Core.Engine.V2 { public class MovieSearchEngineV2 : BaseMediaEngine, IMovieEngineV2 { diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs new file mode 100644 index 000000000..4a31f66e4 --- /dev/null +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -0,0 +1,129 @@ +using AutoMapper; + +using Ombi.Api.Trakt; +using Ombi.Api.TvMaze; +using Ombi.Core.Models.Requests; +using Ombi.Core.Models.Search; +using Ombi.Core.Models.Search.V2; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Store.Repository; + +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Threading.Tasks; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Repository.Requests; +using Ombi.Core.Authentication; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; + +namespace Ombi.Core.Engine.V2 +{ + public class TvSearchEngineV2 : BaseMediaEngine, ITVSearchEngineV2 + { + public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService plexSettings, + ISettingsService embySettings, IPlexContentRepository repo, IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, + ICacheService memCache, ISettingsService s, IRepository sub) + : base(identity, service, r, um, memCache, s, sub) + { + TvMazeApi = tvMaze; + Mapper = mapper; + PlexSettings = plexSettings; + EmbySettings = embySettings; + PlexContentRepo = repo; + TraktApi = trakt; + EmbyContentRepo = embyRepo; + } + + private ITvMazeApi TvMazeApi { get; } + private IMapper Mapper { get; } + private ISettingsService PlexSettings { get; } + private ISettingsService EmbySettings { get; } + private IPlexContentRepository PlexContentRepo { get; } + private IEmbyContentRepository EmbyContentRepo { get; } + private ITraktApi TraktApi { get; } + + + public async Task GetShowInformation(int tvdbid) + { + var tvdbshow = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid); + var show = await TvMazeApi.GetTvFullInformation(tvdbshow.id); + if (show == null) + { + // We don't have enough information + return null; + } + + var mapped = Mapper.Map(show); + + foreach (var e in show._embedded.episodes) + { + var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season); + if (season == null) + { + var newSeason = new SeasonRequests + { + SeasonNumber = e.season, + Episodes = new List() + }; + newSeason.Episodes.Add(new EpisodeRequests + { + Url = e.url, + Title = e.name, + AirDate = e.airstamp, + EpisodeNumber = e.number, + + }); + mapped.SeasonRequests.Add(newSeason); + } + else + { + // We already have the season, so just add the episode + season.Episodes.Add(new EpisodeRequests + { + Url = e.url, + Title = e.name, + AirDate = e.airstamp, + EpisodeNumber = e.number, + }); + } + } + return await ProcessResult(mapped); + } + + private IEnumerable ProcessResults(IEnumerable items) + { + var retVal = new List(); + foreach (var tvMazeSearch in items) + { + retVal.Add(ProcessResult(tvMazeSearch)); + } + return retVal; + } + + private SearchTvShowViewModel ProcessResult(T tvMazeSearch) + { + return Mapper.Map(tvMazeSearch); + } + + private async Task ProcessResult(SearchFullInfoTvShowViewModel item) + { + item.TheTvDbId = item.Id.ToString(); + + var oldModel = Mapper.Map(item); + await RunSearchRules(oldModel); + + item.Available = oldModel.Available; + item.FullyAvailable = oldModel.FullyAvailable; + item.PartlyAvailable = oldModel.PartlyAvailable; + item.Requested = oldModel.Requested; + item.Available = oldModel.Available; + item.Approved = oldModel.Approved; + + return item; + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs b/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs new file mode 100644 index 000000000..8065d497e --- /dev/null +++ b/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs @@ -0,0 +1,113 @@ +using Ombi.Store.Repository.Requests; +using System.Collections.Generic; +using Ombi.Store.Entities; + +namespace Ombi.Core.Models.Search.V2 +{ + public class SearchFullInfoTvShowViewModel : SearchViewModel + { + public string Title { get; set; } + public List Aliases { get; set; } + public string Banner { get; set; } + public int SeriesId { get; set; } + public string Status { get; set; } + public string FirstAired { get; set; } + public string NetworkId { get; set; } + public string Runtime { get; set; } + public List Genre { get; set; } + public string Overview { get; set; } + public int LastUpdated { get; set; } + public string AirsDayOfWeek { get; set; } + public string AirsTime { get; set; } + public string Rating { get; set; } + public int SiteRating { get; set; } + public NetworkViewModel Network { get; set; } + public Images Images { get; set; } + public List Cast { get; set; } + public List Crew { get; set; } + + /// + /// This is used from the Trakt API + /// + /// + /// The trailer. + /// + public string Trailer { get; set; } + + /// + /// This is used from the Trakt API + /// + /// + /// The trailer. + /// + public string Homepage { get; set; } + + public List SeasonRequests { get; set; } = new List(); + + /// + /// If we are requesting the entire series + /// + public bool RequestAll { get; set; } + + public bool FirstSeason { get; set; } + public bool LatestSeason { get; set; } + + /// + /// This is where we have EVERY Episode for that series + /// + public bool FullyAvailable { get; set; } + // We only have some episodes + public bool PartlyAvailable { get; set; } + public override RequestType Type => RequestType.TvShow; + } + + public class NetworkViewModel + { + public int Id { get; set; } + public string Name { get; set; } + public Country Country { get; set; } + } + + public class Country + { + public string Name { get; set; } + public string Code { get; set; } + public string Timezone { get; set; } + } + + public class Images + { + public string Medium { get; set; } + public string Original { get; set; } + } + + public class CastViewModel + { + public PersonViewModel Person { get; set; } + public CharacterViewModel Character { get; set; } + public bool Self { get; set; } + public bool Voice { get; set; } + } + + public class PersonViewModel + { + public int Id { get; set; } + public string Url { get; set; } + public string Name { get; set; } + public Images Image { get; set; } + } + + public class CharacterViewModel + { + public int Id { get; set; } + public string Url { get; set; } + public string Name { get; set; } + public Images Image { get; set; } + } + + public class CrewViewModel + { + public string Type { get; set; } + public PersonViewModel Person { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 4d5ef6fd5..66e6df692 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -50,7 +50,6 @@ using Ombi.Schedule.Jobs.Plex; using Ombi.Schedule.Jobs.Sonarr; using Ombi.Store.Repository.Requests; using Ombi.Updater; -using PlexContentCacher = Ombi.Schedule.Jobs.Plex; using Ombi.Api.Telegram; using Ombi.Core.Authentication; using Ombi.Core.Engine.V2; @@ -59,7 +58,6 @@ using Ombi.Schedule.Jobs.Lidarr; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.SickRage; using Ombi.Schedule.Processor; -using Ombi.Store.Entities; namespace Ombi.DependencyInjection { @@ -100,6 +98,7 @@ namespace Ombi.DependencyInjection { services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterHttp(this IServiceCollection services) diff --git a/src/Ombi.Mapping/AutoMapperProfile.cs b/src/Ombi.Mapping/AutoMapperProfile.cs index b0b1fd725..a39bc8e71 100644 --- a/src/Ombi.Mapping/AutoMapperProfile.cs +++ b/src/Ombi.Mapping/AutoMapperProfile.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using AutoMapper; using AutoMapper.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -12,9 +11,9 @@ namespace Ombi.Mapping { public static IServiceCollection AddOmbiMappingProfile(this IServiceCollection services) { - System.Reflection.Assembly ass = typeof(AutoMapperProfile).GetTypeInfo().Assembly; + Assembly ass = typeof(AutoMapperProfile).GetTypeInfo().Assembly; var assemblies = new List(); - foreach (System.Reflection.TypeInfo ti in ass.DefinedTypes) + foreach (TypeInfo ti in ass.DefinedTypes) { if (ti.ImplementedInterfaces.Contains(typeof(IProfileConfiguration))) { diff --git a/src/Ombi.Mapping/Profiles/TvProfileV2.cs b/src/Ombi.Mapping/Profiles/TvProfileV2.cs new file mode 100644 index 000000000..3a8aeeb62 --- /dev/null +++ b/src/Ombi.Mapping/Profiles/TvProfileV2.cs @@ -0,0 +1,82 @@ +using System.Globalization; +using AutoMapper; +using Ombi.Api.TvMaze.Models.V2; +using Ombi.Core.Models.Search; +using Ombi.Core.Models.Search.V2; +using Ombi.Helpers; + +namespace Ombi.Mapping.Profiles +{ + public class TvProfileV2 : Profile + { + public TvProfileV2() + { + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.externals.thetvdb)) + .ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.premiered)) + .ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.externals.imdb)) + .ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network.name)) + .ForMember(dest => dest.NetworkId, opts => opts.MapFrom(src => src.network.id.ToString())) + .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.summary.RemoveHtml())) + .ForMember(dest => dest.Rating, + opts => opts.MapFrom(src => src.rating.average.ToString(CultureInfo.CurrentUICulture))) + .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime.ToString())) + .ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network)) + .ForMember(dest => dest.Images, opts => opts.MapFrom(src => src.image)) + .ForMember(dest => dest.Cast, opts => opts.MapFrom(src => src._embedded.cast)) + .ForMember(dest => dest.Crew, opts => opts.MapFrom(src => src._embedded.crew)) + .ForMember(dest => dest.Banner, + opts => opts.MapFrom(src => + !string.IsNullOrEmpty(src.image.medium) + ? src.image.medium.Replace("http", "https") + : string.Empty)) + .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status)); + + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Country, opts => opts.MapFrom(src => src.country)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)); + + CreateMap() + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.Code, opts => opts.MapFrom(src => src.code)) + .ForMember(dest => dest.Timezone, opts => opts.MapFrom(src => src.timezone)); + + CreateMap() + .ForMember(dest => dest.Medium, opts => opts.MapFrom(src => src.medium)) + .ForMember(dest => dest.Original, opts => opts.MapFrom(src => src.original)); + + CreateMap() + .ForMember(dest => dest.Character, opts => opts.MapFrom(src => src.character)) + .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person)) + .ForMember(dest => dest.Voice, opts => opts.MapFrom(src => src.voice)) + .ForMember(dest => dest.Self, opts => opts.MapFrom(src => src.self)); + + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.image)) + .ForMember(dest => dest.Url, opts => opts.MapFrom(src => src.url)); + + CreateMap() + .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person)) + .ForMember(dest => dest.Type, opts => opts.MapFrom(src => src.type)); + + CreateMap() + .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person)) + .ForMember(dest => dest.Self, opts => opts.MapFrom(src => src.self)) + .ForMember(dest => dest.Voice, opts => opts.MapFrom(src => src.voice)) + .ForMember(dest => dest.Character, opts => opts.MapFrom(src => src.character)); + + CreateMap() + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Url, opts => opts.MapFrom(src => src.url)) + .ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.image)); + + CreateMap().ReverseMap(); + } + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 6d7a271ea..63a7eea25 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -85,8 +85,6 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo } export function baseurlFact() { - - console.log(window['_app_base']); return "/" + window['_app_base']; } diff --git a/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.html b/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.html index 3de9f0eb8..cf5237867 100644 --- a/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.html +++ b/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.html @@ -1,4 +1,7 @@ -
+
+ +
+
{{data.title}} @@ -19,9 +22,12 @@ [translate]="'Common.NotAvailable'">
- Studio: {{result.productionCompanies[0].name}} + Studio: + {{movie.productionCompanies[0].name}} + Network: + {{tv.network.name}}
-
+
Request Status: @@ -32,13 +38,22 @@
- Director: {{result.credits.crew[0].name}} + Director: + {{movie.credits.crew[0].name}} + Director: + {{tvCreator}}
- In Cinemas: {{result.releaseDate}} + In Cinemas: + {{movie.releaseDate | amLocal | amDateFormat: 'LL'}} + First Aired: + {{tv.firstAired | amLocal | amDateFormat: 'LL'}}
- Writer: {{result.credits.crew[1].name}} + Writer: + {{movie.credits.crew[1].name}} + Exec Producer: + {{tvProducer}}
diff --git a/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.ts b/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.ts index 8a6dcca6c..19b312d5c 100644 --- a/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/card/discover-card-details.component.ts @@ -2,9 +2,9 @@ import { Component, Inject, OnInit } from "@angular/core"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material"; import { IDiscoverCardResult } from "../interfaces"; import { SearchV2Service } from "../../services"; -import { dataURLToBlob } from "blob-util"; import { RequestType } from "../../interfaces"; import { ISearchMovieResultV2 } from "../../interfaces/ISearchMovieResultV2"; +import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2"; @Component({ selector: "discover-card-details", @@ -13,16 +13,33 @@ import { ISearchMovieResultV2 } from "../../interfaces/ISearchMovieResultV2"; }) export class DiscoverCardDetailsComponent implements OnInit { - public result: ISearchMovieResultV2; + public movie: ISearchMovieResultV2; + public tv: ISearchTvResultV2; + public tvCreator: string; + public tvProducer: string; + public loading: boolean;; constructor( public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: IDiscoverCardResult, private searchService: SearchV2Service) { } public async ngOnInit() { + this.loading = true; if (this.data.type === RequestType.movie) { - this.result = await this.searchService.getFullMovieDetailsPromise(this.data.id); + this.movie = await this.searchService.getFullMovieDetailsPromise(this.data.id); + } else if (this.data.type === RequestType.tvShow) { + this.tv = await this.searchService.getTvInfo(this.data.id); + const creator = this.tv.crew.filter(tv => { + return tv.type === "Creator"; + })[0]; + if(creator) { + this.tvCreator = creator.person.name; + } + this.tvProducer = this.tv.crew.filter(tv => { + return tv.type === "Executive Producer"; + })[0].person.name; } + this.loading = false; } public onNoClick(): void { diff --git a/src/Ombi/ClientApp/src/app/discover/discover.component.html b/src/Ombi/ClientApp/src/app/discover/discover.component.html index a0fe044a6..9ac548749 100644 --- a/src/Ombi/ClientApp/src/app/discover/discover.component.html +++ b/src/Ombi/ClientApp/src/app/discover/discover.component.html @@ -2,9 +2,9 @@
- - - + + +
@@ -15,21 +15,3 @@
- \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/discover.component.ts b/src/Ombi/ClientApp/src/app/discover/discover.component.ts index 147382ea8..3074c74d1 100644 --- a/src/Ombi/ClientApp/src/app/discover/discover.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/discover.component.ts @@ -24,6 +24,10 @@ export class DiscoverComponent implements OnInit { public defaultTvPoster: string; + public popularActive: boolean = true; + public trendingActive: boolean; + public upcomingActive: boolean; + constructor(private searchService: SearchV2Service) { } @@ -36,6 +40,9 @@ export class DiscoverComponent implements OnInit { } public async popular() { + this.popularActive = true; + this.trendingActive = false; + this.upcomingActive = false; this.movies = await this.searchService.popularMovies().toPromise(); this.tvShows = await this.searchService.popularTv().toPromise(); @@ -43,6 +50,9 @@ export class DiscoverComponent implements OnInit { } public async trending() { + this.popularActive = false; + this.trendingActive = true; + this.upcomingActive = false; this.movies = await this.searchService.nowPlayingMovies().toPromise(); this.tvShows = await this.searchService.trendingTv().toPromise(); @@ -50,6 +60,9 @@ export class DiscoverComponent implements OnInit { } public async upcoming() { + this.popularActive = false; + this.trendingActive = false; + this.upcomingActive = true; this.movies = await this.searchService.upcomingMovies().toPromise(); this.tvShows = await this.searchService.anticipatedTv().toPromise(); diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts new file mode 100644 index 000000000..51395b9ec --- /dev/null +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts @@ -0,0 +1,86 @@ +import { INewSeasonRequests } from "./IRequestModel"; + +export interface ISearchTvResultV2 { + id: number; + title: string; // used in the request + aliases: string[]; + banner: string; + seriesId: number; + status: string; + firstAired: string; + networkId: string; + runtime: string; + genre: string[]; + overview: string; + lastUpdated: number; + airsDayOfWeek: string; + airsTime: string; + rating: string; + imdbId: string; + siteRating: number; + trailer: string; + homepage: string; + seasonRequests: INewSeasonRequests[]; + requestAll: boolean; + approved: boolean; + requested: boolean; + available: boolean; + plexUrl: string; + embyUrl: string; + quality: string; + firstSeason: boolean; + latestSeason: boolean; + theTvDbId: string; + subscribed: boolean; + showSubscribe: boolean; + fullyAvailable: boolean; + partlyAvailable: boolean; + network: INetwork; + images: IImagesV2; + cast: ICast[]; + crew: ICrew[]; +} + + +export interface INetwork { + id: number; + name: string; + country: ICountry; +} + +export interface ICountry { + name: string; + code: string; + timezone: string; +} + +export interface IImagesV2 { + medium: string; + original: string; +} + +export interface ICast { + self: boolean; + voide: boolean; + person: IPersonViewModel; + character: ICharacterViewModel; +} + +export interface IPersonViewModel { + id: number; + url: string; + name: string; + image: IImagesV2; +} + +export interface ICharacterViewModel { + id: number; + url: string; + name: string; + image: IImagesV2; +} + +export interface ICrew { + type: string; + person: IPersonViewModel; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts index 65b68902a..02d4da317 100644 --- a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts @@ -9,6 +9,7 @@ import { ServiceHelpers } from "./service.helpers"; import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2"; import { promise } from "selenium-webdriver"; +import { ISearchTvResultV2 } from "../interfaces/ISearchTvResultV2"; @Injectable() export class SearchV2Service extends ServiceHelpers { @@ -55,7 +56,12 @@ export class SearchV2Service extends ServiceHelpers { public anticipatedTv(): Observable { return this.http.get(`${this.url}/Tv/anticipated`, { headers: this.headers }); } + public trendingTv(): Observable { return this.http.get(`${this.url}/Tv/trending`, { headers: this.headers }); } + + public getTvInfo(tvdbid: number): Promise { + return this.http.get(`${this.url}/Tv/${tvdbid}`, { headers: this.headers }).toPromise(); + } } diff --git a/src/Ombi/ClientApp/src/app/shared/shared.module.ts b/src/Ombi/ClientApp/src/app/shared/shared.module.ts index 34678f4b7..c28e69183 100644 --- a/src/Ombi/ClientApp/src/app/shared/shared.module.ts +++ b/src/Ombi/ClientApp/src/app/shared/shared.module.ts @@ -11,7 +11,7 @@ import { InputSwitchModule, SidebarModule } from "primeng/primeng"; import { MatButtonModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, MatTooltipModule} from '@angular/material'; - import { MatCardModule, MatInputModule, MatTabsModule, MatAutocompleteModule, MatCheckboxModule, MatExpansionModule, MatDialogModule } from "@angular/material"; + import { MatCardModule, MatInputModule, MatTabsModule, MatAutocompleteModule, MatCheckboxModule, MatExpansionModule, MatDialogModule, MatProgressSpinnerModule } from "@angular/material"; @NgModule({ declarations: [ @@ -25,6 +25,7 @@ import { TruncateModule, MomentModule, MatCardModule, + MatProgressSpinnerModule, MatAutocompleteModule, MatInputModule, MatTabsModule, @@ -43,6 +44,7 @@ import { CommonModule, FormsModule, SidebarModule, + MatProgressSpinnerModule, IssuesReportComponent, TruncateModule, InputSwitchModule, diff --git a/src/Ombi/ClientApp/src/styles/shared.scss b/src/Ombi/ClientApp/src/styles/shared.scss index 1d1c29ad9..dbcaec107 100644 --- a/src/Ombi/ClientApp/src/styles/shared.scss +++ b/src/Ombi/ClientApp/src/styles/shared.scss @@ -24,4 +24,14 @@ .btn-orange { background-color: #F57C00; - } \ No newline at end of file + } + +.spinner-container { + position: relative; + margin-left: 50%; +} + +.active-button { + background-color: #5dffbd !important; + color:white; +} \ No newline at end of file diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index 5e6ba52de..10bc9d07d 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -19,7 +19,7 @@ namespace Ombi.Controllers.V2 public class SearchController : ControllerBase { public SearchController(IMultiSearchEngine multiSearchEngine, IMovieEngine movieEngine, - ITvSearchEngine tvSearchEngine, IMovieEngineV2 v2Movie) + ITvSearchEngine tvSearchEngine, IMovieEngineV2 v2Movie, ITVSearchEngineV2 v2Tv) { _multiSearchEngine = multiSearchEngine; _movieEngine = movieEngine; @@ -27,11 +27,13 @@ namespace Ombi.Controllers.V2 _tvSearchEngine = tvSearchEngine; _tvSearchEngine.ResultLimit = 12; _movieEngineV2 = v2Movie; + _tvEngineV2 = v2Tv; } private readonly IMultiSearchEngine _multiSearchEngine; private readonly IMovieEngine _movieEngine; private readonly IMovieEngineV2 _movieEngineV2; + private readonly ITVSearchEngineV2 _tvEngineV2; private readonly ITvSearchEngine _tvSearchEngine; /// @@ -54,6 +56,17 @@ namespace Ombi.Controllers.V2 return await _movieEngineV2.GetFullMovieInformation(movieDbId); } + + /// + /// Returns details for a single show + /// + /// + [HttpGet("tv/{tvdbId}")] + public async Task GetTvInfo(int tvdbid) + { + return await _tvEngineV2.GetShowInformation(tvdbid); + } + /// /// Returns similar movies to the movie id passed in ///