stuff around episode/season searching/requesting

pull/1425/head
Jamie.Rees 8 years ago
parent 07f5c77c3c
commit b829b10b14

@ -34,7 +34,7 @@ namespace Ombi.Api.TvMaze.Models
public class TvMazeCustomSeason
{
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
public List<int> EpisodeNumber { get; set; }
}
public class Season

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@ -60,11 +61,25 @@ namespace Ombi.Api.TvMaze
foreach (var e in episodes)
{
obj.Season.Add(new TvMazeCustomSeason
// Check if the season exists
var currentSeason = obj.Season.FirstOrDefault(x => x.SeasonNumber == e.season);
if (currentSeason == null)
{
// Create the season
obj.Season.Add(new TvMazeCustomSeason
{
SeasonNumber = e.season,
EpisodeNumber = new List<int> {e.number}
});
}
else
{
SeasonNumber = e.season,
EpisodeNumber = e.number
});
// Just add a new episode into that season
currentSeason.EpisodeNumber.Add(e.number);
}
}
return obj;

@ -2,11 +2,13 @@
using System.Threading.Tasks;
using Ombi.Core.Models.Search;
namespace Ombi.Core.Engine
namespace Ombi.Core.Engine.Interfaces
{
public interface ITvSearchEngine
{
Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm);
Task<SearchTvShowViewModel> GetShowInformation(int tvdbId);
//Task<IEnumerable<SearchTvShowViewModel>> Popular();
//Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
//Task<IEnumerable<SearchTvShowViewModel>> MostWatches();

@ -49,10 +49,32 @@ namespace Ombi.Core.Engine
ImdbId = showInfo.externals?.imdb ?? string.Empty,
TvDbId = tv.Id.ToString(),
ProviderId = tv.Id,
SeasonsNumbersRequested = tv.SeasonNumbersRequested,
RequestAll = tv.RequestAll
};
if (tv.LatestSeason)
{
var latest = showInfo.Season.OrderBy(x => x).FirstOrDefault();
model.SeasonRequests = showInfo.Season.Any()
? new List<SeasonRequestModel> {new SeasonRequestModel
{
SeasonNumber = latest.SeasonNumber,
Episodes = latest.EpisodeNumber
}}
: new List<SeasonRequestModel>();
}
if (tv.FirstSeason)
{
var first = showInfo.Season.OrderByDescending(x => x).FirstOrDefault();
model.SeasonRequests = showInfo.Season.Any()
? new List<SeasonRequestModel> {new SeasonRequestModel
{
SeasonNumber = first.SeasonNumber,
Episodes = first.EpisodeNumber
}}
: new List<SeasonRequestModel>();
}
var existingRequest = await TvRequestService.CheckRequestAsync(model.Id);
if (existingRequest != null)
@ -104,13 +126,13 @@ namespace Ombi.Core.Engine
private async Task<RequestEngineResult> AddExistingRequest(TvRequestModel newRequest, TvRequestModel existingRequest)
{
var episodeDifference = new List<EpisodesModel>();
var episodeDifference = new List<SeasonRequestModel>();
if (existingRequest.HasChildRequests)
{
// Let's check if this has already been requested as a child!
foreach (var children in existingRequest.ChildRequests)
{
var difference = GetListDifferences(children.Episodes, newRequest.Episodes).ToList();
var difference = GetListDifferences(children.SeasonRequests, newRequest.SeasonRequests).ToList();
if (difference.Any())
{
episodeDifference = difference;
@ -121,7 +143,7 @@ namespace Ombi.Core.Engine
if (episodeDifference.Any())
{
// This is where there are some episodes that have been requested, but this list contains the 'new' requests
newRequest.Episodes = episodeDifference;
newRequest.SeasonRequests = episodeDifference;
}
existingRequest.ChildRequests.Add(newRequest);
@ -132,17 +154,17 @@ namespace Ombi.Core.Engine
{
// TODO Auto Approval Code
}
return await AddRequest(newRequest);
return await AfterRequest(newRequest);
}
private IEnumerable<EpisodesModel> GetListDifferences(IEnumerable<EpisodesModel> existing, IEnumerable<EpisodesModel> request)
private IEnumerable<SeasonRequestModel> GetListDifferences(IEnumerable<SeasonRequestModel> existing, IEnumerable<SeasonRequestModel> request)
{
var newRequest = request
.Select(r =>
new EpisodesModel
new SeasonRequestModel
{
SeasonNumber = r.SeasonNumber,
EpisodeNumber = r.EpisodeNumber
Episodes = r.Episodes
}).ToList();
return newRequest.Except(existing);
@ -152,6 +174,11 @@ namespace Ombi.Core.Engine
{
await TvRequestService.AddRequestAsync(model);
return await AfterRequest(model);
}
private async Task<RequestEngineResult> AfterRequest(BaseRequestModel model)
{
if (ShouldSendNotification(model.Type))
{
var notificationModel = new NotificationModel

@ -5,6 +5,8 @@ using System.Threading.Tasks;
using AutoMapper;
using Ombi.Api.Trakt;
using Ombi.Api.TvMaze;
using Ombi.Api.TvMaze.Models;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Requests.Models;
@ -46,6 +48,12 @@ namespace Ombi.Core.Engine
return null;
}
public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbId)
{
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbId);
return Mapper.Map<SearchTvShowViewModel>(show);
}
//public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
//{
// var result = await TraktApi.GetPopularShows();
@ -113,7 +121,7 @@ namespace Ombi.Core.Engine
var dbt = existingRequests[tvdbid];
item.Requested = true;
item.EpisodesRequested = dbt.Episodes.ToList();
item.SeasonRequests = dbt.SeasonRequests.ToList();
item.Approved = dbt.Approved;
}
//if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid))

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Ombi.Store.Entities;
namespace Ombi.Core.Models.Requests
@ -54,4 +55,11 @@ namespace Ombi.Core.Models.Requests
return hashSeason + hashEp;
}
}
public class SeasonRequestModel
{
public int SeasonNumber { get; set; }
public List<int> Episodes { get; set; }
}
}

@ -6,15 +6,14 @@ namespace Ombi.Core.Models.Requests
{
public TvRequestModel()
{
Episodes = new List<EpisodesModel>();
SeasonRequests = new List<SeasonRequestModel>();
ChildRequests = new List<TvRequestModel>();
}
public string ImdbId { get; set; }
public string TvDbId { get; set; }
public bool RequestAll { get; set; }
public List<int> SeasonsNumbersRequested { get; set; }
public List<EpisodesModel> Episodes { get; set; }
public List<SeasonRequestModel> SeasonRequests { get; set; }
/// <summary>
/// This is for TV requests, If there is more than 1 request for a show then it should be a child

@ -39,22 +39,14 @@ namespace Ombi.Core.Models.Search
/// </value>
public string Homepage { get; set; }
/// <summary>
/// This is for when the users requests multiple seasons or a single season
/// </summary>
public List<int> SeasonNumbersRequested { get; set; } = new List<int>();
/// <summary>
/// If we have requested some episodes
/// </summary>
public List<EpisodesModel> EpisodesRequested { get; set; } = new List<EpisodesModel>();
public List<SeasonRequestModel> SeasonRequests { get; set; } = new List<SeasonRequestModel>();
/// <summary>
/// If we are requesting the entire series
/// </summary>
public bool RequestAll { get; set; }
public bool SpecificSeasonsRequested => SeasonNumbersRequested.Count > 0;
public bool SpecificEpisodesRequested => EpisodesRequested.Count > 0;
public bool FirstSeason { get; set; }
public bool LatestSeason { get; set; }
}
}

@ -1,10 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Security.Principal;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Ombi.Api.Emby;
using Ombi.Api.Plex;
@ -13,6 +9,7 @@ using Ombi.Api.TheMovieDb;
using Ombi.Api.TvMaze;
using Ombi.Core;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.IdentityResolver;
using Ombi.Core.Models.Requests;
using Ombi.Core.Requests.Models;
@ -23,14 +20,13 @@ using Ombi.Schedule.Jobs;
using Ombi.Settings.Settings;
using Ombi.Store.Context;
using Ombi.Store.Repository;
using Ombi.TheMovieDbApi;
namespace Ombi.DependencyInjection
{
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static class IocExtensions
{
public static IServiceCollection RegisterDependencies(this IServiceCollection services)
public static void RegisterDependencies(this IServiceCollection services)
{
services.RegisterEngines();
services.RegisterApi();
@ -38,30 +34,26 @@ namespace Ombi.DependencyInjection
services.RegisterStore();
services.RegisterIdentity();
services.RegisterJobs();
return services;
}
public static IServiceCollection RegisterEngines(this IServiceCollection services)
public static void RegisterEngines(this IServiceCollection services)
{
services.AddTransient<IMovieEngine, MovieSearchEngine>();
services.AddTransient<IMovieRequestEngine, MovieRequestEngine>();
services.AddTransient<ITvRequestEngine, TvRequestEngine>();
services.AddTransient<ITvSearchEngine, TvSearchEngine>();
return services;
}
public static IServiceCollection RegisterApi(this IServiceCollection services)
public static void RegisterApi(this IServiceCollection services)
{
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
services.AddTransient<IPlexApi, PlexApi>();
services.AddTransient<IEmbyApi, EmbyApi>();
services.AddTransient<ISonarrApi, SonarrApi>();
services.AddTransient<ITvMazeApi, TvMazeApi>();
return services;
}
public static IServiceCollection RegisterStore(this IServiceCollection services)
public static void RegisterStore(this IServiceCollection services)
{
services.AddEntityFrameworkSqlite().AddDbContext<OmbiContext>();
@ -72,29 +64,22 @@ namespace Ombi.DependencyInjection
services.AddTransient<ISettingsResolver, SettingsResolver>();
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
return services;
}
public static IServiceCollection RegisterServices(this IServiceCollection services)
public static void RegisterServices(this IServiceCollection services)
{
services.AddTransient<IRequestServiceMain, RequestService>();
services.AddTransient(typeof(IRequestService<>), typeof(JsonRequestService<>));
services.AddSingleton<INotificationService, NotificationService>();
return services;
}
public static IServiceCollection RegisterJobs(this IServiceCollection services)
public static void RegisterJobs(this IServiceCollection services)
{
services.AddTransient<IPlexContentCacher, PlexContentCacher>();
services.AddTransient<IJobSetup, JobSetup>();
return services;
}
public static IServiceCollection RegisterIdentity(this IServiceCollection services)
public static void RegisterIdentity(this IServiceCollection services)
{
services.AddTransient<IUserIdentityManager, UserIdentityManager>();
services.AddAuthorization(auth =>
{
@ -102,7 +87,6 @@ namespace Ombi.DependencyInjection
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
return services;
}
}
}

@ -5,5 +5,6 @@ namespace Ombi.Helpers
public class LoggingEvents
{
public static EventId ApiException => new EventId(1000);
public static EventId CacherException => new EventId(2000);
}
}

@ -2,6 +2,7 @@
using System.Globalization;
using AutoMapper;
using Ombi.Api.TvMaze.Models;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Helpers;
//using TraktApiSharp.Objects.Get.Shows;
@ -24,9 +25,26 @@ namespace Ombi.Mapping.Profiles
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.show.runtime.ToString()))
.ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.show.id))
.ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.show.name))
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.show.image.medium) ? src.show.image.medium.Replace("http","https") : string.Empty))
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.show.image.medium) ? src.show.image.medium.Replace("http", "https") : string.Empty))
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.show.status));
CreateMap<TvMazeCustomSeason, SeasonRequestModel>()
.ConstructUsing(x => new SeasonRequestModel { Episodes = x.EpisodeNumber, SeasonNumber = x.SeasonNumber });
CreateMap<TvMazeShow, SearchTvShowViewModel>()
.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.ToString()))
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime.ToString(CultureInfo.CurrentUICulture)))
.ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id))
.ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.name))
.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))
.ForMember(dest => dest.SeasonRequests, opts => opts.MapFrom(src => src.Season));
//CreateMap<TraktShow, SearchTvShowViewModel>()
// .ForMember(dest => dest.Id, opts => opts.MapFrom(src => Convert.ToInt32(src.Ids.Tvdb.ToString())))
// .ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.FirstAired.HasValue ? src.FirstAired.Value.ToString("yyyy-MM-ddTHH:mm:ss") : string.Empty))

@ -68,13 +68,14 @@ namespace Ombi.Schedule.Jobs
}
Logger.LogInformation("Starting Plex Content Cacher");
//TODO
StartTheCache(plexSettings).Wait();
try
{
//if (libraries == null || !libraries.Any())
//{
// return;
//}
StartTheCache(plexSettings).Wait();
}
catch (Exception e) {
Logger.LogWarning(LoggingEvents.CacherException, e, "Exception thrown when attempting to cache the Plex Content");
}
}
//private List<PlexLibraries> CachedLibraries(PlexSettings plexSettings)
//{
@ -122,9 +123,9 @@ namespace Ombi.Schedule.Jobs
// Let's now process this.
var contentToAdd = new List<PlexContent>();
foreach (var content in allContent)
{
var contentToAdd = new List<PlexContent>();
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
{
// Process Shows
@ -188,6 +189,11 @@ namespace Ombi.Schedule.Jobs
}
}
}
if (contentToAdd.Any())
{
await Repo.AddRange(contentToAdd);
}
}
private List<Mediacontainer> GetAllContent(PlexSettings plexSettings)

@ -7,6 +7,7 @@ namespace Ombi.Store.Repository
public interface IPlexContentRepository
{
Task<PlexContent> Add(PlexContent content);
Task AddRange(IEnumerable<PlexContent> content);
Task<bool> ContentExists(string providerId);
Task<IEnumerable<PlexContent>> GetAll();
Task<PlexContent> Get(string providerId);

@ -48,6 +48,12 @@ namespace Ombi.Store.Repository
return await Db.PlexContent.ToListAsync();
}
public async Task AddRange(IEnumerable<PlexContent> content)
{
await Db.PlexContent.AddRangeAsync(content);
await Db.SaveChangesAsync();
}
public async Task<bool> ContentExists(string providerId)
{
return await Db.PlexContent.AnyAsync(x => x.ProviderId == providerId);

@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging;
using Ombi.Core;
using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models.Search;
namespace Ombi.Controllers
@ -65,6 +66,12 @@ namespace Ombi.Controllers
return await TvEngine.Search(searchTerm);
}
[HttpGet("tv/seasons/{tvdbId}")]
public async Task<IEnumerable<int>> GetSeasons(int tvdbId)
{
return await TvEngine.GetSeasons(tvdbId);
}
//[HttpGet("tv/popular")]
//public async Task<IEnumerable<SearchTvShowViewModel>> PopularTv()
//{

@ -1,6 +1,5 @@
import { IEpisodeModel }from "./ISearchTvResult";
export interface IMediaBase {
export interface IMediaBase {
imdbId: string,
id: number,
providerId: number,
title: string,
@ -23,21 +22,27 @@ export interface IMediaBase {
}
export interface IMovieRequestModel extends IMediaBase {
imdbId: string,
}
export interface ITvRequestModel extends IMediaBase {
imdbId: string,
tvDbId: string,
requestAll: boolean,
seasonNumbersRequested: number[],
episodes: IEpisodeModel[],
seasonRequests: ISeasonRequests[],
childRequests: ITvRequestModel[],
hasChildRequests: boolean,
rootFolderSelected: number,
firstAired:string,
}
export interface ISeasonRequests
{
seasonNumber: number,
episodes:number[],
}
export enum RequestType {
movie = 1,
tvShow = 2

@ -1,4 +1,6 @@
export interface ISearchTvResult {
import { ISeasonRequests } from "./IRequestModel";
export interface ISearchTvResult {
id: number,
seriesName: string,
aliases: string[],
@ -19,17 +21,12 @@
siteRating: number,
trailer: string,
homepage:string,
episodesRequested: IEpisodeModel[],
seasonNumbersRequested: number[],
seasonsRequested: ISeasonRequests[],
requestAll:boolean,
approved: boolean,
requested: boolean,
available: boolean,
plexUrl: string,
firstSeason: boolean,
latestSeason:boolean,
}
export interface IEpisodeModel {
seasonNumber: number,
episodeNumber:number,
}

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/debounceTime';
@ -19,18 +20,21 @@ import { IMovieRequestModel } from '../interfaces/IRequestModel';
moduleId: module.id,
templateUrl: './movierequests.component.html'
})
export class MovieRequestsComponent implements OnInit {
export class MovieRequestsComponent implements OnInit, OnDestroy {
constructor(private requestService: RequestService, private identityService: IdentityService) {
this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.takeUntil(this.subscriptions)
.subscribe(x => {
this.searchText = x as string;
if (this.searchText === "") {
this.resetSearch();
return;
}
this.requestService.searchMovieRequests(this.searchText).subscribe(m => this.movieRequests = m);
this.requestService.searchMovieRequests(this.searchText)
.takeUntil(this.subscriptions)
.subscribe(m => this.movieRequests = m);
});
}
@ -39,10 +43,13 @@ export class MovieRequestsComponent implements OnInit {
searchChanged: Subject<string> = new Subject<string>();
searchText: string;
isAdmin : boolean;
isAdmin: boolean;
private currentlyLoaded: number;
private amountToLoad : number;
private amountToLoad: number;
private subscriptions = new Subject<void>();
ngOnInit() {
this.amountToLoad = 5;
@ -50,13 +57,15 @@ export class MovieRequestsComponent implements OnInit {
this.loadInit();
}
loadMore() {
this.requestService.getMovieRequests(this.amountToLoad, this.currentlyLoaded + 1).subscribe(x => {
this.movieRequests.push.apply(this.movieRequests, x);
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
});
this.requestService.getMovieRequests(this.amountToLoad, this.currentlyLoaded + 1)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.movieRequests.push.apply(this.movieRequests, x);
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
});
}
search(text: any) {
@ -70,7 +79,7 @@ export class MovieRequestsComponent implements OnInit {
changeAvailability(request: IMovieRequestModel, available: boolean) {
request.available = available;
this.updateRequest(request);
}
@ -87,11 +96,15 @@ export class MovieRequestsComponent implements OnInit {
}
private updateRequest(request: IMovieRequestModel) {
this.requestService.updateMovieRequest(request).subscribe(x => request = x);
this.requestService.updateMovieRequest(request)
.takeUntil(this.subscriptions)
.subscribe(x => request = x);
}
private loadInit() {
this.requestService.getMovieRequests(this.amountToLoad, 0).subscribe(x => this.movieRequests = x);
this.requestService.getMovieRequests(this.amountToLoad, 0)
.takeUntil(this.subscriptions)
.subscribe(x => this.movieRequests = x);
this.isAdmin = this.identityService.hasRole("Admin");
}
@ -106,4 +119,9 @@ export class MovieRequestsComponent implements OnInit {
this.movieRequests.splice(index, 1);
}
}
ngOnDestroy(): void {
this.subscriptions.next();
this.subscriptions.complete();
}
}

@ -84,6 +84,7 @@
<div *ngFor="let child of request.childRequests">
<hr/>
<div *ngIf="request.requestedUsers">Requested By: <span *ngFor="let user of request.requestedUsers">{{user}} </span></div>
<div>Seasons Requested: <span *ngFor="let s of request.seasonNumbersRequested">{{s}} </span> </div>
<div>
<span>Request status: </span>
<span *ngIf="request.available" class="label label-success">Available</span>

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import "rxjs/add/operator/takeUntil";
import 'rxjs/add/operator/debounceTime';
@ -19,30 +20,37 @@ import { ITvRequestModel } from '../interfaces/IRequestModel';
moduleId: module.id,
templateUrl: './tvrequests.component.html'
})
export class TvRequestsComponent implements OnInit {
export class TvRequestsComponent implements OnInit, OnDestroy {
constructor(private requestService: RequestService, private identityService: IdentityService) {
this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.takeUntil(this.subscriptions)
.subscribe(x => {
this.searchText = x as string;
if (this.searchText === "") {
this.resetSearch();
return;
}
this.requestService.searchTvRequests(this.searchText).subscribe(m => this.tvRequests = m);
this.requestService.searchTvRequests(this.searchText)
.takeUntil(this.subscriptions)
.subscribe(m => this.tvRequests = m);
});
}
private subscriptions = new Subject<void>();
tvRequests: ITvRequestModel[];
searchChanged = new Subject<string>();
searchText: string;
isAdmin : boolean;
isAdmin: boolean;
private currentlyLoaded: number;
private amountToLoad : number;
private amountToLoad: number;
ngOnInit() {
this.amountToLoad = 5;
@ -50,13 +58,15 @@ export class TvRequestsComponent implements OnInit {
this.loadInit();
}
loadMore() {
this.requestService.getTvRequests(this.amountToLoad, this.currentlyLoaded + 1).subscribe(x => {
this.tvRequests.push.apply(this.tvRequests, x);
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
});
this.requestService.getTvRequests(this.amountToLoad, this.currentlyLoaded + 1)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvRequests.push.apply(this.tvRequests, x);
this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad;
});
}
search(text: any) {
@ -70,7 +80,7 @@ export class TvRequestsComponent implements OnInit {
changeAvailability(request: ITvRequestModel, available: boolean) {
request.available = available;
this.updateRequest(request);
}
@ -87,13 +97,17 @@ export class TvRequestsComponent implements OnInit {
}
private updateRequest(request: ITvRequestModel) {
this.requestService.updateTvRequest(request).subscribe(x => request = x);
this.requestService.updateTvRequest(request)
.takeUntil(this.subscriptions)
.subscribe(x => request = x);
}
private loadInit() {
this.requestService.getTvRequests(this.amountToLoad, 0).subscribe(x => {
this.tvRequests = x;
});
this.requestService.getTvRequests(this.amountToLoad, 0)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvRequests = x;
});
this.isAdmin = this.identityService.hasRole("Admin");
}
@ -108,4 +122,9 @@ export class TvRequestsComponent implements OnInit {
this.tvRequests.splice(index, 1);
}
}
ngOnDestroy(): void {
this.subscriptions.next();
this.subscriptions.complete();
}
}

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import "rxjs/add/operator/takeUntil";
import { SearchService } from '../services/search.service';
import { RequestService } from '../services/request.service';
@ -16,31 +17,35 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
moduleId: module.id,
templateUrl: './moviesearch.component.html',
})
export class MovieSearchComponent implements OnInit {
export class MovieSearchComponent implements OnInit, OnDestroy {
searchText: string;
private subscriptions = new Subject<void>();
searchChanged: Subject<string> = new Subject<string>();
movieResults: ISearchMovieResult[];
result: IRequestEngineResult;
searchApplied = false;
searchApplied = false;
constructor(private searchService: SearchService, private requestService: RequestService, private notificationService: NotificationService) {
this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.takeUntil(this.subscriptions)
.subscribe(x => {
this.searchText = x as string;
if (this.searchText === "") {
this.clearResults();
return;
}
this.searchService.searchMovie(this.searchText).subscribe(x => {
this.movieResults = x;
this.searchApplied = true;
// Now let's load some exta info including IMDBId
// This way the search is fast at displaying results.
this.getExtaInfo();
});
this.searchService.searchMovie(this.searchText)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.movieResults = x;
this.searchApplied = true;
// Now let's load some exta info including IMDBId
// This way the search is fast at displaying results.
this.getExtaInfo();
});
});
}
@ -59,49 +64,61 @@ export class MovieSearchComponent implements OnInit {
request(searchResult: ISearchMovieResult) {
searchResult.requested = true;
this.requestService.requestMovie(searchResult).subscribe(x => {
this.result = x;
this.requestService.requestMovie(searchResult)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.result = x;
if (this.result.requestAdded) {
this.notificationService.success("Request Added",
`Request for ${searchResult.title} has been added successfully`);
} else {
this.notificationService.warning("Request Added", this.result.message);
}
});
if (this.result.requestAdded) {
this.notificationService.success("Request Added",
`Request for ${searchResult.title} has been added successfully`);
} else {
this.notificationService.warning("Request Added", this.result.message);
}
});
}
popularMovies() {
this.clearResults();
this.searchService.popularMovies().subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
this.searchService.popularMovies()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
}
nowPlayingMovies() {
this.clearResults();
this.searchService.nowPlayingMovies().subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
this.searchService.nowPlayingMovies()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
}
topRatedMovies() {
this.clearResults();
this.searchService.topRatedMovies().subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
this.searchService.topRatedMovies()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
}
upcomingMovies() {
this.clearResults();
this.searchService.upcomignMovies().subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
this.searchService.upcomignMovies()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.movieResults = x;
this.getExtaInfo();
});
}
private getExtaInfo() {
this.searchService.extraInfo(this.movieResults).subscribe(m => this.movieResults = m);
this.searchService.extraInfo(this.movieResults)
.takeUntil(this.subscriptions)
.subscribe(m => this.movieResults = m);
}
private clearResults() {
@ -109,4 +126,9 @@ export class MovieSearchComponent implements OnInit {
this.searchApplied = false;
}
ngOnDestroy(): void {
this.subscriptions.next();
this.subscriptions.complete();
}
}

@ -48,7 +48,7 @@
</a>
<span *ngIf="result.status" class="label label-primary" style="font-size: 60%" target="_blank">{{result.status}}</span>
<span *ngIf="result.status" class="label label-primary" target="_blank">{{result.status}}</span>
<span *ngIf="result.firstAired" class="label label-info" target="_blank">Air Date: {{result.firstAired}}</span>
@ -95,13 +95,13 @@
</div>
</div>
<div *ngIf="result.requested; then requestedBtn else notRequestedBtn"></div>
<!--<div *ngIf="result.requested; then requestedBtn else notRequestedBtn"></div>
<template #requestedBtn>
<button style="text-align: right" class="btn btn-primary-outline disabled" [disabled]><i class="fa fa-check"></i> Requested</button>
</template>
<template #notRequestedBtn>
<button id="{{result.id}}" style="text-align: right" class="btn btn-primary-outline" (click)="request(result)"><i class="fa fa-plus"></i> Request</button>
</template>
</template>-->
<!--{{#if_eq type "tv"}}
{{#if_eq tvFullyAvailable true}}
@ -111,32 +111,26 @@
{{#if_eq enableTvRequestsForOnlySeries true}}
<button id="{{id}}" style="text-align: right" class="btn {{#if available}}btn-success-outline{{else}}btn-primary-outline dropdownTv{{/if}} btn-primary-outline" season-select="0" type="button" {{#if available}} disabled{{/if}}><i class="fa fa-plus"></i> {{#if available}}@UI.Search_Available{{else}}@UI.Search_Request{{/if}}</button>
{{else}}
-->
<div class="dropdown">
<button id="{{id}}" class="btn {{#if available}}btn-success-outline{{else}}btn-primary-outline{{/if}} dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> {{#if available}}@UI.Search_Available{{else}}@UI.Search_Request {{/if}}
<button class="btn btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> Request
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><a id="{{id}}" season-select="0" class="dropdownTv " href="#">@UI.Search_AllSeasons</a></li>
{{#if_eq disableTvRequestsBySeason false}}
<li><a id="{{id}}" season-select="1" class="dropdownTv" href="#">@UI.Search_FirstSeason</a></li>
<li><a id="{{id}}" season-select="2" class="dropdownTv" href="#">@UI.Search_LatestSeason</a></li>
<li><a id="SeasonSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#seasonsModal" href="#">@UI.Search_SelectSeason...</a></li>
{{/if_eq}}
{{#if_eq disableTvRequestsByEpisode false}}
<li><a id="EpisodeSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#episodesModal" href="#">@UI.Search_SelectEpisode...</a></li>
{{/if_eq}}
<li><a (click)="allSeasons(result)">All Seasons</a></li>
<li><a (click)="firstSeason(result)">First Season</a></li>
<li><a (click)="latestSeason(result)">Latest Season</a></li>
<li><a>Select Season...</a></li>
<li><a>Select Episode...</a></li>
</ul>
</div>
{{/if_eq}}
{{#if available}}
{{#if url}}
<!--
<br/>
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
{{/if}}
{{/if}}
{{/if_eq}}
{{/if_eq}}-->
-->
<br/>

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import "rxjs/add/operator/takeUntil";
import { SearchService } from '../services/search.service';
import { RequestService } from '../services/request.service';
@ -16,8 +17,9 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
moduleId: module.id,
templateUrl: './tvsearch.component.html',
})
export class TvSearchComponent implements OnInit {
export class TvSearchComponent implements OnInit, OnDestroy {
private subscriptions = new Subject<void>();
searchText: string;
searchChanged = new Subject<string>();
tvResults: ISearchTvResult[];
@ -28,16 +30,19 @@ export class TvSearchComponent implements OnInit {
this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.takeUntil(this.subscriptions)
.subscribe(x => {
this.searchText = x as string;
if (this.searchText === "") {
this.clearResults();
return;
}
this.searchService.searchTv(this.searchText).subscribe(x => {
this.tvResults = x;
this.searchApplied = true;
});
this.searchService.searchTv(this.searchText)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvResults = x;
this.searchApplied = true;
});
});
}
@ -57,50 +62,81 @@ export class TvSearchComponent implements OnInit {
popularShows() {
this.clearResults();
this.searchService.popularTv().subscribe(x => {
this.tvResults = x;
});
this.searchService.popularTv()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvResults = x;
});
}
trendingShows() {
this.clearResults();
this.searchService.trendingTv().subscribe(x => {
this.tvResults = x;
});
this.searchService.trendingTv()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvResults = x;
});
}
mostWatchedShows() {
this.clearResults();
this.searchService.mostWatchedTv().subscribe(x => {
this.tvResults = x;
});
this.searchService.mostWatchedTv()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvResults = x;
});
}
anticipatedShows() {
this.clearResults();
this.searchService.anticiplatedTv().subscribe(x => {
this.tvResults = x;
});
this.searchService.anticiplatedTv()
.takeUntil(this.subscriptions)
.subscribe(x => {
this.tvResults = x;
});
}
request(searchResult: ISearchTvResult) {
searchResult.requested = true;
this.requestService.requestTv(searchResult).subscribe(x => {
this.result = x;
if (this.result.requestAdded) {
this.notificationService.success("Request Added",
`Request for ${searchResult.seriesName} has been added successfully`);
} else {
this.notificationService.warning("Request Added", this.result.message);
}
});
this.requestService.requestTv(searchResult)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.result = x;
if (this.result.requestAdded) {
this.notificationService.success("Request Added",
`Request for ${searchResult.seriesName} has been added successfully`);
} else {
this.notificationService.warning("Request Added", this.result.message);
}
});
}
allSeasons(searchResult: ISearchTvResult) {
searchResult.requestAll = true;
this.request(searchResult);
}
firstSeason(searchResult: ISearchTvResult) {
searchResult.firstSeason = true;
this.request(searchResult);
}
latestSeason(searchResult: ISearchTvResult) {
searchResult.latestSeason = true;
this.request(searchResult);
}
private clearResults() {
this.tvResults = [];
this.searchApplied = false;
}
ngOnDestroy(): void {
this.subscriptions.next();
this.subscriptions.complete();
}
}

@ -127,7 +127,7 @@
</div>
</div>
<div id="lightbox">
<div>
<div class="modal fade in " *ngIf="showCreateDialogue" style="display: block;">
<div class="modal-dialog">
<div class="modal-content">

Loading…
Cancel
Save