|
@@ -31,8 +31,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
public class ChannelManager : IChannelManager
|
|
|
{
|
|
|
- internal IChannel[] Channels { get; private set; }
|
|
|
-
|
|
|
private readonly IUserManager _userManager;
|
|
|
private readonly IUserDataManager _userDataManager;
|
|
|
private readonly IDtoService _dtoService;
|
|
@@ -43,11 +41,16 @@ 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);
|
|
|
+
|
|
|
public ChannelManager(
|
|
|
IUserManager userManager,
|
|
|
IDtoService dtoService,
|
|
|
ILibraryManager libraryManager,
|
|
|
- ILoggerFactory loggerFactory,
|
|
|
+ ILogger<ChannelManager> logger,
|
|
|
IServerConfigurationManager config,
|
|
|
IFileSystem fileSystem,
|
|
|
IUserDataManager userDataManager,
|
|
@@ -57,7 +60,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;
|
|
@@ -65,6 +68,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
_providerManager = providerManager;
|
|
|
}
|
|
|
|
|
|
+ internal IChannel[] Channels { get; private set; }
|
|
|
+
|
|
|
private static TimeSpan CacheLength => TimeSpan.FromHours(3);
|
|
|
|
|
|
public void AddParts(IEnumerable<IChannel> channels)
|
|
@@ -85,8 +90,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
|
|
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
|
|
|
|
|
|
- var supportsDelete = channel as ISupportsDelete;
|
|
|
- return supportsDelete != null && supportsDelete.CanDelete(item);
|
|
|
+ return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item);
|
|
|
}
|
|
|
|
|
|
public bool EnableMediaProbe(BaseItem item)
|
|
@@ -146,15 +150,13 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes;
|
|
|
-
|
|
|
- return (hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
|
|
|
+ return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes
|
|
|
+ && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
|
|
|
}
|
|
|
catch
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
}).ToList();
|
|
|
}
|
|
|
|
|
@@ -171,7 +173,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
}).ToList();
|
|
|
}
|
|
|
|
|
@@ -188,9 +189,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
}).ToList();
|
|
|
}
|
|
|
+
|
|
|
if (query.IsFavorite.HasValue)
|
|
|
{
|
|
|
var val = query.IsFavorite.Value;
|
|
@@ -215,7 +216,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
}).ToList();
|
|
|
}
|
|
|
|
|
@@ -226,6 +226,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
all = all.Skip(query.StartIndex.Value).ToList();
|
|
|
}
|
|
|
+
|
|
|
if (query.Limit.HasValue)
|
|
|
{
|
|
|
all = all.Take(query.Limit.Value).ToList();
|
|
@@ -256,11 +257,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>
|
|
@@ -341,8 +340,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
catch
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
+
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -365,11 +364,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);
|
|
@@ -384,9 +381,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))
|
|
@@ -444,18 +438,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);
|
|
@@ -472,10 +469,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;
|
|
|
}
|
|
@@ -509,12 +508,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
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();
|
|
|
}
|
|
|
|
|
|
public ChannelFeatures GetChannelFeatures(string id)
|
|
@@ -532,13 +531,13 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
public bool SupportsExternalTransfer(Guid channelId)
|
|
|
{
|
|
|
- //var channel = GetChannel(channelId);
|
|
|
var channelProvider = GetChannelProvider(channelId);
|
|
|
|
|
|
return channelProvider.GetChannelFeatures().SupportsContentDownloading;
|
|
|
}
|
|
|
|
|
|
- public ChannelFeatures GetChannelFeaturesDto(Channel channel,
|
|
|
+ public ChannelFeatures GetChannelFeaturesDto(
|
|
|
+ Channel channel,
|
|
|
IChannel provider,
|
|
|
InternalChannelFeatures features)
|
|
|
{
|
|
@@ -567,6 +566,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
throw new ArgumentNullException(nameof(name));
|
|
|
}
|
|
|
+
|
|
|
return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel));
|
|
|
}
|
|
|
|
|
@@ -614,7 +614,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)
|
|
|
{
|
|
@@ -640,10 +640,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);
|
|
|
|
|
@@ -651,13 +653,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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -672,7 +676,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,
|
|
@@ -684,13 +689,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)
|
|
|
{
|
|
@@ -707,12 +711,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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -735,7 +742,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,
|
|
@@ -743,7 +749,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
bool sortDescending,
|
|
|
CancellationToken cancellationToken)
|
|
|
{
|
|
|
- var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture);
|
|
|
+ var userId = user?.Id.ToString("N", CultureInfo.InvariantCulture);
|
|
|
|
|
|
var cacheLength = CacheLength;
|
|
|
var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending);
|
|
@@ -761,11 +767,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
catch (FileNotFoundException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
catch (IOException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
|
|
|
await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
@@ -785,11 +789,9 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
catch (FileNotFoundException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
catch (IOException)
|
|
|
{
|
|
|
-
|
|
|
}
|
|
|
|
|
|
var query = new InternalChannelItemQuery
|
|
@@ -833,7 +835,8 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private string GetChannelDataCachePath(IChannel channel,
|
|
|
+ private string GetChannelDataCachePath(
|
|
|
+ IChannel channel,
|
|
|
string userId,
|
|
|
string externalFolderId,
|
|
|
ChannelItemSortField? sortField,
|
|
@@ -843,8 +846,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
var userCacheKey = string.Empty;
|
|
|
|
|
|
- var hasCacheKey = channel as IHasCacheKey;
|
|
|
- if (hasCacheKey != null)
|
|
|
+ if (channel is IHasCacheKey hasCacheKey)
|
|
|
{
|
|
|
userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty;
|
|
|
}
|
|
@@ -858,6 +860,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
filename += "-sortField-" + sortField.Value;
|
|
|
}
|
|
|
+
|
|
|
if (sortDescending)
|
|
|
{
|
|
|
filename += "-sortDescending";
|
|
@@ -865,7 +868,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,
|
|
@@ -981,7 +985,6 @@ namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
item.RunTimeTicks = null;
|
|
|
}
|
|
|
-
|
|
|
else if (isNew || !enableMediaProbe)
|
|
|
{
|
|
|
item.RunTimeTicks = info.RunTimeTicks;
|
|
@@ -1014,26 +1017,24 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- var hasArtists = item as IHasArtist;
|
|
|
- if (hasArtists != null)
|
|
|
+ if (item is IHasArtist hasArtists)
|
|
|
{
|
|
|
hasArtists.Artists = info.Artists.ToArray();
|
|
|
}
|
|
|
|
|
|
- var hasAlbumArtists = item as IHasAlbumArtist;
|
|
|
- if (hasAlbumArtists != null)
|
|
|
+ if (item is IHasAlbumArtist hasAlbumArtists)
|
|
|
{
|
|
|
hasAlbumArtists.AlbumArtists = info.AlbumArtists.ToArray();
|
|
|
}
|
|
|
|
|
|
- var trailer = item as Trailer;
|
|
|
- if (trailer != null)
|
|
|
+ if (item is Trailer trailer)
|
|
|
{
|
|
|
if (!info.TrailerTypes.SequenceEqual(trailer.TrailerTypes))
|
|
|
{
|
|
|
_logger.LogDebug("Forcing update due to TrailerTypes {0}", item.Name);
|
|
|
forceUpdate = true;
|
|
|
}
|
|
|
+
|
|
|
trailer.TrailerTypes = info.TrailerTypes.ToArray();
|
|
|
}
|
|
|
|
|
@@ -1057,6 +1058,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))
|
|
@@ -1064,16 +1066,17 @@ namespace Emby.Server.Implementations.Channels
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to parent folder Id {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
item.ParentId = parentFolderId;
|
|
|
|
|
|
- var hasSeries = item as IHasSeries;
|
|
|
- if (hasSeries != null)
|
|
|
+ if (item is IHasSeries hasSeries)
|
|
|
{
|
|
|
if (!string.Equals(hasSeries.SeriesName, info.SeriesName, StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to SeriesName {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
hasSeries.SeriesName = info.SeriesName;
|
|
|
}
|
|
|
|
|
@@ -1082,24 +1085,23 @@ namespace Emby.Server.Implementations.Channels
|
|
|
forceUpdate = true;
|
|
|
_logger.LogDebug("Forcing update due to ExternalId {0}", item.Name);
|
|
|
}
|
|
|
+
|
|
|
item.ExternalId = info.Id;
|
|
|
|
|
|
- var channelAudioItem = item as Audio;
|
|
|
- if (channelAudioItem != null)
|
|
|
+ if (item is Audio channelAudioItem)
|
|
|
{
|
|
|
channelAudioItem.ExtraType = info.ExtraType;
|
|
|
|
|
|
var mediaSource = info.MediaSources.FirstOrDefault();
|
|
|
- item.Path = mediaSource == null ? null : mediaSource.Path;
|
|
|
+ item.Path = mediaSource?.Path;
|
|
|
}
|
|
|
|
|
|
- var channelVideoItem = item as Video;
|
|
|
- if (channelVideoItem != null)
|
|
|
+ if (item is Video channelVideoItem)
|
|
|
{
|
|
|
channelVideoItem.ExtraType = info.ExtraType;
|
|
|
|
|
|
var mediaSource = info.MediaSources.FirstOrDefault();
|
|
|
- item.Path = mediaSource == null ? null : mediaSource.Path;
|
|
|
+ item.Path = mediaSource?.Path;
|
|
|
}
|
|
|
|
|
|
if (!string.IsNullOrEmpty(info.ImageUrl) && !item.HasImage(ImageType.Primary))
|
|
@@ -1156,7 +1158,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);
|
|
|
}
|