|
@@ -1,5 +1,3 @@
|
|
|
-#pragma warning disable CS1591
|
|
|
-
|
|
|
using System;
|
|
|
using System.Collections.Concurrent;
|
|
|
using System.Collections.Generic;
|
|
@@ -29,6 +27,9 @@ using Microsoft.Extensions.Logging;
|
|
|
|
|
|
namespace Emby.Server.Implementations.Channels
|
|
|
{
|
|
|
+ /// <summary>
|
|
|
+ /// The LiveTV channel manager.
|
|
|
+ /// </summary>
|
|
|
public class ChannelManager : IChannelManager
|
|
|
{
|
|
|
private readonly IUserManager _userManager;
|
|
@@ -45,7 +46,19 @@ namespace Emby.Server.Implementations.Channels
|
|
|
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>
|
|
|
+ /// <param name="userManager">The user manager.</param>
|
|
|
+ /// <param name="dtoService">The dto service.</param>
|
|
|
+ /// <param name="libraryManager">The library manager.</param>
|
|
|
+ /// <param name="loggerFactory">The logger factory.</param>
|
|
|
+ /// <param name="config">The server configuration manager.</param>
|
|
|
+ /// <param name="fileSystem">The filesystem.</param>
|
|
|
+ /// <param name="userDataManager">The user data manager.</param>
|
|
|
+ /// <param name="jsonSerializer">The JSON serializer.</param>
|
|
|
+ /// <param name="providerManager">The provider manager.</param>
|
|
|
public ChannelManager(
|
|
|
IUserManager userManager,
|
|
|
IDtoService dtoService,
|
|
@@ -72,11 +85,13 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
private static TimeSpan CacheLength => TimeSpan.FromHours(3);
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public void AddParts(IEnumerable<IChannel> channels)
|
|
|
{
|
|
|
Channels = channels.ToArray();
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public bool EnableMediaSourceDisplay(BaseItem item)
|
|
|
{
|
|
|
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
|
@@ -85,6 +100,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return !(channel is IDisableMediaSourceDisplay);
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public bool CanDelete(BaseItem item)
|
|
|
{
|
|
|
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
|
@@ -93,6 +109,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item);
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public bool EnableMediaProbe(BaseItem item)
|
|
|
{
|
|
|
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
|
@@ -101,6 +118,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return channel is ISupportsMediaProbe;
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public Task DeleteItem(BaseItem item)
|
|
|
{
|
|
|
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
|
@@ -127,11 +145,16 @@ namespace Emby.Server.Implementations.Channels
|
|
|
.OrderBy(i => i.Name);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Get the installed channel IDs.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>An <see cref="IEnumerable{T}"/> containing installed channel IDs.</returns>
|
|
|
public IEnumerable<Guid> GetInstalledChannelIds()
|
|
|
{
|
|
|
return GetAllChannels().Select(i => GetInternalChannelId(i.Name));
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public QueryResult<Channel> GetChannelsInternal(ChannelQuery query)
|
|
|
{
|
|
|
var user = query.UserId.Equals(Guid.Empty)
|
|
@@ -249,6 +272,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public QueryResult<BaseItemDto> GetChannels(ChannelQuery query)
|
|
|
{
|
|
|
var user = query.UserId.Equals(Guid.Empty)
|
|
@@ -271,6 +295,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Refreshes the associated channels.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="progress">The progress.</param>
|
|
|
+ /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
|
|
|
+ /// <returns>The completed task.</returns>
|
|
|
public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var allChannelsList = GetAllChannels().ToList();
|
|
@@ -304,14 +334,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
private Channel GetChannelEntity(IChannel channel)
|
|
|
{
|
|
|
- var item = GetChannel(GetInternalChannelId(channel.Name));
|
|
|
-
|
|
|
- if (item == null)
|
|
|
- {
|
|
|
- item = GetChannel(channel, CancellationToken.None).Result;
|
|
|
- }
|
|
|
-
|
|
|
- return item;
|
|
|
+ return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).Result;
|
|
|
}
|
|
|
|
|
|
private List<MediaSourceInfo> GetSavedMediaSources(BaseItem item)
|
|
@@ -350,6 +373,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
_jsonSerializer.SerializeToFile(mediaSources, path);
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken)
|
|
|
{
|
|
|
IEnumerable<MediaSourceInfo> results = GetSavedMediaSources(item);
|
|
@@ -359,6 +383,12 @@ namespace Emby.Server.Implementations.Channels
|
|
|
.ToList();
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the dynamic media sources based on the provided item.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="item">The item.</param>
|
|
|
+ /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
|
|
|
+ /// <returns>The task representing the operation to get the media sources.</returns>
|
|
|
public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var channel = GetChannel(item.ChannelId);
|
|
@@ -403,7 +433,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
private static MediaSourceInfo NormalizeMediaSource(BaseItem item, MediaSourceInfo info)
|
|
|
{
|
|
|
- info.RunTimeTicks = info.RunTimeTicks ?? item.RunTimeTicks;
|
|
|
+ info.RunTimeTicks ??= item.RunTimeTicks;
|
|
|
|
|
|
return info;
|
|
|
}
|
|
@@ -481,31 +511,33 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
private static string GetOfficialRating(ChannelParentalRating rating)
|
|
|
{
|
|
|
- switch (rating)
|
|
|
- {
|
|
|
- case ChannelParentalRating.Adult:
|
|
|
- return "XXX";
|
|
|
- case ChannelParentalRating.UsR:
|
|
|
- return "R";
|
|
|
- case ChannelParentalRating.UsPG13:
|
|
|
- return "PG-13";
|
|
|
- case ChannelParentalRating.UsPG:
|
|
|
- return "PG";
|
|
|
- default:
|
|
|
- return null;
|
|
|
- }
|
|
|
+ return rating switch
|
|
|
+ {
|
|
|
+ ChannelParentalRating.Adult => "XXX",
|
|
|
+ ChannelParentalRating.UsR => "R",
|
|
|
+ ChannelParentalRating.UsPG13 => "PG-13",
|
|
|
+ ChannelParentalRating.UsPG => "PG",
|
|
|
+ _ => null
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a channel with the provided Guid.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id">The Guid.</param>
|
|
|
+ /// <returns>The corresponding channel.</returns>
|
|
|
public Channel GetChannel(Guid id)
|
|
|
{
|
|
|
return _libraryManager.GetItemById(id) as Channel;
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public Channel GetChannel(string id)
|
|
|
{
|
|
|
return _libraryManager.GetItemById(id) as Channel;
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public ChannelFeatures[] GetAllChannelFeatures()
|
|
|
{
|
|
|
return _libraryManager.GetItemIds(
|
|
@@ -516,6 +548,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public ChannelFeatures GetChannelFeatures(string id)
|
|
|
{
|
|
|
if (string.IsNullOrEmpty(id))
|
|
@@ -529,6 +562,11 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Checks whether the provided Guid supports external transfer.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="channelId">The Guid.</param>
|
|
|
+ /// <returns>Whether or not the provided Guid supports external transfer.</returns>
|
|
|
public bool SupportsExternalTransfer(Guid channelId)
|
|
|
{
|
|
|
var channelProvider = GetChannelProvider(channelId);
|
|
@@ -536,6 +574,13 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return channelProvider.GetChannelFeatures().SupportsContentDownloading;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the provided channel's supported features.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="channel">The channel.</param>
|
|
|
+ /// <param name="provider">The provider.</param>
|
|
|
+ /// <param name="features">The features.</param>
|
|
|
+ /// <returns>The supported features.</returns>
|
|
|
public ChannelFeatures GetChannelFeaturesDto(
|
|
|
Channel channel,
|
|
|
IChannel provider,
|
|
@@ -570,6 +615,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel));
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public async Task<QueryResult<BaseItemDto>> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var internalResult = await GetLatestChannelItemsInternal(query, cancellationToken).ConfigureAwait(false);
|
|
@@ -588,6 +634,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public async Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var channels = GetAllChannels().Where(i => i is ISupportsLatestMedia).ToArray();
|
|
@@ -666,6 +713,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(InternalItemsQuery query, IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
{
|
|
|
// Get the internal channel entity
|
|
@@ -727,6 +775,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
return _libraryManager.GetItemsResult(query);
|
|
|
}
|
|
|
|
|
|
+ /// <inheritdoc />
|
|
|
public async Task<QueryResult<BaseItemDto>> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var internalResult = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
|
|
@@ -796,7 +845,7 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
var query = new InternalChannelItemQuery
|
|
|
{
|
|
|
- UserId = user == null ? Guid.Empty : user.Id,
|
|
|
+ UserId = user?.Id ?? Guid.Empty,
|
|
|
SortBy = sortField,
|
|
|
SortDescending = sortDescending,
|
|
|
FolderId = externalFolderId
|
|
@@ -923,60 +972,32 @@ namespace Emby.Server.Implementations.Channels
|
|
|
|
|
|
if (info.Type == ChannelItemType.Folder)
|
|
|
{
|
|
|
- if (info.FolderType == ChannelFolderType.MusicAlbum)
|
|
|
- {
|
|
|
- item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else if (info.FolderType == ChannelFolderType.MusicArtist)
|
|
|
- {
|
|
|
- item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else if (info.FolderType == ChannelFolderType.PhotoAlbum)
|
|
|
- {
|
|
|
- item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else if (info.FolderType == ChannelFolderType.Series)
|
|
|
- {
|
|
|
- item = GetItemById<Series>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else if (info.FolderType == ChannelFolderType.Season)
|
|
|
+ item = info.FolderType switch
|
|
|
{
|
|
|
- item = GetItemById<Season>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- item = GetItemById<Folder>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
+ ChannelFolderType.MusicAlbum => GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ ChannelFolderType.MusicArtist => GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ ChannelFolderType.PhotoAlbum => GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ ChannelFolderType.Series => GetItemById<Series>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ ChannelFolderType.Season => GetItemById<Season>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ _ => GetItemById<Folder>(info.Id, channelProvider.Name, out isNew)
|
|
|
+ };
|
|
|
}
|
|
|
else if (info.MediaType == ChannelMediaType.Audio)
|
|
|
{
|
|
|
- if (info.ContentType == ChannelMediaContentType.Podcast)
|
|
|
- {
|
|
|
- item = GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- item = GetItemById<Audio>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
+ item = info.ContentType == ChannelMediaContentType.Podcast
|
|
|
+ ? GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew)
|
|
|
+ : GetItemById<Audio>(info.Id, channelProvider.Name, out isNew);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (info.ContentType == ChannelMediaContentType.Episode)
|
|
|
+ item = info.ContentType switch
|
|
|
{
|
|
|
- item = GetItemById<Episode>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else if (info.ContentType == ChannelMediaContentType.Movie)
|
|
|
- {
|
|
|
- item = GetItemById<Movie>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer)
|
|
|
- {
|
|
|
- item = GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- item = GetItemById<Video>(info.Id, channelProvider.Name, out isNew);
|
|
|
- }
|
|
|
+ ChannelMediaContentType.Episode => GetItemById<Episode>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ ChannelMediaContentType.Movie => GetItemById<Movie>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ var x when x == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer
|
|
|
+ => GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew),
|
|
|
+ _ => GetItemById<Video>(info.Id, channelProvider.Name, out isNew)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
var enableMediaProbe = channelProvider is ISupportsMediaProbe;
|