mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
Merge pull request #4104 from Ombi-app/feature/tvmaze-replacement
Feature/tvmaze replacement
This commit is contained in:
commit
b61c9003db
139 changed files with 7710 additions and 3070 deletions
10
.github/labeler.yml
vendored
10
.github/labeler.yml
vendored
|
@ -1,9 +1,5 @@
|
||||||
# Add 'test' label to any change to *.spec.js files within the source dir
|
automation: tests/**/*
|
||||||
automation:
|
|
||||||
- tests/**/*
|
|
||||||
|
|
||||||
frontend:
|
frontend: src/Ombi/ClientApp/**/*
|
||||||
- any: ['src/Ombi/ClientApp/**/*']
|
|
||||||
|
|
||||||
backend:
|
backend: src/**/*.cs
|
||||||
- any: ['src/**/*.cs']
|
|
||||||
|
|
4
.github/workflows/cypress.yml
vendored
4
.github/workflows/cypress.yml
vendored
|
@ -2,9 +2,9 @@ name: Automation Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ feature/ui-automation, develop ]
|
branches: [ develop, feature/** ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ feature/ui-automation, develop ]
|
branches: [ develop ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
|
BIN
Tools/nuget.exe
BIN
Tools/nuget.exe
Binary file not shown.
|
@ -86,12 +86,16 @@ namespace Ombi.Api
|
||||||
_handler = await GetHandler();
|
_handler = await GetHandler();
|
||||||
}
|
}
|
||||||
_client = new HttpClient(_handler);
|
_client = new HttpClient(_handler);
|
||||||
_client.DefaultRequestHeaders.Add("User-Agent",$"Ombi/{_runtimeVersion} (https://ombi.io/)");
|
_client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{_runtimeVersion} (https://ombi.io/)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<HttpMessageHandler> GetHandler()
|
private async Task<HttpMessageHandler> GetHandler()
|
||||||
{
|
{
|
||||||
|
if (_cache == null)
|
||||||
|
{
|
||||||
|
return new HttpClientHandler();
|
||||||
|
}
|
||||||
var settings = await _cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await _settings.GetSettingsAsync(), DateTime.Now.AddHours(1));
|
var settings = await _cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await _settings.GetSettingsAsync(), DateTime.Now.AddHours(1));
|
||||||
if (settings.IgnoreCertificateErrors)
|
if (settings.IgnoreCertificateErrors)
|
||||||
{
|
{
|
||||||
|
|
|
@ -69,8 +69,8 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
var allResults = await TvRepository.Get().ToListAsync();
|
var allResults = await TvRepository.Get().ToListAsync();
|
||||||
|
|
||||||
var distinctResults = allResults.DistinctBy(x => x.TvDbId);
|
var distinctResults = allResults.DistinctBy(x => x.ExternalProviderId);
|
||||||
_dbTv = distinctResults.ToDictionary(x => x.TvDbId);
|
_dbTv = distinctResults.ToDictionary(x => x.ExternalProviderId);
|
||||||
_cacheTime = now;
|
_cacheTime = now;
|
||||||
}
|
}
|
||||||
return _dbTv;
|
return _dbTv;
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace Ombi.Core.Engine.Demo
|
||||||
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
|
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
|
||||||
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists, IImageService imageService,
|
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists, IImageService imageService,
|
||||||
ISettingsService<CustomizationSettings> custom)
|
ISettingsService<CustomizationSettings> custom)
|
||||||
: base(identity, service, tvMaze, mapper, trakt, r, um, custom, memCache, s, sub, imageService)
|
: base(identity, service, tvMaze, mapper, trakt, r, um, custom, memCache, s, sub, imageService, null)
|
||||||
{
|
{
|
||||||
_demoLists = lists.Value;
|
_demoLists = lists.Value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,5 +27,6 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
|
|
||||||
Task<MovieFullInfoViewModel> GetMovieInfoByImdbId(string imdbId, CancellationToken requestAborted);
|
Task<MovieFullInfoViewModel> GetMovieInfoByImdbId(string imdbId, CancellationToken requestAborted);
|
||||||
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
||||||
|
Task<IEnumerable<SearchMovieViewModel>> RecentlyRequestedMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task RemoveTvRequest(int requestId);
|
Task RemoveTvRequest(int requestId);
|
||||||
Task<TvRequests> GetTvRequest(int requestId);
|
Task<TvRequests> GetTvRequest(int requestId);
|
||||||
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
|
Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
|
||||||
|
Task<RequestEngineResult> RequestTvShow(TvRequestViewModelV2 tv);
|
||||||
Task<RequestEngineResult> DenyChildRequest(int requestId, string reason);
|
Task<RequestEngineResult> DenyChildRequest(int requestId, string reason);
|
||||||
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
Task<RequestsViewModel<TvRequests>> GetRequestsLite(int count, int position, OrderFilterModel type);
|
||||||
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine.Interfaces
|
namespace Ombi.Core.Engine.Interfaces
|
||||||
|
@ -7,7 +8,7 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
public interface ITvSearchEngine
|
public interface ITvSearchEngine
|
||||||
{
|
{
|
||||||
Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm);
|
Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm);
|
||||||
Task<SearchTvShowViewModel> GetShowInformation(int tvdbid);
|
Task<SearchTvShowViewModel> GetShowInformation(string movieDbId, CancellationToken token);
|
||||||
Task<IEnumerable<SearchTvShowViewModel>> Popular();
|
Task<IEnumerable<SearchTvShowViewModel>> Popular();
|
||||||
Task<IEnumerable<SearchTvShowViewModel>> Popular(int currentlyLoaded, int amountToLoad, bool includeImages = false);
|
Task<IEnumerable<SearchTvShowViewModel>> Popular(int currentlyLoaded, int amountToLoad, bool includeImages = false);
|
||||||
Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
|
Task<IEnumerable<SearchTvShowViewModel>> Anticipated();
|
||||||
|
|
|
@ -7,8 +7,8 @@ namespace Ombi.Core
|
||||||
{
|
{
|
||||||
public interface ITVSearchEngineV2
|
public interface ITVSearchEngineV2
|
||||||
{
|
{
|
||||||
Task<SearchFullInfoTvShowViewModel> GetShowInformation(int tvdbid);
|
Task<SearchFullInfoTvShowViewModel> GetShowInformation(string tvdbid, CancellationToken token);
|
||||||
Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId);
|
Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId, CancellationToken token);
|
||||||
Task<IEnumerable<StreamingData>> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken);
|
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -189,7 +189,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||||
IEnumerable<MovieSearchResult> movies)
|
IEnumerable<MovieDbSearchResult> movies)
|
||||||
{
|
{
|
||||||
var viewMovies = new List<SearchMovieViewModel>();
|
var viewMovies = new List<SearchMovieViewModel>();
|
||||||
foreach (var movie in movies)
|
foreach (var movie in movies)
|
||||||
|
@ -244,7 +244,7 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie)
|
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieDbSearchResult movie)
|
||||||
{
|
{
|
||||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movie);
|
var viewMovie = Mapper.Map<SearchMovieViewModel>(movie);
|
||||||
return await ProcessSingleMovie(viewMovie);
|
return await ProcessSingleMovie(viewMovie);
|
||||||
|
|
|
@ -148,6 +148,106 @@ namespace Ombi.Core.Engine
|
||||||
return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf);
|
return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModelV2 tv)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
var canRequestOnBehalf = false;
|
||||||
|
|
||||||
|
if (tv.RequestOnBehalf.HasValue())
|
||||||
|
{
|
||||||
|
canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
|
||||||
|
|
||||||
|
if (!canRequestOnBehalf)
|
||||||
|
{
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = false,
|
||||||
|
Message = "You do not have the correct permissions to request on behalf of users!",
|
||||||
|
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tvBuilder = new TvShowRequestBuilderV2(MovieDbApi);
|
||||||
|
(await tvBuilder
|
||||||
|
.GetShowInfo(tv.TheMovieDbId))
|
||||||
|
.CreateTvList(tv)
|
||||||
|
.CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id);
|
||||||
|
|
||||||
|
await tvBuilder.BuildEpisodes(tv);
|
||||||
|
|
||||||
|
var ruleResults = await RunRequestRules(tvBuilder.ChildRequest);
|
||||||
|
var results = ruleResults as RuleResult[] ?? ruleResults.ToArray();
|
||||||
|
if (results.Any(x => !x.Success))
|
||||||
|
{
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
ErrorMessage = results.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have auto approved the request, if we have then mark the episodes as approved
|
||||||
|
if (tvBuilder.ChildRequest.Approved)
|
||||||
|
{
|
||||||
|
foreach (var seasons in tvBuilder.ChildRequest.SeasonRequests)
|
||||||
|
{
|
||||||
|
foreach (var ep in seasons.Episodes)
|
||||||
|
{
|
||||||
|
ep.Approved = true;
|
||||||
|
ep.Requested = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.ExternalProviderId == tv.TheMovieDbId);
|
||||||
|
if (existingRequest != null)
|
||||||
|
{
|
||||||
|
// Remove requests we already have, we just want new ones
|
||||||
|
foreach (var existingSeason in existingRequest.ChildRequests)
|
||||||
|
foreach (var existing in existingSeason.SeasonRequests)
|
||||||
|
{
|
||||||
|
var newChild = tvBuilder.ChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == existing.SeasonNumber);
|
||||||
|
if (newChild != null)
|
||||||
|
{
|
||||||
|
// We have some requests in this season...
|
||||||
|
// Let's find the episodes.
|
||||||
|
foreach (var existingEp in existing.Episodes)
|
||||||
|
{
|
||||||
|
var duplicateEpisode = newChild.Episodes.FirstOrDefault(x => x.EpisodeNumber == existingEp.EpisodeNumber);
|
||||||
|
if (duplicateEpisode != null)
|
||||||
|
{
|
||||||
|
// Remove it.
|
||||||
|
newChild.Episodes.Remove(duplicateEpisode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!newChild.Episodes.Any())
|
||||||
|
{
|
||||||
|
// We may have removed all episodes
|
||||||
|
tvBuilder.ChildRequest.SeasonRequests.Remove(newChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the ID since this is a new child
|
||||||
|
// This was a TVDBID for the request rules to run
|
||||||
|
tvBuilder.ChildRequest.Id = 0;
|
||||||
|
if (!tvBuilder.ChildRequest.SeasonRequests.Any())
|
||||||
|
{
|
||||||
|
// Looks like we have removed them all! They were all duplicates...
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = false,
|
||||||
|
ErrorMessage = "This has already been requested"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a new request
|
||||||
|
var newRequest = tvBuilder.CreateNewRequest(tv);
|
||||||
|
return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, OrderFilterModel type)
|
public async Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, OrderFilterModel type)
|
||||||
{
|
{
|
||||||
var shouldHide = await HideFromOtherUsers();
|
var shouldHide = await HideFromOtherUsers();
|
||||||
|
|
|
@ -6,7 +6,6 @@ using Ombi.Core.Engine.Interfaces;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models.External;
|
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
@ -16,12 +15,13 @@ using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
using Ombi.Store.Repository.Requests;
|
using Ombi.Store.Repository.Requests;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using Ombi.Core.Authentication;
|
using Ombi.Core.Authentication;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using TraktSharp.Entities;
|
using Ombi.Api.TheMovieDb;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -29,13 +29,16 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
private readonly ISettingsService<CustomizationSettings> _customizationSettings;
|
private readonly ISettingsService<CustomizationSettings> _customizationSettings;
|
||||||
private readonly IImageService _imageService;
|
private readonly IImageService _imageService;
|
||||||
|
private readonly IMovieDbApi _theMovieDbApi;
|
||||||
|
|
||||||
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
||||||
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ISettingsService<CustomizationSettings> customizationSettings,
|
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ISettingsService<CustomizationSettings> customizationSettings,
|
||||||
ICacheService memCache, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IImageService imageService)
|
ICacheService memCache, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IImageService imageService,
|
||||||
|
IMovieDbApi theMovieDbApi)
|
||||||
: base(identity, service, r, um, memCache, s, sub)
|
: base(identity, service, r, um, memCache, s, sub)
|
||||||
{
|
{
|
||||||
_imageService = imageService;
|
_imageService = imageService;
|
||||||
|
_theMovieDbApi = theMovieDbApi;
|
||||||
TvMazeApi = tvMaze;
|
TvMazeApi = tvMaze;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
TraktApi = trakt;
|
TraktApi = trakt;
|
||||||
|
@ -48,18 +51,18 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm)
|
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string searchTerm)
|
||||||
{
|
{
|
||||||
var searchResult = await TvMazeApi.Search(searchTerm);
|
var searchResult = await _theMovieDbApi.SearchTv(searchTerm);
|
||||||
|
|
||||||
if (searchResult != null)
|
if (searchResult != null)
|
||||||
{
|
{
|
||||||
var retVal = new List<SearchTvShowViewModel>();
|
var retVal = new List<SearchTvShowViewModel>();
|
||||||
foreach (var tvMazeSearch in searchResult)
|
foreach (var result in searchResult)
|
||||||
{
|
{
|
||||||
if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
|
//if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
|
||||||
{
|
//{
|
||||||
continue;
|
// continue;
|
||||||
}
|
//}
|
||||||
var mappedResult = await ProcessResult(tvMazeSearch, false);
|
var mappedResult = await ProcessResult(result, false);
|
||||||
if (mappedResult == null)
|
if (mappedResult == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -71,58 +74,64 @@ namespace Ombi.Core.Engine
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbid)
|
public async Task<SearchTvShowViewModel> GetShowInformation(string theMovieDbId, CancellationToken token)
|
||||||
{
|
{
|
||||||
var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid,
|
var show = await Cache.GetOrAdd(nameof(GetShowInformation) + theMovieDbId,
|
||||||
async () => await TvMazeApi.ShowLookupByTheTvDbId(tvdbid), DateTime.Now.AddHours(12));
|
async () => await _theMovieDbApi.GetTVInfo(theMovieDbId), DateTime.Now.AddHours(12));
|
||||||
if (show == null)
|
if (show == null)
|
||||||
{
|
{
|
||||||
// We don't have enough information
|
// We don't have enough information
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var episodes = await Cache.GetOrAdd("TvMazeEpisodeLookup" + show.id,
|
//var episodes = await Cache.GetOrAdd("TvMazeEpisodeLookup" + show.id,
|
||||||
async () => await TvMazeApi.EpisodeLookup(show.id), DateTime.Now.AddHours(12));
|
// async () => await TvMazeApi.EpisodeLookup(show.id), DateTime.Now.AddHours(12));
|
||||||
if (episodes == null || !episodes.Any())
|
//if (episodes == null || !episodes.Any())
|
||||||
{
|
//{
|
||||||
// We don't have enough information
|
// // We don't have enough information
|
||||||
return null;
|
// return null;
|
||||||
}
|
//}
|
||||||
|
|
||||||
var mapped = Mapper.Map<SearchTvShowViewModel>(show);
|
var mapped = Mapper.Map<SearchTvShowViewModel>(show);
|
||||||
|
|
||||||
foreach (var e in episodes)
|
foreach(var tvSeason in show.seasons)
|
||||||
{
|
{
|
||||||
var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
|
var seasonEpisodes = (await _theMovieDbApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token));
|
||||||
if (season == null)
|
|
||||||
{
|
|
||||||
var newSeason = new SeasonRequests
|
|
||||||
{
|
|
||||||
SeasonNumber = e.season,
|
|
||||||
Episodes = new List<EpisodeRequests>()
|
|
||||||
};
|
|
||||||
newSeason.Episodes.Add(new EpisodeRequests
|
|
||||||
{
|
|
||||||
Url = e.url.ToHttpsUrl(),
|
|
||||||
Title = e.name,
|
|
||||||
AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
|
|
||||||
EpisodeNumber = e.number,
|
|
||||||
|
|
||||||
});
|
foreach (var episode in seasonEpisodes.episodes)
|
||||||
mapped.SeasonRequests.Add(newSeason);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// We already have the season, so just add the episode
|
var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == episode.season_number);
|
||||||
season.Episodes.Add(new EpisodeRequests
|
if (season == null)
|
||||||
{
|
{
|
||||||
Url = e.url.ToHttpsUrl(),
|
var newSeason = new SeasonRequests
|
||||||
Title = e.name,
|
{
|
||||||
AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue,
|
SeasonNumber = episode.season_number,
|
||||||
EpisodeNumber = e.number,
|
Episodes = new List<EpisodeRequests>()
|
||||||
});
|
};
|
||||||
|
newSeason.Episodes.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
//Url = episode...ToHttpsUrl(),
|
||||||
|
Title = episode.name,
|
||||||
|
AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue,
|
||||||
|
EpisodeNumber = episode.episode_number,
|
||||||
|
|
||||||
|
});
|
||||||
|
mapped.SeasonRequests.Add(newSeason);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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,
|
||||||
|
EpisodeNumber = episode.episode_number,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await ProcessResult(mapped, false);
|
return await ProcessResult(mapped, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,12 +144,14 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
public async Task<IEnumerable<SearchTvShowViewModel>> Popular(int currentlyLoaded, int amountToLoad, bool includeImages = false)
|
public async Task<IEnumerable<SearchTvShowViewModel>> Popular(int currentlyLoaded, int amountToLoad, bool includeImages = false)
|
||||||
{
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
|
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
|
||||||
var results = new List<TraktShow>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(Popular) + pagesToLoad.Page,
|
var apiResult = await Cache.GetOrAdd(nameof(Popular) + langCode + pagesToLoad.Page,
|
||||||
async () => await TraktApi.GetPopularShows(pagesToLoad.Page, ResultLimit), DateTime.Now.AddHours(12));
|
async () => await _theMovieDbApi.PopularTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12));
|
||||||
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,12 +169,14 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated(int currentlyLoaded, int amountToLoad)
|
public async Task<IEnumerable<SearchTvShowViewModel>> Anticipated(int currentlyLoaded, int amountToLoad)
|
||||||
{
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
|
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
|
||||||
var results = new List<TraktShow>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(Anticipated) + pagesToLoad.Page,
|
var apiResult = await Cache.GetOrAdd(nameof(Anticipated) + langCode + pagesToLoad.Page,
|
||||||
async () => await TraktApi.GetAnticipatedShows(pagesToLoad.Page, ResultLimit), DateTime.Now.AddHours(12));
|
async () => await _theMovieDbApi.UpcomingTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12));
|
||||||
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
||||||
}
|
}
|
||||||
var processed = ProcessResults(results);
|
var processed = ProcessResults(results);
|
||||||
|
@ -180,12 +193,14 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
public async Task<IEnumerable<SearchTvShowViewModel>> Trending(int currentlyLoaded, int amountToLoad)
|
public async Task<IEnumerable<SearchTvShowViewModel>> Trending(int currentlyLoaded, int amountToLoad)
|
||||||
{
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
|
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
|
||||||
var results = new List<TraktShow>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(Trending) + pagesToLoad.Page,
|
var apiResult = await Cache.GetOrAdd(nameof(Trending) + langCode + pagesToLoad.Page,
|
||||||
async () => await TraktApi.GetTrendingShows(pagesToLoad.Page, ResultLimit), DateTime.Now.AddHours(12));
|
async () => await _theMovieDbApi.TopRatedTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12));
|
||||||
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
||||||
}
|
}
|
||||||
var processed = ProcessResults(results);
|
var processed = ProcessResults(results);
|
||||||
|
@ -220,14 +235,15 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.TheTvDbId = item.Id.ToString();
|
item.TheMovieDbId = item.Id.ToString();
|
||||||
if (includeImages)
|
//item.TheTvDbId = item.Id.ToString();
|
||||||
{
|
//if (includeImages)
|
||||||
if (item.TheTvDbId.HasValue())
|
//{
|
||||||
{
|
// if (item.TheTvDbId.HasValue())
|
||||||
item.BackdropPath = await _imageService.GetTvBackground(item.TheTvDbId);
|
// {
|
||||||
}
|
// item.BackdropPath = await _imageService.GetTvBackground(item.TheTvDbId);
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
await RunSearchRules(item);
|
await RunSearchRules(item);
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
new ExtraParams
|
new ExtraParams
|
||||||
{
|
{
|
||||||
Overview = e.Season?.ChildRequest?.ParentRequest?.Overview ?? string.Empty,
|
Overview = e.Season?.ChildRequest?.ParentRequest?.Overview ?? string.Empty,
|
||||||
ProviderId = e.Season?.ChildRequest?.ParentRequest?.TvDbId ?? 0,
|
ProviderId = e.Season?.ChildRequest?.ParentRequest?.ExternalProviderId ?? 0,
|
||||||
Type = RequestType.TvShow,
|
Type = RequestType.TvShow,
|
||||||
ReleaseDate = e.AirDate,
|
ReleaseDate = e.AirDate,
|
||||||
RequestStatus = e.RequestStatus
|
RequestStatus = e.RequestStatus
|
||||||
|
|
|
@ -8,11 +8,13 @@ using Ombi.Core.Engine.Interfaces;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Core.Models.Search.V2;
|
using Ombi.Core.Models.Search.V2;
|
||||||
|
using Ombi.Core.Models.UI;
|
||||||
using Ombi.Core.Rule.Interfaces;
|
using Ombi.Core.Rule.Interfaces;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Entities.Requests;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -27,20 +29,21 @@ namespace Ombi.Core.Engine.V2
|
||||||
{
|
{
|
||||||
public MovieSearchEngineV2(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
public MovieSearchEngineV2(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper,
|
||||||
ILogger<MovieSearchEngineV2> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
|
ILogger<MovieSearchEngineV2> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
|
||||||
ISettingsService<CustomizationSettings> customizationSettings)
|
ISettingsService<CustomizationSettings> customizationSettings, IMovieRequestEngine movieRequestEngine)
|
||||||
: base(identity, service, r, um, mem, s, sub)
|
: base(identity, service, r, um, mem, s, sub)
|
||||||
{
|
{
|
||||||
MovieApi = movApi;
|
MovieApi = movApi;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
_customizationSettings = customizationSettings;
|
_customizationSettings = customizationSettings;
|
||||||
|
_movieRequestEngine = movieRequestEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMovieDbApi MovieApi { get; }
|
private IMovieDbApi MovieApi { get; }
|
||||||
private IMapper Mapper { get; }
|
private IMapper Mapper { get; }
|
||||||
private ILogger Logger { get; }
|
private ILogger Logger { get; }
|
||||||
private readonly ISettingsService<CustomizationSettings> _customizationSettings;
|
private readonly ISettingsService<CustomizationSettings> _customizationSettings;
|
||||||
|
private readonly IMovieRequestEngine _movieRequestEngine;
|
||||||
|
|
||||||
public async Task<MovieFullInfoViewModel> GetFullMovieInformation(int theMovieDbId, CancellationToken cancellationToken, string langCode = null)
|
public async Task<MovieFullInfoViewModel> GetFullMovieInformation(int theMovieDbId, CancellationToken cancellationToken, string langCode = null)
|
||||||
{
|
{
|
||||||
|
@ -127,7 +130,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems);
|
var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems);
|
||||||
|
|
||||||
var results = new List<MovieSearchResult>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(PopularMovies) + pagesToLoad.Page + langCode,
|
var apiResult = await Cache.GetOrAdd(nameof(PopularMovies) + pagesToLoad.Page + langCode,
|
||||||
|
@ -161,7 +164,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
|
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
|
||||||
|
|
||||||
var results = new List<MovieSearchResult>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(TopRatedMovies) + pagesToLoad.Page + langCode,
|
var apiResult = await Cache.GetOrAdd(nameof(TopRatedMovies) + pagesToLoad.Page + langCode,
|
||||||
|
@ -177,7 +180,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
|
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
|
||||||
|
|
||||||
var results = new List<MovieSearchResult>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode,
|
var apiResult = await Cache.GetOrAdd(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode,
|
||||||
|
@ -187,6 +190,41 @@ namespace Ombi.Core.Engine.V2
|
||||||
return await TransformMovieResultsToResponse(results);
|
return await TransformMovieResultsToResponse(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets recently requested movies
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<IEnumerable<SearchMovieViewModel>> RecentlyRequestedMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
|
||||||
|
var results = new List<MovieResponseDto>();
|
||||||
|
|
||||||
|
var requestResult = await Cache.GetOrAdd(nameof(RecentlyRequestedMovies) + "Requests" + toLoad + langCode,
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
return await _movieRequestEngine.GetRequests(toLoad, currentlyLoaded, new Models.UI.OrderFilterModel
|
||||||
|
{
|
||||||
|
OrderType = OrderType.RequestedDateDesc
|
||||||
|
});
|
||||||
|
}, DateTime.Now.AddMinutes(15), cancellationToken);
|
||||||
|
|
||||||
|
var movieDBResults = await Cache.GetOrAdd(nameof(RecentlyRequestedMovies) + toLoad + langCode,
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
var responses = new List<MovieResponseDto>();
|
||||||
|
foreach(var movie in requestResult.Collection)
|
||||||
|
{
|
||||||
|
responses.Add(await MovieApi.GetMovieInformation(movie.TheMovieDbId));
|
||||||
|
}
|
||||||
|
return responses;
|
||||||
|
}, DateTime.Now.AddHours(12), cancellationToken);
|
||||||
|
|
||||||
|
results.AddRange(movieDBResults);
|
||||||
|
|
||||||
|
return await TransformMovieResultsToResponse(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets upcoming movies.
|
/// Gets upcoming movies.
|
||||||
|
@ -213,7 +251,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
|
|
||||||
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
|
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
|
||||||
|
|
||||||
var results = new List<MovieSearchResult>();
|
var results = new List<MovieDbSearchResult>();
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAdd(nameof(UpcomingMovies) + pagesToLoad.Page + langCode,
|
var apiResult = await Cache.GetOrAdd(nameof(UpcomingMovies) + pagesToLoad.Page + langCode,
|
||||||
|
@ -269,8 +307,8 @@ namespace Ombi.Core.Engine.V2
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse<T>(
|
||||||
IEnumerable<MovieSearchResult> movies)
|
IEnumerable<T> movies) where T: new()
|
||||||
{
|
{
|
||||||
var settings = await _customizationSettings.GetSettingsAsync();
|
var settings = await _customizationSettings.GetSettingsAsync();
|
||||||
var viewMovies = new List<SearchMovieViewModel>();
|
var viewMovies = new List<SearchMovieViewModel>();
|
||||||
|
@ -286,7 +324,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
return viewMovies;
|
return viewMovies;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie)
|
private async Task<SearchMovieViewModel> ProcessSingleMovie<T>(T movie) where T : new()
|
||||||
{
|
{
|
||||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movie);
|
var viewMovie = Mapper.Map<SearchMovieViewModel>(movie);
|
||||||
return await ProcessSingleMovie(viewMovie);
|
return await ProcessSingleMovie(viewMovie);
|
||||||
|
|
|
@ -63,7 +63,8 @@ namespace Ombi.Core.Engine.V2
|
||||||
var result = new MultiSearchResult
|
var result = new MultiSearchResult
|
||||||
{
|
{
|
||||||
MediaType = multiSearch.media_type,
|
MediaType = multiSearch.media_type,
|
||||||
Poster = multiSearch.poster_path
|
Poster = multiSearch.poster_path,
|
||||||
|
Overview = multiSearch.overview
|
||||||
};
|
};
|
||||||
|
|
||||||
if (multiSearch.media_type.Equals("movie", StringComparison.InvariantCultureIgnoreCase) && filter.Movies)
|
if (multiSearch.media_type.Equals("movie", StringComparison.InvariantCultureIgnoreCase) && filter.Movies)
|
||||||
|
|
|
@ -43,89 +43,70 @@ namespace Ombi.Core.Engine.V2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId)
|
public async Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId, CancellationToken token)
|
||||||
{
|
{
|
||||||
var request = await RequestService.TvRequestService.Get().FirstOrDefaultAsync(x => x.Id == requestId);
|
var request = await RequestService.TvRequestService.Get().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||||
return await GetShowInformation(request.TvDbId);
|
return await GetShowInformation(request.ExternalProviderId.ToString(), token); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchFullInfoTvShowViewModel> GetShowInformation(int tvdbid)
|
public async Task<SearchFullInfoTvShowViewModel> GetShowInformation(string tvdbid, CancellationToken token)
|
||||||
{
|
{
|
||||||
var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid,
|
var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid,
|
||||||
async () => await _tvMaze.ShowLookupByTheTvDbId(tvdbid), DateTime.Now.AddHours(12));
|
async () => await _movieApi.GetTVInfo(tvdbid), DateTime.Now.AddHours(12));
|
||||||
if (tvdbshow == null)
|
if (show == null || show.name == null)
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var show = await Cache.GetOrAdd("GetTvFullInformation" + tvdbshow.id,
|
|
||||||
async () => await _tvMaze.GetTvFullInformation(tvdbshow.id), DateTime.Now.AddHours(12));
|
|
||||||
if (show == null)
|
|
||||||
{
|
{
|
||||||
// We don't have enough information
|
// We don't have enough information
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the task so we can get the data later on if we have a IMDBID
|
|
||||||
Task<TraktShow> traktInfoTask = null;
|
|
||||||
if (show.externals?.imdb.HasValue() ?? false)
|
|
||||||
{
|
|
||||||
traktInfoTask = Cache.GetOrAdd("GetExtendedTvInfoTrakt" + show.externals?.imdb,
|
|
||||||
() => _traktApi.GetTvExtendedInfo(show.externals?.imdb), DateTime.Now.AddHours(12));
|
|
||||||
}
|
|
||||||
|
|
||||||
var mapped = _mapper.Map<SearchFullInfoTvShowViewModel>(show);
|
var mapped = _mapper.Map<SearchFullInfoTvShowViewModel>(show);
|
||||||
|
|
||||||
foreach (var e in show._embedded?.episodes ?? new Api.TvMaze.Models.V2.Episode[0])
|
|
||||||
{
|
|
||||||
var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
|
|
||||||
if (season == null)
|
|
||||||
{
|
|
||||||
var newSeason = new SeasonRequests
|
|
||||||
{
|
|
||||||
SeasonNumber = e.season,
|
|
||||||
Episodes = new List<EpisodeRequests>()
|
|
||||||
};
|
|
||||||
newSeason.Episodes.Add(new EpisodeRequests
|
|
||||||
{
|
|
||||||
Url = e.url.ToHttpsUrl(),
|
|
||||||
Title = e.name,
|
|
||||||
AirDate = e.airstamp,
|
|
||||||
EpisodeNumber = e.number,
|
|
||||||
|
|
||||||
});
|
foreach (var tvSeason in show.seasons.Where(x => x.season_number != 0)) // skip the first season
|
||||||
mapped.SeasonRequests.Add(newSeason);
|
{
|
||||||
}
|
var seasonEpisodes = (await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token));
|
||||||
else
|
|
||||||
|
foreach (var episode in seasonEpisodes.episodes)
|
||||||
{
|
{
|
||||||
// We already have the season, so just add the episode
|
var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == episode.season_number);
|
||||||
season.Episodes.Add(new EpisodeRequests
|
if (season == null)
|
||||||
{
|
{
|
||||||
Url = e.url.ToHttpsUrl(),
|
var newSeason = new SeasonRequests
|
||||||
Title = e.name,
|
{
|
||||||
AirDate = e.airstamp,
|
SeasonNumber = episode.season_number,
|
||||||
EpisodeNumber = e.number,
|
Overview = tvSeason.overview,
|
||||||
});
|
Episodes = new List<EpisodeRequests>()
|
||||||
|
};
|
||||||
|
newSeason.Episodes.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
//Url = episode...ToHttpsUrl(),
|
||||||
|
Title = episode.name,
|
||||||
|
AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue,
|
||||||
|
EpisodeNumber = episode.episode_number,
|
||||||
|
|
||||||
|
});
|
||||||
|
mapped.SeasonRequests.Add(newSeason);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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,
|
||||||
|
EpisodeNumber = episode.episode_number,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await ProcessResult(mapped, traktInfoTask);
|
|
||||||
|
return await ProcessResult(mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken)
|
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeId,
|
var providers = await _movieApi.GetTvWatchProviders(movieDbId, cancellationToken);
|
||||||
async () => await _tvMaze.ShowLookupByTheTvDbId(tvMazeId), DateTime.Now.AddHours(12));
|
|
||||||
if (tvdbshow == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// this is a best effort guess since TV maze do not provide the TheMovieDbId
|
|
||||||
var movieDbResults = await _movieApi.SearchTv(tvdbshow.name, tvdbshow.premiered.Substring(0, 4));
|
|
||||||
var potential = movieDbResults.FirstOrDefault();
|
|
||||||
tvDbId = potential.Id;
|
|
||||||
// end guess
|
|
||||||
|
|
||||||
var providers = await _movieApi.GetTvWatchProviders(tvDbId, cancellationToken);
|
|
||||||
var results = await GetUserWatchProvider(providers);
|
var results = await GetUserWatchProvider(providers);
|
||||||
|
|
||||||
var data = new List<StreamingData>();
|
var data = new List<StreamingData>();
|
||||||
|
@ -158,9 +139,9 @@ namespace Ombi.Core.Engine.V2
|
||||||
return _mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
return _mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SearchFullInfoTvShowViewModel> ProcessResult(SearchFullInfoTvShowViewModel item, Task<TraktShow> showInfoTask)
|
private async Task<SearchFullInfoTvShowViewModel> ProcessResult(SearchFullInfoTvShowViewModel item)
|
||||||
{
|
{
|
||||||
item.TheTvDbId = item.Id.ToString();
|
item.TheMovieDbId = item.Id.ToString();
|
||||||
|
|
||||||
var oldModel = _mapper.Map<SearchTvShowViewModel>(item);
|
var oldModel = _mapper.Map<SearchTvShowViewModel>(item);
|
||||||
await RunSearchRules(oldModel);
|
await RunSearchRules(oldModel);
|
||||||
|
@ -179,18 +160,9 @@ namespace Ombi.Core.Engine.V2
|
||||||
item.Images.Medium = item.Images.Medium.ToHttpsUrl();
|
item.Images.Medium = item.Images.Medium.ToHttpsUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Cast?.Any() ?? false)
|
|
||||||
{
|
return item;
|
||||||
foreach (var cast in item.Cast)
|
//return await GetExtraInfo(showInfoTask, item);
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(cast.Character?.Image?.Medium))
|
|
||||||
{
|
|
||||||
cast.Character.Image.Medium = cast.Character?.Image?.Medium.ToHttpsUrl();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await GetExtraInfo(showInfoTask, item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SearchFullInfoTvShowViewModel> GetExtraInfo(Task<TraktShow> showInfoTask, SearchFullInfoTvShowViewModel model)
|
private async Task<SearchFullInfoTvShowViewModel> GetExtraInfo(Task<TraktShow> showInfoTask, SearchFullInfoTvShowViewModel model)
|
||||||
|
|
|
@ -35,14 +35,17 @@ namespace Ombi.Core.Helpers
|
||||||
public TvRequests NewRequest { get; protected set; }
|
public TvRequests NewRequest { get; protected set; }
|
||||||
protected TvMazeShow ShowInfo { get; set; }
|
protected TvMazeShow ShowInfo { get; set; }
|
||||||
protected List<TvSearchResult> Results { get; set; }
|
protected List<TvSearchResult> Results { get; set; }
|
||||||
|
protected TvSearchResult TheMovieDbRecord { get; set; }
|
||||||
|
|
||||||
public async Task<TvShowRequestBuilder> GetShowInfo(int id)
|
public async Task<TvShowRequestBuilder> GetShowInfo(int id)
|
||||||
{
|
{
|
||||||
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
|
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
|
||||||
Results = await MovieDbApi.SearchTv(ShowInfo.name);
|
Results = await MovieDbApi.SearchTv(ShowInfo.name);
|
||||||
foreach (TvSearchResult result in Results) {
|
foreach (TvSearchResult result in Results)
|
||||||
|
{
|
||||||
if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase))
|
if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
|
TheMovieDbRecord = result;
|
||||||
var showIds = await MovieDbApi.GetTvExternals(result.Id);
|
var showIds = await MovieDbApi.GetTvExternals(result.Id);
|
||||||
ShowInfo.externals.imdb = showIds.imdb_id;
|
ShowInfo.externals.imdb = showIds.imdb_id;
|
||||||
BackdropPath = result.BackdropPath;
|
BackdropPath = result.BackdropPath;
|
||||||
|
@ -59,7 +62,7 @@ namespace Ombi.Core.Helpers
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TvShowRequestBuilder CreateChild(TvRequestViewModel model, string userId)
|
public TvShowRequestBuilder CreateChild(TvRequestViewModel model, string userId)
|
||||||
{
|
{
|
||||||
ChildRequest = new ChildRequests
|
ChildRequest = new ChildRequests
|
||||||
|
@ -240,6 +243,7 @@ namespace Ombi.Core.Helpers
|
||||||
PosterPath = PosterPath,
|
PosterPath = PosterPath,
|
||||||
Title = ShowInfo.name,
|
Title = ShowInfo.name,
|
||||||
ReleaseDate = FirstAir,
|
ReleaseDate = FirstAir,
|
||||||
|
ExternalProviderId = TheMovieDbRecord.Id,
|
||||||
Status = ShowInfo.status,
|
Status = ShowInfo.status,
|
||||||
ImdbId = ShowInfo.externals?.imdb ?? string.Empty,
|
ImdbId = ShowInfo.externals?.imdb ?? string.Empty,
|
||||||
TvDbId = tv.TvDbId,
|
TvDbId = tv.TvDbId,
|
||||||
|
|
247
src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs
Normal file
247
src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Api.TheMovieDb;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using Ombi.Core.Models.Requests;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Entities.Requests;
|
||||||
|
using Ombi.Store.Repository.Requests;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Helpers
|
||||||
|
{
|
||||||
|
public class TvShowRequestBuilderV2
|
||||||
|
{
|
||||||
|
|
||||||
|
public TvShowRequestBuilderV2(IMovieDbApi movApi)
|
||||||
|
{
|
||||||
|
MovieDbApi = movApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IMovieDbApi MovieDbApi { get; }
|
||||||
|
|
||||||
|
public ChildRequests ChildRequest { get; set; }
|
||||||
|
public List<SeasonsViewModel> TvRequests { get; protected set; }
|
||||||
|
public string PosterPath { get; protected set; }
|
||||||
|
public string BackdropPath { get; protected set; }
|
||||||
|
public DateTime FirstAir { get; protected set; }
|
||||||
|
public TvRequests NewRequest { get; protected set; }
|
||||||
|
protected TvInfo TheMovieDbRecord { get; set; }
|
||||||
|
|
||||||
|
public async Task<TvShowRequestBuilderV2> GetShowInfo(int id)
|
||||||
|
{
|
||||||
|
TheMovieDbRecord = await MovieDbApi.GetTVInfo(id.ToString());
|
||||||
|
BackdropPath = TheMovieDbRecord.Images?.Backdrops?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).FirstOrDefault()?.FilePath; ;
|
||||||
|
|
||||||
|
DateTime.TryParse(TheMovieDbRecord.first_air_date, out var dt);
|
||||||
|
|
||||||
|
FirstAir = dt;
|
||||||
|
|
||||||
|
// For some reason the poster path is always http
|
||||||
|
PosterPath = TheMovieDbRecord.Images?.Posters?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).FirstOrDefault()?.FilePath;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TvShowRequestBuilderV2 CreateChild(TvRequestViewModelV2 model, string userId)
|
||||||
|
{
|
||||||
|
var animationGenre = TheMovieDbRecord.genres?.Any(s => s.name.Equals("Animation", StringComparison.InvariantCultureIgnoreCase)) ?? false;
|
||||||
|
var animeKeyword = TheMovieDbRecord.Keywords?.KeywordsValue?.Any(s => s.Name.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ?? false;
|
||||||
|
ChildRequest = new ChildRequests
|
||||||
|
{
|
||||||
|
Id = model.TheMovieDbId, // This is set to 0 after the request rules have run, the request rules needs it to identify the request
|
||||||
|
RequestType = RequestType.TvShow,
|
||||||
|
RequestedDate = DateTime.UtcNow,
|
||||||
|
Approved = false,
|
||||||
|
RequestedUserId = userId,
|
||||||
|
SeasonRequests = new List<SeasonRequests>(),
|
||||||
|
Title = TheMovieDbRecord.name,
|
||||||
|
ReleaseYear = FirstAir,
|
||||||
|
RequestedByAlias = model.RequestedByAlias,
|
||||||
|
SeriesType = animationGenre && animeKeyword ? SeriesType.Anime : SeriesType.Standard
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TvShowRequestBuilderV2 CreateTvList(TvRequestViewModelV2 tv)
|
||||||
|
{
|
||||||
|
TvRequests = new List<SeasonsViewModel>();
|
||||||
|
// Only have the TV requests we actually requested and not everything
|
||||||
|
foreach (var season in tv.Seasons)
|
||||||
|
{
|
||||||
|
if (season.Episodes.Any())
|
||||||
|
{
|
||||||
|
TvRequests.Add(season);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<TvShowRequestBuilderV2> BuildEpisodes(TvRequestViewModelV2 tv)
|
||||||
|
{
|
||||||
|
var allEpisodes = new List<Episode>();
|
||||||
|
|
||||||
|
foreach (var season in TheMovieDbRecord.seasons)
|
||||||
|
{
|
||||||
|
var seasonEpisodes = await MovieDbApi.GetSeasonEpisodes(TheMovieDbRecord.id, season.season_number, CancellationToken.None);
|
||||||
|
allEpisodes.AddRange(seasonEpisodes.episodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tv.RequestAll)
|
||||||
|
{
|
||||||
|
foreach (var ep in allEpisodes)
|
||||||
|
{
|
||||||
|
var season = ChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == ep.season_number);
|
||||||
|
if (season == null)
|
||||||
|
{
|
||||||
|
ChildRequest.SeasonRequests.Add(new SeasonRequests
|
||||||
|
{
|
||||||
|
Episodes = new List<EpisodeRequests>{
|
||||||
|
new EpisodeRequests
|
||||||
|
{
|
||||||
|
EpisodeNumber = ep.episode_number,
|
||||||
|
AirDate = FormatDate(ep.air_date),
|
||||||
|
Title = ep.name,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SeasonNumber = ep.season_number,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
season.Episodes.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
EpisodeNumber = ep.episode_number,
|
||||||
|
AirDate = FormatDate(ep.air_date),
|
||||||
|
Title = ep.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (tv.LatestSeason)
|
||||||
|
{
|
||||||
|
var latest = allEpisodes.OrderByDescending(x => x.season_number).FirstOrDefault();
|
||||||
|
var episodesRequests = new List<EpisodeRequests>();
|
||||||
|
foreach (var ep in allEpisodes)
|
||||||
|
{
|
||||||
|
if (ep.season_number == latest.season_number)
|
||||||
|
{
|
||||||
|
episodesRequests.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
EpisodeNumber = ep.episode_number,
|
||||||
|
AirDate = FormatDate(ep.air_date),
|
||||||
|
Title = ep.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChildRequest.SeasonRequests.Add(new SeasonRequests
|
||||||
|
{
|
||||||
|
Episodes = episodesRequests,
|
||||||
|
SeasonNumber = latest.season_number,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (tv.FirstSeason)
|
||||||
|
{
|
||||||
|
var first = allEpisodes.OrderBy(x => x.season_number).FirstOrDefault();
|
||||||
|
var episodesRequests = new List<EpisodeRequests>();
|
||||||
|
foreach (var ep in allEpisodes)
|
||||||
|
{
|
||||||
|
if (ep.season_number == first.season_number)
|
||||||
|
{
|
||||||
|
episodesRequests.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
EpisodeNumber = ep.episode_number,
|
||||||
|
AirDate = FormatDate(ep.air_date),
|
||||||
|
Title = ep.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChildRequest.SeasonRequests.Add(new SeasonRequests
|
||||||
|
{
|
||||||
|
Episodes = episodesRequests,
|
||||||
|
SeasonNumber = first.season_number,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It's a custom request
|
||||||
|
var seasonRequests = new List<SeasonRequests>();
|
||||||
|
foreach (var ep in allEpisodes)
|
||||||
|
{
|
||||||
|
var existingSeasonRequest = seasonRequests.FirstOrDefault(x => x.SeasonNumber == ep.season_number);
|
||||||
|
if (existingSeasonRequest != null)
|
||||||
|
{
|
||||||
|
var requestedSeason = tv.Seasons.FirstOrDefault(x => x.SeasonNumber == ep.season_number);
|
||||||
|
var requestedEpisode = requestedSeason?.Episodes?.Any(x => x.EpisodeNumber == ep.episode_number) ?? false;
|
||||||
|
if (requestedSeason != null && requestedEpisode)
|
||||||
|
{
|
||||||
|
// We already have this, let's just add the episodes to it
|
||||||
|
existingSeasonRequest.Episodes.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
EpisodeNumber = ep.episode_number,
|
||||||
|
AirDate = FormatDate(ep.air_date),
|
||||||
|
Title = ep.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newRequest = new SeasonRequests { SeasonNumber = ep.season_number };
|
||||||
|
var requestedSeason = tv.Seasons.FirstOrDefault(x => x.SeasonNumber == ep.season_number);
|
||||||
|
var requestedEpisode = requestedSeason?.Episodes?.Any(x => x.EpisodeNumber == ep.episode_number) ?? false;
|
||||||
|
if (requestedSeason != null && requestedEpisode)
|
||||||
|
{
|
||||||
|
newRequest.Episodes.Add(new EpisodeRequests
|
||||||
|
{
|
||||||
|
EpisodeNumber = ep.episode_number,
|
||||||
|
AirDate = FormatDate(ep.air_date),
|
||||||
|
Title = ep.name,
|
||||||
|
});
|
||||||
|
seasonRequests.Add(newRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var s in seasonRequests)
|
||||||
|
{
|
||||||
|
ChildRequest.SeasonRequests.Add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TvShowRequestBuilderV2 CreateNewRequest(TvRequestViewModelV2 tv)
|
||||||
|
{
|
||||||
|
int.TryParse(TheMovieDbRecord.ExternalIds?.TvDbId, out var tvdbId);
|
||||||
|
NewRequest = new TvRequests
|
||||||
|
{
|
||||||
|
Overview = TheMovieDbRecord.overview,
|
||||||
|
PosterPath = PosterPath,
|
||||||
|
Title = TheMovieDbRecord.name,
|
||||||
|
ReleaseDate = FirstAir,
|
||||||
|
ExternalProviderId = TheMovieDbRecord.id,
|
||||||
|
Status = TheMovieDbRecord.status,
|
||||||
|
ImdbId = TheMovieDbRecord.ExternalIds?.ImdbId ?? string.Empty,
|
||||||
|
TvDbId = tvdbId,
|
||||||
|
ChildRequests = new List<ChildRequests>(),
|
||||||
|
TotalSeasons = tv.Seasons.Count(),
|
||||||
|
Background = BackdropPath
|
||||||
|
};
|
||||||
|
NewRequest.ChildRequests.Add(ChildRequest);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime FormatDate(string date)
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(date) ? DateTime.MinValue : DateTime.Parse(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,17 +3,9 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Ombi.Core.Models.Requests
|
namespace Ombi.Core.Models.Requests
|
||||||
{
|
{
|
||||||
public class TvRequestViewModel
|
public class TvRequestViewModel : TvRequestViewModelBase
|
||||||
{
|
{
|
||||||
public bool RequestAll { get; set; }
|
|
||||||
public bool LatestSeason { get; set; }
|
|
||||||
public bool FirstSeason { get; set; }
|
|
||||||
public int TvDbId { get; set; }
|
public int TvDbId { get; set; }
|
||||||
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
|
||||||
[JsonIgnore]
|
|
||||||
public string RequestedByAlias { get; set; }
|
|
||||||
|
|
||||||
public string RequestOnBehalf { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SeasonsViewModel
|
public class SeasonsViewModel
|
||||||
|
@ -27,4 +19,16 @@ namespace Ombi.Core.Models.Requests
|
||||||
public int EpisodeNumber { get; set; }
|
public int EpisodeNumber { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class TvRequestViewModelBase
|
||||||
|
{
|
||||||
|
public bool RequestAll { get; set; }
|
||||||
|
public bool LatestSeason { get; set; }
|
||||||
|
public bool FirstSeason { get; set; }
|
||||||
|
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
||||||
|
[JsonIgnore]
|
||||||
|
public string RequestedByAlias { get; set; }
|
||||||
|
|
||||||
|
public string RequestOnBehalf { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
10
src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs
Normal file
10
src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Models.Requests
|
||||||
|
{
|
||||||
|
public class TvRequestViewModelV2 : TvRequestViewModelBase
|
||||||
|
{
|
||||||
|
public int TheMovieDbId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,5 +6,6 @@
|
||||||
public string MediaType { get; set; }
|
public string MediaType { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Poster { get; set; }
|
public string Poster { get; set; }
|
||||||
|
public string Overview { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ namespace Ombi.Core.Models.Search.V2
|
||||||
public string FirstAired { get; set; }
|
public string FirstAired { get; set; }
|
||||||
public string NetworkId { get; set; }
|
public string NetworkId { get; set; }
|
||||||
public string Runtime { get; set; }
|
public string Runtime { get; set; }
|
||||||
public List<string> Genre { get; set; }
|
public GenreViewModel[] Genres { get; set; }
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public int LastUpdated { get; set; }
|
public int LastUpdated { get; set; }
|
||||||
public string AirsDayOfWeek { get; set; }
|
public string AirsDayOfWeek { get; set; }
|
||||||
|
@ -24,23 +24,12 @@ namespace Ombi.Core.Models.Search.V2
|
||||||
public NetworkViewModel Network { get; set; }
|
public NetworkViewModel Network { get; set; }
|
||||||
public Images Images { get; set; }
|
public Images Images { get; set; }
|
||||||
public List<CastViewModel> Cast { get; set; }
|
public List<CastViewModel> Cast { get; set; }
|
||||||
public List<CrewViewModel> Crew { get; set; }
|
public List<PersonViewModel> Crew { get; set; }
|
||||||
public string Certification { get; set; }
|
public string Certification { get; set; }
|
||||||
|
public string Tagline { get; set; }
|
||||||
/// <summary>
|
public Keywords Keywords { get; set; }
|
||||||
/// This is used from the Trakt API
|
public ExternalIds ExternalIds { get; set; }
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// The trailer.
|
|
||||||
/// </value>
|
|
||||||
public string Trailer { get; set; }
|
public string Trailer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is used from the Trakt API
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// The trailer.
|
|
||||||
/// </value>
|
|
||||||
public string Homepage { get; set; }
|
public string Homepage { get; set; }
|
||||||
|
|
||||||
public List<SeasonRequests> SeasonRequests { get; set; } = new List<SeasonRequests>();
|
public List<SeasonRequests> SeasonRequests { get; set; } = new List<SeasonRequests>();
|
||||||
|
@ -66,7 +55,7 @@ namespace Ombi.Core.Models.Search.V2
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public Country Country { get; set; }
|
public string Country { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Country
|
public class Country
|
||||||
|
@ -84,8 +73,10 @@ namespace Ombi.Core.Models.Search.V2
|
||||||
|
|
||||||
public class CastViewModel
|
public class CastViewModel
|
||||||
{
|
{
|
||||||
public PersonViewModel Person { get; set; }
|
public int Id { get; set; }
|
||||||
public CharacterViewModel Character { get; set; }
|
public string Person { get; set; }
|
||||||
|
public string Character { get; set; }
|
||||||
|
public string Image { get; set; }
|
||||||
public bool Self { get; set; }
|
public bool Self { get; set; }
|
||||||
public bool Voice { get; set; }
|
public bool Voice { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -95,7 +86,7 @@ namespace Ombi.Core.Models.Search.V2
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public Images Image { get; set; }
|
public string Image { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CharacterViewModel
|
public class CharacterViewModel
|
||||||
|
@ -106,9 +97,4 @@ namespace Ombi.Core.Models.Search.V2
|
||||||
public Images Image { get; set; }
|
public Images Image { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CrewViewModel
|
|
||||||
{
|
|
||||||
public string Type { get; set; }
|
|
||||||
public PersonViewModel Person { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -32,8 +32,8 @@ namespace Ombi.Core.Rule.Rules.Request
|
||||||
|
|
||||||
var tvContent = _plexContent.GetAll().Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show);
|
var tvContent = _plexContent.GetAll().Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show);
|
||||||
// We need to do a check on the TVDBId
|
// We need to do a check on the TVDBId
|
||||||
var anyTvDbMatches = await tvContent.FirstOrDefaultAsync(x => x.TvDbId.Length > 0 && x.TvDbId == tvRequest.Id.ToString()); // the Id on the child is the tvdbid at this point
|
var anyMovieDbMatches = await tvContent.FirstOrDefaultAsync(x => x.TheMovieDbId.Length > 0 && x.TheMovieDbId == tvRequest.Id.ToString());
|
||||||
if (anyTvDbMatches == null)
|
if (anyMovieDbMatches == null)
|
||||||
{
|
{
|
||||||
// So we do not have a TVDB Id, that really sucks.
|
// So we do not have a TVDB Id, that really sucks.
|
||||||
// Let's try and match on the title and year of the show
|
// Let's try and match on the title and year of the show
|
||||||
|
@ -50,7 +50,7 @@ namespace Ombi.Core.Rule.Rules.Request
|
||||||
return Success();
|
return Success();
|
||||||
}
|
}
|
||||||
// looks like we have a match on the TVDbID
|
// looks like we have a match on the TVDbID
|
||||||
return CheckExistingContent(tvRequest, anyTvDbMatches);
|
return CheckExistingContent(tvRequest, anyMovieDbMatches);
|
||||||
}
|
}
|
||||||
return Success();
|
return Success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace Ombi.Core.Rule.Rules.Request
|
||||||
{
|
{
|
||||||
var tv = (ChildRequests) obj;
|
var tv = (ChildRequests) obj;
|
||||||
var tvRequests = Tv.GetChild();
|
var tvRequests = Tv.GetChild();
|
||||||
var currentRequest = await tvRequests.FirstOrDefaultAsync(x => x.ParentRequest.TvDbId == tv.Id); // the Id on the child is the tvdbid at this point
|
var currentRequest = await tvRequests.FirstOrDefaultAsync(x => x.ParentRequest.ExternalProviderId == tv.Id); // the Id on the child is the TheMovieDb at this point
|
||||||
if (currentRequest == null)
|
if (currentRequest == null)
|
||||||
{
|
{
|
||||||
return Success();
|
return Success();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Context;
|
using Ombi.Store.Context;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Entities.Requests;
|
using Ombi.Store.Entities.Requests;
|
||||||
|
@ -22,7 +23,7 @@ namespace Ombi.Core.Rule.Rules
|
||||||
if (obj.RequestType == RequestType.TvShow)
|
if (obj.RequestType == RequestType.TvShow)
|
||||||
{
|
{
|
||||||
var vm = (ChildRequests) obj;
|
var vm = (ChildRequests) obj;
|
||||||
var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id);
|
var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id); // TODO lookup the external provider in the sonarr sync to use themoviedb
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
if (vm.SeasonRequests.Any())
|
if (vm.SeasonRequests.Any())
|
||||||
|
@ -53,8 +54,13 @@ namespace Ombi.Core.Rule.Rules
|
||||||
if (obj.Type == RequestType.TvShow)
|
if (obj.Type == RequestType.TvShow)
|
||||||
{
|
{
|
||||||
var vm = (SearchTvShowViewModel) obj;
|
var vm = (SearchTvShowViewModel) obj;
|
||||||
// Check if it's in Radarr
|
// Check if it's in Sonarr
|
||||||
var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id);
|
if (!vm.TheTvDbId.HasValue())
|
||||||
|
{
|
||||||
|
return new RuleResult { Success = true };
|
||||||
|
}
|
||||||
|
var tvdbidint = int.Parse(vm.TheTvDbId);
|
||||||
|
var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == tvdbidint);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
vm.Approved = true;
|
vm.Approved = true;
|
||||||
|
@ -69,7 +75,7 @@ namespace Ombi.Core.Rule.Rules
|
||||||
// Check if we have it
|
// Check if we have it
|
||||||
var monitoredInSonarr = await sonarrEpisodes.FirstOrDefaultAsync(x =>
|
var monitoredInSonarr = await sonarrEpisodes.FirstOrDefaultAsync(x =>
|
||||||
x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == season.SeasonNumber
|
x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == season.SeasonNumber
|
||||||
&& x.TvDbId == vm.Id);
|
&& x.TvDbId == tvdbidint);
|
||||||
if (monitoredInSonarr != null)
|
if (monitoredInSonarr != null)
|
||||||
{
|
{
|
||||||
ep.Approved = true;
|
ep.Approved = true;
|
||||||
|
|
|
@ -140,7 +140,7 @@ namespace Ombi.Core.Senders
|
||||||
|
|
||||||
private async Task<DogNzbAddResult> SendToDogNzb(ChildRequests model, DogNzbSettings settings)
|
private async Task<DogNzbAddResult> SendToDogNzb(ChildRequests model, DogNzbSettings settings)
|
||||||
{
|
{
|
||||||
var id = model.ParentRequest.TvDbId;
|
var id = model.ParentRequest.ExternalProviderId;
|
||||||
return await DogNzbApi.AddTvShow(settings.ApiKey, id.ToString());
|
return await DogNzbApi.AddTvShow(settings.ApiKey, id.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using AutoMapper;
|
||||||
using Ombi.Api.TheMovieDb.Models;
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Core.Models.Search.V2;
|
using Ombi.Core.Models.Search.V2;
|
||||||
|
using Ombi.Store.Entities.Requests;
|
||||||
using Ombi.TheMovieDbApi.Models;
|
using Ombi.TheMovieDbApi.Models;
|
||||||
using Keywords = Ombi.Core.Models.Search.V2.Keywords;
|
using Keywords = Ombi.Core.Models.Search.V2.Keywords;
|
||||||
using KeywordsValue = Ombi.Api.TheMovieDb.Models.KeywordsValue;
|
using KeywordsValue = Ombi.Api.TheMovieDb.Models.KeywordsValue;
|
||||||
|
@ -14,7 +15,7 @@ namespace Ombi.Mapping.Profiles
|
||||||
{
|
{
|
||||||
public MovieProfile()
|
public MovieProfile()
|
||||||
{
|
{
|
||||||
CreateMap<SearchResult, MovieSearchResult>()
|
CreateMap<SearchResult, MovieDbSearchResult>()
|
||||||
.ForMember(dest => dest.Adult, opts => opts.MapFrom(src => src.adult))
|
.ForMember(dest => dest.Adult, opts => opts.MapFrom(src => src.adult))
|
||||||
.ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path))
|
.ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path))
|
||||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
|
||||||
|
@ -24,7 +25,7 @@ namespace Ombi.Mapping.Profiles
|
||||||
.ForMember(dest => dest.Popularity, opts => opts.MapFrom(src => src.popularity))
|
.ForMember(dest => dest.Popularity, opts => opts.MapFrom(src => src.popularity))
|
||||||
.ForMember(dest => dest.PosterPath, opts => opts.MapFrom(src => src.poster_path))
|
.ForMember(dest => dest.PosterPath, opts => opts.MapFrom(src => src.poster_path))
|
||||||
.ForMember(dest => dest.ReleaseDate, opts => opts.MapFrom(src => src.release_date))
|
.ForMember(dest => dest.ReleaseDate, opts => opts.MapFrom(src => src.release_date))
|
||||||
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.title))
|
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => string.IsNullOrEmpty(src.title) ? src.name : src.title))
|
||||||
.ForMember(dest => dest.Video, opts => opts.MapFrom(src => src.video))
|
.ForMember(dest => dest.Video, opts => opts.MapFrom(src => src.video))
|
||||||
.ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average))
|
.ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average))
|
||||||
.ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count));
|
.ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count));
|
||||||
|
@ -75,7 +76,8 @@ namespace Ombi.Mapping.Profiles
|
||||||
|
|
||||||
CreateMap<TheMovieDbApi.Models.Genre, GenreDto>();
|
CreateMap<TheMovieDbApi.Models.Genre, GenreDto>();
|
||||||
|
|
||||||
CreateMap<MovieSearchResult, SearchMovieViewModel>().ReverseMap();
|
CreateMap<MovieDbSearchResult, SearchMovieViewModel>().ReverseMap();
|
||||||
|
|
||||||
CreateMap<MovieResponseDto, SearchMovieViewModel>().ReverseMap();
|
CreateMap<MovieResponseDto, SearchMovieViewModel>().ReverseMap();
|
||||||
|
|
||||||
CreateMap<FullMovieInfo, SearchMovieViewModel>().ReverseMap();
|
CreateMap<FullMovieInfo, SearchMovieViewModel>().ReverseMap();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Api.TvMaze.Models;
|
using Ombi.Api.TvMaze.Models;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
@ -27,6 +28,15 @@ namespace Ombi.Mapping.Profiles
|
||||||
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.show.image.medium) ? src.show.image.medium.ToHttpsUrl() : string.Empty))
|
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.show.image.medium) ? src.show.image.medium.ToHttpsUrl() : string.Empty))
|
||||||
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.show.status));
|
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.show.status));
|
||||||
|
|
||||||
|
CreateMap<TvSearchResult, SearchTvShowViewModel>()
|
||||||
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id))
|
||||||
|
.ForMember(dest => dest.TheMovieDbId, opts => opts.MapFrom(src => src.Id.ToString()))
|
||||||
|
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.ReleaseDate))
|
||||||
|
.ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Overview))
|
||||||
|
.ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.VoteAverage.ToString(CultureInfo.CurrentUICulture)))
|
||||||
|
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Name))
|
||||||
|
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.BackdropPath) ? src.BackdropPath.ToHttpsUrl() : src.PosterPath));
|
||||||
|
|
||||||
CreateMap<TvMazeShow, SearchTvShowViewModel>()
|
CreateMap<TvMazeShow, SearchTvShowViewModel>()
|
||||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.externals.thetvdb))
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.externals.thetvdb))
|
||||||
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.premiered))
|
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.premiered))
|
||||||
|
@ -56,10 +66,27 @@ namespace Ombi.Mapping.Profiles
|
||||||
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString()))
|
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString()))
|
||||||
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title))
|
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title))
|
||||||
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status)))
|
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status)))
|
||||||
.ForMember(dest => dest.Trailer,
|
.ForMember(dest => dest.Trailer,
|
||||||
opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty))
|
opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty))
|
||||||
.ForMember(dest => dest.Homepage,
|
.ForMember(dest => dest.Homepage,
|
||||||
opts => opts.MapFrom(src => src.Homepage != null ? src.Homepage.ToString().ToHttpsUrl() : string.Empty));
|
opts => opts.MapFrom(src => src.Homepage != null ? src.Homepage.ToString().ToHttpsUrl() : string.Empty));
|
||||||
|
|
||||||
|
|
||||||
|
CreateMap<MovieDbSearchResult, SearchTvShowViewModel>()
|
||||||
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id))
|
||||||
|
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.ReleaseDate))
|
||||||
|
//.ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.Ids.Imdb))
|
||||||
|
//.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.Network))
|
||||||
|
.ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Overview))
|
||||||
|
.ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.VoteAverage.ToString()))
|
||||||
|
.ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.PosterPath))
|
||||||
|
//.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString()))
|
||||||
|
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title));
|
||||||
|
//.ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status)))
|
||||||
|
//.ForMember(dest => dest.Trailer,
|
||||||
|
// opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty))
|
||||||
|
//.ForMember(dest => dest.Homepage,
|
||||||
|
// opts => opts.MapFrom(src => src.Homepage != null ? src.Homepage.ToString().ToHttpsUrl() : string.Empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
using System.Globalization;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Api.TvMaze.Models.V2;
|
using Ombi.Api.TvMaze.Models.V2;
|
||||||
using Ombi.Core.Models.Search;
|
using Ombi.Core.Models.Search;
|
||||||
using Ombi.Core.Models.Search.V2;
|
using Ombi.Core.Models.Search.V2;
|
||||||
|
@ -11,72 +14,90 @@ namespace Ombi.Mapping.Profiles
|
||||||
{
|
{
|
||||||
public TvProfileV2()
|
public TvProfileV2()
|
||||||
{
|
{
|
||||||
CreateMap<FullSearch, SearchFullInfoTvShowViewModel>()
|
|
||||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.externals.thetvdb))
|
CreateMap<TvInfo, SearchFullInfoTvShowViewModel>()
|
||||||
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.premiered))
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
|
||||||
.ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.externals.imdb))
|
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.first_air_date))
|
||||||
.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network.name))
|
.ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.ExternalIds.ImdbId))
|
||||||
.ForMember(dest => dest.NetworkId, opts => opts.MapFrom(src => src.network.id.ToString()))
|
.ForMember(dest => dest.TheTvDbId, opts => opts.MapFrom(src => src.ExternalIds.TvDbId))
|
||||||
.ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.summary.RemoveHtml()))
|
.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.networks.FirstOrDefault()))
|
||||||
|
.ForMember(dest => dest.NetworkId, opts => opts.MapFrom(src => src.networks.FirstOrDefault().id))
|
||||||
|
.ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.overview))
|
||||||
.ForMember(dest => dest.Rating,
|
.ForMember(dest => dest.Rating,
|
||||||
opts => opts.MapFrom(src => src.rating.average.ToString(CultureInfo.CurrentUICulture)))
|
opts => opts.MapFrom(src => src.vote_average.ToString(CultureInfo.CurrentUICulture)))
|
||||||
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime.ToString()))
|
//.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime.ToString()))
|
||||||
.ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id))
|
.ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id))
|
||||||
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.name))
|
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.name))
|
||||||
.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network))
|
//.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network))
|
||||||
.ForMember(dest => dest.Images, opts => opts.MapFrom(src => src.image))
|
.ForMember(dest => dest.Images, opts => opts.MapFrom(src => src.Images))
|
||||||
.ForMember(dest => dest.Cast, opts => opts.MapFrom(src => src._embedded.cast))
|
.ForMember(dest => dest.Cast, opts => opts.MapFrom(src => src.Credits.cast))
|
||||||
.ForMember(dest => dest.Crew, opts => opts.MapFrom(src => src._embedded.crew))
|
.ForMember(dest => dest.Crew, opts => opts.MapFrom(src => src.Credits.crew))
|
||||||
.ForMember(dest => dest.Banner,
|
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => GetBanner(src.Images)))
|
||||||
opts => opts.MapFrom(src =>
|
.ForMember(dest => dest.Genres, opts => opts.MapFrom(src => src.genres))
|
||||||
!string.IsNullOrEmpty(src.image.medium)
|
.ForMember(dest => dest.Keywords, opts => opts.MapFrom(src => src.Keywords))
|
||||||
? src.image.medium.ToHttpsUrl()
|
.ForMember(dest => dest.Tagline, opts => opts.MapFrom(src => src.tagline))
|
||||||
: string.Empty))
|
.ForMember(dest => dest.ExternalIds, opts => opts.MapFrom(src => src.ExternalIds))
|
||||||
|
.ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => GetTrailer(src.Videos)))
|
||||||
|
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.episode_run_time.FirstOrDefault()))
|
||||||
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status));
|
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status));
|
||||||
|
|
||||||
CreateMap<Network, NetworkViewModel>()
|
CreateMap<Ombi.Api.TheMovieDb.Models.ExternalIds, Ombi.Core.Models.Search.V2.ExternalIds>().ReverseMap();
|
||||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
|
CreateMap<Ombi.Api.TheMovieDb.Models.Images, Ombi.Core.Models.Search.V2.Images>()
|
||||||
.ForMember(dest => dest.Country, opts => opts.MapFrom(src => src.country))
|
.ForMember(dest => dest.Original, opts => opts.MapFrom(src => src.Posters.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).FirstOrDefault().FilePath));
|
||||||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name));
|
|
||||||
|
|
||||||
|
CreateMap<Api.TheMovieDb.Models.Keywords, Ombi.Core.Models.Search.V2.Keywords>().ReverseMap();
|
||||||
|
CreateMap<Ombi.Api.TheMovieDb.Models.KeywordsValue, Ombi.Core.Models.Search.V2.KeywordsValue>().ReverseMap();
|
||||||
|
|
||||||
|
|
||||||
|
CreateMap<GenreViewModel, Genre>()
|
||||||
|
.ForMember(dest => dest.id, opts => opts.MapFrom(src => src.id))
|
||||||
|
.ForMember(dest => dest.name, opts => opts.MapFrom(src => src.name));
|
||||||
|
|
||||||
|
CreateMap<Api.TheMovieDb.Models.Network, NetworkViewModel>()
|
||||||
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
|
||||||
|
.ForMember(dest => dest.Country, opts => opts.MapFrom(src => src.origin_country))
|
||||||
|
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name));
|
||||||
|
|
||||||
CreateMap<Api.TvMaze.Models.V2.Country, Core.Models.Search.V2.Country>()
|
CreateMap<Api.TvMaze.Models.V2.Country, Core.Models.Search.V2.Country>()
|
||||||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name))
|
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name))
|
||||||
.ForMember(dest => dest.Code, opts => opts.MapFrom(src => src.code))
|
.ForMember(dest => dest.Code, opts => opts.MapFrom(src => src.code))
|
||||||
.ForMember(dest => dest.Timezone, opts => opts.MapFrom(src => src.timezone));
|
.ForMember(dest => dest.Timezone, opts => opts.MapFrom(src => src.timezone));
|
||||||
|
|
||||||
CreateMap<Api.TvMaze.Models.V2.Image, Images>()
|
CreateMap<FullMovieCast, CastViewModel>()
|
||||||
.ForMember(dest => dest.Medium, opts => opts.MapFrom(src => src.medium.ToHttpsUrl()))
|
.ForMember(dest => dest.Character, opts => opts.MapFrom(src => src.character))
|
||||||
.ForMember(dest => dest.Original, opts => opts.MapFrom(src => src.original.ToHttpsUrl()));
|
.ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.name))
|
||||||
|
.ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.profile_path))
|
||||||
|
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id));
|
||||||
|
|
||||||
CreateMap<Api.TvMaze.Models.V2.Cast, CastViewModel>()
|
CreateMap<FullMovieCrew, PersonViewModel>()
|
||||||
.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<Api.TvMaze.Models.V2.Person, PersonViewModel>()
|
|
||||||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
|
.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.profile_path))
|
||||||
.ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.image))
|
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name));
|
||||||
.ForMember(dest => dest.Url, opts => opts.MapFrom(src => src.url.ToHttpsUrl()));
|
|
||||||
|
|
||||||
CreateMap<Api.TvMaze.Models.V2.Crew, CrewViewModel>()
|
|
||||||
.ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person))
|
|
||||||
.ForMember(dest => dest.Type, opts => opts.MapFrom(src => src.type));
|
|
||||||
|
|
||||||
CreateMap<Api.TvMaze.Models.V2.Cast, CastViewModel>()
|
|
||||||
.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<Api.TvMaze.Models.V2.Character, CharacterViewModel>()
|
|
||||||
.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.ToHttpsUrl()))
|
|
||||||
.ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.image));
|
|
||||||
|
|
||||||
CreateMap<SearchTvShowViewModel, SearchFullInfoTvShowViewModel>().ReverseMap();
|
CreateMap<SearchTvShowViewModel, SearchFullInfoTvShowViewModel>().ReverseMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetBanner(Api.TheMovieDb.Models.Images images)
|
||||||
|
{
|
||||||
|
var hasBackdrop = images?.Backdrops?.Any();
|
||||||
|
if (hasBackdrop ?? false)
|
||||||
|
{
|
||||||
|
return images.Backdrops?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).Select(x => x.FilePath).FirstOrDefault();
|
||||||
|
}
|
||||||
|
else if (images != null)
|
||||||
|
{
|
||||||
|
return images.Posters?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).Select(x => x.FilePath).FirstOrDefault();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTrailer(Api.TheMovieDb.Models.Videos trailer)
|
||||||
|
{
|
||||||
|
return trailer?.results?.FirstOrDefault(x => x.type.Equals("trailer", StringComparison.InvariantCultureIgnoreCase))?.key ?? null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates
|
||||||
if (string.IsNullOrEmpty(_templateLocation))
|
if (string.IsNullOrEmpty(_templateLocation))
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp3.1", "Templates",
|
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net5.0", "Templates",
|
||||||
"BasicTemplate.html");
|
"BasicTemplate.html");
|
||||||
#else
|
#else
|
||||||
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html");
|
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html");
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace Ombi.Notifications
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
RequestId = req?.Id.ToString();
|
RequestId = req?.Id.ToString();
|
||||||
ProviderId = req?.ParentRequest?.TvDbId.ToString() ?? string.Empty;
|
ProviderId = req?.ParentRequest?.ExternalProviderId.ToString() ?? string.Empty;
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,8 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ombi.Api.Plex;
|
using Ombi.Api.Plex;
|
||||||
using Ombi.Api.Plex.Models;
|
using Ombi.Api.Plex.Models;
|
||||||
|
using Ombi.Api.TheMovieDb;
|
||||||
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models.External;
|
using Ombi.Core.Settings.Models.External;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
@ -49,8 +51,9 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
public class PlexContentSync : IPlexContentSync
|
public class PlexContentSync : IPlexContentSync
|
||||||
{
|
{
|
||||||
|
private readonly IMovieDbApi _movieApi;
|
||||||
public PlexContentSync(ISettingsService<PlexSettings> plex, IPlexApi plexApi, ILogger<PlexContentSync> logger, IPlexContentRepository repo,
|
public PlexContentSync(ISettingsService<PlexSettings> plex, IPlexApi plexApi, ILogger<PlexContentSync> logger, IPlexContentRepository repo,
|
||||||
IPlexEpisodeSync epsiodeSync, IHubContext<NotificationHub> hub)
|
IPlexEpisodeSync epsiodeSync, IHubContext<NotificationHub> hub, IMovieDbApi movieDbApi)
|
||||||
{
|
{
|
||||||
Plex = plex;
|
Plex = plex;
|
||||||
PlexApi = plexApi;
|
PlexApi = plexApi;
|
||||||
|
@ -58,6 +61,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
Repo = repo;
|
Repo = repo;
|
||||||
EpisodeSync = epsiodeSync;
|
EpisodeSync = epsiodeSync;
|
||||||
Notification = hub;
|
Notification = hub;
|
||||||
|
_movieApi = movieDbApi;
|
||||||
Plex.ClearCache();
|
Plex.ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +422,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
|
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
|
||||||
existingContent.Key);
|
existingContent.Key);
|
||||||
GetProviderIds(showMetadata, existingContent);
|
await GetProviderIds(showMetadata, existingContent);
|
||||||
|
|
||||||
await Repo.Update(existingContent);
|
await Repo.Update(existingContent);
|
||||||
}
|
}
|
||||||
|
@ -531,7 +535,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey),
|
Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey),
|
||||||
Seasons = new List<PlexSeasonsContent>()
|
Seasons = new List<PlexSeasonsContent>()
|
||||||
};
|
};
|
||||||
GetProviderIds(showMetadata, item);
|
await GetProviderIds(showMetadata, item);
|
||||||
|
|
||||||
// Let's just double check to make sure we do not have it now we have some id's
|
// Let's just double check to make sure we do not have it now we have some id's
|
||||||
var existingImdb = false;
|
var existingImdb = false;
|
||||||
|
@ -573,7 +577,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GetProviderIds(PlexMetadata showMetadata, PlexServerContent existingContent)
|
private async Task GetProviderIds(PlexMetadata showMetadata, PlexServerContent existingContent)
|
||||||
{
|
{
|
||||||
var metadata = showMetadata.MediaContainer.Metadata.FirstOrDefault();
|
var metadata = showMetadata.MediaContainer.Metadata.FirstOrDefault();
|
||||||
var guids = new List<string>
|
var guids = new List<string>
|
||||||
|
@ -601,6 +605,13 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
|
|
||||||
if (providerIds.TheTvDb.HasValue())
|
if (providerIds.TheTvDb.HasValue())
|
||||||
{
|
{
|
||||||
|
// Lookup TheMovieDbId
|
||||||
|
var findResult = await _movieApi.Find(providerIds.TheTvDb, ExternalSource.tvdb_id);
|
||||||
|
var tvResult = findResult.tv_results.FirstOrDefault();
|
||||||
|
if (tvResult != null)
|
||||||
|
{
|
||||||
|
existingContent.TheMovieDbId = tvResult.id.ToString();
|
||||||
|
}
|
||||||
existingContent.TvDbId = providerIds.TheTvDb;
|
existingContent.TvDbId = providerIds.TheTvDb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -683,6 +694,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
{
|
{
|
||||||
public string BaseUrl { get; set; }
|
public string BaseUrl { get; set; }
|
||||||
public bool CollectAnalyticData { get; set; }
|
public bool CollectAnalyticData { get; set; }
|
||||||
public bool Set { get; set; }
|
|
||||||
public bool Wizard { get; set; }
|
public bool Wizard { get; set; }
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
public bool IgnoreCertificateErrors { get; set; }
|
public bool IgnoreCertificateErrors { get; set; }
|
||||||
|
@ -14,5 +13,9 @@
|
||||||
public string DefaultLanguageCode { get; set; } = "en";
|
public string DefaultLanguageCode { get; set; } = "en";
|
||||||
public bool AutoDeleteAvailableRequests { get; set; }
|
public bool AutoDeleteAvailableRequests { get; set; }
|
||||||
public int AutoDeleteAfterDays { get; set; }
|
public int AutoDeleteAfterDays { get; set; }
|
||||||
|
|
||||||
|
//INTERNAL
|
||||||
|
public bool HasMigratedOldTvDbData { get; set; }
|
||||||
|
public bool Set { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ namespace Ombi.Store.Repository.Requests
|
||||||
public class SeasonRequests : Entity
|
public class SeasonRequests : Entity
|
||||||
{
|
{
|
||||||
public int SeasonNumber { get; set; }
|
public int SeasonNumber { get; set; }
|
||||||
|
public string Overview { get; set; }
|
||||||
public List<EpisodeRequests> Episodes { get; set; } = new List<EpisodeRequests>();
|
public List<EpisodeRequests> Episodes { get; set; } = new List<EpisodeRequests>();
|
||||||
|
|
||||||
public int ChildRequestId { get; set; }
|
public int ChildRequestId { get; set; }
|
||||||
|
|
|
@ -6,7 +6,13 @@ namespace Ombi.Store.Entities.Requests
|
||||||
{
|
{
|
||||||
public class TvRequests : Entity
|
public class TvRequests : Entity
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No longer used since moved away from TvMaze
|
||||||
|
/// </summary>
|
||||||
public int TvDbId { get; set; }
|
public int TvDbId { get; set; }
|
||||||
|
|
||||||
|
// TheMovieDbId
|
||||||
|
public int ExternalProviderId { get; set; }
|
||||||
public string ImdbId { get; set; }
|
public string ImdbId { get; set; }
|
||||||
public int? QualityOverride { get; set; }
|
public int? QualityOverride { get; set; }
|
||||||
public int? RootFolder { get; set; }
|
public int? RootFolder { get; set; }
|
||||||
|
|
1232
src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.Designer.cs
generated
Normal file
1232
src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,52 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations.OmbiMySql
|
||||||
|
{
|
||||||
|
public partial class TvRequestProviderId : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ExternalProviderId",
|
||||||
|
table: "TvRequests",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Overview",
|
||||||
|
table: "SeasonRequests",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "StreamingCountry",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "",
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "longtext",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ExternalProviderId",
|
||||||
|
table: "TvRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Overview",
|
||||||
|
table: "SeasonRequests");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "StreamingCountry",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "longtext",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "longtext");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -308,6 +308,7 @@ namespace Ombi.Store.Migrations.OmbiMySql
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("StreamingCountry")
|
b.Property<string>("StreamingCountry")
|
||||||
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<bool>("TwoFactorEnabled")
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
@ -764,6 +765,9 @@ namespace Ombi.Store.Migrations.OmbiMySql
|
||||||
b.Property<string>("Background")
|
b.Property<string>("Background")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("ExternalProviderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("ImdbId")
|
b.Property<string>("ImdbId")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
@ -954,6 +958,9 @@ namespace Ombi.Store.Migrations.OmbiMySql
|
||||||
b.Property<int>("ChildRequestId")
|
b.Property<int>("ChildRequestId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Overview")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<int>("SeasonNumber")
|
b.Property<int>("SeasonNumber")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
|
1231
src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.Designer.cs
generated
Normal file
1231
src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,34 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations.OmbiSqlite
|
||||||
|
{
|
||||||
|
public partial class TvRequestProviderId : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ExternalProviderId",
|
||||||
|
table: "TvRequests",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Overview",
|
||||||
|
table: "SeasonRequests",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ExternalProviderId",
|
||||||
|
table: "TvRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Overview",
|
||||||
|
table: "SeasonRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -764,6 +764,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
|
||||||
b.Property<string>("Background")
|
b.Property<string>("Background")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ExternalProviderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("ImdbId")
|
b.Property<string>("ImdbId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
@ -954,6 +957,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
|
||||||
b.Property<int>("ChildRequestId")
|
b.Property<int>("ChildRequestId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Overview")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("SeasonNumber")
|
b.Property<int>("SeasonNumber")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Ombi.Store.Repository.Requests
|
||||||
IQueryable<TvRequests> Get(string userId);
|
IQueryable<TvRequests> Get(string userId);
|
||||||
IQueryable<TvRequests> GetLite(string userId);
|
IQueryable<TvRequests> GetLite(string userId);
|
||||||
Task<TvRequests> GetRequestAsync(int tvDbId);
|
Task<TvRequests> GetRequestAsync(int tvDbId);
|
||||||
TvRequests GetRequest(int tvDbId);
|
TvRequests GetRequest(int theMovieDbId);
|
||||||
Task Update(TvRequests request);
|
Task Update(TvRequests request);
|
||||||
Task UpdateChild(ChildRequests request);
|
Task UpdateChild(ChildRequests request);
|
||||||
IQueryable<ChildRequests> GetChild();
|
IQueryable<ChildRequests> GetChild();
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace Ombi.Store.Repository.Requests
|
||||||
|
|
||||||
public async Task<TvRequests> GetRequestAsync(int tvDbId)
|
public async Task<TvRequests> GetRequestAsync(int tvDbId)
|
||||||
{
|
{
|
||||||
return await Db.TvRequests.Where(x => x.TvDbId == tvDbId)
|
return await Db.TvRequests.Where(x => x.ExternalProviderId == tvDbId)
|
||||||
.Include(x => x.ChildRequests)
|
.Include(x => x.ChildRequests)
|
||||||
.ThenInclude(x => x.RequestedUser)
|
.ThenInclude(x => x.RequestedUser)
|
||||||
.Include(x => x.ChildRequests)
|
.Include(x => x.ChildRequests)
|
||||||
|
@ -29,9 +29,9 @@ namespace Ombi.Store.Repository.Requests
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TvRequests GetRequest(int tvDbId)
|
public TvRequests GetRequest(int theMovieDbId)
|
||||||
{
|
{
|
||||||
return Db.TvRequests.Where(x => x.TvDbId == tvDbId).AsSplitQuery()
|
return Db.TvRequests.Where(x => x.ExternalProviderId == theMovieDbId).AsSplitQuery()
|
||||||
.Include(x => x.ChildRequests)
|
.Include(x => x.ChildRequests)
|
||||||
.ThenInclude(x => x.RequestedUser)
|
.ThenInclude(x => x.RequestedUser)
|
||||||
.Include(x => x.ChildRequests)
|
.Include(x => x.ChildRequests)
|
||||||
|
|
|
@ -10,16 +10,20 @@ namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
Task<MovieResponseDto> GetMovieInformation(int movieId);
|
Task<MovieResponseDto> GetMovieInformation(int movieId);
|
||||||
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en");
|
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en");
|
||||||
Task<List<MovieSearchResult>> NowPlaying(string languageCode, int? page = null);
|
Task<List<MovieDbSearchResult>> NowPlaying(string languageCode, int? page = null);
|
||||||
Task<List<MovieSearchResult>> PopularMovies(string languageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken));
|
Task<List<MovieDbSearchResult>> PopularMovies(string languageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string languageCode);
|
Task<List<MovieDbSearchResult>> PopularTv(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
Task<List<MovieDbSearchResult>> SearchMovie(string searchTerm, int? year, string languageCode);
|
||||||
Task<List<TvSearchResult>> SearchTv(string searchTerm, string year = default);
|
Task<List<TvSearchResult>> SearchTv(string searchTerm, string year = default);
|
||||||
Task<List<MovieSearchResult>> TopRated(string languageCode, int? page = null);
|
Task<List<MovieDbSearchResult>> TopRated(string languageCode, int? page = null);
|
||||||
Task<List<MovieSearchResult>> Upcoming(string languageCode, int? page = null);
|
Task<List<MovieDbSearchResult>> Upcoming(string languageCode, int? page = null);
|
||||||
Task<List<MovieSearchResult>> SimilarMovies(int movieId, string langCode);
|
Task<List<MovieDbSearchResult>> TopRatedTv(string languageCode, int? page = null);
|
||||||
|
Task<List<MovieDbSearchResult>> UpcomingTv(string languageCode, int? page = null);
|
||||||
|
Task<List<MovieDbSearchResult>> SimilarMovies(int movieId, string langCode);
|
||||||
Task<FindResult> Find(string externalId, ExternalSource source);
|
Task<FindResult> Find(string externalId, ExternalSource source);
|
||||||
Task<TvExternals> GetTvExternals(int theMovieDbId);
|
Task<TvExternals> GetTvExternals(int theMovieDbId);
|
||||||
Task<TvInfo> GetTVInfo(string themoviedbid);
|
Task<SeasonDetails> GetSeasonEpisodes(int theMovieDbId, int seasonNumber, CancellationToken token, string langCode = "en");
|
||||||
|
Task<TvInfo> GetTVInfo(string themoviedbid, string langCode = "en");
|
||||||
Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode);
|
Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode);
|
||||||
Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode);
|
Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode);
|
||||||
Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken);
|
Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken);
|
||||||
|
|
|
@ -6,7 +6,12 @@
|
||||||
public object[] person_results { get; set; }
|
public object[] person_results { get; set; }
|
||||||
public TvResults[] tv_results { get; set; }
|
public TvResults[] tv_results { get; set; }
|
||||||
public object[] tv_episode_results { get; set; }
|
public object[] tv_episode_results { get; set; }
|
||||||
public object[] tv_season_results { get; set; }
|
public FindSeasonResults[] tv_season_results { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FindSeasonResults
|
||||||
|
{
|
||||||
|
public int show_id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Movie_Results
|
public class Movie_Results
|
||||||
|
|
|
@ -87,6 +87,10 @@ namespace Ombi.Api.TheMovieDb.Models
|
||||||
{
|
{
|
||||||
[JsonProperty("file_path")]
|
[JsonProperty("file_path")]
|
||||||
public string FilePath { get; set; }
|
public string FilePath { get; set; }
|
||||||
|
[JsonProperty("vote_count")]
|
||||||
|
public int VoteCount { get; set; }
|
||||||
|
[JsonProperty("vote_average")]
|
||||||
|
public double VoteAverage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Keywords
|
public class Keywords
|
||||||
|
@ -128,6 +132,8 @@ namespace Ombi.Api.TheMovieDb.Models
|
||||||
public string InstagramId { get; set; }
|
public string InstagramId { get; set; }
|
||||||
[JsonProperty("twitter_id")]
|
[JsonProperty("twitter_id")]
|
||||||
public string TwitterId { get; set; }
|
public string TwitterId { get; set; }
|
||||||
|
[JsonProperty("tvdb_id")]
|
||||||
|
public string TvDbId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Credits
|
public class Credits
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ombi.Api.TheMovieDb.Models
|
namespace Ombi.Api.TheMovieDb.Models
|
||||||
{
|
{
|
||||||
public class MovieSearchResult
|
public class MovieDbSearchResult
|
||||||
{
|
{
|
||||||
public string PosterPath { get; set; }
|
public string PosterPath { get; set; }
|
||||||
public bool Adult { get; set; }
|
public bool Adult { get; set; }
|
54
src/Ombi.TheMovieDbApi/Models/SeasonDetails.cs
Normal file
54
src/Ombi.TheMovieDbApi/Models/SeasonDetails.cs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Api.TheMovieDb.Models
|
||||||
|
{
|
||||||
|
public class SeasonDetails
|
||||||
|
{
|
||||||
|
public string _id { get; set; }
|
||||||
|
public string air_date { get; set; }
|
||||||
|
public Episode[] episodes { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string overview { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public string poster_path { get; set; }
|
||||||
|
public int season_number { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Episode
|
||||||
|
{
|
||||||
|
public string air_date { get; set; }
|
||||||
|
public int episode_number { get; set; }
|
||||||
|
public Crew[] crew { get; set; }
|
||||||
|
public Guest_Stars[] guest_stars { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string overview { get; set; }
|
||||||
|
public string production_code { get; set; }
|
||||||
|
public int season_number { get; set; }
|
||||||
|
public string still_path { get; set; }
|
||||||
|
public float vote_average { get; set; }
|
||||||
|
public int vote_count { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class Guest_Stars
|
||||||
|
{
|
||||||
|
public string character { get; set; }
|
||||||
|
public string credit_id { get; set; }
|
||||||
|
public int order { get; set; }
|
||||||
|
public bool adult { get; set; }
|
||||||
|
public int gender { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public string known_for_department { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string original_name { get; set; }
|
||||||
|
public float popularity { get; set; }
|
||||||
|
public string profile_path { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ namespace Ombi.Api.TheMovieDb.Models
|
||||||
public Network[] networks { get; set; }
|
public Network[] networks { get; set; }
|
||||||
public int number_of_episodes { get; set; }
|
public int number_of_episodes { get; set; }
|
||||||
public int number_of_seasons { get; set; }
|
public int number_of_seasons { get; set; }
|
||||||
|
public string tagline { get; set; }
|
||||||
public string[] origin_country { get; set; }
|
public string[] origin_country { get; set; }
|
||||||
public string original_language { get; set; }
|
public string original_language { get; set; }
|
||||||
public string original_name { get; set; }
|
public string original_name { get; set; }
|
||||||
|
@ -30,7 +31,21 @@ namespace Ombi.Api.TheMovieDb.Models
|
||||||
public string type { get; set; }
|
public string type { get; set; }
|
||||||
public float vote_average { get; set; }
|
public float vote_average { get; set; }
|
||||||
public int vote_count { get; set; }
|
public int vote_count { get; set; }
|
||||||
[JsonProperty("external_ids")] public TvExternalIds TvExternalIds { get; set; }
|
[JsonProperty("videos")]
|
||||||
|
public Videos Videos { get; set; }
|
||||||
|
[JsonProperty("credits")]
|
||||||
|
public Credits Credits { get; set; }
|
||||||
|
[JsonProperty("similar")]
|
||||||
|
public Similar Similar { get; set; }
|
||||||
|
[JsonProperty("recommendations")]
|
||||||
|
public Recommendations Recommendations { get; set; }
|
||||||
|
[JsonProperty("external_ids")]
|
||||||
|
public ExternalIds ExternalIds { get; set; }
|
||||||
|
[JsonProperty("keywords")]
|
||||||
|
public Keywords Keywords { get; set; }
|
||||||
|
[JsonProperty("images")]
|
||||||
|
public Images Images { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Created_By
|
public class Created_By
|
||||||
|
|
|
@ -132,7 +132,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return await Api.Request<TvExternals>(request);
|
return await Api.Request<TvExternals>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId, string langCode)
|
public async Task<List<MovieDbSearchResult>> SimilarMovies(int movieId, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
@ -140,7 +140,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en")
|
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en")
|
||||||
|
@ -154,7 +154,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return Mapper.Map<MovieResponseDto>(result);
|
return Mapper.Map<MovieResponseDto>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string langCode)
|
public async Task<List<MovieDbSearchResult>> SearchMovie(string searchTerm, int? year, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
@ -171,15 +171,25 @@ namespace Ombi.Api.TheMovieDb
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-popular-movies">/movie/popular</a>.
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-popular-movies">/movie/popular</a>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public async Task<List<MovieSearchResult>> PopularMovies(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken))
|
public async Task<List<MovieDbSearchResult>> PopularMovies(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
return await Popular("movie", langCode, page, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<MovieDbSearchResult>> PopularTv(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return await Popular("tv", langCode, page, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<MovieDbSearchResult>> Popular(string type, string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.AddQueryString("language", langCode);
|
request.AddQueryString("language", langCode);
|
||||||
request.AddQueryString("sort_by", "popularity.desc");
|
request.AddQueryString("sort_by", "popularity.desc");
|
||||||
|
@ -187,18 +197,28 @@ namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
request.AddQueryString("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
await AddDiscoverMovieSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<MovieDbSearchResult>> TopRated(string langCode, int? page = null)
|
||||||
|
{
|
||||||
|
return TopRated("movie", langCode, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<MovieDbSearchResult>> TopRatedTv(string langCode, int? page = null)
|
||||||
|
{
|
||||||
|
return TopRated("tv", langCode, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-top-rated-movies">/movie/top_rated</a>.
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-top-rated-movies">/movie/top_rated</a>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public async Task<List<MovieSearchResult>> TopRated(string langCode, int? page = null)
|
private async Task<List<MovieDbSearchResult>> TopRated(string type, string langCode, int? page = null)
|
||||||
{
|
{
|
||||||
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.AddQueryString("language", langCode);
|
request.AddQueryString("language", langCode);
|
||||||
request.AddQueryString("sort_by", "vote_average.desc");
|
request.AddQueryString("sort_by", "vote_average.desc");
|
||||||
|
@ -212,18 +232,27 @@ namespace Ombi.Api.TheMovieDb
|
||||||
// to filter out extremely high-rated movies due to very little votes
|
// to filter out extremely high-rated movies due to very little votes
|
||||||
request.AddQueryString("vote_count.gte", "250");
|
request.AddQueryString("vote_count.gte", "250");
|
||||||
|
|
||||||
await AddDiscoverMovieSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<MovieDbSearchResult>> Upcoming(string langCode, int? page = null)
|
||||||
|
{
|
||||||
|
return Upcoming("movie", langCode, page);
|
||||||
|
}
|
||||||
|
public Task<List<MovieDbSearchResult>> UpcomingTv(string langCode, int? page = null)
|
||||||
|
{
|
||||||
|
return Upcoming("tv", langCode, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-upcoming">/movie/upcoming</a>.
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-upcoming">/movie/upcoming</a>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public async Task<List<MovieSearchResult>> Upcoming(string langCode, int? page = null)
|
private async Task<List<MovieDbSearchResult>> Upcoming(string type, string langCode, int? page = null)
|
||||||
{
|
{
|
||||||
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.AddQueryString("language", langCode);
|
request.AddQueryString("language", langCode);
|
||||||
|
|
||||||
|
@ -239,16 +268,16 @@ namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
request.AddQueryString("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
await AddDiscoverMovieSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-now-playing">/movie/now_playing</a>.
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-now-playing">/movie/now_playing</a>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public async Task<List<MovieSearchResult>> NowPlaying(string langCode, int? page = null)
|
public async Task<List<MovieDbSearchResult>> NowPlaying(string langCode, int? page = null)
|
||||||
{
|
{
|
||||||
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
@ -267,22 +296,33 @@ namespace Ombi.Api.TheMovieDb
|
||||||
request.AddQueryString("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
await AddDiscoverMovieSettings(request);
|
await AddDiscoverSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TvInfo> GetTVInfo(string themoviedbid)
|
public async Task<TvInfo> GetTVInfo(string themoviedbid, string langCode = "en")
|
||||||
{
|
{
|
||||||
var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get);
|
var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get);
|
||||||
request.AddQueryString("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.AddQueryString("append_to_response", "external_ids");
|
request.AddQueryString("language", langCode);
|
||||||
|
request.AddQueryString("append_to_response", "videos,credits,similar,recommendations,external_ids,keywords,images");
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
return await Api.Request<TvInfo>(request);
|
return await Api.Request<TvInfo>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<SeasonDetails> GetSeasonEpisodes(int theMovieDbId, int seasonNumber, CancellationToken token, string langCode = "en")
|
||||||
|
{
|
||||||
|
var request = new Request($"/tv/{theMovieDbId}/season/{seasonNumber}", BaseUri, HttpMethod.Get);
|
||||||
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
request.AddQueryString("language", langCode);
|
||||||
|
AddRetry(request);
|
||||||
|
|
||||||
|
return await Api.Request<SeasonDetails>(request, token);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<Keyword>> SearchKeyword(string searchTerm)
|
public async Task<List<Keyword>> SearchKeyword(string searchTerm)
|
||||||
{
|
{
|
||||||
var request = new Request("search/keyword", BaseUri, HttpMethod.Get);
|
var request = new Request("search/keyword", BaseUri, HttpMethod.Get);
|
||||||
|
@ -330,7 +370,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return Api.Request<WatchProviders>(request, token);
|
return Api.Request<WatchProviders>(request, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddDiscoverMovieSettings(Request request)
|
private async Task AddDiscoverSettings(Request request)
|
||||||
{
|
{
|
||||||
var settings = await Settings;
|
var settings = await Settings;
|
||||||
request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower());
|
request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower());
|
||||||
|
|
|
@ -7,6 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj",
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9D30CCF8-A115-4EB7-A34D-07780D752789}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9D30CCF8-A115-4EB7-A34D-07780D752789}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
..\.azuredevops\pipelines\templates\build-steps.yml = ..\.azuredevops\pipelines\templates\build-steps.yml
|
..\.azuredevops\pipelines\templates\build-steps.yml = ..\.azuredevops\pipelines\templates\build-steps.yml
|
||||||
..\build.cake = ..\build.cake
|
..\build.cake = ..\build.cake
|
||||||
..\CHANGELOG.md = ..\CHANGELOG.md
|
..\CHANGELOG.md = ..\CHANGELOG.md
|
||||||
|
@ -121,7 +122,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Webhook", "Ombi.Ap
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.CloudService", "Ombi.Api.CloudService\Ombi.Api.CloudService.csproj", "{5DE40A66-B369-469E-8626-ECE23D9D8034}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.CloudService", "Ombi.Api.CloudService\Ombi.Api.CloudService.csproj", "{5DE40A66-B369-469E-8626-ECE23D9D8034}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.RottenTomatoes", "Ombi.Api.RottenTomatoes\Ombi.Api.RottenTomatoes.csproj", "{8F19C701-7881-4BC7-8BBA-B068A6B954AD}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.RottenTomatoes", "Ombi.Api.RottenTomatoes\Ombi.Api.RottenTomatoes.csproj", "{8F19C701-7881-4BC7-8BBA-B068A6B954AD}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
"options": {
|
"options": {
|
||||||
"aot": true,
|
"aot": true,
|
||||||
"progress": true,
|
"progress": true,
|
||||||
"extractCss": true,
|
|
||||||
"outputPath": "dist",
|
"outputPath": "dist",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
|
@ -65,7 +64,6 @@
|
||||||
"optimization": true,
|
"optimization": true,
|
||||||
"outputHashing": "all",
|
"outputHashing": "all",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"extractCss": true,
|
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
"aot": true,
|
"aot": true,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
|
@ -99,8 +97,7 @@
|
||||||
},
|
},
|
||||||
"hmr": {
|
"hmr": {
|
||||||
"hmr": true,
|
"hmr": true,
|
||||||
"browserTarget": "ombi:build:hmr",
|
"browserTarget": "ombi:build:hmr"
|
||||||
"hmrWarning": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,22 +9,22 @@
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^9.1.7",
|
"@angular/animations": "^11.2.6",
|
||||||
"@angular/cdk": "^9.2.3",
|
"@angular/cdk": "^11.2.5",
|
||||||
"@angular/common": "^9.1.7",
|
"@angular/common": "^11.2.6",
|
||||||
"@angular/compiler": "^9.1.7",
|
"@angular/compiler": "^11.2.6",
|
||||||
"@angular/core": "^9.1.7",
|
"@angular/core": "^11.2.6",
|
||||||
"@angular/forms": "^9.1.7",
|
"@angular/forms": "^11.2.6",
|
||||||
"@angular/localize": "^11.0.2",
|
"@angular/localize": "^11.2.6",
|
||||||
"@angular/material": "^9.2.3",
|
"@angular/material": "^11.2.5",
|
||||||
"@angular/platform-browser": "^9.1.7",
|
"@angular/platform-browser": "^11.2.6",
|
||||||
"@angular/platform-browser-dynamic": "^9.1.7",
|
"@angular/platform-browser-dynamic": "^11.2.6",
|
||||||
"@angular/platform-server": "^9.1.7",
|
"@angular/platform-server": "^11.2.6",
|
||||||
"@angular/router": "^9.1.7",
|
"@angular/router": "^11.2.6",
|
||||||
"@angularclass/hmr": "^2.1.3",
|
"@angularclass/hmr": "^2.1.3",
|
||||||
"@aspnet/signalr": "^1.1.0",
|
"@aspnet/signalr": "^1.1.0",
|
||||||
"@auth0/angular-jwt": "^2.1.0",
|
"@auth0/angular-jwt": "^2.1.0",
|
||||||
"@fortawesome/fontawesome-free": "^5.15.2",
|
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||||
"@fullcalendar/core": "^4.2.0",
|
"@fullcalendar/core": "^4.2.0",
|
||||||
"@fullcalendar/daygrid": "^4.4.0",
|
"@fullcalendar/daygrid": "^4.4.0",
|
||||||
"@fullcalendar/interaction": "^4.2.0",
|
"@fullcalendar/interaction": "^4.2.0",
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
"@yellowspot/ng-truncate": "^1.4.0",
|
"@yellowspot/ng-truncate": "^1.4.0",
|
||||||
"angular-bootstrap-md": "^7.5.4",
|
"angular-bootstrap-md": "^7.5.4",
|
||||||
"angular-router-loader": "^0.8.5",
|
"angular-router-loader": "^0.8.5",
|
||||||
"angularx-qrcode": "^2.1.0",
|
"angularx-qrcode": "^11.0.0",
|
||||||
"bootstrap": "^4.2.1",
|
"bootstrap": "^4.2.1",
|
||||||
"chart.js": "2.9.4",
|
"chart.js": "2.9.4",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
|
@ -52,26 +52,26 @@
|
||||||
"please-wait": "^0.0.5",
|
"please-wait": "^0.0.5",
|
||||||
"popper.js": "^1.14.3",
|
"popper.js": "^1.14.3",
|
||||||
"primeicons": "^4.0.0",
|
"primeicons": "^4.0.0",
|
||||||
"primeng": "^11.0.0",
|
"primeng": "^11.3.1",
|
||||||
"rxjs": "^6.5.2",
|
"rxjs": "^6.6.6",
|
||||||
"sass-recursive-map-merge": "^1.0.1",
|
"sass-recursive-map-merge": "^1.0.1",
|
||||||
"spinkit": "^1.2.5",
|
"spinkit": "^1.2.5",
|
||||||
"store": "^2.0.12",
|
"store": "^2.0.12",
|
||||||
"ts-md5": "^1.2.7",
|
"ts-md5": "^1.2.7",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
"tslint-angular": "^1.1.2",
|
"tslint-angular": "^1.1.2",
|
||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "~0.901.6",
|
"@angular-devkit/build-angular": "~0.1102.5",
|
||||||
"@angular/cli": "~9.1.6",
|
"@angular/cli": "~11.2.5",
|
||||||
"@angular/compiler-cli": "^9.1.7",
|
"@angular/compiler-cli": "^11.2.6",
|
||||||
"@angular/language-service": "^9.1.7",
|
"@angular/language-service": "^11.2.6",
|
||||||
"@types/jasmine": "~2.8.6",
|
"@types/jasmine": "~3.6.7",
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.8",
|
||||||
"@types/node": "^12.11.1",
|
"@types/node": "^14.14.35",
|
||||||
"codelyzer": "^5.1.2",
|
"codelyzer": "^6.0.1",
|
||||||
"typescript": "~3.8.3"
|
"typescript": "~4.1.5"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
|
|
|
@ -82,7 +82,7 @@ export class AppComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
window["loading_screen"].finish();
|
// window["loading_screen"].finish();
|
||||||
|
|
||||||
this.settingsService.getCustomization().subscribe(x => {
|
this.settingsService.getCustomization().subscribe(x => {
|
||||||
this.customizationSettings = x;
|
this.customizationSettings = x;
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class DiscoverActorComponent implements AfterViewInit {
|
||||||
public loadingFlag: boolean;
|
public loadingFlag: boolean;
|
||||||
|
|
||||||
public discoverResults: IDiscoverCardResult[] = [];
|
public discoverResults: IDiscoverCardResult[] = [];
|
||||||
|
|
||||||
constructor(private searchService: SearchV2Service,
|
constructor(private searchService: SearchV2Service,
|
||||||
private route: ActivatedRoute) {
|
private route: ActivatedRoute) {
|
||||||
this.route.params.subscribe((params: any) => {
|
this.route.params.subscribe((params: any) => {
|
||||||
|
|
|
@ -29,7 +29,8 @@ export class DiscoverCardComponent implements OnInit {
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
if (this.result.type == RequestType.tvShow) {
|
if (this.result.type == RequestType.tvShow) {
|
||||||
this.getExtraTvInfo();
|
this.fullyLoaded = true;
|
||||||
|
// this.getExtraTvInfo();
|
||||||
}
|
}
|
||||||
if (this.result.type == RequestType.movie) {
|
if (this.result.type == RequestType.movie) {
|
||||||
this.getExtraMovieInfo();
|
this.getExtraMovieInfo();
|
||||||
|
@ -44,15 +45,15 @@ export class DiscoverCardComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getExtraTvInfo() {
|
public async getExtraTvInfo() {
|
||||||
if (this.result.tvMovieDb) {
|
// if (this.result.tvMovieDb) {
|
||||||
this.tvSearchResult = await this.searchService.getTvInfoWithMovieDbId(+this.result.id);
|
this.tvSearchResult = await this.searchService.getTvInfoWithMovieDbId(+this.result.id);
|
||||||
} else {
|
// } else {
|
||||||
this.tvSearchResult = await this.searchService.getTvInfo(+this.result.id);
|
// this.tvSearchResult = await this.searchService.getTvInfo(+this.result.id);
|
||||||
}
|
// }
|
||||||
if (!this.tvSearchResult || this.tvSearchResult?.status.length > 0 && this.tvSearchResult?.status === "404") {
|
// if (!this.tvSearchResult || this.tvSearchResult?.status.length > 0 && this.tvSearchResult?.status === "404") {
|
||||||
this.hide = true;
|
// this.hide = true;
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.setTvDefaults(this.tvSearchResult);
|
this.setTvDefaults(this.tvSearchResult);
|
||||||
this.updateTvItem(this.tvSearchResult);
|
this.updateTvItem(this.tvSearchResult);
|
||||||
|
@ -172,8 +173,8 @@ export class DiscoverCardComponent implements OnInit {
|
||||||
private updateTvItem(updated: ISearchTvResultV2) {
|
private updateTvItem(updated: ISearchTvResultV2) {
|
||||||
this.result.title = updated.title;
|
this.result.title = updated.title;
|
||||||
this.result.id = updated.id;
|
this.result.id = updated.id;
|
||||||
this.result.available = updated.fullyAvailable || updated.partlyAvailable;
|
// this.result.available = updated.fullyAvailable || updated.partlyAvailable;
|
||||||
this.result.posterPath = updated.banner;
|
// this.result.posterPath = updated.banner;
|
||||||
this.result.requested = updated.requested;
|
this.result.requested = updated.requested;
|
||||||
this.result.url = updated.imdbId;
|
this.result.url = updated.imdbId;
|
||||||
this.result.overview = updated.overview;
|
this.result.overview = updated.overview;
|
||||||
|
|
|
@ -10,6 +10,7 @@ export enum DiscoverType {
|
||||||
Upcoming,
|
Upcoming,
|
||||||
Trending,
|
Trending,
|
||||||
Popular,
|
Popular,
|
||||||
|
RecentlyRequested,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -216,6 +217,8 @@ export class CarouselListComponent implements OnInit {
|
||||||
case DiscoverType.Upcoming:
|
case DiscoverType.Upcoming:
|
||||||
this.movies = await this.searchService.upcomingMoviesByPage(this.currentlyLoaded, this.amountToLoad);
|
this.movies = await this.searchService.upcomingMoviesByPage(this.currentlyLoaded, this.amountToLoad);
|
||||||
break
|
break
|
||||||
|
case DiscoverType.RecentlyRequested:
|
||||||
|
this.movies = await this.searchService.recentlyRequestedMoviesByPage(this.currentlyLoaded, this.amountToLoad);
|
||||||
}
|
}
|
||||||
this.currentlyLoaded += this.amountToLoad;
|
this.currentlyLoaded += this.amountToLoad;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +293,7 @@ export class CarouselListComponent implements OnInit {
|
||||||
this.tvShows.forEach(m => {
|
this.tvShows.forEach(m => {
|
||||||
tempResults.push({
|
tempResults.push({
|
||||||
available: m.available,
|
available: m.available,
|
||||||
posterPath: "../../../images/default_tv_poster.png",
|
posterPath: m.backdropPath ? `https://image.tmdb.org/t/p/w500/${m.backdropPath}` : "../../../images/default_tv_poster.png",
|
||||||
requested: m.requested,
|
requested: m.requested,
|
||||||
title: m.title,
|
title: m.title,
|
||||||
type: RequestType.tvShow,
|
type: RequestType.tvShow,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
<div class="small-middle-container">
|
<div class="small-middle-container">
|
||||||
<div *ngIf="loadingFlag" class="row justify-content-md-center top-spacing loading-spinner">
|
|
||||||
</div>
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>Popular</h2>
|
<h2>{{'Discovery.PopularTab' | translate}}</h2>
|
||||||
<div>
|
<div>
|
||||||
<carousel-list [id]="'popular'" [discoverType]="DiscoverType.Popular"></carousel-list>
|
<carousel-list [id]="'popular'" [discoverType]="DiscoverType.Popular"></carousel-list>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>Trending</h2>
|
<h2>{{'Discovery.TrendingTab' | translate}}</h2>
|
||||||
<div >
|
<div >
|
||||||
<carousel-list [id]="'trending'" [discoverType]="DiscoverType.Trending"></carousel-list>
|
<carousel-list [id]="'trending'" [discoverType]="DiscoverType.Trending"></carousel-list>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,10 +16,15 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>Upcoming</h2>
|
<h2>{{'Discovery.UpcomingTab' | translate}}</h2>
|
||||||
<div>
|
<div>
|
||||||
<carousel-list [id]="'upcoming'" [discoverType]="DiscoverType.Upcoming"></carousel-list>
|
<carousel-list [id]="'upcoming'" [discoverType]="DiscoverType.Upcoming"></carousel-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- <div class="section">
|
||||||
|
<h2>{{'Discovery.RecentlyRequestedTab' | translate}}</h2>
|
||||||
|
<div>
|
||||||
|
<carousel-list [id]="'recentlyRequested'" [discoverType]="DiscoverType.RecentlyRequested"></carousel-list>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
|
@ -1,344 +1,15 @@
|
||||||
import { Component, OnInit, Inject } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { SearchV2Service } from "../../../services";
|
|
||||||
import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces";
|
|
||||||
import { IDiscoverCardResult, DiscoverOption, DisplayOption } from "../../interfaces";
|
|
||||||
import { trigger, transition, style, animate } from "@angular/animations";
|
|
||||||
import { StorageService } from "../../../shared/storage/storage-service";
|
|
||||||
import { DOCUMENT } from "@angular/common";
|
|
||||||
import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2";
|
|
||||||
import { DiscoverType } from "../carousel-list/carousel-list.component";
|
import { DiscoverType } from "../carousel-list/carousel-list.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./discover.component.html",
|
templateUrl: "./discover.component.html",
|
||||||
styleUrls: ["./discover.component.scss"],
|
styleUrls: ["./discover.component.scss"],
|
||||||
animations: [
|
|
||||||
trigger('slideIn', [
|
|
||||||
transition(':enter', [
|
|
||||||
style({ transform: 'translateX(100%)' }),
|
|
||||||
animate('200ms ease-in', style({ transform: 'translateY(0%)' }))
|
|
||||||
])
|
|
||||||
])
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class DiscoverComponent implements OnInit {
|
export class DiscoverComponent {
|
||||||
|
|
||||||
public upcomingMovies: IDiscoverCardResult[] = [];
|
|
||||||
public trendingMovies: IDiscoverCardResult[] = [];
|
|
||||||
|
|
||||||
|
|
||||||
public discoverResults: IDiscoverCardResult[] = [];
|
|
||||||
public movies: ISearchMovieResult[] = [];
|
|
||||||
public tvShows: ISearchTvResult[] = [];
|
|
||||||
|
|
||||||
public discoverOptions: DiscoverOption = DiscoverOption.Combined;
|
|
||||||
public DiscoverType = DiscoverType;
|
public DiscoverType = DiscoverType;
|
||||||
public DiscoverOption = DiscoverOption;
|
|
||||||
public displayOption: DisplayOption = DisplayOption.Card;
|
|
||||||
public DisplayOption = DisplayOption;
|
|
||||||
|
|
||||||
public defaultTvPoster: string;
|
constructor() { }
|
||||||
|
|
||||||
public popularActive: boolean = true;
|
|
||||||
public trendingActive: boolean;
|
|
||||||
public upcomingActive: boolean;
|
|
||||||
|
|
||||||
public loadingFlag: boolean;
|
|
||||||
public scrollDisabled: boolean;
|
|
||||||
|
|
||||||
private amountToLoad = 14;
|
|
||||||
|
|
||||||
private contentLoaded: number;
|
|
||||||
private isScrolling: boolean = false;
|
|
||||||
private mediaTypeStorageKey = "DiscoverOptions";
|
|
||||||
private displayOptionsKey = "DiscoverDisplayOptions";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(private searchService: SearchV2Service,
|
|
||||||
private storageService: StorageService,
|
|
||||||
@Inject(DOCUMENT) private container: Document) { }
|
|
||||||
|
|
||||||
|
|
||||||
public async ngOnInit() {
|
|
||||||
this.loading()
|
|
||||||
// this.upcomingMovies = this.mapTvModel(await this.searchService.popularTvByPage(0, 14));
|
|
||||||
// this.trendingMovies = this.mapMovieModel(await this.searchService.popularMoviesByPage(0, 14));
|
|
||||||
this.finishLoading();
|
|
||||||
// const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey);
|
|
||||||
// if (localDiscoverOptions) {
|
|
||||||
// this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]];
|
|
||||||
// }
|
|
||||||
// const localDisplayOptions = +this.storageService.get(this.displayOptionsKey);
|
|
||||||
// if (localDisplayOptions) {
|
|
||||||
// this.displayOption = DisplayOption[DisplayOption[localDisplayOptions]];
|
|
||||||
// }
|
|
||||||
// this.scrollDisabled = true;
|
|
||||||
// switch (this.discoverOptions) {
|
|
||||||
// case DiscoverOption.Combined:
|
|
||||||
// this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
|
|
||||||
// this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
|
|
||||||
// break;
|
|
||||||
// case DiscoverOption.Movie:
|
|
||||||
// this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
|
|
||||||
// break;
|
|
||||||
// case DiscoverOption.Tv:
|
|
||||||
// this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.contentLoaded = this.amountToLoad;
|
|
||||||
|
|
||||||
// this.createInitialModel();
|
|
||||||
// this.scrollDisabled = false;
|
|
||||||
// if (!this.containerHasScrollBar()) {
|
|
||||||
// await this.onScroll();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onScroll() {
|
|
||||||
console.log("scrolled");
|
|
||||||
if (!this.contentLoaded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.isScrolling) {
|
|
||||||
this.isScrolling = true;
|
|
||||||
this.loading();
|
|
||||||
if (this.popularActive) {
|
|
||||||
switch (this.discoverOptions) {
|
|
||||||
case DiscoverOption.Combined:
|
|
||||||
this.movies = await this.searchService.popularMoviesByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
this.tvShows = await this.searchService.popularTvByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Movie:
|
|
||||||
this.movies = await this.searchService.popularMoviesByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Tv:
|
|
||||||
this.tvShows = await this.searchService.popularTvByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.trendingActive) {
|
|
||||||
switch (this.discoverOptions) {
|
|
||||||
case DiscoverOption.Combined:
|
|
||||||
this.movies = await this.searchService.nowPlayingMoviesByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
this.tvShows = await this.searchService.trendingTvByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Movie:
|
|
||||||
this.movies = await this.searchService.nowPlayingMoviesByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Tv:
|
|
||||||
this.tvShows = await this.searchService.trendingTvByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.upcomingActive) {
|
|
||||||
switch (this.discoverOptions) {
|
|
||||||
case DiscoverOption.Combined:
|
|
||||||
this.movies = await this.searchService.upcomingMoviesByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Movie:
|
|
||||||
this.movies = await this.searchService.upcomingMoviesByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Tv:
|
|
||||||
this.tvShows = await this.searchService.anticipatedTvByPage(this.contentLoaded, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.contentLoaded += 12;
|
|
||||||
|
|
||||||
this.createModel();
|
|
||||||
this.isScrolling = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async popular() {
|
|
||||||
this.clear();
|
|
||||||
this.scrollDisabled = true;
|
|
||||||
this.isScrolling = false;
|
|
||||||
this.contentLoaded = 12;
|
|
||||||
this.loading()
|
|
||||||
this.popularActive = true;
|
|
||||||
this.trendingActive = false;
|
|
||||||
this.upcomingActive = false;
|
|
||||||
switch (this.discoverOptions) {
|
|
||||||
case DiscoverOption.Combined:
|
|
||||||
this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
|
|
||||||
this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Movie:
|
|
||||||
this.movies = await this.searchService.popularMoviesByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Tv:
|
|
||||||
this.tvShows = await this.searchService.popularTvByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createModel();
|
|
||||||
this.scrollDisabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async trending() {
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
this.scrollDisabled = true;
|
|
||||||
this.isScrolling = false;
|
|
||||||
this.contentLoaded = 12;
|
|
||||||
this.loading()
|
|
||||||
this.popularActive = false;
|
|
||||||
this.trendingActive = true;
|
|
||||||
this.upcomingActive = false;
|
|
||||||
switch (this.discoverOptions) {
|
|
||||||
case DiscoverOption.Combined:
|
|
||||||
this.movies = await this.searchService.nowPlayingMoviesByPage(0, this.amountToLoad);
|
|
||||||
this.tvShows = await this.searchService.trendingTvByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Movie:
|
|
||||||
this.movies = await this.searchService.nowPlayingMoviesByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Tv:
|
|
||||||
this.tvShows = await this.searchService.trendingTvByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createModel();
|
|
||||||
this.scrollDisabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async upcoming() {
|
|
||||||
this.clear();
|
|
||||||
this.scrollDisabled = true;
|
|
||||||
this.isScrolling = false;
|
|
||||||
this.contentLoaded = 12;
|
|
||||||
this.loading()
|
|
||||||
this.popularActive = false;
|
|
||||||
this.trendingActive = false;
|
|
||||||
this.upcomingActive = true;
|
|
||||||
switch (this.discoverOptions) {
|
|
||||||
case DiscoverOption.Combined:
|
|
||||||
this.movies = await this.searchService.upcomingMoviesByPage(0, this.amountToLoad);
|
|
||||||
this.tvShows = await this.searchService.anticipatedTvByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Movie:
|
|
||||||
this.movies = await this.searchService.upcomingMoviesByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
case DiscoverOption.Tv:
|
|
||||||
this.tvShows = await this.searchService.anticipatedTvByPage(0, this.amountToLoad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createModel();
|
|
||||||
this.scrollDisabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async switchDiscoverMode(newMode: DiscoverOption) {
|
|
||||||
this.loading();
|
|
||||||
this.clear();
|
|
||||||
this.discoverOptions = newMode;
|
|
||||||
this.storageService.save(this.mediaTypeStorageKey, newMode.toString());
|
|
||||||
await this.ngOnInit();
|
|
||||||
this.finishLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
public changeView(view: DisplayOption) {
|
|
||||||
this.displayOption = view;
|
|
||||||
this.storageService.save(this.displayOptionsKey, view.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private createModel() {
|
|
||||||
const tempResults = <IDiscoverCardResult[]>[];
|
|
||||||
|
|
||||||
// switch (this.discoverOptions) {
|
|
||||||
// case DiscoverOption.Combined:
|
|
||||||
// tempResults.push(...this.mapMovieModel());
|
|
||||||
// tempResults.push(...this.mapTvModel());
|
|
||||||
// break;
|
|
||||||
// case DiscoverOption.Movie:
|
|
||||||
// tempResults.push(...this.mapMovieModel());
|
|
||||||
// break;
|
|
||||||
// case DiscoverOption.Tv:
|
|
||||||
// tempResults.push(...this.mapTvModel());
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.shuffle(tempResults);
|
|
||||||
this.discoverResults.push(...tempResults);
|
|
||||||
|
|
||||||
this.finishLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapMovieModel(movies: ISearchMovieResult[]): IDiscoverCardResult[] {
|
|
||||||
const tempResults = <IDiscoverCardResult[]>[];
|
|
||||||
movies.forEach(m => {
|
|
||||||
tempResults.push({
|
|
||||||
available: m.available,
|
|
||||||
posterPath: m.posterPath ? `https://image.tmdb.org/t/p/w500/${m.posterPath}` : "../../../images/default_movie_poster.png",
|
|
||||||
requested: m.requested,
|
|
||||||
title: m.title,
|
|
||||||
type: RequestType.movie,
|
|
||||||
id: m.id,
|
|
||||||
url: `http://www.imdb.com/title/${m.imdbId}/`,
|
|
||||||
rating: m.voteAverage,
|
|
||||||
overview: m.overview,
|
|
||||||
approved: m.approved,
|
|
||||||
imdbid: m.imdbId,
|
|
||||||
denied: false,
|
|
||||||
background: m.backdropPath
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return tempResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapTvModel(tv: ISearchTvResult[]): IDiscoverCardResult[] {
|
|
||||||
const tempResults = <IDiscoverCardResult[]>[];
|
|
||||||
tv.forEach(m => {
|
|
||||||
tempResults.push({
|
|
||||||
available: m.available,
|
|
||||||
posterPath: "../../../images/default_tv_poster.png",
|
|
||||||
requested: m.requested,
|
|
||||||
title: m.title,
|
|
||||||
type: RequestType.tvShow,
|
|
||||||
id: m.id,
|
|
||||||
url: undefined,
|
|
||||||
rating: +m.rating,
|
|
||||||
overview: m.overview,
|
|
||||||
approved: m.approved || m.partlyAvailable,
|
|
||||||
imdbid: m.imdbId,
|
|
||||||
denied: false,
|
|
||||||
background: m.background
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return tempResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createInitialModel() {
|
|
||||||
this.clear();
|
|
||||||
this.createModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private shuffle(discover: IDiscoverCardResult[]): IDiscoverCardResult[] {
|
|
||||||
for (let i = discover.length - 1; i > 0; i--) {
|
|
||||||
const j = Math.floor(Math.random() * (i + 1));
|
|
||||||
[discover[i], discover[j]] = [discover[j], discover[i]];
|
|
||||||
}
|
|
||||||
return discover;
|
|
||||||
}
|
|
||||||
|
|
||||||
private loading() {
|
|
||||||
this.loadingFlag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private clear() {
|
|
||||||
this.discoverResults = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private finishLoading() {
|
|
||||||
this.loadingFlag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private containerHasScrollBar(): boolean {
|
|
||||||
return this.container.documentElement.scrollHeight > this.container.documentElement.clientHeight;
|
|
||||||
// div.scrollHeight > div.clientHeight;
|
|
||||||
// this.container.documentElement.scrollHeight > (window.innerHeight + window.pageYOffset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,6 @@ export const components: any[] = [
|
||||||
CarouselListComponent,
|
CarouselListComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
export const entryComponents: any[] = [
|
|
||||||
DiscoverCardDetailsComponent
|
|
||||||
];
|
|
||||||
|
|
||||||
export const providers: any[] = [
|
export const providers: any[] = [
|
||||||
SearchService,
|
SearchService,
|
||||||
MatDialog,
|
MatDialog,
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
<div *ngIf="loadingFlag" class="row justify-content-md-center top-spacing loading-spinner">
|
<div *ngIf="loadingFlag" class="row justify-content-md-center top-spacing loading-spinner">
|
||||||
<mat-spinner [color]="'accent'"></mat-spinner>
|
<mat-spinner [color]="'accent'"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="discoverResults && discoverResults.length > 0" class="row full-height discoverResults col">
|
<div *ngIf="discoverResults && discoverResults.length > 0" class="row full-height discoverResults col" >
|
||||||
<div class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults">
|
<div id="searchResults" class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults" data-test="searchResultsCount" attr.search-count="{{discoverResults.length}}">
|
||||||
<discover-card [result]="result"></discover-card>
|
<discover-card [result]="result"></discover-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!discoverResults || discoverResults.length === 0">
|
<div *ngIf="!discoverResults || discoverResults.length === 0">
|
||||||
<div class="row justify-content-md-center top-spacing loading-spinner">
|
<div class="row justify-content-md-center top-spacing loading-spinner">
|
||||||
<h1> {{'Discovery.NoSearch' | translate}} <i class="far fa-frown" aria-hidden="true"></i></h1>
|
<h1 id="noSearchResult"> {{'Discovery.NoSearch' | translate}} <i class="far fa-frown" aria-hidden="true"></i></h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -87,7 +87,7 @@ export class DiscoverSearchResultsComponent implements OnInit {
|
||||||
id: m.id,
|
id: m.id,
|
||||||
url: "",
|
url: "",
|
||||||
rating: 0,
|
rating: 0,
|
||||||
overview: "",
|
overview: m.overview,
|
||||||
approved: false,
|
approved: false,
|
||||||
imdbid: "",
|
imdbid: "",
|
||||||
denied: false,
|
denied: false,
|
||||||
|
|
|
@ -24,9 +24,6 @@ import * as fromComponents from './components';
|
||||||
declarations: [
|
declarations: [
|
||||||
...fromComponents.components
|
...fromComponents.components
|
||||||
],
|
],
|
||||||
entryComponents: [
|
|
||||||
...fromComponents.entryComponents
|
|
||||||
],
|
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
],
|
],
|
||||||
|
|
|
@ -28,6 +28,7 @@ export interface IUsersModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INavBar {
|
export interface INavBar {
|
||||||
|
id: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
faIcon: string;
|
faIcon: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -100,6 +100,7 @@ export interface IBaseRequest {
|
||||||
export interface ITvRequests {
|
export interface ITvRequests {
|
||||||
id: number;
|
id: number;
|
||||||
imdbId: string;
|
imdbId: string;
|
||||||
|
externalProviderId: number;
|
||||||
rootFolder: number;
|
rootFolder: number;
|
||||||
overview: string;
|
overview: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -111,7 +112,8 @@ export interface ITvRequests {
|
||||||
qualityOverride: number;
|
qualityOverride: number;
|
||||||
background: any;
|
background: any;
|
||||||
totalSeasons: number;
|
totalSeasons: number;
|
||||||
tvDbId: number;
|
tvDbId: number; // NO LONGER USED
|
||||||
|
|
||||||
open: boolean; // THIS IS FOR THE UI
|
open: boolean; // THIS IS FOR THE UI
|
||||||
|
|
||||||
// For UI display
|
// For UI display
|
||||||
|
@ -146,6 +148,7 @@ export enum OrderType {
|
||||||
|
|
||||||
export interface INewSeasonRequests {
|
export interface INewSeasonRequests {
|
||||||
id: number;
|
id: number;
|
||||||
|
overview: string;
|
||||||
seasonNumber: number;
|
seasonNumber: number;
|
||||||
episodes: IEpisodesRequests[];
|
episodes: IEpisodesRequests[];
|
||||||
seasonAvailable: boolean;
|
seasonAvailable: boolean;
|
||||||
|
|
|
@ -40,6 +40,7 @@ export interface IMultiSearchResult {
|
||||||
mediaType: string;
|
mediaType: string;
|
||||||
title: string;
|
title: string;
|
||||||
poster: string;
|
poster: string;
|
||||||
|
overview: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILanguageRefine {
|
export interface ILanguageRefine {
|
||||||
|
|
|
@ -36,16 +36,21 @@ export interface ISearchTvResult {
|
||||||
subscribed: boolean;
|
subscribed: boolean;
|
||||||
showSubscribe: boolean;
|
showSubscribe: boolean;
|
||||||
fullyAvailable: boolean;
|
fullyAvailable: boolean;
|
||||||
|
backdropPath: string;
|
||||||
partlyAvailable: boolean;
|
partlyAvailable: boolean;
|
||||||
background: any;
|
background: any;
|
||||||
open: boolean; // THIS IS FOR THE UI
|
open: boolean; // THIS IS FOR THE UI
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITvRequestViewModel {
|
export interface ITvRequestViewModelV2 extends ITvRequestViewModelBase {
|
||||||
|
theMovieDbId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ITvRequestViewModelBase {
|
||||||
requestAll: boolean;
|
requestAll: boolean;
|
||||||
firstSeason: boolean;
|
firstSeason: boolean;
|
||||||
latestSeason: boolean;
|
latestSeason: boolean;
|
||||||
tvDbId: number;
|
|
||||||
seasons: ISeasonsViewModel[];
|
seasons: ISeasonsViewModel[];
|
||||||
requestOnBehalf: string | undefined;
|
requestOnBehalf: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { INewSeasonRequests, RequestType } from "./IRequestModel";
|
import { INewSeasonRequests, RequestType } from "./IRequestModel";
|
||||||
|
import { IExternalIds, IGenresViewModel, IKeywords } from "./ISearchMovieResultV2";
|
||||||
|
|
||||||
export interface ISearchTvResultV2 {
|
export interface ISearchTvResultV2 {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -11,7 +12,7 @@ export interface ISearchTvResultV2 {
|
||||||
firstAired: string;
|
firstAired: string;
|
||||||
networkId: string;
|
networkId: string;
|
||||||
runtime: string;
|
runtime: string;
|
||||||
genre: string[];
|
genres: IGenresViewModel[],
|
||||||
overview: string;
|
overview: string;
|
||||||
lastUpdated: number;
|
lastUpdated: number;
|
||||||
airsDayOfWeek: string;
|
airsDayOfWeek: string;
|
||||||
|
@ -21,7 +22,6 @@ export interface ISearchTvResultV2 {
|
||||||
siteRating: number;
|
siteRating: number;
|
||||||
trailer: string;
|
trailer: string;
|
||||||
homepage: string;
|
homepage: string;
|
||||||
certification: string;
|
|
||||||
seasonRequests: INewSeasonRequests[];
|
seasonRequests: INewSeasonRequests[];
|
||||||
requestAll: boolean;
|
requestAll: boolean;
|
||||||
approved: boolean;
|
approved: boolean;
|
||||||
|
@ -30,6 +30,7 @@ export interface ISearchTvResultV2 {
|
||||||
plexUrl: string;
|
plexUrl: string;
|
||||||
embyUrl: string;
|
embyUrl: string;
|
||||||
jellyfinUrl: string;
|
jellyfinUrl: string;
|
||||||
|
tagline: string;
|
||||||
quality: string;
|
quality: string;
|
||||||
firstSeason: boolean;
|
firstSeason: boolean;
|
||||||
latestSeason: boolean;
|
latestSeason: boolean;
|
||||||
|
@ -38,8 +39,10 @@ export interface ISearchTvResultV2 {
|
||||||
showSubscribe: boolean;
|
showSubscribe: boolean;
|
||||||
fullyAvailable: boolean;
|
fullyAvailable: boolean;
|
||||||
partlyAvailable: boolean;
|
partlyAvailable: boolean;
|
||||||
|
externalIds: IExternalIds;
|
||||||
network: INetwork;
|
network: INetwork;
|
||||||
images: IImagesV2;
|
images: IImagesV2;
|
||||||
|
keywords: IKeywords;
|
||||||
cast: ICast[];
|
cast: ICast[];
|
||||||
crew: ICrew[];
|
crew: ICrew[];
|
||||||
requestId: number;
|
requestId: number;
|
||||||
|
|
|
@ -45,15 +45,6 @@ export const components: any[] = [
|
||||||
TvRequestGridComponent,
|
TvRequestGridComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const entryComponents: any[] = [
|
|
||||||
YoutubeTrailerComponent,
|
|
||||||
DenyDialogComponent,
|
|
||||||
MovieAdvancedOptionsComponent,
|
|
||||||
TvAdvancedOptionsComponent,
|
|
||||||
NewIssueComponent,
|
|
||||||
RequestBehalfComponent,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const providers: any[] = [
|
export const providers: any[] = [
|
||||||
SearchService,
|
SearchService,
|
||||||
RequestService,
|
RequestService,
|
||||||
|
|
|
@ -42,31 +42,31 @@
|
||||||
<div class="details-button-container">
|
<div class="details-button-container">
|
||||||
<div class="col-12 media-row">
|
<div class="col-12 media-row">
|
||||||
<span *ngIf="movie.available">
|
<span *ngIf="movie.available">
|
||||||
<a *ngIf="movie.plexUrl" href="{{movie.plexUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn plex">
|
<a id="viewOnPlexButton" *ngIf="movie.plexUrl" href="{{movie.plexUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn plex">
|
||||||
{{'Search.ViewOnPlex' | translate}}
|
{{'Search.ViewOnPlex' | translate}}
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="movie.embyUrl" href="{{movie.embyUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn emby">
|
<a id="viewOnEmbyButton" *ngIf="movie.embyUrl" href="{{movie.embyUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn emby">
|
||||||
{{'Search.ViewOnEmby' | translate}}
|
{{'Search.ViewOnEmby' | translate}}
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="movie.jellyfinUrl" href="{{movie.jellyfinUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn jellyfinUrl">
|
<a id="viewOnJellyfinButton" *ngIf="movie.jellyfinUrl" href="{{movie.jellyfinUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn jellyfinUrl">
|
||||||
{{'Search.ViewOnJellyfin' | translate}}
|
{{'Search.ViewOnJellyfin' | translate}}
|
||||||
<i class="far fa-play-circle fa-2x"></i>
|
<i class="far fa-play-circle fa-2x"></i>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<button mat-raised-button class="btn-green btn-spacing" *ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{
|
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn" *ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{
|
||||||
'Common.Available' | translate }}</button>
|
'Common.Available' | translate }}</button>
|
||||||
<span *ngIf="!movie.available">
|
<span *ngIf="!movie.available">
|
||||||
<span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
|
<span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
|
||||||
<ng-template #requestedBtn>
|
<ng-template #requestedBtn>
|
||||||
<button mat-raised-button *ngIf="!hasRequest || hasRequest && movieRequest && !movieRequest.denied" class="btn-spacing" color="warn" [disabled]>
|
<button id="requestedBtn" mat-raised-button *ngIf="!hasRequest || hasRequest && movieRequest && !movieRequest.denied" class="btn-spacing" color="warn" [disabled]>
|
||||||
<i class="fas fa-check"></i>
|
<i class="fas fa-check"></i>
|
||||||
{{ 'Common.Requested' | translate }}
|
{{ 'Common.Requested' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #notRequestedBtn>
|
<ng-template #notRequestedBtn>
|
||||||
<button mat-raised-button class="btn-spacing" color="primary" (click)="request()">
|
<button id="requestBtn" mat-raised-button class="btn-spacing" color="primary" (click)="request()">
|
||||||
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
|
||||||
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
|
||||||
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
|
||||||
|
@ -75,26 +75,26 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="isAdmin && hasRequest">
|
<span *ngIf="isAdmin && hasRequest">
|
||||||
<button *ngIf="!movie.approved" (click)="approve()" mat-raised-button class="btn-spacing" color="accent">
|
<button id="approveBtn" *ngIf="!movie.approved" (click)="approve()" mat-raised-button class="btn-spacing" color="accent">
|
||||||
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="!movie.available" (click)="markAvailable()" mat-raised-button class="btn-spacing"
|
<button id="markAvailableBtn" *ngIf="!movie.available" (click)="markAvailable()" mat-raised-button class="btn-spacing"
|
||||||
color="accent">
|
color="accent">
|
||||||
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button *ngIf="movieRequest && !movieRequest.denied && !movie.available" mat-raised-button class="btn-spacing" color="warn" (click)="deny()">
|
<button id="denyBtn" *ngIf="movieRequest && !movieRequest.denied && !movie.available" mat-raised-button class="btn-spacing" color="warn" (click)="deny()">
|
||||||
<i class="fas fa-times"></i> {{'Requests.Deny' | translate }}
|
<i class="fas fa-times"></i> {{'Requests.Deny' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="movieRequest && movieRequest.denied" [matTooltip]="movieRequest.deniedReason" mat-raised-button class="btn-spacing" color="warn">
|
<button id="deniedButton" *ngIf="movieRequest && movieRequest.denied" [matTooltip]="movieRequest.deniedReason" mat-raised-button class="btn-spacing" color="warn">
|
||||||
<i class="fas fa-times"></i> {{'MediaDetails.Denied' | translate }}
|
<i class="fas fa-times"></i> {{'MediaDetails.Denied' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<button mat-raised-button class="btn-spacing" color="danger" (click)="issue()" *ngIf="issuesEnabled">
|
<button id="reportIssueBtn" mat-raised-button class="btn-spacing" color="danger" (click)="issue()" *ngIf="issuesEnabled">
|
||||||
<i class="fas fa-exclamation"></i> {{'Requests.ReportIssue' | translate }}
|
<i class="fas fa-exclamation"></i> {{'Requests.ReportIssue' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="movie.belongsToCollection" [routerLink]="'/discover/collection/' + movie.belongsToCollection.id" mat-raised-button class="btn-spacing">
|
<button id="viewCollectionBtn" *ngIf="movie.belongsToCollection" [routerLink]="'/discover/collection/' + movie.belongsToCollection.id" mat-raised-button class="btn-spacing">
|
||||||
<i class="fas fa-list"></i> {{'MediaDetails.ViewCollection' | translate}}
|
<i class="fas fa-list"></i> {{'MediaDetails.ViewCollection' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -107,10 +107,8 @@ export class MovieDetailsComponent {
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
this.movieRequest.denied = result;
|
this.movieRequest.denied = result.denied;
|
||||||
if (this.movieRequest.denied) {
|
this.movieRequest.deniedReason = result.reason;
|
||||||
this.movie.approved = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<div class="rating medium-font">
|
<div class="rating medium-font">
|
||||||
<span *ngIf="movie.voteAverage"
|
<span *ngIf="movie.voteAverage"
|
||||||
matTooltip="{{'MediaDetails.Votes' | translate }} {{movie.voteCount | thousandShort: 1}}">
|
matTooltip="{{'MediaDetails.Votes' | translate }} {{movie.voteCount | thousandShort: 1}}">
|
||||||
<img class="rating-small" src="{{baseUrl}}/images/tmdb-logo.svg"> {{movie.voteAverage | number:'1.0-1'}}/10
|
<img class="rating-small" src="{{baseUrl}}images/tmdb-logo.svg"> {{movie.voteAverage | number:'1.0-1'}}/10
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="ratings?.critics_rating && ratings?.critics_score">
|
<span *ngIf="ratings?.critics_rating && ratings?.critics_score">
|
||||||
<img class="rating-small"
|
<img class="rating-small"
|
||||||
src="{{baseUrl}}/images/{{ratings.critics_rating === 'Rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg'}}">
|
src="{{baseUrl}}images/{{ratings.critics_rating === 'Rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg'}}">
|
||||||
{{ratings.critics_score}}%
|
{{ratings.critics_score}}%
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="ratings?.audience_rating && ratings?.audience_score">
|
<span *ngIf="ratings?.audience_rating && ratings?.audience_score">
|
||||||
<img class="rating-small"
|
<img class="rating-small"
|
||||||
src="{{baseUrl}}/images/{{ratings.audience_rating === 'Upright' ? 'rotten-audience-fresh.svg' : 'rotten-audience-rotten.svg'}}">
|
src="{{baseUrl}}images/{{ratings.audience_rating === 'Upright' ? 'rotten-audience-fresh.svg' : 'rotten-audience-rotten.svg'}}">
|
||||||
{{ratings.audience_score}}%
|
{{ratings.audience_score}}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,11 @@
|
||||||
{{request.requestedDate | date}}
|
{{request.requestedDate | date}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="request && request.denied">
|
||||||
|
<span class="label">{{'Requests.DeniedReason' | translate }}: </span>
|
||||||
|
<span id="deniedReasonInfo">{{request.deniedReason}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div *ngIf="movie.quality">
|
<div *ngIf="movie.quality">
|
||||||
<span class="label">{{'MediaDetails.Quality' | translate }}:</span>
|
<span class="label">{{'MediaDetails.Quality' | translate }}:</span>
|
||||||
|
|
|
@ -5,22 +5,21 @@
|
||||||
<ng-template let-item pTemplate="item">
|
<ng-template let-item pTemplate="item">
|
||||||
<div class="row justify-content-md-center mat-card mat-card-flat carousel-item">
|
<div class="row justify-content-md-center mat-card mat-card-flat carousel-item">
|
||||||
<div class="bottom-space">
|
<div class="bottom-space">
|
||||||
<a *ngIf="item.profile_path" [routerLink]="'/discover/actor/' + item.id">
|
<a *ngIf="item.image" [routerLink]="'/discover/actor/' + item.id">
|
||||||
<img class="cast-profile-img" src="https://image.tmdb.org/t/p/w300/{{item.profile_path}}">
|
<img class="cast-profile-img" src="https://image.tmdb.org/t/p/w300{{item.image}}">
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="item.character?.image?.medium" [routerLink]="'/discover/actor/' + item.person.id">
|
<a *ngIf="item.profile_path" [routerLink]="'/discover/actor/' + item.id">
|
||||||
<img class="cast-profile-img" [src]="item.character.image.medium">
|
<img class="cast-profile-img" src="https://image.tmdb.org/t/p/w300{{item.profile_path}}">
|
||||||
</a>
|
</a>
|
||||||
<!-- TODO get profile image default -->
|
<!-- TODO get profile image default -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<span *ngIf="item.name"><strong>{{item.name}}</strong></span>
|
<span *ngIf="item.name"><strong>{{item.name}}</strong></span>
|
||||||
<span *ngIf="item.person?.name"><strong>{{item.person.name}}</strong></span>
|
<span *ngIf="item.person"><strong>{{item.person}}</strong></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<span *ngIf="!item.character?.name"><small>{{item.character}}</small></span>
|
<span *ngIf="item.character"><small>{{item.character}}</small></span>
|
||||||
<span *ngIf="item.character.name"><small>{{item.character.name}}</small></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<h1 mat-dialog-title>{{ 'Requests.DenyReason' | translate}}</h1>
|
<h1 mat-dialog-title>{{ 'Requests.DenyReason' | translate}}</h1>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput [(ngModel)]="denyReason">
|
<input id="denyInput" matInput [(ngModel)]="denyReason">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions>
|
<div mat-dialog-actions>
|
||||||
<button mat-raised-button (click)="onNoClick()" [mat-dialog-close]="data.denied">Cancel</button>
|
<button mat-raised-button (click)="onNoClick()" [mat-dialog-close]="data.denied">{{ 'Common.Cancel' | translate }}</button>
|
||||||
<button mat-raised-button (click)="deny()" color="danger" [mat-dialog-close]="data.denied" cdkFocusInitial>Deny</button>
|
<button mat-raised-button id="denyButton" (click)="deny()" color="danger" [mat-dialog-close]="data.denied" cdkFocusInitial>{{ 'Requests.Deny' | translate}}</button>
|
||||||
</div>
|
</div>
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Inject, Output, EventEmitter } from "@angular/core";
|
import { Component, Inject } from "@angular/core";
|
||||||
import { IDenyDialogData } from "../interfaces/interfaces";
|
import { IDenyDialogData } from "../interfaces/interfaces";
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
||||||
import { RequestService, MessageService } from "../../../../services";
|
import { RequestService, MessageService } from "../../../../services";
|
||||||
|
@ -36,8 +36,8 @@ export class DenyDialogComponent {
|
||||||
this.messageService.send(result.errorMessage, "Ok");
|
this.messageService.send(result.errorMessage, "Ok");
|
||||||
this.data.denied = false;
|
this.data.denied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dialogRef.close();
|
this.dialogRef.close({denied: this.data.denied, reason: this.denyReason});
|
||||||
}
|
}
|
||||||
|
|
||||||
onNoClick(): void {
|
onNoClick(): void {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<div class="social-icons-container-inner">
|
<div class="social-icons-container-inner">
|
||||||
|
|
||||||
<a *ngIf="homepage" class="media-icons" href="{{homepage}}" target="_blank">
|
<a *ngIf="homepage" class="media-icons" href="{{homepage}}" target="_blank">
|
||||||
<i matTooltip="Homepage" class="sfa-home fa-2x grow-social"></i>
|
<i matTooltip="Homepage" class="sfa-home fa-2x grow-social"></i>
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="theMoviedbId" href="https://www.themoviedb.org/movie/{{theMoviedbId}}" class="media-icons"
|
<a *ngIf="theMoviedbId" href="https://www.themoviedb.org/{{type === RequestType.movie ? 'movie' : 'tv'}}/{{theMoviedbId}}" class="media-icons"
|
||||||
target="_blank">
|
target="_blank">
|
||||||
<i matTooltip="The Movie DB" class="fas fa-film fa-2x grow-social"></i>
|
<i matTooltip="The Movie DB" class="fas fa-film fa-2x grow-social"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div
|
<div
|
||||||
class="mobile-top-text">
|
class="mobile-top-text">
|
||||||
<h1 class="large-text">{{title}} <span *ngIf="releaseDate" class="grey-text">
|
<h1 id="mediaTitle" class="large-text">{{title}} <span *ngIf="releaseDate" class="grey-text">
|
||||||
({{releaseDate | amLocal | amDateFormat: 'YYYY'}})</span>
|
({{releaseDate | amLocal | amDateFormat: 'YYYY'}})</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Inject, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
|
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="rating medium-font">
|
<div class="rating medium-font">
|
||||||
<span *ngIf="tv.rating">
|
<span *ngIf="tv.rating">
|
||||||
<img class="rating-small" src="{{baseUrl}}/images/tvm-logo.png"> {{tv.rating}}/10
|
<img class="rating-small" src="{{baseUrl}}/images/tmdb-logo.svg"> {{tv.rating * 10}}%
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="ratings?.score && ratings?.class">
|
<span *ngIf="ratings?.score && ratings?.class">
|
||||||
<img class="rating-small" src="{{baseUrl}}/images/{{ratings.class === 'rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg'}}"> {{ratings.score}}%
|
<img class="rating-small" src="{{baseUrl}}/images/{{ratings.class === 'rotten' ? 'rotten-rotten.svg' : 'rotten-fresh.svg'}}"> {{ratings.score}}%
|
||||||
|
@ -56,13 +56,27 @@
|
||||||
{{tv.network.name}}
|
{{tv.network.name}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="tv.genre">
|
|
||||||
|
<div class="genre-button-container" *ngIf="tv.genres">
|
||||||
<span class="label">{{'MediaDetails.Genres' | translate }}:</span>
|
<span class="label">{{'MediaDetails.Genres' | translate }}:</span>
|
||||||
<div>
|
<div>
|
||||||
<span *ngFor="let genre of tv.genre">
|
<mat-chip-list>
|
||||||
{{genre}} |
|
<mat-chip selected *ngFor="let genre of tv.genres">
|
||||||
</span>
|
{{genre.name}}
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div class="keyword-button-container" *ngIf="tv?.keywords?.keywordsValue?.length > 0">
|
||||||
|
<span class="label">{{'MediaDetails.Keywords' | translate }}:</span>
|
||||||
|
<mat-chip-list>
|
||||||
|
<mat-chip selected *ngFor="let keyword of tv.keywords.keywordsValue">
|
||||||
|
{{keyword.name}}
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class TvInformationPanelComponent implements OnInit {
|
||||||
this.searchService.getRottenTvRatings(this.tv.title, +this.tv.firstAired.toString().substring(0,4))
|
this.searchService.getRottenTvRatings(this.tv.title, +this.tv.firstAired.toString().substring(0,4))
|
||||||
.subscribe(x => this.ratings = x);
|
.subscribe(x => this.ratings = x);
|
||||||
|
|
||||||
this.searchService.getTvStreams(+this.tv.theTvDbId, this.tv.id).subscribe(x => this.streams = x);
|
this.searchService.getTvStreams(+this.tv.id ).subscribe(x => this.streams = x);
|
||||||
this.tv.seasonRequests.forEach(season => {
|
this.tv.seasonRequests.forEach(season => {
|
||||||
this.totalEpisodes = this.totalEpisodes + season.episodes.length;
|
this.totalEpisodes = this.totalEpisodes + season.episodes.length;
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,13 @@
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<mat-card *ngIf="season.overview" class="mat-elevation-z8">
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{season.overview}}</p>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
|
||||||
<table mat-table [dataSource]="season.episodes" class="mat-elevation-z8">
|
<table mat-table [dataSource]="season.episodes" class="mat-elevation-z8">
|
||||||
|
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
import { IChildRequests, IEpisodesRequests, INewSeasonRequests, ISeasonsViewModel, ITvRequestViewModel, RequestType } from "../../../../../interfaces";
|
import { IChildRequests, IEpisodesRequests, INewSeasonRequests, ISeasonsViewModel, ITvRequestViewModelV2, RequestType } from "../../../../../interfaces";
|
||||||
import { RequestService } from "../../../../../services/request.service";
|
import { RequestService } from "../../../../../services/request.service";
|
||||||
import { MessageService } from "../../../../../services";
|
import { MessageService } from "../../../../../services";
|
||||||
import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component";
|
import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component";
|
||||||
import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2";
|
import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2";
|
||||||
import { MatDialog } from "@angular/material/dialog";
|
import { MatDialog } from "@angular/material/dialog";
|
||||||
import { SelectionModel } from "@angular/cdk/collections";
|
import { SelectionModel } from "@angular/cdk/collections";
|
||||||
|
import { RequestServiceV2 } from "../../../../../services/requestV2.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./tv-request-grid.component.html",
|
templateUrl: "./tv-request-grid.component.html",
|
||||||
|
@ -24,7 +25,7 @@ export class TvRequestGridComponent {
|
||||||
|
|
||||||
public displayedColumns: string[] = ['select', 'number', 'title', 'airDate', 'status'];
|
public displayedColumns: string[] = ['select', 'number', 'title', 'airDate', 'status'];
|
||||||
|
|
||||||
constructor(private requestService: RequestService, private notificationService: MessageService,
|
constructor(private requestService: RequestService, private requestServiceV2: RequestServiceV2, private notificationService: MessageService,
|
||||||
private dialog: MatDialog) {
|
private dialog: MatDialog) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,8 +40,8 @@ export class TvRequestGridComponent {
|
||||||
|
|
||||||
this.tv.requested = true;
|
this.tv.requested = true;
|
||||||
|
|
||||||
const viewModel = <ITvRequestViewModel>{
|
const viewModel = <ITvRequestViewModelV2>{
|
||||||
firstSeason: this.tv.firstSeason, latestSeason: this.tv.latestSeason, requestAll: this.tv.requestAll, tvDbId: this.tv.id,
|
firstSeason: this.tv.firstSeason, latestSeason: this.tv.latestSeason, requestAll: this.tv.requestAll, theMovieDbId: this.tv.id,
|
||||||
requestOnBehalf: null
|
requestOnBehalf: null
|
||||||
};
|
};
|
||||||
viewModel.seasons = [];
|
viewModel.seasons = [];
|
||||||
|
@ -58,12 +59,13 @@ export class TvRequestGridComponent {
|
||||||
viewModel.seasons.push(seasonsViewModel);
|
viewModel.seasons.push(seasonsViewModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
const requestResult = await this.requestService.requestTv(viewModel).toPromise();
|
const requestResult = await this.requestServiceV2.requestTv(viewModel).toPromise();
|
||||||
|
|
||||||
if (requestResult.result) {
|
if (requestResult.result) {
|
||||||
this.notificationService.send(
|
this.notificationService.send(
|
||||||
`Request for ${this.tv.title} has been added successfully`);
|
`Request for ${this.tv.title} has been added successfully`);
|
||||||
|
|
||||||
|
debugger;
|
||||||
this.selection.clear();
|
this.selection.clear();
|
||||||
|
|
||||||
if (this.tv.firstSeason) {
|
if (this.tv.firstSeason) {
|
||||||
|
@ -80,7 +82,7 @@ export class TvRequestGridComponent {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.requestLatestSeason) {
|
if (this.tv.latestSeason) {
|
||||||
this.tv.seasonRequests[this.tv.seasonRequests.length - 1].episodes.forEach(ep => {
|
this.tv.seasonRequests[this.tv.seasonRequests.length - 1].episodes.forEach(ep => {
|
||||||
ep.requested = true;
|
ep.requested = true;
|
||||||
ep.requestStatus = "Common.PendingApproval";
|
ep.requestStatus = "Common.PendingApproval";
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<mat-accordion class="mat-elevation-z8">
|
<mat-accordion class="mat-elevation-z8">
|
||||||
|
|
||||||
<mat-expansion-panel *ngFor="let request of tvRequest">
|
<mat-expansion-panel *ngFor="let request of tvRequest">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
|
|
|
@ -15,12 +15,15 @@
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<top-banner [background]="tv.background" [available]="tv.available" [title]="tv.title"
|
<top-banner [background]="tv.background" [available]="tv.available" [title]="tv.title"
|
||||||
[releaseDate]="tv.firstAired" [tagline]="tv.certification"></top-banner>
|
[releaseDate]="tv.firstAired" [tagline]="tv.tagline"></top-banner>
|
||||||
<div class="social-icons-container">
|
<div class="social-icons-container">
|
||||||
<social-icons
|
<social-icons
|
||||||
[homepage]="tv.homepage"
|
[homepage]="tv.homepage"
|
||||||
[tvdbId]="tv.id"
|
[theMoviedbId]="tv.id"
|
||||||
[hasTrailer]="tv.trailer"
|
[hasTrailer]="tv.trailer"
|
||||||
|
[twitter]="tv.externalIds.twitterId"
|
||||||
|
[facebook]="tv.externalIds.facebookId"
|
||||||
|
[instagram]="tv.externalIds.instagramId"
|
||||||
(openTrailer)="openDialog()"
|
(openTrailer)="openDialog()"
|
||||||
[imdbId]="tv.imdbId"
|
[imdbId]="tv.imdbId"
|
||||||
[available]="tv.available || tv.partlyAvailable"
|
[available]="tv.available || tv.partlyAvailable"
|
||||||
|
@ -41,7 +44,7 @@
|
||||||
<div class="small-middle-container">
|
<div class="small-middle-container">
|
||||||
<div class="row justify-content-center justify-content-sm-start header-container">
|
<div class="row justify-content-center justify-content-sm-start header-container">
|
||||||
<div class="details-poster-container">
|
<div class="details-poster-container">
|
||||||
<media-poster [posterPath]="tv.images?.medium"></media-poster>
|
<media-poster [posterPath]="'https://image.tmdb.org/t/p/w300/' + tv.images.original"></media-poster>
|
||||||
</div>
|
</div>
|
||||||
<!--Next to poster-->
|
<!--Next to poster-->
|
||||||
<div class="details-button-container">
|
<div class="details-button-container">
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { TvAdvancedOptionsComponent } from "./panels/tv-advanced-options/tv-adva
|
||||||
import { RequestServiceV2 } from "../../../services/requestV2.service";
|
import { RequestServiceV2 } from "../../../services/requestV2.service";
|
||||||
import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component";
|
import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component";
|
||||||
import { forkJoin } from "rxjs";
|
import { forkJoin } from "rxjs";
|
||||||
|
import { TopBannerComponent } from "../shared/top-banner/top-banner.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./tv-details.component.html",
|
templateUrl: "./tv-details.component.html",
|
||||||
|
@ -57,12 +58,12 @@ export class TvDetailsComponent implements OnInit {
|
||||||
this.showAdvanced = await this.sonarrService.isEnabled();
|
this.showAdvanced = await this.sonarrService.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fromSearch) {
|
// if (this.fromSearch) {
|
||||||
this.tv = await this.searchService.getTvInfoWithMovieDbId(this.tvdbId);
|
// this.tv = await this.searchService.getTvInfoWithMovieDbId(this.tvdbId);
|
||||||
this.tvdbId = this.tv.id;
|
// this.tvdbId = this.tv.id;
|
||||||
} else {
|
// } else {
|
||||||
this.tv = await this.searchService.getTvInfo(this.tvdbId);
|
this.tv = await this.searchService.getTvInfo(this.tvdbId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (this.tv.requestId) {
|
if (this.tv.requestId) {
|
||||||
this.tvRequest = await this.requestService.getChildRequests(this.tv.requestId).toPromise();
|
this.tvRequest = await this.requestService.getChildRequests(this.tv.requestId).toPromise();
|
||||||
|
@ -70,8 +71,8 @@ export class TvDetailsComponent implements OnInit {
|
||||||
this.loadAdvancedInfo();
|
this.loadAdvancedInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
const tvBanner = await this.imageService.getTvBanner(this.tvdbId).toPromise();
|
// const tvBanner = await this.imageService.getTvBanner(this.tvdbId).toPromise();
|
||||||
this.tv.background = this.sanitizer.bypassSecurityTrustStyle("url(" + tvBanner + ")");
|
this.tv.background = this.sanitizer.bypassSecurityTrustStyle("url(https://image.tmdb.org/t/p/original" + this.tv.banner + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async request(userId: string) {
|
public async request(userId: string) {
|
||||||
|
@ -87,7 +88,6 @@ export class TvDetailsComponent implements OnInit {
|
||||||
|
|
||||||
public openDialog() {
|
public openDialog() {
|
||||||
let trailerLink = this.tv.trailer;
|
let trailerLink = this.tv.trailer;
|
||||||
trailerLink = trailerLink.split('?v=')[1];
|
|
||||||
|
|
||||||
this.dialog.open(YoutubeTrailerComponent, {
|
this.dialog.open(YoutubeTrailerComponent, {
|
||||||
width: '560px',
|
width: '560px',
|
||||||
|
|
|
@ -210,7 +210,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.rating-small {
|
.rating-small {
|
||||||
width: 1.3em;
|
width: 1.4em;
|
||||||
}
|
}
|
||||||
.stream-small {
|
.stream-small {
|
||||||
width: 3em;
|
width: 3em;
|
||||||
|
|
|
@ -34,9 +34,6 @@ const routes: Routes = [
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
|
||||||
...fromComponents.entryComponents
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
...fromComponents.providers
|
...fromComponents.providers
|
||||||
],
|
],
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'"
|
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'"
|
||||||
[opened]="!(isHandset$ | async)">
|
[opened]="!(isHandset$ | async)">
|
||||||
|
|
||||||
<mat-toolbar class="application-name">{{applicationName}}</mat-toolbar>
|
<mat-toolbar class="application-name" id="nav-applicationName">{{applicationName}}</mat-toolbar>
|
||||||
|
|
||||||
<div class="outer-profile">
|
<div class="outer-profile">
|
||||||
<div class="profile-img-container">
|
<div class="profile-img-container">
|
||||||
<div class="profile-img">
|
<div class="profile-img">
|
||||||
<img
|
<img id="profile-image"
|
||||||
src="https://www.gravatar.com/avatar/{{emailHash}}?d={{applicationLogo ? applicationLogo : 'https://raw.githubusercontent.com/Ombi-app/Ombi/gh-pages/img/android-chrome-512x512.png'}}" />
|
src="https://www.gravatar.com/avatar/{{emailHash}}?d={{applicationLogo ? applicationLogo : 'https://raw.githubusercontent.com/Ombi-app/Ombi/gh-pages/img/android-chrome-512x512.png'}}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-info">
|
<div class="profile-info">
|
||||||
<h3>{{username}}</h3>
|
<h3 id="profile-username">{{username}}</h3>
|
||||||
<p>{{welcomeText | translate}}</p>
|
<p>{{welcomeText | translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<div *ngIf="(nav.requiresAdmin && isAdmin || !nav.requiresAdmin) && nav.enabled">
|
<div *ngIf="(nav.requiresAdmin && isAdmin || !nav.requiresAdmin) && nav.enabled">
|
||||||
|
|
||||||
|
|
||||||
<a *ngIf="nav.externalLink" mat-list-item [href]="nav.link" target="_blank"
|
<a id="{{nav.id}}" *ngIf="nav.externalLink" mat-list-item [href]="nav.link" target="_blank"
|
||||||
matTooltip="{{nav.toolTipMessage | translate}}" matTooltipPosition="right"
|
matTooltip="{{nav.toolTipMessage | translate}}" matTooltipPosition="right"
|
||||||
[routerLinkActive]="'active-list-item'">
|
[routerLinkActive]="'active-list-item'">
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
style="padding-left: 5px; padding-right: 5px;" aria-hidden="true"></i>
|
style="padding-left: 5px; padding-right: 5px;" aria-hidden="true"></i>
|
||||||
{{nav.name | translate}}
|
{{nav.name | translate}}
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="!nav.externalLink" mat-list-item [routerLink]="nav.link" [style]="nav.color"
|
<a id="{{nav.id}}" *ngIf="!nav.externalLink" mat-list-item [routerLink]="nav.link" [style]="nav.color"
|
||||||
[routerLinkActive]="'active-list-item'">
|
[routerLinkActive]="'active-list-item'">
|
||||||
|
|
||||||
<mat-icon aria-label="Side nav toggle icon">{{nav.icon}}</mat-icon>
|
<mat-icon aria-label="Side nav toggle icon">{{nav.icon}}</mat-icon>
|
||||||
|
@ -76,22 +76,22 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2 top-filter">
|
<div class="col-2 top-filter">
|
||||||
<button mat-icon-button [matMenuTriggerFor]="filterMenu">
|
<button id="search-filter" mat-icon-button [matMenuTriggerFor]="filterMenu">
|
||||||
<mat-icon>filter_alt</mat-icon>
|
<mat-icon>filter_alt</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #filterMenu="matMenu" yPosition="below" class="smaller-panel">
|
<mat-menu #filterMenu="matMenu" yPosition="below" class="smaller-panel">
|
||||||
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.movies"
|
<mat-slide-toggle id="filterMovies" class="mat-menu-item slide-menu" [checked]="searchFilter.movies"
|
||||||
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Movie)">
|
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Movie)">
|
||||||
{{ 'NavigationBar.Filter.Movies' | translate}}</mat-slide-toggle>
|
{{ 'NavigationBar.Filter.Movies' | translate}}</mat-slide-toggle>
|
||||||
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.tvShows"
|
<mat-slide-toggle id="filterTv" class="mat-menu-item slide-menu" [checked]="searchFilter.tvShows"
|
||||||
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.TvShow)">
|
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.TvShow)">
|
||||||
{{ 'NavigationBar.Filter.TvShows' | translate}}</mat-slide-toggle>
|
{{ 'NavigationBar.Filter.TvShows' | translate}}</mat-slide-toggle>
|
||||||
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.music"
|
<mat-slide-toggle id="filterMusic" class="mat-menu-item slide-menu" [checked]="searchFilter.music"
|
||||||
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Music)">
|
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Music)">
|
||||||
{{ 'NavigationBar.Filter.Music' | translate}}</mat-slide-toggle>
|
{{ 'NavigationBar.Filter.Music' | translate}}</mat-slide-toggle>
|
||||||
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.people"
|
<!-- <mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.people"
|
||||||
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.People)">
|
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.People)">
|
||||||
{{ 'NavigationBar.Filter.People' | translate}}</mat-slide-toggle>
|
{{ 'NavigationBar.Filter.People' | translate}}</mat-slide-toggle> -->
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -83,16 +83,16 @@ export class MyNavComponent implements OnInit {
|
||||||
this.filterService.changeFilter(this.searchFilter);
|
this.filterService.changeFilter(this.searchFilter);
|
||||||
}
|
}
|
||||||
this.navItems = [
|
this.navItems = [
|
||||||
{ name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true, faIcon: null },
|
{ id: "nav-discover", name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true, faIcon: null },
|
||||||
{ name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false, enabled: true, faIcon: null },
|
{ id: "nav-requests", name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false, enabled: true, faIcon: null },
|
||||||
{ name: "NavigationBar.Issues", icon: "notification_important", link: "/issues", requiresAdmin: false, enabled: this.issuesEnabled, faIcon: null },
|
{ id: "nav-issues", name: "NavigationBar.Issues", icon: "notification_important", link: "/issues", requiresAdmin: false, enabled: this.issuesEnabled, faIcon: null },
|
||||||
{ name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true, enabled: true, faIcon: null },
|
{ id: "nav-userManagement", name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true, enabled: true, faIcon: null },
|
||||||
// { name: "NavigationBar.Calendar", icon: "calendar_today", link: "/calendar", requiresAdmin: false, enabled: true },
|
//id: "", { name: "NavigationBar.Calendar", icon: "calendar_today", link: "/calendar", requiresAdmin: false, enabled: true },
|
||||||
{ name: "NavigationBar.Donate", icon: "attach_money", link: "https://www.paypal.me/PlexRequestsNet", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, style: "color:red;", toolTipMessage: 'NavigationBar.DonateTooltip', faIcon: null },
|
{ id: "nav-adminDonate", name: "NavigationBar.Donate", icon: "attach_money", link: "https://www.paypal.me/PlexRequestsNet", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, style: "color:red;", toolTipMessage: 'NavigationBar.DonateTooltip', faIcon: null },
|
||||||
{ name: "NavigationBar.Donate", icon: "attach_money", link: customizationSettings.customDonationUrl, externalLink: true, requiresAdmin: false, enabled: customizationSettings.enableCustomDonations, toolTip: true, toolTipMessage: customizationSettings.customDonationMessage, faIcon: null },
|
{ id: "nav-userDonate", name: "NavigationBar.Donate", icon: "attach_money", link: customizationSettings.customDonationUrl, externalLink: true, requiresAdmin: false, enabled: customizationSettings.enableCustomDonations, toolTip: true, toolTipMessage: customizationSettings.customDonationMessage, faIcon: null },
|
||||||
{ name: "NavigationBar.FeatureSuggestion", icon: null, link: "https://features.ombi.io/", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, toolTipMessage: 'NavigationBar.FeatureSuggestionTooltip', faIcon: "fa-lightbulb" },
|
{ id: "nav-featureSuggestion", name: "NavigationBar.FeatureSuggestion", icon: null, link: "https://features.ombi.io/", externalLink: true, requiresAdmin: true, enabled: true, toolTip: true, toolTipMessage: 'NavigationBar.FeatureSuggestionTooltip', faIcon: "fa-lightbulb" },
|
||||||
{ name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true, enabled: true, faIcon: null },
|
{ id: "nav-settings", name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true, enabled: true, faIcon: null },
|
||||||
{ name: "NavigationBar.UserPreferences", icon: "person", link: "/user-preferences", requiresAdmin: false, enabled: true, faIcon: null },
|
{ id: "nav-userPreferences", name: "NavigationBar.UserPreferences", icon: "person", link: "/user-preferences", requiresAdmin: false, enabled: true, faIcon: null },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,6 @@ export const components: any[] = [
|
||||||
AlbumsGridComponent
|
AlbumsGridComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
export const entryComponents: any[] = [
|
|
||||||
RequestOptionsComponent
|
|
||||||
];
|
|
||||||
|
|
||||||
export const providers: any[] = [
|
export const providers: any[] = [
|
||||||
RequestService,
|
RequestService,
|
||||||
RequestServiceV2,
|
RequestServiceV2,
|
||||||
|
|
|
@ -5,20 +5,20 @@
|
||||||
<!-- <div class="row"> -->
|
<!-- <div class="row"> -->
|
||||||
<div class="row justify-content-md-center top-spacing">
|
<div class="row justify-content-md-center top-spacing">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.AllRequests' | translate}}</button>
|
<button type="button" id="filterAll" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.AllRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.PendingRequests' | translate}}</button>
|
<button type="button" id="filterPending" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.PendingRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
<button type="button" id="filterProcessing" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
||||||
class="btn grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
class="btn grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
<button type="button" id="filterAvailable" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
||||||
class="btn grow">{{'Requests.AvailableRequests' | translate}}</button>
|
class="btn grow">{{'Requests.AvailableRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.DeniedRequests' | translate}}</button>
|
<button type="button" id="filterDenied" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.DeniedRequests' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2 offset-md-10">
|
<div class="col-md-2 offset-md-10">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
<mat-select id="requestsToDisplayDropdown" placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
||||||
<mat-option value="10">10</mat-option>
|
<mat-option value="10">10</mat-option>
|
||||||
<mat-option value="15">15</mat-option>
|
<mat-option value="15">15</mat-option>
|
||||||
<mat-option value="30">30</mat-option>
|
<mat-option value="30">30</mat-option>
|
||||||
|
@ -31,14 +31,14 @@
|
||||||
<table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">
|
<table mat-table [dataSource]="dataSource" class="requests table" matSort [matSortActive]="defaultSort" matSortDisableClear [matSortDirection]="defaultOrder">
|
||||||
|
|
||||||
<ng-container matColumnDef="select" *ngIf="isAdmin">
|
<ng-container matColumnDef="select" *ngIf="isAdmin">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox (change)="$event ? masterToggle() : null"
|
<mat-checkbox id="adminMasterCheckbox" (change)="$event ? masterToggle() : null"
|
||||||
[checked]="selection.hasValue() && isAllSelected()"
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</th>
|
</th>
|
||||||
<td mat-cell *matCellDef="let row">
|
<td mat-cell *matCellDef="let row">
|
||||||
<mat-checkbox (click)="$event.stopPropagation()"
|
<mat-checkbox id="adminMasterCheckbox" (click)="$event.stopPropagation()"
|
||||||
(change)="$event ? selection.toggle(row) : null"
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
[checked]="selection.isSelected(row)">
|
[checked]="selection.isSelected(row)">
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
@ -47,36 +47,36 @@
|
||||||
|
|
||||||
<ng-container matColumnDef="title">
|
<ng-container matColumnDef="title">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestsTitle' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestsTitle' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.title}} ({{element.releaseDate | amLocal | amDateFormat: 'YYYY'}}) </td>
|
<td mat-cell id="title{{element.id}}" *matCellDef="let element"> {{element.title}} ({{element.releaseDate | amLocal | amDateFormat: 'YYYY'}}) </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedUser.requestedBy">
|
<ng-container matColumnDef="requestedUser.requestedBy">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestedUser?.userAlias}} </td>
|
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedUser?.userAlias}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedDate">
|
<ng-container matColumnDef="requestedDate">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestDate' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestDate' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestedDate | amLocal | amDateFormat: 'LL'}} </td>
|
<td mat-cell id="requestedDate{{element.id}}" *matCellDef="let element"> {{element.requestedDate | amLocal | amDateFormat: 'LL'}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="status">
|
<ng-container matColumnDef="status">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.Status' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.Status' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.status}} </td>
|
<td mat-cell id="status{{element.id}}" *matCellDef="let element"> {{element.status}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
<ng-container matColumnDef="requestStatus">
|
<ng-container matColumnDef="requestStatus">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestStatus' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{ 'Requests.RequestStatus' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestStatus | translate}} </td>
|
<td mat-cell id="requestedStatus{{element.id}}" *matCellDef="let element"> {{element.requestStatus | translate}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
||||||
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin"> {{ 'Requests.Options' | translate}}</button>
|
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin"> {{ 'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -87,9 +87,9 @@
|
||||||
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
<mat-paginator [length]="resultsLength" [pageSize]="gridCount"></mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button *ngIf="selection.hasValue() && isAdmin" mat-fab color="accent" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
|
<button id="bulkFab" *ngIf="selection.hasValue() && isAdmin" mat-fab color="accent" class="floating-fab" [matMenuTriggerFor]="aboveMenu">
|
||||||
<i class="fas fa-bars"></i></button>
|
<i class="fas fa-bars"></i></button>
|
||||||
<mat-menu #aboveMenu="matMenu" yPosition="above">
|
<mat-menu #aboveMenu="matMenu" yPosition="above">
|
||||||
<button mat-menu-item (click)="bulkDelete()">{{'Requests.RequestPanel.Delete' | translate}}</button>
|
<button id="deleteFabButton" mat-menu-item (click)="bulkDelete()">{{'Requests.RequestPanel.Delete' | translate}}</button>
|
||||||
<button mat-menu-item (click)="bulkApprove()">{{'Requests.RequestPanel.Approve' | translate}}</button>
|
<button id="approveFabButton" mat-menu-item (click)="bulkApprove()">{{'Requests.RequestPanel.Approve' | translate}}</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
|
@ -1,11 +1,11 @@
|
||||||
<mat-nav-list>
|
<mat-nav-list>
|
||||||
<a (click)="delete()" mat-list-item>
|
<a id="requestDelete" (click)="delete()" mat-list-item>
|
||||||
<span mat-line>{{'Requests.RequestPanel.Delete' | translate}}</span>
|
<span mat-line>{{'Requests.RequestPanel.Delete' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="data.canApprove" (click)="approve()" mat-list-item>
|
<a id="requestApprove" *ngIf="data.canApprove" (click)="approve()" mat-list-item>
|
||||||
<span mat-line>{{'Requests.RequestPanel.Approve' | translate}}</span>
|
<span mat-line>{{'Requests.RequestPanel.Approve' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="data.type !== RequestType.tvShow" (click)="changeAvailability()" mat-list-item>
|
<a id="requestChangeAvailability" *ngIf="data.type !== RequestType.tvShow" (click)="changeAvailability()" mat-list-item>
|
||||||
<span mat-line>{{'Requests.RequestPanel.ChangeAvailability' | translate}}</span>
|
<span mat-line>{{'Requests.RequestPanel.ChangeAvailability' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
</mat-nav-list>
|
</mat-nav-list>
|
|
@ -5,20 +5,20 @@
|
||||||
|
|
||||||
<div class="row justify-content-md-center top-spacing">
|
<div class="row justify-content-md-center top-spacing">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.AllRequests' | translate}}</button>
|
<button type="button" id="filterAll" (click)="switchFilter(RequestFilter.All)" [attr.color]="currentFilter === RequestFilter.All ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.All ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.AllRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.PendingRequests' | translate}}</button>
|
<button type="button" id="filterPending" (click)="switchFilter(RequestFilter.Pending)" [attr.color]="currentFilter === RequestFilter.Pending ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Pending ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.PendingRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
<button type="button" id="filterProcessing" (click)="switchFilter(RequestFilter.Processing)" [attr.color]="currentFilter === RequestFilter.Processing ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Processing ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
||||||
class="btn grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
class="btn grow">{{'Requests.ProcessingRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
<button type="button" id="filterAvailable" (click)="switchFilter(RequestFilter.Available)" [attr.color]="currentFilter === RequestFilter.Available ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Available ? 'mat-accent' : 'mat-primary'" mat-raised-button
|
||||||
class="btn grow">{{'Requests.AvailableRequests' | translate}}</button>
|
class="btn grow">{{'Requests.AvailableRequests' | translate}}</button>
|
||||||
<button type="button" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.DeniedRequests' | translate}}</button>
|
<button type="button" id="filterDenied" (click)="switchFilter(RequestFilter.Denied)" [attr.color]="currentFilter === RequestFilter.Denied ? 'accent' : 'primary'" [ngClass]="currentFilter === RequestFilter.Denied ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Requests.DeniedRequests' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2 offset-md-10">
|
<div class="col-md-2 offset-md-10">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
<mat-select id="requestsToDisplayDropdown" placeholder="{{'Requests.RequestsToDisplay' | translate}}" [(value)]="gridCount" (selectionChange)="ngAfterViewInit()">
|
||||||
<mat-option value="10">10</mat-option>
|
<mat-option value="10">10</mat-option>
|
||||||
<mat-option value="15">15</mat-option>
|
<mat-option value="15">15</mat-option>
|
||||||
<mat-option value="30">30</mat-option>
|
<mat-option value="30">30</mat-option>
|
||||||
|
@ -33,24 +33,24 @@
|
||||||
|
|
||||||
<ng-container matColumnDef="series">
|
<ng-container matColumnDef="series">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestsTitle' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestsTitle' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.parentRequest.title}} </td>
|
<td mat-cell id="title{{element.id}}" *matCellDef="let element"> {{element.parentRequest.title}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedBy">
|
<ng-container matColumnDef="requestedBy">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestedBy' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element"> {{element.requestedUser.userAlias}} </td>
|
<td mat-cell id="requestedBy{{element.id}}" *matCellDef="let element"> {{element.requestedUser.userAlias}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestedDate">
|
<ng-container matColumnDef="requestedDate">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{'Requests.RequestDate' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header disableClear> {{'Requests.RequestDate' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td id="requestedDate{{element.id}}" mat-cell *matCellDef="let element">
|
||||||
{{element.requestedDate | amLocal | amDateFormat: 'LL'}}
|
{{element.requestedDate | amLocal | amDateFormat: 'LL'}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="requestStatus">
|
<ng-container matColumnDef="requestStatus">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestStatus' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef> {{'Requests.RequestStatus' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell id="requestedStatus{{element.id}}" *matCellDef="let element">
|
||||||
<div *ngIf="element.approved && !element.available">{{'Common.ProcessingRequest' | translate}}</div>
|
<div *ngIf="element.approved && !element.available">{{'Common.ProcessingRequest' | translate}}</div>
|
||||||
<div *ngIf="!element.approved && !element.available">{{'Common.PendingApproval' |translate}}</div>
|
<div *ngIf="!element.approved && !element.available">{{'Common.PendingApproval' |translate}}</div>
|
||||||
<div *ngIf="element.available">{{'Common.Available' | translate}}</div>
|
<div *ngIf="element.available">{{'Common.Available' | translate}}</div>
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
|
|
||||||
<ng-container matColumnDef="status">
|
<ng-container matColumnDef="status">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'Requests.Status' | translate}} </th>
|
<th mat-header-cell *matHeaderCellDef> {{'Requests.Status' | translate}} </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell id="status{{element.id}}" *matCellDef="let element">
|
||||||
{{element.parentRequest.status}}
|
{{element.parentRequest.status}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -68,8 +68,8 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button mat-raised-button color="accent" [routerLink]="'/details/tv/' + element.parentRequest.tvDbId">{{'Requests.Details' | translate}}</button>
|
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/tv/' + element.parentRequest.externalProviderId">{{'Requests.Details' | translate}}</button>
|
||||||
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">{{'Requests.Options' | translate}}</button>
|
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">{{'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,6 @@ const routes: Routes = [
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
|
||||||
...fromComponents.entryComponents
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
...fromComponents.providers
|
...fromComponents.providers
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { Observable } from "rxjs";
|
||||||
import { UITreeNode } from "primeng/tree";
|
import { UITreeNode } from "primeng/tree";
|
||||||
import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IDenyAlbumModel, IDenyMovieModel, IFilter,
|
import { FilterType, IAlbumRequest, IAlbumRequestModel, IAlbumUpdateModel, IChildRequests, IDenyAlbumModel, IDenyMovieModel, IFilter,
|
||||||
IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvDenyModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
|
IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestEngineResult, IRequestsViewModel, ITvDenyModel, ITvRequests, ITvUpdateModel, OrderType } from "../interfaces";
|
||||||
import { ITvRequestViewModel } from "../interfaces";
|
|
||||||
import { ServiceHelpers } from "./service.helpers";
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
import { IRemainingRequests } from "../interfaces/IRemainingRequests";
|
import { IRemainingRequests } from "../interfaces/IRemainingRequests";
|
||||||
|
@ -42,10 +41,6 @@ export class RequestService extends ServiceHelpers {
|
||||||
return this.http.get<number>(`${this.url}tv/total`, {headers: this.headers});
|
return this.http.get<number>(`${this.url}tv/total`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public requestTv(tv: ITvRequestViewModel): Observable<IRequestEngineResult> {
|
|
||||||
return this.http.post<IRequestEngineResult>(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers});
|
|
||||||
}
|
|
||||||
|
|
||||||
public approveMovie(movie: IMovieUpdateModel): Observable<IRequestEngineResult> {
|
public approveMovie(movie: IMovieUpdateModel): Observable<IRequestEngineResult> {
|
||||||
return this.http.post<IRequestEngineResult>(`${this.url}Movie/Approve`, JSON.stringify(movie), {headers: this.headers});
|
return this.http.post<IRequestEngineResult>(`${this.url}Movie/Approve`, JSON.stringify(movie), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { ServiceHelpers } from "./service.helpers";
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
import { IRequestsViewModel, IMovieRequests, IChildRequests, IMovieAdvancedOptions as IMediaAdvancedOptions, IRequestEngineResult, IAlbumRequest } from "../interfaces";
|
import { IRequestsViewModel, IMovieRequests, IChildRequests, IMovieAdvancedOptions as IMediaAdvancedOptions, IRequestEngineResult, IAlbumRequest, ITvRequestViewModelV2 } from "../interfaces";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -88,4 +88,8 @@ export class RequestServiceV2 extends ServiceHelpers {
|
||||||
public getAlbumDeniedRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IAlbumRequest>> {
|
public getAlbumDeniedRequests(count: number, position: number, sortProperty: string , order: string): Observable<IRequestsViewModel<IAlbumRequest>> {
|
||||||
return this.http.get<IRequestsViewModel<IAlbumRequest>>(`${this.url}Album/denied/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
|
return this.http.get<IRequestsViewModel<IAlbumRequest>>(`${this.url}Album/denied/${count}/${position}/${sortProperty}/${order}`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public requestTv(tv: ITvRequestViewModelV2): Observable<IRequestEngineResult> {
|
||||||
|
return this.http.post<IRequestEngineResult>(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,10 @@ export class SearchV2Service extends ServiceHelpers {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/upcoming/${currentlyLoaded}/${toLoad}`).toPromise();
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/upcoming/${currentlyLoaded}/${toLoad}`).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public recentlyRequestedMoviesByPage(currentlyLoaded: number, toLoad: number): Promise<ISearchMovieResult[]> {
|
||||||
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/requested/${currentlyLoaded}/${toLoad}`).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
public nowPlayingMovies(): Observable<ISearchMovieResult[]> {
|
public nowPlayingMovies(): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/nowplaying`);
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/nowplaying`);
|
||||||
}
|
}
|
||||||
|
@ -140,8 +144,8 @@ export class SearchV2Service extends ServiceHelpers {
|
||||||
return this.http.get<IStreamingData[]>(`${this.url}/stream/movie/${theMovieDbId}`);
|
return this.http.get<IStreamingData[]>(`${this.url}/stream/movie/${theMovieDbId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTvStreams(theTvDbId: number, tvMaze: number): Observable<IStreamingData[]> {
|
public getTvStreams(movieDbId: number): Observable<IStreamingData[]> {
|
||||||
return this.http.get<IStreamingData[]>(`${this.url}/stream/tv/${theTvDbId}/${tvMaze}`);
|
return this.http.get<IStreamingData[]>(`${this.url}/stream/tv/${movieDbId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Component, OnInit, Inject } from "@angular/core";
|
import { Component, Inject } from "@angular/core";
|
||||||
import { MatCheckboxChange } from "@angular/material/checkbox";
|
import { MatCheckboxChange } from "@angular/material/checkbox";
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
||||||
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
|
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
|
||||||
import { RequestService, MessageService } from "../../services";
|
import { MessageService } from "../../services";
|
||||||
import { ITvRequestViewModel, ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests } from "../../interfaces";
|
import { ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests, ITvRequestViewModelV2 } from "../../interfaces";
|
||||||
import { ThousandShortPipe } from "../../pipes/ThousandShortPipe";
|
import { RequestServiceV2 } from "../../services/requestV2.service";
|
||||||
|
|
||||||
export interface EpisodeRequestData {
|
export interface EpisodeRequestData {
|
||||||
series: ISearchTvResultV2;
|
series: ISearchTvResultV2;
|
||||||
|
@ -21,7 +21,7 @@ export class EpisodeRequestComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(public dialogRef: MatDialogRef<EpisodeRequestComponent>, @Inject(MAT_DIALOG_DATA) public data: EpisodeRequestData,
|
constructor(public dialogRef: MatDialogRef<EpisodeRequestComponent>, @Inject(MAT_DIALOG_DATA) public data: EpisodeRequestData,
|
||||||
private requestService: RequestService, private notificationService: MessageService) { }
|
private requestService: RequestServiceV2, private notificationService: MessageService) { }
|
||||||
|
|
||||||
|
|
||||||
public async submitRequests() {
|
public async submitRequests() {
|
||||||
|
@ -31,7 +31,7 @@ export class EpisodeRequestComponent {
|
||||||
return ep.selected;
|
return ep.selected;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
debugger;
|
|
||||||
if (!selected && !this.data.series.requestAll && !this.data.series.firstSeason && !this.data.series.latestSeason) {
|
if (!selected && !this.data.series.requestAll && !this.data.series.firstSeason && !this.data.series.latestSeason) {
|
||||||
this.notificationService.send("You need to select some episodes!", "OK");
|
this.notificationService.send("You need to select some episodes!", "OK");
|
||||||
return;
|
return;
|
||||||
|
@ -39,8 +39,8 @@ export class EpisodeRequestComponent {
|
||||||
|
|
||||||
this.data.series.requested = true;
|
this.data.series.requested = true;
|
||||||
|
|
||||||
const viewModel = <ITvRequestViewModel>{
|
const viewModel = <ITvRequestViewModelV2>{
|
||||||
firstSeason: this.data.series.firstSeason, latestSeason: this.data.series.latestSeason, requestAll: this.data.series.requestAll, tvDbId: this.data.series.id,
|
firstSeason: this.data.series.firstSeason, latestSeason: this.data.series.latestSeason, requestAll: this.data.series.requestAll, theMovieDbId: this.data.series.id,
|
||||||
requestOnBehalf: this.data.requestOnBehalf
|
requestOnBehalf: this.data.requestOnBehalf
|
||||||
};
|
};
|
||||||
viewModel.seasons = [];
|
viewModel.seasons = [];
|
||||||
|
|
|
@ -76,10 +76,6 @@ import { DetailsGroupComponent } from "../issues/components/details-group/detail
|
||||||
MatStepperModule,
|
MatStepperModule,
|
||||||
MatSnackBarModule,
|
MatSnackBarModule,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
|
||||||
EpisodeRequestComponent,
|
|
||||||
DetailsGroupComponent,
|
|
||||||
],
|
|
||||||
exports: [
|
exports: [
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue