|
@@ -32,8 +32,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
/// </summary>
|
|
|
public class ChannelManager : IChannelManager
|
|
|
{
|
|
|
- internal IChannel[] Channels { get; private set; }
|
|
|
-
|
|
|
private readonly IUserManager _userManager;
|
|
|
private readonly IUserDataManager _userDataManager;
|
|
|
private readonly IDtoService _dtoService;
|
|
@@ -44,6 +42,11 @@ namespace Emby.Server.Implementations.Channels
|
|
|
private readonly IJsonSerializer _jsonSerializer;
|
|
|
private readonly IProviderManager _providerManager;
|
|
|
|
|
|
+ private readonly ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>> _channelItemMediaInfo =
|
|
|
+ new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
|
|
|
+
|
|
|
+ private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="ChannelManager"/> class.
|
|
|
/// </summary>
|
|
@@ -60,7 +63,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
IUserManager userManager,
|
|
|
IDtoService dtoService,
|
|
|
ILibraryManager libraryManager,
|
|
|
- ILoggerFactory loggerFactory,
|
|
|
+ ILogger<ChannelManager> logger,
|
|
|
IServerConfigurationManager config,
|
|
|
IFileSystem fileSystem,
|
|
|
IUserDataManager userDataManager,
|
|
@@ -70,7 +73,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
_userManager = userManager;
|
|
|
_dtoService = dtoService;
|
|
|
_libraryManager = libraryManager;
|
|
|
- _logger = loggerFactory.CreateLogger(nameof(ChannelManager));
|
|
|
+ _logger = logger;
|
|
|
_config = config;
|
|
|
_fileSystem = fileSystem;
|
|
|
_userDataManager = userDataManager;
|
|
@@ -78,6 +81,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
_providerManager = providerManager;
|
|
|
}
|
|
|
|
|
|
+ internal IChannel[] Channels { get; private set; }
|
|
|
+
|
|
|
private static TimeSpan CacheLength => TimeSpan.FromHours(3);
|
|
|
|
|
|
/// <inheritdoc />
|
|
@@ -169,7 +174,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
try
|
|
|
{
|
|
|
return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes
|
|
|
- && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
|
|
|
+ && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
|
|
|
}
|
|
|
catch
|
|
|
{
|
|
@@ -276,11 +281,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
var internalResult = GetChannelsInternal(query);
|
|
|
|
|
|
- var dtoOptions = new DtoOptions()
|
|
|
- {
|
|
|
- };
|
|
|
+ var dtoOptions = new DtoOptions();
|
|
|
|
|
|
- //TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues.
|
|
|
+ // TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues.
|
|
|
var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user);
|
|
|
|
|
|
var result = new QueryResult<BaseItemDto>
|
|
@@ -360,8 +363,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
catch
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
+
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -391,11 +394,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
var channel = GetChannel(item.ChannelId);
|
|
|
var channelPlugin = GetChannelProvider(channel);
|
|
|
|
|
|
- var requiresCallback = channelPlugin as IRequiresMediaInfoCallback;
|
|
|
-
|
|
|
IEnumerable<MediaSourceInfo> results;
|
|
|
|
|
|
- if (requiresCallback != null)
|
|
|
+ if (channelPlugin is IRequiresMediaInfoCallback requiresCallback)
|
|
|
{
|
|
|
results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
|
|
|
.ConfigureAwait(false);
|
|
@@ -410,9 +411,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
.ToList();
|
|
|
}
|
|
|
|
|
|
- private readonly ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>> _channelItemMediaInfo =
|
|
|
- new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
|
|
|
-
|
|
|
private async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken)
|
|
|
{
|
|
|
if (_channelItemMediaInfo.TryGetValue(id, out Tuple<DateTime, List<MediaSourceInfo>> cachedInfo))
|
|
@@ -470,18 +468,21 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
isNew = true;
|
|
|
}
|
|
|
+
|
|
|
item.Path = path;
|
|
|
|
|
|
if (!item.ChannelId.Equals(id))
|
|
|
{
|
|
|
forceUpdate = true;
|
|
|
}
|
|
|
+
|
|
|
item.ChannelId = id;
|
|
|
|
|
|
if (item.ParentId != parentFolderId)
|
|
|
{
|
|
|
forceUpdate = true;
|
|
|
}
|
|
|
+
|
|
|
item.ParentId = parentFolderId;
|
|
|
|
|
|
item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating);
|
|
@@ -498,10 +499,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
_libraryManager.CreateItem(item, null);
|
|
|
}
|
|
|
|
|
|
- await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
|
|
- {
|
|
|
- ForceSave = !isNew && forceUpdate
|
|
|
- }, cancellationToken).ConfigureAwait(false);
|
|
|
+ await item.RefreshMetadata(
|
|
|
+ new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
|
|
+ {
|
|
|
+ ForceSave = !isNew && forceUpdate
|
|
|
+ },
|
|
|
+ cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
return item;
|
|
|
}
|
|
@@ -537,11 +540,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
/// <inheritdoc />
|
|
|
public ChannelFeatures[] GetAllChannelFeatures()
|
|
|
{
|
|
|
- return _libraryManager.GetItemIds(new InternalItemsQuery
|
|
|
- {
|
|
|
- IncludeItemTypes = new[] { typeof(Channel).Name },
|
|
|
- OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
|
|
|
- }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
|
|
|
+ return _libraryManager.GetItemIds(
|
|
|
+ new InternalItemsQuery
|
|
|
+ {
|
|
|
+ IncludeItemTypes = new[] { typeof(Channel).Name },
|
|
|
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
|
|
|
+ }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
@@ -565,7 +569,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
/// <returns>Whether or not the provided Guid supports external transfer.</returns>
|
|
|
public bool SupportsExternalTransfer(Guid channelId)
|
|
|
{
|
|
|
- //var channel = GetChannel(channelId);
|
|
|
var channelProvider = GetChannelProvider(channelId);
|
|
|
|
|
|
return channelProvider.GetChannelFeatures().SupportsContentDownloading;
|
|
@@ -608,6 +611,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
throw new ArgumentNullException(nameof(name));
|
|
|
}
|
|
|
+
|
|
|
return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel));
|
|
|
}
|
|
|
|
|
@@ -657,7 +661,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
query.IsFolder = false;
|
|
|
|
|
|
// hack for trailers, figure out a better way later
|
|
|
- var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.IndexOf("Trailer") != -1;
|
|
|
+ var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.Contains("Trailer", StringComparison.Ordinal);
|
|
|
|
|
|
if (sortByPremiereDate)
|
|
|
{
|
|
@@ -683,10 +687,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
var internalChannel = await GetChannel(channel, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
- var query = new InternalItemsQuery();
|
|
|
- query.Parent = internalChannel;
|
|
|
- query.EnableTotalRecordCount = false;
|
|
|
- query.ChannelIds = new Guid[] { internalChannel.Id };
|
|
|
+ var query = new InternalItemsQuery
|
|
|
+ {
|
|
|
+ Parent = internalChannel,
|
|
|
+ EnableTotalRecordCount = false,
|
|
|
+ ChannelIds = new Guid[] { internalChannel.Id }
|
|
|
+ };
|
|
|
|
|
|
var result = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
|
|
|
|
|
@@ -694,13 +700,15 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
if (item is Folder folder)
|
|
|
{
|
|
|
- await GetChannelItemsInternal(new InternalItemsQuery
|
|
|
- {
|
|
|
- Parent = folder,
|
|
|
- EnableTotalRecordCount = false,
|
|
|
- ChannelIds = new Guid[] { internalChannel.Id }
|
|
|
-
|
|
|
- }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
|
|
|
+ await GetChannelItemsInternal(
|
|
|
+ new InternalItemsQuery
|
|
|
+ {
|
|
|
+ Parent = folder,
|
|
|
+ EnableTotalRecordCount = false,
|
|
|
+ ChannelIds = new Guid[] { internalChannel.Id }
|
|
|
+ },
|
|
|
+ new SimpleProgress<double>(),
|
|
|
+ cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -716,7 +724,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
var parentItem = query.ParentId == Guid.Empty ? channel : _libraryManager.GetItemById(query.ParentId);
|
|
|
|
|
|
- var itemsResult = await GetChannelItems(channelProvider,
|
|
|
+ var itemsResult = await GetChannelItems(
|
|
|
+ channelProvider,
|
|
|
query.User,
|
|
|
parentItem is Channel ? null : parentItem.ExternalId,
|
|
|
null,
|
|
@@ -728,13 +737,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
query.Parent = channel;
|
|
|
}
|
|
|
+
|
|
|
query.ChannelIds = Array.Empty<Guid>();
|
|
|
|
|
|
// Not yet sure why this is causing a problem
|
|
|
query.GroupByPresentationUniqueKey = false;
|
|
|
|
|
|
- //_logger.LogDebug("GetChannelItemsInternal");
|
|
|
-
|
|
|
// null if came from cache
|
|
|
if (itemsResult != null)
|
|
|
{
|
|
@@ -751,11 +759,15 @@ namespace Emby.Server.Implementations.Channels
|
|
|
var deadItem = _libraryManager.GetItemById(deadId);
|
|
|
if (deadItem != null)
|
|
|
{
|
|
|
- _libraryManager.DeleteItem(deadItem, new DeleteOptions
|
|
|
- {
|
|
|
- DeleteFileLocation = false,
|
|
|
- DeleteFromExternalProvider = false
|
|
|
- }, parentItem, false);
|
|
|
+ _libraryManager.DeleteItem(
|
|
|
+ deadItem,
|
|
|
+ new DeleteOptions
|
|
|
+ {
|
|
|
+ DeleteFileLocation = false,
|
|
|
+ DeleteFromExternalProvider = false
|
|
|
+ },
|
|
|
+ parentItem,
|
|
|
+ false);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -779,7 +791,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
|
|
|
private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
|
|
|
User user,
|
|
|
string externalFolderId,
|
|
@@ -805,11 +816,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
catch (FileNotFoundException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
catch (IOException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
|
|
|
await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
@@ -829,11 +838,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
catch (FileNotFoundException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
catch (IOException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
|
|
|
var query = new InternalChannelItemQuery
|
|
@@ -877,7 +884,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private string GetChannelDataCachePath(IChannel channel,
|
|
|
+ private string GetChannelDataCachePath(
|
|
|
+ IChannel channel,
|
|
|
string userId,
|
|
|
string externalFolderId,
|
|
|
ChannelItemSortField? sortField,
|
|
@@ -901,6 +909,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
filename += "-sortField-" + sortField.Value;
|
|
|
}
|
|
|
+
|
|
|
if (sortDescending)
|
|
|
{
|
|
|
filename += "-sortDescending";
|
|
@@ -908,7 +917,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture);
|
|
|
|
|
|
- return Path.Combine(_config.ApplicationPaths.CachePath,
|
|
|
+ return Path.Combine(
|
|
|
+ _config.ApplicationPaths.CachePath,
|
|
|
"channels",
|
|
|
channelId,
|
|
|
version,
|
|
@@ -1045,6 +1055,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
_logger.LogDebug("Forcing update due to TrailerTypes {0}", item.Name);
|
|
|
forceUpdate = true;
|
|
|
}
|
|
|
+
|
|
|
trailer.TrailerTypes = info.TrailerTypes.ToArray();
|
|
|
}
|
|
|
|
|
@@ -1068,6 +1079,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to ChannelId {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
item.ChannelId = internalChannelId;
|
|
|
|
|
|
if (!item.ParentId.Equals(parentFolderId))
|
|
@@ -1075,6 +1087,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to parent folder Id {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
item.ParentId = parentFolderId;
|
|
|
|
|
|
if (item is IHasSeries hasSeries)
|
|
@@ -1084,6 +1097,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to SeriesName {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
hasSeries.SeriesName = info.SeriesName;
|
|
|
}
|
|
|
|
|
@@ -1092,6 +1106,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to ExternalId {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
item.ExternalId = info.Id;
|
|
|
|
|
|
if (item is Audio channelAudioItem)
|
|
@@ -1164,7 +1179,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (isNew || forceUpdate || item.DateLastRefreshed == default(DateTime))
|
|
|
+ if (isNew || forceUpdate || item.DateLastRefreshed == default)
|
|
|
{
|
|
|
_providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
|
|
|
}
|