aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs32
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs9
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvService.cs25
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs21
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramQuery.cs27
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs140
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js13
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
11 files changed, 263 insertions, 16 deletions
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 50e7319b9..f92c4932e 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -154,6 +154,23 @@ namespace MediaBrowser.Api.LiveTv
public string MaxEndDate { get; set; }
}
+ [Route("/LiveTv/Programs/Recommended", "GET")]
+ [Api(Description = "Gets available live tv epgs..")]
+ public class GetRecommendedPrograms : IReturn<QueryResult<ProgramInfoDto>>
+ {
+ [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
+ public string UserId { get; set; }
+
+ [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? Limit { get; set; }
+
+ [ApiMember(Name = "IsAiring", Description = "Optional. Filter by programs that are currently airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsAiring { get; set; }
+
+ [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? HasAired { get; set; }
+ }
+
[Route("/LiveTv/Programs/{Id}", "GET")]
[Api(Description = "Gets a live tv program")]
public class GetProgram : IReturn<ProgramInfoDto>
@@ -331,6 +348,21 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(result);
}
+ public object Get(GetRecommendedPrograms request)
+ {
+ var query = new RecommendedProgramQuery
+ {
+ UserId = request.UserId,
+ IsAiring = request.IsAiring,
+ Limit = request.Limit,
+ HasAired = request.HasAired
+ };
+
+ var result = _liveTvManager.GetRecommendedPrograms(query, CancellationToken.None).Result;
+
+ return ToOptimizedResult(result);
+ }
+
public object Post(GetPrograms request)
{
return Get(request);
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 1856182da..31c336932 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -240,5 +240,14 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <returns>GuideInfo.</returns>
GuideInfo GetGuideInfo();
+
+ /// <summary>
+ /// Gets the recommended programs.
+ /// </summary>
+ /// <param name="query">The query.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
+ Task<QueryResult<ProgramInfoDto>> GetRecommendedPrograms(RecommendedProgramQuery query,
+ CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
index 1e535139c..81613c1df 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken);
-
+
/// <summary>
/// Deletes the recording asynchronous.
/// </summary>
@@ -77,7 +78,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
-
+
/// <summary>
/// Gets the channel image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ChannelInfo
/// </summary>
@@ -102,7 +103,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ImageResponseInfo}.</returns>
Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
-
+
/// <summary>
/// Gets the recordings asynchronous.
/// </summary>
@@ -123,21 +124,23 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{TimerInfo}.</returns>
Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken);
-
+
/// <summary>
/// Gets the series timers asynchronous.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{SeriesTimerInfo}}.</returns>
Task<IEnumerable<SeriesTimerInfo>> GetSeriesTimersAsync(CancellationToken cancellationToken);
-
+
/// <summary>
/// Gets the programs asynchronous.
/// </summary>
/// <param name="channelId">The channel identifier.</param>
+ /// <param name="startDateUtc">The start date UTC.</param>
+ /// <param name="endDateUtc">The end date UTC.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
- Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, CancellationToken cancellationToken);
+ Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
/// <summary>
/// Gets the recording stream.
@@ -162,5 +165,13 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CloseLiveStream(string id, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Records the live stream.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task RecordLiveStream(string id, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index abacc0c18..aceb32885 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.LiveTv;
+using System;
namespace MediaBrowser.Controller.LiveTv
{
@@ -28,6 +29,26 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ public bool IsAiring
+ {
+ get
+ {
+ var now = DateTime.UtcNow;
+
+ return now >= ProgramInfo.StartDate && now < ProgramInfo.EndDate;
+ }
+ }
+
+ public bool HasAired
+ {
+ get
+ {
+ var now = DateTime.UtcNow;
+
+ return now >= ProgramInfo.EndDate;
+ }
+ }
+
public override string GetClientTypeName()
{
return "Program";
diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs
index 36c06d4c0..a2a824994 100644
--- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs
+++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs
@@ -32,4 +32,31 @@ namespace MediaBrowser.Model.LiveTv
ChannelIdList = new string[] { };
}
}
+
+ public class RecommendedProgramQuery
+ {
+ /// <summary>
+ /// Gets or sets the user identifier.
+ /// </summary>
+ /// <value>The user identifier.</value>
+ public string UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is airing.
+ /// </summary>
+ /// <value><c>true</c> if this instance is airing; otherwise, <c>false</c>.</value>
+ public bool? IsAiring { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has aired.
+ /// </summary>
+ /// <value><c>null</c> if [has aired] contains no value, <c>true</c> if [has aired]; otherwise, <c>false</c>.</value>
+ public bool? HasAired { get; set; }
+
+ /// <summary>
+ /// The maximum number of items to return
+ /// </summary>
+ /// <value>The limit.</value>
+ public int? Limit { get; set; }
+ }
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 17e17ab70..91766e0f8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -32,6 +32,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly ILogger _logger;
private readonly IItemRepository _itemRepo;
private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager;
private readonly IMediaEncoder _mediaEncoder;
@@ -54,6 +55,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_userManager = userManager;
_libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
+ _userDataManager = userDataManager;
_tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
}
@@ -428,7 +430,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
var returnArray = programs
- .OrderBy(i => i.ProgramInfo.StartDate)
.Select(i =>
{
var channel = GetChannel(i);
@@ -450,6 +451,138 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return result;
}
+ public async Task<QueryResult<ProgramInfoDto>> GetRecommendedPrograms(RecommendedProgramQuery query, CancellationToken cancellationToken)
+ {
+ IEnumerable<LiveTvProgram> programs = _programs.Values;
+
+ var user = _userManager.GetUserById(new Guid(query.UserId));
+
+ // Avoid implicitly captured closure
+ var currentUser = user;
+ programs = programs.Where(i => i.IsParentalAllowed(currentUser));
+
+ if (query.IsAiring.HasValue)
+ {
+ var val = query.IsAiring.Value;
+ programs = programs.Where(i => i.IsAiring == val);
+ }
+
+ if (query.HasAired.HasValue)
+ {
+ var val = query.HasAired.Value;
+ programs = programs.Where(i => i.HasAired == val);
+ }
+
+ var serviceName = ActiveService.Name;
+
+ var programList = programs.ToList();
+
+ var genres = programList.SelectMany(i => i.Genres)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Select(i => _libraryManager.GetGenre(i))
+ .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
+
+ programs = programList.OrderByDescending(i => GetRecommendationScore(i.ProgramInfo, user.Id, serviceName, genres))
+ .ThenBy(i => i.ProgramInfo.StartDate);
+
+ if (query.Limit.HasValue)
+ {
+ programs = programs.Take(query.Limit.Value)
+ .OrderBy(i => i.ProgramInfo.StartDate);
+ }
+
+ var returnArray = programs
+ .Select(i =>
+ {
+ var channel = GetChannel(i);
+
+ var channelName = channel == null ? null : channel.ChannelInfo.Name;
+
+ return _tvDtoService.GetProgramInfoDto(i, channelName, user);
+ })
+ .ToArray();
+
+ await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
+
+ var result = new QueryResult<ProgramInfoDto>
+ {
+ Items = returnArray,
+ TotalRecordCount = returnArray.Length
+ };
+
+ return result;
+ }
+
+ private int GetRecommendationScore(ProgramInfo program, Guid userId, string serviceName, Dictionary<string, Genre> genres)
+ {
+ var score = 0;
+
+ if (program.IsLive)
+ {
+ score++;
+ }
+
+ if (program.IsSeries && !program.IsRepeat)
+ {
+ score++;
+ }
+
+ var internalChannelId = _tvDtoService.GetInternalChannelId(serviceName, program.ChannelId);
+ var channel = GetInternalChannel(internalChannelId);
+
+ var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey());
+
+ if ((channelUserdata.Likes ?? false))
+ {
+ score += 2;
+ }
+ else if (!(channelUserdata.Likes ?? true))
+ {
+ score -= 2;
+ }
+
+ if (channelUserdata.IsFavorite)
+ {
+ score += 3;
+ }
+
+ score += GetGenreScore(program.Genres, userId, genres);
+
+ return score;
+ }
+
+ private int GetGenreScore(IEnumerable<string> programGenres, Guid userId, Dictionary<string, Genre> genres)
+ {
+ return programGenres.Select(i =>
+ {
+ var score = 0;
+
+ Genre genre;
+
+ if (genres.TryGetValue(i, out genre))
+ {
+ var genreUserdata = _userDataManager.GetUserData(userId, genre.GetUserDataKey());
+
+ if ((genreUserdata.Likes ?? false))
+ {
+ score++;
+ }
+ else if (!(genreUserdata.Likes ?? true))
+ {
+ score--;
+ }
+
+ if (genreUserdata.IsFavorite)
+ {
+ score += 2;
+ }
+ }
+
+ return score;
+
+ }).Sum();
+ }
+
private async Task AddRecordingInfo(IEnumerable<ProgramInfoDto> programs, CancellationToken cancellationToken)
{
var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
@@ -533,7 +666,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
try
{
- var channelPrograms = await service.GetProgramsAsync(currentChannel.ChannelInfo.Id, cancellationToken).ConfigureAwait(false);
+ var start = DateTime.UtcNow;
+ var end = start.AddDays(3);
+
+ var channelPrograms = await service.GetProgramsAsync(currentChannel.ChannelInfo.Id, start, end, cancellationToken).ConfigureAwait(false);
var programTasks = channelPrograms.Select(program => GetProgram(program, currentChannel.ChannelInfo.ChannelType, service.Name, cancellationToken));
var programEntities = await Task.WhenAll(programTasks).ConfigureAwait(false);
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index fe0e5e541..b35341ebb 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -438,7 +438,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
options = options || {};
- if (options.channelIds) {
+ if (options.channelIds && options.channelIds.length > 1800) {
return self.ajax({
type: "POST",
@@ -458,6 +458,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
}
};
+ self.getLiveTvRecommendedPrograms = function (options) {
+
+ options = options || {};
+
+ return self.ajax({
+ type: "GET",
+ url: self.getUrl("LiveTv/Programs/Recommended", options),
+ dataType: "json"
+ });
+ };
+
self.getLiveTvRecordings = function (options) {
var url = self.getUrl("LiveTv/Recordings", options || {});
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 6c512e8bb..b28f5655a 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.224" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.226" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index 534957de6..9c011b3f4 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.298</version>
+ <version>3.0.299</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.298" />
+ <dependency id="MediaBrowser.Common" version="3.0.299" />
<dependency id="NLog" version="2.1.0" />
<dependency id="SimpleInjector" version="2.4.0" />
<dependency id="sharpcompress" version="0.10.2" />
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index e6792e1df..d0cd3a306 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.298</version>
+ <version>3.0.299</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index db48a2891..cc15b8c4d 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.298</version>
+ <version>3.0.299</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.298" />
+ <dependency id="MediaBrowser.Common" version="3.0.299" />
</dependencies>
</metadata>
<files>