aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/Channels/ChannelManager.cs')
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelManager.cs164
1 files changed, 135 insertions, 29 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
index e028065c3..748bc4b9c 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
@@ -23,6 +24,7 @@ namespace MediaBrowser.Server.Implementations.Channels
public class ChannelManager : IChannelManager
{
private IChannel[] _channels;
+ private IChannelFactory[] _factories;
private List<Channel> _channelEntities = new List<Channel>();
private readonly IUserManager _userManager;
@@ -32,8 +34,9 @@ namespace MediaBrowser.Server.Implementations.Channels
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
+ private readonly IJsonSerializer _jsonSerializer;
- public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager)
+ public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer)
{
_userManager = userManager;
_dtoService = dtoService;
@@ -42,11 +45,32 @@ namespace MediaBrowser.Server.Implementations.Channels
_config = config;
_fileSystem = fileSystem;
_userDataManager = userDataManager;
+ _jsonSerializer = jsonSerializer;
}
- public void AddParts(IEnumerable<IChannel> channels)
+ public void AddParts(IEnumerable<IChannel> channels, IEnumerable<IChannelFactory> factories)
{
_channels = channels.ToArray();
+ _factories = factories.ToArray();
+ }
+
+ private IEnumerable<IChannel> GetAllChannels()
+ {
+ return _factories
+ .SelectMany(i =>
+ {
+ try
+ {
+ return i.GetChannels().ToList();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting channel list", ex);
+ return new List<IChannel>();
+ }
+ })
+ .Concat(_channels)
+ .OrderBy(i => i.Name);
}
public Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
@@ -82,7 +106,7 @@ namespace MediaBrowser.Server.Implementations.Channels
public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
{
- var allChannelsList = _channels.ToList();
+ var allChannelsList = GetAllChannels().ToList();
var list = new List<Channel>();
@@ -196,47 +220,123 @@ namespace MediaBrowser.Server.Implementations.Channels
? null
: _userManager.GetUserById(new Guid(query.UserId));
- var id = new Guid(query.ChannelId);
- var channel = _channelEntities.First(i => i.Id == id);
- var channelProvider = GetChannelProvider(channel);
+ var queryChannelId = query.ChannelId;
+ var channels = string.IsNullOrWhiteSpace(queryChannelId)
+ ? _channelEntities
+ : _channelEntities.Where(i => i.Id == new Guid(queryChannelId));
+
+ var itemTasks = channels.Select(async channel =>
+ {
+ var channelProvider = GetChannelProvider(channel);
+
+ var items = await GetChannelItems(channelProvider, user, query.CategoryId, cancellationToken)
+ .ConfigureAwait(false);
+
+ var channelId = channel.Id.ToString("N");
+
+ var tasks = items.Select(i => GetChannelItemEntity(i, channelId, cancellationToken));
+
+ return await Task.WhenAll(tasks).ConfigureAwait(false);
+ });
- var items = await GetChannelItems(channelProvider, user, query.CategoryId, cancellationToken)
- .ConfigureAwait(false);
+ var results = await Task.WhenAll(itemTasks).ConfigureAwait(false);
- return await GetReturnItems(items, user, query, cancellationToken).ConfigureAwait(false);
+ return await GetReturnItems(results.SelectMany(i => i), user, query, cancellationToken).ConfigureAwait(false);
}
+ private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
private async Task<IEnumerable<ChannelItemInfo>> GetChannelItems(IChannel channel, User user, string categoryId, CancellationToken cancellationToken)
{
- // TODO: Put some caching in here
+ var cachePath = GetChannelDataCachePath(channel, user, categoryId);
- var query = new InternalChannelItemQuery
+ try
{
- User = user,
- CategoryId = categoryId
- };
+ var channelItemResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+
+ if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(channelItemResult.CacheLength) > DateTime.UtcNow)
+ {
+ return channelItemResult.Items;
+ }
+ }
+ catch (FileNotFoundException)
+ {
+
+ }
+ catch (DirectoryNotFoundException)
+ {
+
+ }
+
+ await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
- var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ try
+ {
+ var channelItemResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+
+ if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(channelItemResult.CacheLength) > DateTime.UtcNow)
+ {
+ return channelItemResult.Items;
+ }
+ }
+ catch (FileNotFoundException)
+ {
- return result.Items;
+ }
+ catch (DirectoryNotFoundException)
+ {
+
+ }
+
+ var query = new InternalChannelItemQuery
+ {
+ User = user,
+ CategoryId = categoryId
+ };
+
+ var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
+
+ CacheResponse(result, cachePath);
+
+ return result.Items;
+ }
+ finally
+ {
+ _resourcePool.Release();
+ }
}
- private async Task<QueryResult<BaseItemDto>> GetReturnItems(IEnumerable<ChannelItemInfo> items, User user, ChannelItemQuery query, CancellationToken cancellationToken)
+ private void CacheResponse(ChannelItemResult result, string path)
{
- // Get everything
- var fields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
+ try
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
- var tasks = items.Select(i => GetChannelItemEntity(i, cancellationToken));
+ _jsonSerializer.SerializeToFile(result, path);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error writing to channel cache file: {0}", ex, path);
+ }
+ }
- IEnumerable<BaseItem> entities = await Task.WhenAll(tasks).ConfigureAwait(false);
+ private string GetChannelDataCachePath(IChannel channel, User user, string categoryId)
+ {
+ var channelId = GetInternalChannelId(channel.Name).ToString("N");
- entities = ApplyFilters(entities, query.Filters, user);
+ var categoryKey = string.IsNullOrWhiteSpace(categoryId) ? "root" : categoryId.GetMD5().ToString("N");
- entities = _libraryManager.Sort(entities, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending);
+ return Path.Combine(_config.ApplicationPaths.CachePath, "channels", channelId, categoryKey, user.Id.ToString("N") + ".json");
+ }
- var all = entities.ToList();
+ private async Task<QueryResult<BaseItemDto>> GetReturnItems(IEnumerable<BaseItem> items, User user, ChannelItemQuery query, CancellationToken cancellationToken)
+ {
+ items = ApplyFilters(items, query.Filters, user);
+
+ items = _libraryManager.Sort(items, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending);
+
+ var all = items.ToList();
var totalCount = all.Count;
if (query.StartIndex.HasValue)
@@ -250,6 +350,11 @@ namespace MediaBrowser.Server.Implementations.Channels
await RefreshIfNeeded(all, cancellationToken).ConfigureAwait(false);
+ // Get everything
+ var fields = Enum.GetNames(typeof(ItemFields))
+ .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+ .ToList();
+
var returnItemArray = all.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
.ToArray();
@@ -263,10 +368,10 @@ namespace MediaBrowser.Server.Implementations.Channels
private string GetIdToHash(string externalId)
{
// Increment this as needed to force new downloads
- return externalId + "3";
+ return externalId + "4";
}
- private async Task<BaseItem> GetChannelItemEntity(ChannelItemInfo info, CancellationToken cancellationToken)
+ private async Task<BaseItem> GetChannelItemEntity(ChannelItemInfo info, string internalChannnelId, CancellationToken cancellationToken)
{
BaseItem item;
Guid id;
@@ -339,6 +444,7 @@ namespace MediaBrowser.Server.Implementations.Channels
channelItem.OriginalImageUrl = info.ImageUrl;
channelItem.ExternalId = info.Id;
+ channelItem.ChannelId = internalChannnelId;
channelItem.ChannelItemType = info.Type;
var channelMediaItem = item as IChannelMediaItem;
@@ -380,7 +486,7 @@ namespace MediaBrowser.Server.Implementations.Channels
internal IChannel GetChannelProvider(Channel channel)
{
- return _channels.First(i => string.Equals(i.Name, channel.OriginalChannelName, StringComparison.OrdinalIgnoreCase));
+ return GetAllChannels().First(i => string.Equals(i.Name, channel.OriginalChannelName, StringComparison.OrdinalIgnoreCase));
}
private IEnumerable<BaseItem> ApplyFilters(IEnumerable<BaseItem> items, IEnumerable<ItemFilter> filters, User user)