Merge pull request #964 from tidusjar/dev

Dev
This commit is contained in:
Jamie 2017-01-20 17:43:42 +00:00 committed by GitHub
commit 1d242705c3
20 changed files with 692 additions and 258 deletions

View file

@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using TraktApiSharp.Enums;
using TraktApiSharp.Objects.Get.Shows;
using TraktApiSharp.Objects.Get.Shows.Common;
namespace Ombi.Api.Interfaces
{
public interface ITraktApi
{
Task<IEnumerable<TraktMostAnticipatedShow>> GetAnticipatedShows(int? page = default(int?), int? limitPerPage = default(int?));
Task<IEnumerable<TraktMostWatchedShow>> GetMostWatchesShows(TraktTimePeriod period = null, int? page = default(int?), int? limitPerPage = default(int?));
Task<IEnumerable<TraktShow>> GetPopularShows(int? page = default(int?), int? limitPerPage = default(int?));
Task<IEnumerable<TraktTrendingShow>> GetTrendingShows(int? page = default(int?), int? limitPerPage = default(int?));
}
}

View file

@ -31,6 +31,10 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath> <HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -43,6 +47,10 @@
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="TraktApiSharp, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\TraktApiSharp.0.8.0\lib\portable-net45+netcore45+wpa81\TraktApiSharp.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="IApiRequest.cs" /> <Compile Include="IApiRequest.cs" />
@ -58,6 +66,7 @@
<Compile Include="IPushoverApi.cs" /> <Compile Include="IPushoverApi.cs" />
<Compile Include="ISickRageApi.cs" /> <Compile Include="ISickRageApi.cs" />
<Compile Include="ISonarrApi.cs" /> <Compile Include="ISonarrApi.cs" />
<Compile Include="ITraktApi.cs" />
<Compile Include="IWatcherApi.cs" /> <Compile Include="IWatcherApi.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" /> <package id="RestSharp" version="105.2.3" targetFramework="net45" />
<package id="TraktApiSharp" version="0.8.0" targetFramework="net45" />
</packages> </packages>

View file

@ -66,12 +66,17 @@
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath> <HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="TraktApiSharp, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\TraktApiSharp.0.8.0\lib\portable-net45+netcore45+wpa81\TraktApiSharp.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ApiRequest.cs" /> <Compile Include="ApiRequest.cs" />
<Compile Include="DiscordApi.cs" /> <Compile Include="DiscordApi.cs" />
<Compile Include="NetflixRouletteApi.cs" /> <Compile Include="NetflixRouletteApi.cs" />
<Compile Include="RadarrApi.cs" /> <Compile Include="RadarrApi.cs" />
<Compile Include="TraktApi.cs" />
<Compile Include="WatcherApi.cs" /> <Compile Include="WatcherApi.cs" />
<Compile Include="MusicBrainzApi.cs" /> <Compile Include="MusicBrainzApi.cs" />
<Compile Include="SlackApi.cs" /> <Compile Include="SlackApi.cs" />

51
Ombi.Api/TraktApi.cs Normal file
View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ombi.Api.Interfaces;
using Ombi.Helpers;
using TraktApiSharp;
using TraktApiSharp.Enums;
using TraktApiSharp.Objects.Get.Shows;
using TraktApiSharp.Objects.Get.Shows.Common;
using TraktApiSharp.Requests.Params;
namespace Ombi.Api
{
public class TraktApi : ITraktApi
{
private TraktClient Client { get; }
private static readonly string Encrypted = "z/56wM/oEkkCWEvSIZCrzQyUvvqmafQ3njqf0UNK5xuKbNYh5Wz8ocoG2QDa5y1DBkozLaKsGxORmAB1XUvwbnom8DVNo9gE++9GTuwxmGlLDD318PXpRmYmpKqNwFSKRZgF6ewiY9qR4t3iG0pGQwPA08FK3+H7kpOKAGJNR9RMDP9wwB6Vl4DuOiZb9/DETjzZ+/zId0ZqimrbN+PLrg==";
private readonly string _apiKey = StringCipher.Decrypt(Encrypted, "ApiKey");
public TraktApi()
{
Client = new TraktClient(_apiKey);
}
public async Task<IEnumerable<TraktShow>> GetPopularShows(int? page = null, int? limitPerPage = null)
{
var popular = await Client.Shows.GetPopularShowsAsync(new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
return popular.Items;
}
public async Task<IEnumerable<TraktTrendingShow>> GetTrendingShows(int? page = null, int? limitPerPage = null)
{
var trendingShowsTop10 = await Client.Shows.GetTrendingShowsAsync(new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
return trendingShowsTop10.Items;
}
public async Task<IEnumerable<TraktMostAnticipatedShow>> GetAnticipatedShows(int? page = null, int? limitPerPage = null)
{
var anticipatedShows = await Client.Shows.GetMostAnticipatedShowsAsync(new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
return anticipatedShows.Items;
}
public async Task<IEnumerable<TraktMostWatchedShow>> GetMostWatchesShows(TraktTimePeriod period = null, int? page = null, int? limitPerPage = null)
{
var anticipatedShows = await Client.Shows.GetMostWatchedShowsAsync(period ?? TraktTimePeriod.Monthly, new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
return anticipatedShows.Items;
}
}
}

View file

@ -8,4 +8,5 @@
<package id="RestSharp" version="105.2.3" targetFramework="net45" /> <package id="RestSharp" version="105.2.3" targetFramework="net45" />
<package id="System.Net.Http" version="4.0.0" targetFramework="net45" /> <package id="System.Net.Http" version="4.0.0" targetFramework="net45" />
<package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net45" /> <package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net45" />
<package id="TraktApiSharp" version="0.8.0" targetFramework="net45" />
</packages> </packages>

View file

@ -46,5 +46,12 @@ namespace Ombi.Helpers
dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime(); dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
return dtDateTime; return dtDateTime;
} }
public static long ToJavascriptTimestamp(this DateTime input)
{
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var time = input.Subtract(new TimeSpan(epoch.Ticks));
return (long)(time.Ticks / 10000);
}
} }
} }

View file

@ -455,7 +455,7 @@ namespace Ombi.Services.Jobs
if (!testEmail) if (!testEmail)
{ {
var users = UserHelper.GetUsersWithFeature(Features.RequestAddedNotification); var users = UserHelper.GetUsersWithFeature(Features.Newsletter);
if (users != null) if (users != null)
{ {
foreach (var user in users) foreach (var user in users)

View file

@ -244,9 +244,9 @@ namespace Ombi.Services.Notification
var email = new EmailBasicTemplate(); var email = new EmailBasicTemplate();
var html = email.LoadTemplate( var html = email.LoadTemplate(
$"Ombi: {model.Title} is now available!", $"Ombi: {model.Title} is now available!",
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)", $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)",
model.ImgSrc); model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)" }; var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)" };
var message = new MimeMessage var message = new MimeMessage
{ {

View file

@ -72,6 +72,25 @@ $(function () {
moviesInTheaters(); moviesInTheaters();
}); });
// TV DropDown
$('#popularShows').on('click', function (e) {
e.preventDefault();
popularShows();
});
$('#trendingShows').on('click', function (e) {
e.preventDefault();
trendingTv();
});
$('#mostWatchedShows').on('click', function (e) {
e.preventDefault();
mostwatchedTv();
});
$('#anticipatedShows').on('click', function (e) {
e.preventDefault();
anticipatedTv();
});
// Type in TV search // Type in TV search
$("#tvSearchContent").on("input", function () { $("#tvSearchContent").on("input", function () {
if (searchTimer) { if (searchTimer) {
@ -293,6 +312,23 @@ $(function () {
getMovies(url); getMovies(url);
} }
function popularShows() {
var url = createBaseUrl(base, '/search/tv/popular');
getTvShows(url, true);
}
function anticipatedTv() {
var url = createBaseUrl(base, '/search/tv/anticipated');
getTvShows(url, true);
}
function trendingTv() {
var url = createBaseUrl(base, '/search/tv/trending');
getTvShows(url, true);
}
function mostwatchedTv() {
var url = createBaseUrl(base, '/search/tv/mostwatched');
getTvShows(url, true);
}
function getMovies(url) { function getMovies(url) {
resetMovies(); resetMovies();
@ -323,10 +359,10 @@ $(function () {
var query = $("#tvSearchContent").val(); var query = $("#tvSearchContent").val();
var url = createBaseUrl(base, '/search/tv/'); var url = createBaseUrl(base, '/search/tv/');
query ? getTvShows(url + query) : resetTvShows(); query ? getTvShows(url + query, false) : resetTvShows();
} }
function getTvShows(url) { function getTvShows(url, loadImage) {
resetTvShows(); resetTvShows();
$('#tvSearchButton').attr("class", "fa fa-spinner fa-spin"); $('#tvSearchButton').attr("class", "fa fa-spinner fa-spin");
@ -338,7 +374,9 @@ $(function () {
$("#tvList").append(html); $("#tvList").append(html);
checkNetflix(context.title, context.id); checkNetflix(context.title, context.id);
if (loadImage) {
getTvPoster(result.id);
}
}); });
} }
else { else {
@ -406,6 +444,16 @@ $(function () {
}); });
}; };
function getTvPoster(theTvDbId) {
var url = createBaseUrl(base, '/search/tv/poster/');
$.ajax(url + theTvDbId).success(function (result) {
if (result) {
$('#' + theTvDbId + "imgDiv").html(" <img class='img-responsive' src='" + result + "' width='150' alt='poster'>");
}
});
};
function buildMovieContext(result) { function buildMovieContext(result) {
var date = new Date(result.releaseDate); var date = new Date(result.releaseDate);
var year = date.getFullYear(); var year = date.getFullYear();
@ -432,6 +480,7 @@ $(function () {
var date = new Date(result.firstAired); var date = new Date(result.firstAired);
var year = date.getFullYear(); var year = date.getFullYear();
var context = { var context = {
status: result.status,
posterPath: result.banner, posterPath: result.banner,
id: result.id, id: result.id,
title: result.seriesName, title: result.seriesName,
@ -448,7 +497,10 @@ $(function () {
tvPartialAvailable: result.tvPartialAvailable, tvPartialAvailable: result.tvPartialAvailable,
disableTvRequestsByEpisode: result.disableTvRequestsByEpisode, disableTvRequestsByEpisode: result.disableTvRequestsByEpisode,
disableTvRequestsBySeason: result.disableTvRequestsBySeason, disableTvRequestsBySeason: result.disableTvRequestsBySeason,
enableTvRequestsForOnlySeries: result.enableTvRequestsForOnlySeries enableTvRequestsForOnlySeries: result.enableTvRequestsForOnlySeries,
trailer: result.trailer,
homepage: result.homepage,
firstAired: Humanize(result.firstAired)
}; };
return context; return context;

View file

@ -58,5 +58,20 @@ namespace Ombi.UI.Models
public bool DisableTvRequestsByEpisode { get; set; } public bool DisableTvRequestsByEpisode { get; set; }
public bool DisableTvRequestsBySeason { get; set; } public bool DisableTvRequestsBySeason { get; set; }
public bool EnableTvRequestsForOnlySeries { get; set; } public bool EnableTvRequestsForOnlySeries { get; set; }
/// <summary>
/// This is used from the Trakt API
/// </summary>
/// <value>
/// The trailer.
/// </value>
public string Trailer { get; set; }
/// <summary>
/// This is used from the Trakt API
/// </summary>
/// <value>
/// The trailer.
/// </value>
public string Homepage { get; set; }
} }
} }

View file

@ -235,6 +235,8 @@ namespace Ombi.UI.Modules
} }
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests); var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
var viewModel = dbTv.Select(tv => new RequestViewModel var viewModel = dbTv.Select(tv => new RequestViewModel
{ {
@ -243,7 +245,7 @@ namespace Ombi.UI.Modules
Status = tv.Status, Status = tv.Status,
ImdbId = tv.ImdbId, ImdbId = tv.ImdbId,
Id = tv.Id, Id = tv.Id,
PosterPath = tv.PosterPath, PosterPath = tv.PosterPath.Contains("http:") ? tv.PosterPath.Replace("http:", "https:") : tv.PosterPath, // We make the poster path https on request, but this is just incase
ReleaseDate = tv.ReleaseDate, ReleaseDate = tv.ReleaseDate,
ReleaseDateTicks = tv.ReleaseDate.Ticks, ReleaseDateTicks = tv.ReleaseDate.Ticks,
RequestedDate = tv.RequestedDate, RequestedDate = tv.RequestedDate,

View file

@ -58,6 +58,7 @@ using Ombi.Store.Repository;
using Ombi.UI.Helpers; using Ombi.UI.Helpers;
using Ombi.UI.Models; using Ombi.UI.Models;
using TMDbLib.Objects.General; using TMDbLib.Objects.General;
using TraktApiSharp.Objects.Get.Shows;
using Action = Ombi.Helpers.Analytics.Action; using Action = Ombi.Helpers.Analytics.Action;
using EpisodesModel = Ombi.Store.EpisodesModel; using EpisodesModel = Ombi.Store.EpisodesModel;
using ISecurityExtensions = Ombi.Core.ISecurityExtensions; using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
@ -76,7 +77,7 @@ namespace Ombi.UI.Modules
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth, ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth,
IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email, IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue, IRepository<PlexContent> content, IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue, IRepository<PlexContent> content,
ISecurityExtensions security, IMovieSender movieSender, IRadarrCacher radarrCacher) ISecurityExtensions security, IMovieSender movieSender, IRadarrCacher radarrCacher, ITraktApi traktApi)
: base("search", prSettings, security) : base("search", prSettings, security)
{ {
Auth = auth; Auth = auth;
@ -109,6 +110,7 @@ namespace Ombi.UI.Modules
MovieSender = movieSender; MovieSender = movieSender;
WatcherCacher = watcherCacher; WatcherCacher = watcherCacher;
RadarrCacher = radarrCacher; RadarrCacher = radarrCacher;
TraktApi = traktApi;
Get["SearchIndex", "/", true] = async (x, ct) => await RequestLoad(); Get["SearchIndex", "/", true] = async (x, ct) => await RequestLoad();
@ -120,6 +122,13 @@ namespace Ombi.UI.Modules
Get["movie/upcoming", true] = async (x, ct) => await UpcomingMovies(); Get["movie/upcoming", true] = async (x, ct) => await UpcomingMovies();
Get["movie/playing", true] = async (x, ct) => await CurrentlyPlayingMovies(); Get["movie/playing", true] = async (x, ct) => await CurrentlyPlayingMovies();
Get["tv/popular", true] = async (x, ct) => await ProcessShows(ShowSearchType.Popular);
Get["tv/trending", true] = async (x, ct) => await ProcessShows(ShowSearchType.Trending);
Get["tv/mostwatched", true] = async (x, ct) => await ProcessShows(ShowSearchType.MostWatched);
Get["tv/anticipated", true] = async (x, ct) => await ProcessShows(ShowSearchType.Anticipated);
Get["tv/poster/{id}"] = p => GetTvPoster((int)p.id);
Post["request/movie", true] = async (x, ct) => await RequestMovie((int)Request.Form.movieId); Post["request/movie", true] = async (x, ct) => await RequestMovie((int)Request.Form.movieId);
Post["request/tv", true] = Post["request/tv", true] =
async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons); async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons);
@ -129,6 +138,7 @@ namespace Ombi.UI.Modules
Get["/seasons"] = x => GetSeasons(); Get["/seasons"] = x => GetSeasons();
Get["/episodes", true] = async (x, ct) => await GetEpisodes(); Get["/episodes", true] = async (x, ct) => await GetEpisodes();
} }
private ITraktApi TraktApi { get; }
private IWatcherCacher WatcherCacher { get; } private IWatcherCacher WatcherCacher { get; }
private IMovieSender MovieSender { get; } private IMovieSender MovieSender { get; }
private IRepository<PlexContent> PlexContentRepository { get; } private IRepository<PlexContent> PlexContentRepository { get; }
@ -190,6 +200,17 @@ namespace Ombi.UI.Modules
return await ProcessMovies(MovieSearchType.Search, searchTerm); return await ProcessMovies(MovieSearchType.Search, searchTerm);
} }
private Response GetTvPoster(int theTvDbId)
{
var result = TvApi.ShowLookupByTheTvDbId(theTvDbId);
var banner = result.image?.medium;
if (!string.IsNullOrEmpty(banner))
{
banner = banner.Replace("http", "https"); // Always use the Https banners
}
return banner;
}
private async Task<Response> ProcessMovies(MovieSearchType searchType, string searchTerm) private async Task<Response> ProcessMovies(MovieSearchType searchType, string searchTerm)
{ {
List<MovieResult> apiMovies; List<MovieResult> apiMovies;
@ -322,6 +343,186 @@ namespace Ombi.UI.Modules
return true; return true;
} }
private async Task<Response> ProcessShows(ShowSearchType type)
{
var shows = new List<SearchTvShowViewModel>();
var prSettings = await PrService.GetSettingsAsync();
switch (type)
{
case ShowSearchType.Popular:
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Popular", Username, CookieHelper.GetAnalyticClientId(Cookies));
var popularShows = await TraktApi.GetPopularShows();
foreach (var popularShow in popularShows)
{
var theTvDbId = int.Parse(popularShow.Ids.Tvdb.ToString());
var model = new SearchTvShowViewModel
{
FirstAired = popularShow.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
Id = theTvDbId,
ImdbId = popularShow.Ids.Imdb,
Network = popularShow.Network,
Overview = popularShow.Overview.RemoveHtml(),
Rating = popularShow.Rating.ToString(),
Runtime = popularShow.Runtime.ToString(),
SeriesName = popularShow.Title,
Status = popularShow.Status.DisplayName,
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
Trailer = popularShow.Trailer,
Homepage = popularShow.Homepage
};
shows.Add(model);
}
shows = await MapToTvModel(shows, prSettings);
break;
case ShowSearchType.Anticipated:
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Anticipated", Username, CookieHelper.GetAnalyticClientId(Cookies));
var anticipated = await TraktApi.GetAnticipatedShows();
foreach (var anticipatedShow in anticipated)
{
var show = anticipatedShow.Show;
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
var model = new SearchTvShowViewModel
{
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
Id = theTvDbId,
ImdbId = show.Ids.Imdb,
Network = show.Network ?? string.Empty,
Overview = show.Overview?.RemoveHtml() ?? string.Empty,
Rating = show.Rating.ToString(),
Runtime = show.Runtime.ToString(),
SeriesName = show.Title,
Status = show.Status?.DisplayName ?? string.Empty,
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
Trailer = show.Trailer,
Homepage = show.Homepage
};
shows.Add(model);
}
shows = await MapToTvModel(shows, prSettings);
break;
case ShowSearchType.MostWatched:
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "MostWatched", Username, CookieHelper.GetAnalyticClientId(Cookies));
var mostWatched = await TraktApi.GetMostWatchesShows();
foreach (var watched in mostWatched)
{
var show = watched.Show;
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
var model = new SearchTvShowViewModel
{
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
Id = theTvDbId,
ImdbId = show.Ids.Imdb,
Network = show.Network,
Overview = show.Overview.RemoveHtml(),
Rating = show.Rating.ToString(),
Runtime = show.Runtime.ToString(),
SeriesName = show.Title,
Status = show.Status.DisplayName,
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
Trailer = show.Trailer,
Homepage = show.Homepage
};
shows.Add(model);
}
shows = await MapToTvModel(shows, prSettings);
break;
case ShowSearchType.Trending:
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Trending", Username, CookieHelper.GetAnalyticClientId(Cookies));
var trending = await TraktApi.GetTrendingShows();
foreach (var watched in trending)
{
var show = watched.Show;
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
var model = new SearchTvShowViewModel
{
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
Id = theTvDbId,
ImdbId = show.Ids.Imdb,
Network = show.Network,
Overview = show.Overview.RemoveHtml(),
Rating = show.Rating.ToString(),
Runtime = show.Runtime.ToString(),
SeriesName = show.Title,
Status = show.Status.DisplayName,
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
Trailer = show.Trailer,
Homepage = show.Homepage
};
shows.Add(model);
}
shows = await MapToTvModel(shows, prSettings);
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
return Response.AsJson(shows);
}
private async Task<List<SearchTvShowViewModel>> MapToTvModel(List<SearchTvShowViewModel> shows, PlexRequestSettings prSettings)
{
var plexSettings = await PlexService.GetSettingsAsync();
var providerId = string.Empty;
// Get the requests
var allResults = await RequestService.GetAllAsync();
allResults = allResults.Where(x => x.Type == RequestType.TvShow);
var distinctResults = allResults.DistinctBy(x => x.ProviderId);
var dbTv = distinctResults.ToDictionary(x => x.ProviderId);
// Check the external applications
var sonarrCached = SonarrCacher.QueuedIds().ToList();
var sickRageCache = SickRageCacher.QueuedIds(); // consider just merging sonarr/sickrage arrays
var content = PlexContentRepository.GetAll();
var plexTvShows = Checker.GetPlexTvShows(content).ToList();
foreach (var show in shows)
{
if (plexSettings.AdvancedSearch)
{
providerId = show.Id.ToString();
}
var plexShow = Checker.GetTvShow(plexTvShows.ToArray(), show.SeriesName, show.FirstAired?.Substring(0, 4),
providerId);
if (plexShow != null)
{
show.Available = true;
show.PlexUrl = plexShow.Url;
}
else
{
if (dbTv.ContainsKey(show.Id))
{
var dbt = dbTv[show.Id];
show.Requested = true;
show.Episodes = dbt.Episodes.ToList();
show.Approved = dbt.Approved;
}
if (sonarrCached.Select(x => x.TvdbId).Contains(show.Id) || sickRageCache.Contains(show.Id))
// compare to the sonarr/sickrage db
{
show.Requested = true;
}
}
}
return shows;
}
private async Task<Response> SearchTvShow(string searchTerm) private async Task<Response> SearchTvShow(string searchTerm)
{ {
@ -693,11 +894,13 @@ namespace Ombi.UI.Modules
DateTime.TryParse(showInfo.premiered, out firstAir); DateTime.TryParse(showInfo.premiered, out firstAir);
string fullShowName = $"{showInfo.name} ({firstAir.Year})"; string fullShowName = $"{showInfo.name} ({firstAir.Year})";
// For some reason the poster path is always http
var posterPath = showInfo.image?.medium.Replace("http:", "https:");
var model = new RequestedModel var model = new RequestedModel
{ {
Type = RequestType.TvShow, Type = RequestType.TvShow,
Overview = showInfo.summary.RemoveHtml(), Overview = showInfo.summary.RemoveHtml(),
PosterPath = showInfo.image?.medium, PosterPath = posterPath,
Title = showInfo.name, Title = showInfo.name,
ReleaseDate = firstAir, ReleaseDate = firstAir,
Status = showInfo.status, Status = showInfo.status,
@ -1399,5 +1602,13 @@ namespace Ombi.UI.Modules
return false; return false;
} }
} }
private enum ShowSearchType
{
Popular,
Anticipated,
MostWatched,
Trending
}
} }
} }

View file

@ -233,13 +233,28 @@ namespace Ombi.UI.Modules
var result = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner); var result = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner);
var landingSettings = await LandingPageSettings.GetSettingsAsync();
if (landingSettings.Enabled)
{
if (!landingSettings.BeforeLogin) // After Login
{
var uri = Linker.BuildRelativeUri(Context, "LandingPageIndex");
if (loginGuid != Guid.Empty)
{
return CustomModuleExtensions.LoginAndRedirect(this, result.LoginGuid, null, uri.ToString());
}
return Response.AsRedirect(uri.ToString());
}
}
var retVal = Linker.BuildRelativeUri(Context, "SearchIndex"); var retVal = Linker.BuildRelativeUri(Context, "SearchIndex");
if (result.LoginGuid != Guid.Empty) if (result.LoginGuid != Guid.Empty)
{ {
return CustomModuleExtensions.LoginAndRedirect(this, result.LoginGuid, null, retVal.ToString()); return CustomModuleExtensions.LoginAndRedirect(this, result.LoginGuid, null, retVal.ToString());
} }
return Response.AsJson(new { result = true, url = retVal.ToString() }); return Response.AsJson(new { result = true, url = retVal.ToString() });
} }
private async Task<PlexUsers> IsPlexUser(string username) private async Task<PlexUsers> IsPlexUser(string username)
@ -318,6 +333,21 @@ namespace Ombi.UI.Modules
var m = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner); var m = await AuthenticationSetup(userId, username, dateTimeOffset, loginGuid, isOwner);
var landingSettings = await LandingPageSettings.GetSettingsAsync();
if (landingSettings.Enabled)
{
if (!landingSettings.BeforeLogin) // After Login
{
var uri = Linker.BuildRelativeUri(Context, "LandingPageIndex");
if (m.LoginGuid != Guid.Empty)
{
return CustomModuleExtensions.LoginAndRedirect(this, m.LoginGuid, null, uri.ToString());
}
return Response.AsRedirect(uri.ToString());
}
}
var retVal = Linker.BuildRelativeUri(Context, "SearchIndex"); var retVal = Linker.BuildRelativeUri(Context, "SearchIndex");
if (m.LoginGuid != Guid.Empty) if (m.LoginGuid != Guid.Empty)
{ {

View file

@ -420,7 +420,7 @@ namespace Ombi.UI.Modules
FeaturesFormattedString = newUser ? "Processing..." : features.ToString(), FeaturesFormattedString = newUser ? "Processing..." : features.ToString(),
Username = plexInfo.Title, Username = plexInfo.Title,
Type = UserType.PlexUser, Type = UserType.PlexUser,
EmailAddress = plexInfo.Email, EmailAddress = string.IsNullOrEmpty(plexInfo.Email) ? dbUser.EmailAddress : plexInfo.Email,
Alias = dbUser?.UserAlias ?? string.Empty, Alias = dbUser?.UserAlias ?? string.Empty,
LastLoggedIn = lastLoggedIn, LastLoggedIn = lastLoggedIn,
PlexInfo = new UserManagementPlexInformation PlexInfo = new UserManagementPlexInformation

View file

@ -49,6 +49,7 @@ namespace Ombi.UI.NinjectModules
Bind<INetflixApi>().To<NetflixRouletteApi>(); Bind<INetflixApi>().To<NetflixRouletteApi>();
Bind<IDiscordApi>().To<DiscordApi>(); Bind<IDiscordApi>().To<DiscordApi>();
Bind<IRadarrApi>().To<RadarrApi>(); Bind<IRadarrApi>().To<RadarrApi>();
Bind<ITraktApi>().To<TraktApi>();
} }
} }
} }

View file

@ -206,6 +206,10 @@
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath> <HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="TraktApiSharp, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\TraktApiSharp.0.8.0\lib\portable-net45+netcore45+wpa81\TraktApiSharp.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Authentication\CustomAuthenticationConfiguration.cs" /> <Compile Include="Authentication\CustomAuthenticationConfiguration.cs" />

View file

@ -75,9 +75,20 @@
<!-- TV tab --> <!-- TV tab -->
<div role="tabpanel" class="tab-pane" id="TvShowTab"> <div role="tabpanel" class="tab-pane" id="TvShowTab">
<div class="input-group"> <div class="input-group">
<input id="tvSearchContent" type="text" class="form-control form-control-custom form-control-search"> <input id="tvSearchContent" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons">
<div class="input-group-addon"> <div class="input-group-addon">
<i id="tvSearchButton" class="fa fa-search"></i> <div class="btn-group">
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
@UI.Search_Suggestions
<i class="fa fa-chevron-down"></i>
</a>
<ul class="dropdown-menu">
<li><a id="popularShows" href="#">Popular Shows</a></li>
<li><a id="trendingShows" href="#">Trending Shows</a></li>
<li><a id="mostWatchedShows" href="#">Most Watched Shows</a></li>
<li><a id="anticipatedShows" href="#">Most Anticipated Shows</a></li>
</ul>
</div><i id="tvSearchButton" class="fa fa-search"></i>
</div> </div>
</div> </div>
<br /> <br />
@ -106,10 +117,10 @@
</div> </div>
} }
<!-- Movie and TV Results template --> <!-- Movie and TV Results template -->
<script id="search-template" type="text/x-handlebars-template"> <script id="search-template" type="text/x-handlebars-template">
<div class="row"> <div class="row">
<div class="col-sm-2"> <div id="{{id}}imgDiv" class="col-sm-2">
{{#if_eq type "movie"}} {{#if_eq type "movie"}}
{{#if posterPath}} {{#if posterPath}}
@ -134,6 +145,18 @@
<h4>{{title}} ({{year}})</h4> <h4>{{title}} ({{year}})</h4>
</a> </a>
{{/if_eq}} {{/if_eq}}
{{#if status}}
<span class="label label-info" target="_blank">{{status}}</span>
{{/if}}
{{#if homepage}}
<a href="{{homepage}}" target="_blank"><span class="label label-info">HomePage</span></a>
{{/if}}
{{#if trailer}}
<a href="{{trailer}}" target="_blank"><span class="label label-info">Trailer</span></a>
{{/if}}
{{#if firstAired}}
<span class="label label-info" target="_blank">First Aired: {{firstAired}}</span>
{{/if}}
{{#if available}} {{#if available}}
<span class="label label-success">@UI.Search_Available_on_plex</span> <span class="label label-success">@UI.Search_Available_on_plex</span>
{{else}} {{else}}
@ -230,11 +253,11 @@
</div> </div>
<hr /> <hr />
</script> </script>
<!-- Music Results template --> <!-- Music Results template -->
<script id="music-template" type="text/x-handlebars-template"> <script id="music-template" type="text/x-handlebars-template">
<div class="row"> <div class="row">
<div id="{{id}}imageDiv" class="col-sm-2"> <div id="{{id}}imageDiv" class="col-sm-2">
{{#if coverArtUrl}} {{#if coverArtUrl}}
@ -277,9 +300,9 @@
</div> </div>
<hr /> <hr />
</script> </script>
<div class="modal fade" id="seasonsModal"> <div class="modal fade" id="seasonsModal">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -297,9 +320,9 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="episodesModal"> <div class="modal fade" id="episodesModal">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content col-md-12"> <div class="modal-content col-md-12">
<div class="modal-header"> <div class="modal-header">
@ -319,10 +342,10 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="issuesModal"> <div class="modal fade" id="issuesModal">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -343,27 +366,27 @@
</form> </form>
</div> </div>
</div> </div>
</div> </div>
<script id="seasons-template" type="text/x-handlebars-template"> <script id="seasons-template" type="text/x-handlebars-template">
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" class="selectedSeasons" id="{{id}}" name="{{id}}"><label for="{{id}}">@UI.Search_Season {{id}}</label> <input type="checkbox" class="selectedSeasons" id="{{id}}" name="{{id}}"><label for="{{id}}">@UI.Search_Season {{id}}</label>
</div> </div>
</div> </div>
</script> </script>
<script id="seasonNumber-template" type="text/x-handlebars-template"> <script id="seasonNumber-template" type="text/x-handlebars-template">
<div id="seasonNumber{{seasonNumber}}" class="col-md-12"> <div id="seasonNumber{{seasonNumber}}" class="col-md-12">
<strong>@UI.Search_Season {{seasonNumber}}</strong> <strong>@UI.Search_Season {{seasonNumber}}</strong>
</div> </div>
</script> </script>
<script id="episode-template" type="text/x-handlebars-template"> <script id="episode-template" type="text/x-handlebars-template">
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<div class="checkbox" style="margin-bottom:0px; margin-top:0px;"> <div class="checkbox" style="margin-bottom:0px; margin-top:0px;">
{{#if_eq requested true}} {{#if_eq requested true}}
@ -374,6 +397,6 @@
</div> </div>
</div> </div>
</script> </script>
@Html.LoadSearchAssets() @Html.LoadSearchAssets()

View file

@ -8,6 +8,10 @@
<legend>User Management Settings</legend> <legend>User Management Settings</legend>
<span>Here you can manage the default permissions and features that your users get</span> <span>Here you can manage the default permissions and features that your users get</span>
<small>
Note: This will not update your users that are currently there, this is to set the default settings to any users added outside of Ombi e.g. You share your Plex Server with a new user, they will be added into Ombi
automatically and will take the permissions and features you have selected below.
</small>
<h3>Permissions</h3> <h3>Permissions</h3>

View file

@ -53,4 +53,5 @@
<package id="System.Runtime.Extensions" version="4.0.0" targetFramework="net45" /> <package id="System.Runtime.Extensions" version="4.0.0" targetFramework="net45" />
<package id="System.Text.RegularExpressions" version="4.0.0" targetFramework="net45" /> <package id="System.Text.RegularExpressions" version="4.0.0" targetFramework="net45" />
<package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net45" /> <package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net45" />
<package id="TraktApiSharp" version="0.8.0" targetFramework="net45" />
</packages> </packages>