jellyfin-server/Emby.Server.Implementations/LiveTv/LiveTvManager.cs

2469 lines
89 KiB
C#
Raw Normal View History

#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
2020-07-22 23:52:14 +00:00
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
2020-05-20 17:07:53 +00:00
using Jellyfin.Data.Entities;
2020-05-13 02:10:35 +00:00
using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
2014-07-27 22:01:29 +00:00
using MediaBrowser.Common.Extensions;
2015-02-10 19:47:54 +00:00
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
2014-01-12 16:55:38 +00:00
using MediaBrowser.Controller.Configuration;
2013-11-26 02:53:48 +00:00
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
2013-11-24 20:51:45 +00:00
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
2014-01-13 16:25:18 +00:00
using MediaBrowser.Controller.Sorting;
2014-06-07 19:46:24 +00:00
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
2016-10-24 02:45:23 +00:00
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
2016-10-23 19:47:34 +00:00
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
2020-05-20 17:07:53 +00:00
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
2016-11-03 23:35:19 +00:00
namespace Emby.Server.Implementations.LiveTv
{
/// <summary>
/// Class LiveTvManager.
/// </summary>
2014-01-12 06:31:21 +00:00
public class LiveTvManager : ILiveTvManager, IDisposable
{
2020-04-11 10:03:10 +00:00
private const string ExternalServiceTag = "ExternalServiceId";
private const string EtagKey = "ProgramEtag";
2014-01-12 16:55:38 +00:00
private readonly IServerConfigurationManager _config;
2020-06-06 00:15:56 +00:00
private readonly ILogger<LiveTvManager> _logger;
2013-11-24 20:51:45 +00:00
private readonly IItemRepository _itemRepo;
2013-11-26 02:53:48 +00:00
private readonly IUserManager _userManager;
private readonly IDtoService _dtoService;
2014-01-12 15:58:47 +00:00
private readonly IUserDataManager _userDataManager;
2014-01-11 05:49:18 +00:00
private readonly ILibraryManager _libraryManager;
2014-01-15 05:38:08 +00:00
private readonly ITaskManager _taskManager;
private readonly ILocalizationManager _localization;
private readonly IFileSystem _fileSystem;
private readonly IChannelManager _channelManager;
2013-12-15 01:17:57 +00:00
private readonly LiveTvDtoService _tvDtoService;
2013-11-26 02:53:48 +00:00
2019-02-02 20:45:29 +00:00
private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
2018-09-12 17:26:21 +00:00
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
2015-07-23 13:23:22 +00:00
public LiveTvManager(
IServerConfigurationManager config,
ILogger<LiveTvManager> logger,
IItemRepository itemRepo,
IUserDataManager userDataManager,
IDtoService dtoService,
IUserManager userManager,
ILibraryManager libraryManager,
ITaskManager taskManager,
ILocalizationManager localization,
IFileSystem fileSystem,
IChannelManager channelManager,
LiveTvDtoService liveTvDtoService)
2013-11-24 20:51:45 +00:00
{
2014-01-12 16:55:38 +00:00
_config = config;
_logger = logger;
2013-11-24 20:51:45 +00:00
_itemRepo = itemRepo;
2013-12-15 01:17:57 +00:00
_userManager = userManager;
2014-01-11 05:49:18 +00:00
_libraryManager = libraryManager;
_taskManager = taskManager;
2014-06-07 19:46:24 +00:00
_localization = localization;
2015-08-18 15:25:57 +00:00
_fileSystem = fileSystem;
2014-06-07 19:46:24 +00:00
_dtoService = dtoService;
2014-01-12 15:58:47 +00:00
_userDataManager = userDataManager;
2018-09-12 17:26:21 +00:00
_channelManager = channelManager;
_tvDtoService = liveTvDtoService;
2013-11-24 20:51:45 +00:00
}
2019-09-20 10:42:08 +00:00
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
/// <summary>
/// Gets the services.
/// </summary>
/// <value>The services.</value>
public IReadOnlyList<ILiveTvService> Services => _services;
2019-09-20 10:42:08 +00:00
public ITunerHost[] TunerHosts => _tunerHosts;
public IListingsProvider[] ListingProviders => _listingProviders;
2014-07-27 22:01:29 +00:00
private LiveTvOptions GetConfiguration()
{
return _config.GetConfiguration<LiveTvOptions>("livetv");
}
2019-09-20 10:42:08 +00:00
public string GetEmbyTvActiveRecordingPath(string id)
{
return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
}
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="services">The services.</param>
2015-07-23 13:23:22 +00:00
/// <param name="tunerHosts">The tuner hosts.</param>
/// <param name="listingProviders">The listing providers.</param>
public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
{
2017-08-23 19:45:52 +00:00
_services = services.ToArray();
2018-09-12 17:26:21 +00:00
_tunerHosts = tunerHosts.Where(i => i.IsSupported).ToArray();
_listingProviders = listingProviders.ToArray();
2013-12-04 20:55:42 +00:00
2015-03-12 15:51:48 +00:00
foreach (var service in _services)
2014-01-15 05:38:08 +00:00
{
if (service is EmbyTV.EmbyTV embyTv)
2018-09-12 17:26:21 +00:00
{
2019-09-20 10:42:08 +00:00
embyTv.TimerCreated += OnEmbyTvTimerCreated;
embyTv.TimerCancelled += OnEmbyTvTimerCancelled;
2018-09-12 17:26:21 +00:00
}
2014-01-15 05:38:08 +00:00
}
}
2019-09-20 10:42:08 +00:00
private void OnEmbyTvTimerCancelled(object sender, GenericEventArgs<string> e)
2018-09-12 17:26:21 +00:00
{
var timerId = e.Argument;
TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(timerId)));
2018-09-12 17:26:21 +00:00
}
2019-09-20 10:42:08 +00:00
private void OnEmbyTvTimerCreated(object sender, GenericEventArgs<TimerInfo> e)
2016-10-11 06:46:59 +00:00
{
2018-09-12 17:26:21 +00:00
var timer = e.Argument;
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
new TimerEventInfo(timer.Id)
2018-09-12 17:26:21 +00:00
{
ProgramId = _tvDtoService.GetInternalProgramId(timer.ProgramId)
}));
2016-10-11 06:46:59 +00:00
}
2017-03-13 18:57:45 +00:00
public List<NameIdPair> GetTunerHostTypes()
{
return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Type
}).ToList();
}
2017-03-15 19:57:18 +00:00
public Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
2017-03-13 20:42:21 +00:00
{
2017-03-15 19:57:18 +00:00
return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken);
2017-03-13 20:42:21 +00:00
}
2017-10-03 18:39:37 +00:00
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
2013-11-24 20:51:45 +00:00
{
2019-02-09 10:53:07 +00:00
var user = query.UserId == Guid.Empty ? null : _userManager.GetUserById(query.UserId);
2013-11-26 02:53:48 +00:00
2017-10-03 18:39:37 +00:00
var topFolder = GetInternalLiveTvFolder(cancellationToken);
2016-06-20 04:14:47 +00:00
2016-09-30 06:50:06 +00:00
var internalQuery = new InternalItemsQuery(user)
2015-06-01 14:49:23 +00:00
{
2016-09-29 12:55:49 +00:00
IsMovie = query.IsMovie,
IsNews = query.IsNews,
IsKids = query.IsKids,
IsSports = query.IsSports,
IsSeries = query.IsSeries,
2016-05-11 22:08:19 +00:00
IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
2018-09-12 17:26:21 +00:00
TopParentIds = new[] { topFolder.Id },
2016-09-30 06:50:06 +00:00
IsFavorite = query.IsFavorite,
IsLiked = query.IsLiked,
StartIndex = query.StartIndex,
2017-05-21 07:25:49 +00:00
Limit = query.Limit,
DtoOptions = dtoOptions
2016-09-30 06:50:06 +00:00
};
2015-06-01 14:49:23 +00:00
2017-09-04 19:28:22 +00:00
var orderBy = internalQuery.OrderBy.ToList();
2019-10-20 14:08:40 +00:00
orderBy.AddRange(query.SortBy.Select(i => (i, query.SortOrder ?? SortOrder.Ascending)));
2013-11-26 02:53:48 +00:00
2016-09-30 06:50:06 +00:00
if (query.EnableFavoriteSorting)
2013-11-26 02:53:48 +00:00
{
2019-10-20 14:08:40 +00:00
orderBy.Insert(0, (ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
2013-11-26 02:53:48 +00:00
}
2016-09-30 06:50:06 +00:00
if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
2014-09-26 03:47:46 +00:00
{
2019-10-20 14:08:40 +00:00
orderBy.Add((ItemSortBy.SortName, SortOrder.Ascending));
2014-01-10 13:52:01 +00:00
}
2017-09-04 19:28:22 +00:00
internalQuery.OrderBy = orderBy.ToArray();
return _libraryManager.GetItemsResult(internalQuery);
2014-08-14 13:24:30 +00:00
}
2018-09-12 17:26:21 +00:00
public async Task<Tuple<MediaSourceInfo, ILiveStream>> GetChannelStream(string id, string mediaSourceId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
2013-12-28 16:58:13 +00:00
{
2018-09-12 17:26:21 +00:00
if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
{
mediaSourceId = null;
}
2013-12-22 17:16:24 +00:00
2018-09-12 17:26:21 +00:00
var channel = (LiveTvChannel)_libraryManager.GetItemById(id);
bool isVideo = channel.ChannelType == ChannelType.TV;
2019-01-13 20:37:13 +00:00
var service = GetService(channel);
_logger.LogInformation("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
MediaSourceInfo info;
ILiveStream liveStream;
if (service is ISupportsDirectStreamProvider supportsManagedStream)
2016-02-14 17:58:31 +00:00
{
2018-09-12 17:26:21 +00:00
liveStream = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
info = liveStream.MediaSource;
2016-02-14 17:58:31 +00:00
}
2018-09-12 17:26:21 +00:00
else
2015-03-12 15:51:48 +00:00
{
2018-09-12 17:26:21 +00:00
info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
var openedId = info.Id;
Func<Task> closeFn = () => service.CloseLiveStream(openedId, CancellationToken.None);
2013-12-19 21:51:32 +00:00
2018-09-12 17:26:21 +00:00
liveStream = new ExclusiveLiveStream(info, closeFn);
2013-12-21 18:37:34 +00:00
2018-09-12 17:26:21 +00:00
var startTime = DateTime.UtcNow;
await liveStream.Open(cancellationToken).ConfigureAwait(false);
var endTime = DateTime.UtcNow;
_logger.LogInformation("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds);
2016-11-13 21:04:21 +00:00
}
2020-04-11 10:03:10 +00:00
2018-09-12 17:26:21 +00:00
info.RequiresClosing = true;
2016-11-13 21:04:21 +00:00
var idPrefix = service.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_";
2016-11-13 21:04:21 +00:00
2018-09-12 17:26:21 +00:00
info.LiveStreamId = idPrefix + info.Id;
2015-03-29 04:56:39 +00:00
2018-09-12 17:26:21 +00:00
Normalize(info, service, isVideo);
return new Tuple<MediaSourceInfo, ILiveStream>(info, liveStream);
2015-03-29 04:56:39 +00:00
}
2018-09-12 17:26:21 +00:00
public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(BaseItem item, CancellationToken cancellationToken)
2015-03-29 04:56:39 +00:00
{
2016-10-09 07:18:43 +00:00
var baseItem = (LiveTvChannel)item;
var service = GetService(baseItem);
2015-03-29 04:56:39 +00:00
2016-10-09 07:18:43 +00:00
var sources = await service.GetChannelStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
2015-09-01 19:18:25 +00:00
if (sources.Count == 0)
{
throw new NotImplementedException();
}
foreach (var source in sources)
2015-05-18 16:40:20 +00:00
{
2016-10-09 07:18:43 +00:00
Normalize(source, service, baseItem.ChannelType == ChannelType.TV);
2015-05-18 16:40:20 +00:00
}
return sources;
2015-03-29 04:56:39 +00:00
}
2018-09-12 17:26:21 +00:00
private ILiveTvService GetService(LiveTvChannel item)
2016-03-19 21:17:08 +00:00
{
2018-09-12 17:26:21 +00:00
var name = item.ServiceName;
return GetService(name);
2016-03-19 21:17:08 +00:00
}
2018-09-12 17:26:21 +00:00
private ILiveTvService GetService(LiveTvProgram item)
2015-03-12 15:51:48 +00:00
{
2018-09-12 17:26:21 +00:00
var channel = _libraryManager.GetItemById(item.ChannelId) as LiveTvChannel;
return GetService(channel);
2015-03-12 15:51:48 +00:00
}
private ILiveTvService GetService(string name)
2019-12-12 16:25:58 +00:00
=> Array.Find(_services, x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase))
?? throw new KeyNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
"No service with the name '{0}' can be found.",
name));
2015-03-12 15:51:48 +00:00
private static void Normalize(MediaSourceInfo mediaSource, ILiveTvService service, bool isVideo)
2014-06-02 19:32:41 +00:00
{
2018-09-12 17:26:21 +00:00
// Not all of the plugins are setting this
mediaSource.IsInfiniteStream = true;
2015-03-20 05:40:51 +00:00
if (mediaSource.MediaStreams.Count == 0)
2014-10-15 04:11:40 +00:00
{
2015-03-27 20:55:31 +00:00
if (isVideo)
2015-03-20 05:40:51 +00:00
{
2015-03-27 20:55:31 +00:00
mediaSource.MediaStreams.AddRange(new List<MediaStream>
2015-03-20 05:40:51 +00:00
{
2015-03-27 20:55:31 +00:00
new MediaStream
{
Type = MediaStreamType.Video,
// Set the index to -1 because we don't know the exact index of the video stream within the container
2015-03-29 04:56:39 +00:00
Index = -1,
// Set to true if unknown to enable deinterlacing
IsInterlaced = true
2015-03-27 20:55:31 +00:00
},
new MediaStream
{
Type = MediaStreamType.Audio,
// Set the index to -1 because we don't know the exact index of the audio stream within the container
Index = -1
}
});
}
else
{
mediaSource.MediaStreams.AddRange(new List<MediaStream>
2015-03-20 05:40:51 +00:00
{
2015-03-27 20:55:31 +00:00
new MediaStream
{
Type = MediaStreamType.Audio,
// Set the index to -1 because we don't know the exact index of the audio stream within the container
Index = -1
}
});
}
2014-10-15 04:11:40 +00:00
}
2015-03-20 05:40:51 +00:00
// Clean some bad data coming from providers
foreach (var stream in mediaSource.MediaStreams)
2014-10-15 04:11:40 +00:00
{
2015-03-20 05:40:51 +00:00
if (stream.BitRate.HasValue && stream.BitRate <= 0)
{
stream.BitRate = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.Channels.HasValue && stream.Channels <= 0)
{
stream.Channels = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.AverageFrameRate.HasValue && stream.AverageFrameRate <= 0)
{
stream.AverageFrameRate = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.RealFrameRate.HasValue && stream.RealFrameRate <= 0)
{
stream.RealFrameRate = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.Width.HasValue && stream.Width <= 0)
{
stream.Width = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.Height.HasValue && stream.Height <= 0)
{
stream.Height = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.SampleRate.HasValue && stream.SampleRate <= 0)
{
stream.SampleRate = null;
}
2020-04-11 10:03:10 +00:00
2015-03-20 05:40:51 +00:00
if (stream.Level.HasValue && stream.Level <= 0)
{
stream.Level = null;
}
2014-10-15 04:11:40 +00:00
}
2015-03-20 05:40:51 +00:00
var indexes = mediaSource.MediaStreams.Select(i => i.Index).Distinct().ToList();
// If there are duplicate stream indexes, set them all to unknown
if (indexes.Count != mediaSource.MediaStreams.Count)
2014-10-15 04:11:40 +00:00
{
2015-03-20 05:40:51 +00:00
foreach (var stream in mediaSource.MediaStreams)
{
stream.Index = -1;
}
2014-06-02 19:32:41 +00:00
}
2015-05-16 02:36:47 +00:00
// Set the total bitrate if not already supplied
2017-01-21 20:27:07 +00:00
mediaSource.InferTotalBitrate();
2016-02-27 17:41:28 +00:00
if (!(service is EmbyTV.EmbyTV))
{
2017-05-20 16:42:47 +00:00
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
2020-06-14 09:11:11 +00:00
// mediaSource.SupportsDirectPlay = false;
// mediaSource.SupportsDirectStream = false;
2016-03-28 03:37:33 +00:00
mediaSource.SupportsTranscoding = true;
2016-04-18 17:43:00 +00:00
foreach (var stream in mediaSource.MediaStreams)
{
if (stream.Type == MediaStreamType.Video && string.IsNullOrWhiteSpace(stream.NalLengthSize))
{
stream.NalLengthSize = "0";
}
if (stream.Type == MediaStreamType.Video)
{
stream.IsInterlaced = true;
}
2016-04-18 17:43:00 +00:00
}
2016-02-27 17:41:28 +00:00
}
2014-06-02 19:32:41 +00:00
}
2018-09-12 17:26:21 +00:00
private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
2013-11-24 20:51:45 +00:00
{
2018-09-12 17:26:21 +00:00
var parentFolderId = parentFolder.Id;
2013-11-24 20:51:45 +00:00
var isNew = false;
2016-05-17 19:18:50 +00:00
var forceUpdate = false;
2013-11-24 20:51:45 +00:00
2013-12-23 03:46:03 +00:00
var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id);
2013-11-24 20:51:45 +00:00
2016-11-21 08:54:53 +00:00
var item = _libraryManager.GetItemById(id) as LiveTvChannel;
2013-11-24 20:51:45 +00:00
2015-01-17 18:15:09 +00:00
if (item == null)
2013-11-24 20:51:45 +00:00
{
2013-12-19 21:51:32 +00:00
item = new LiveTvChannel
2013-11-24 20:51:45 +00:00
{
Name = channelInfo.Name,
Id = id,
2018-09-12 17:26:21 +00:00
DateCreated = DateTime.UtcNow
2013-11-24 20:51:45 +00:00
};
isNew = true;
}
2018-09-12 17:26:21 +00:00
if (channelInfo.Tags != null)
2015-10-06 02:50:20 +00:00
{
2018-09-12 17:26:21 +00:00
if (!channelInfo.Tags.SequenceEqual(item.Tags, StringComparer.OrdinalIgnoreCase))
{
isNew = true;
}
2020-04-11 10:03:10 +00:00
2018-09-12 17:26:21 +00:00
item.Tags = channelInfo.Tags;
2015-10-06 02:50:20 +00:00
}
2015-11-23 16:04:57 +00:00
if (!item.ParentId.Equals(parentFolderId))
{
isNew = true;
}
2020-04-11 10:03:10 +00:00
2015-11-23 16:04:57 +00:00
item.ParentId = parentFolderId;
2015-10-06 02:50:20 +00:00
item.ChannelType = channelInfo.ChannelType;
2013-12-19 21:51:32 +00:00
item.ServiceName = serviceName;
2017-10-13 05:44:40 +00:00
2018-09-12 17:26:21 +00:00
if (!string.Equals(item.GetProviderId(ExternalServiceTag), serviceName, StringComparison.OrdinalIgnoreCase))
{
forceUpdate = true;
}
2020-04-11 10:03:10 +00:00
2018-09-12 17:26:21 +00:00
item.SetProviderId(ExternalServiceTag, serviceName);
if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
{
forceUpdate = true;
}
2020-04-11 10:03:10 +00:00
2018-09-12 17:26:21 +00:00
item.ExternalId = channelInfo.Id;
2017-10-13 05:44:40 +00:00
if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal))
{
forceUpdate = true;
}
2020-04-11 10:03:10 +00:00
2014-01-23 18:05:41 +00:00
item.Number = channelInfo.Number;
2017-10-13 05:44:40 +00:00
if (!string.Equals(channelInfo.Name, item.Name, StringComparison.Ordinal))
{
forceUpdate = true;
}
2020-04-11 10:03:10 +00:00
2017-10-13 05:44:40 +00:00
item.Name = channelInfo.Name;
2014-08-26 02:30:52 +00:00
2015-11-21 05:01:16 +00:00
if (!item.HasImage(ImageType.Primary))
2015-11-21 04:54:41 +00:00
{
2015-11-21 05:01:16 +00:00
if (!string.IsNullOrWhiteSpace(channelInfo.ImagePath))
{
item.SetImagePath(ImageType.Primary, channelInfo.ImagePath);
2016-05-17 19:18:50 +00:00
forceUpdate = true;
2015-11-21 05:01:16 +00:00
}
else if (!string.IsNullOrWhiteSpace(channelInfo.ImageUrl))
{
item.SetImagePath(ImageType.Primary, channelInfo.ImageUrl);
2016-05-17 19:18:50 +00:00
forceUpdate = true;
2015-11-21 05:01:16 +00:00
}
2015-10-25 17:13:30 +00:00
}
2015-02-10 19:47:54 +00:00
2016-05-17 19:18:50 +00:00
if (isNew)
{
2018-09-12 17:26:21 +00:00
_libraryManager.CreateItem(item, parentFolder);
2016-05-17 19:18:50 +00:00
}
else if (forceUpdate)
{
2018-09-12 17:26:21 +00:00
_libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
2016-05-17 19:18:50 +00:00
}
2013-11-24 20:51:45 +00:00
return item;
}
2013-11-25 16:15:31 +00:00
2016-10-06 18:55:01 +00:00
private Tuple<LiveTvProgram, bool, bool> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
2013-12-21 18:37:34 +00:00
{
2018-09-12 17:26:21 +00:00
var id = _tvDtoService.GetInternalProgramId(info.Id);
2013-12-21 18:37:34 +00:00
2015-06-01 14:49:23 +00:00
var isNew = false;
2015-11-21 05:01:16 +00:00
var forceUpdate = false;
2013-12-21 18:37:34 +00:00
if (!allExistingPrograms.TryGetValue(id, out LiveTvProgram item))
2013-12-21 18:37:34 +00:00
{
2015-06-01 14:49:23 +00:00
isNew = true;
2013-12-21 18:37:34 +00:00
item = new LiveTvProgram
{
Name = info.Name,
Id = id,
DateCreated = DateTime.UtcNow,
2018-09-12 17:26:21 +00:00
DateModified = DateTime.UtcNow
2013-12-21 18:37:34 +00:00
};
2018-09-12 17:26:21 +00:00
if (!string.IsNullOrEmpty(info.Etag))
{
item.SetProviderId(EtagKey, info.Etag);
}
2013-12-21 18:37:34 +00:00
}
2017-09-08 16:13:58 +00:00
if (!string.Equals(info.ShowId, item.ShowId, StringComparison.OrdinalIgnoreCase))
{
item.ShowId = info.ShowId;
forceUpdate = true;
}
2016-10-01 07:06:00 +00:00
var seriesId = info.SeriesId;
2015-11-23 16:04:57 +00:00
if (!item.ParentId.Equals(channel.Id))
{
forceUpdate = true;
}
2020-06-15 21:43:52 +00:00
2015-11-23 16:04:57 +00:00
item.ParentId = channel.Id;
2020-06-14 09:11:11 +00:00
// item.ChannelType = channelType;
2013-12-21 18:37:34 +00:00
2014-01-23 18:05:41 +00:00
item.Audio = info.Audio;
2018-09-12 17:26:21 +00:00
item.ChannelId = channel.Id;
2015-06-01 14:49:23 +00:00
item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
if ((item.CommunityRating ?? 0).Equals(0))
{
item.CommunityRating = null;
}
2016-06-28 04:47:30 +00:00
2014-01-23 18:05:41 +00:00
item.EpisodeTitle = info.EpisodeTitle;
item.ExternalId = info.Id;
2016-10-01 07:06:00 +00:00
if (!string.IsNullOrWhiteSpace(seriesId) && !string.Equals(item.ExternalSeriesId, seriesId, StringComparison.Ordinal))
{
forceUpdate = true;
}
2020-06-15 21:43:52 +00:00
2016-10-01 07:06:00 +00:00
item.ExternalSeriesId = seriesId;
2018-09-12 17:26:21 +00:00
var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
if (isSeries || !string.IsNullOrEmpty(info.EpisodeTitle))
{
item.SeriesName = info.Name;
}
var tags = new List<string>();
if (info.IsLive)
{
tags.Add("Live");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (info.IsPremiere)
{
tags.Add("Premiere");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (info.IsNews)
{
tags.Add("News");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (info.IsSports)
{
tags.Add("Sports");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (info.IsKids)
{
tags.Add("Kids");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (info.IsRepeat)
{
tags.Add("Repeat");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (info.IsMovie)
{
tags.Add("Movie");
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
if (isSeries)
{
tags.Add("Series");
}
item.Tags = tags.ToArray();
item.Genres = info.Genres.ToArray();
if (info.IsHD ?? false)
{
item.Width = 1280;
item.Height = 720;
}
2014-01-23 18:05:41 +00:00
item.IsMovie = info.IsMovie;
item.IsRepeat = info.IsRepeat;
2018-09-12 17:26:21 +00:00
if (item.IsSeries != isSeries)
{
forceUpdate = true;
}
2020-06-15 21:43:52 +00:00
2018-09-12 17:26:21 +00:00
item.IsSeries = isSeries;
2014-01-23 18:05:41 +00:00
item.Name = info.Name;
2015-06-01 14:49:23 +00:00
item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
item.Overview = item.Overview ?? info.Overview;
2014-01-23 18:05:41 +00:00
item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
2018-09-12 17:26:21 +00:00
item.ProviderIds = info.ProviderIds;
foreach (var providerId in info.SeriesProviderIds)
{
info.ProviderIds["Series" + providerId.Key] = providerId.Value;
}
2016-06-28 04:47:30 +00:00
if (item.StartDate != info.StartDate)
{
forceUpdate = true;
}
2020-06-15 21:43:52 +00:00
2014-01-23 18:05:41 +00:00
item.StartDate = info.StartDate;
2016-06-28 04:47:30 +00:00
if (item.EndDate != info.EndDate)
{
forceUpdate = true;
}
2020-06-15 21:43:52 +00:00
2016-06-28 04:47:30 +00:00
item.EndDate = info.EndDate;
2015-03-31 18:50:08 +00:00
item.ProductionYear = info.ProductionYear;
2016-09-24 06:22:03 +00:00
2018-09-12 17:26:21 +00:00
if (!isSeries || info.IsRepeat)
2016-09-24 06:22:03 +00:00
{
item.PremiereDate = info.OriginalAirDate;
}
2015-04-16 03:23:13 +00:00
2015-08-19 06:12:58 +00:00
item.IndexNumber = info.EpisodeNumber;
item.ParentIndexNumber = info.SeasonNumber;
2015-11-02 19:29:40 +00:00
if (!item.HasImage(ImageType.Primary))
2015-10-25 17:13:30 +00:00
{
2015-11-02 19:29:40 +00:00
if (!string.IsNullOrWhiteSpace(info.ImagePath))
{
2015-11-21 00:27:34 +00:00
item.SetImage(new ItemImageInfo
{
Path = info.ImagePath,
2017-06-11 21:58:49 +00:00
Type = ImageType.Primary
2015-11-21 00:27:34 +00:00
}, 0);
2015-11-02 19:29:40 +00:00
}
else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
{
2015-11-21 00:27:34 +00:00
item.SetImage(new ItemImageInfo
{
Path = info.ImageUrl,
2017-06-11 21:58:49 +00:00
Type = ImageType.Primary
2015-11-21 00:27:34 +00:00
}, 0);
2015-11-02 19:29:40 +00:00
}
2015-10-25 17:13:30 +00:00
}
2015-11-21 05:01:16 +00:00
2017-06-11 20:40:25 +00:00
if (!item.HasImage(ImageType.Thumb))
{
if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
{
item.SetImage(new ItemImageInfo
{
2017-06-11 21:58:49 +00:00
Path = info.ThumbImageUrl,
Type = ImageType.Thumb
}, 0);
}
}
if (!item.HasImage(ImageType.Logo))
{
if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
{
item.SetImage(new ItemImageInfo
{
Path = info.LogoImageUrl,
Type = ImageType.Logo
}, 0);
}
}
if (!item.HasImage(ImageType.Backdrop))
{
if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
{
item.SetImage(new ItemImageInfo
{
Path = info.BackdropImageUrl,
Type = ImageType.Backdrop
2017-06-11 20:40:25 +00:00
}, 0);
}
}
2016-10-06 18:55:01 +00:00
var isUpdated = false;
2015-06-01 14:49:23 +00:00
if (isNew)
{
}
2015-11-21 05:01:16 +00:00
else if (forceUpdate || string.IsNullOrWhiteSpace(info.Etag))
2015-09-21 16:26:05 +00:00
{
2016-10-06 18:55:01 +00:00
isUpdated = true;
2015-09-21 16:26:05 +00:00
}
2015-06-01 14:49:23 +00:00
else
{
2018-09-12 17:26:21 +00:00
var etag = info.Etag;
2015-09-21 16:26:05 +00:00
2018-09-12 17:26:21 +00:00
if (!string.Equals(etag, item.GetProviderId(EtagKey), StringComparison.OrdinalIgnoreCase))
2015-08-22 02:59:10 +00:00
{
2018-09-12 17:26:21 +00:00
item.SetProviderId(EtagKey, etag);
2016-10-06 18:55:01 +00:00
isUpdated = true;
2015-08-22 02:59:10 +00:00
}
2015-06-01 14:49:23 +00:00
}
2017-09-13 18:41:48 +00:00
if (isNew || isUpdated)
{
item.OnMetadataChanged();
}
2016-10-06 18:55:01 +00:00
return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
2013-12-21 18:37:34 +00:00
}
2020-05-20 17:07:53 +00:00
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
2013-12-17 20:02:12 +00:00
{
2018-09-12 17:26:21 +00:00
var program = _libraryManager.GetItemById(id);
2013-12-21 18:37:34 +00:00
2015-05-31 19:12:58 +00:00
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
2013-12-21 18:37:34 +00:00
2020-05-13 02:10:35 +00:00
var list = new List<Tuple<BaseItemDto, string, string>>
{
new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId)
};
2015-11-21 05:01:16 +00:00
await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
2013-12-17 20:02:12 +00:00
2013-12-21 18:37:34 +00:00
return dto;
2013-12-17 20:02:12 +00:00
}
2018-09-12 17:26:21 +00:00
public async Task<QueryResult<BaseItemDto>> GetPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
2013-11-25 16:15:31 +00:00
{
2018-09-12 17:26:21 +00:00
var user = query.User;
2015-11-21 05:01:16 +00:00
2017-10-03 18:39:37 +00:00
var topFolder = GetInternalLiveTvFolder(cancellationToken);
2016-06-20 04:14:47 +00:00
2019-10-20 14:08:40 +00:00
if (query.OrderBy.Count == 0)
2016-06-20 17:07:53 +00:00
{
2020-05-25 21:52:51 +00:00
// Unless something else was specified, order by start date to take advantage of a specialized index
query.OrderBy = new[]
2018-09-12 17:26:21 +00:00
{
2020-05-25 21:52:51 +00:00
(ItemSortBy.StartDate, SortOrder.Ascending)
};
2016-06-20 17:07:53 +00:00
}
2017-05-21 07:25:49 +00:00
RemoveFields(options);
2015-11-23 16:04:57 +00:00
var internalQuery = new InternalItemsQuery(user)
2014-01-06 01:59:21 +00:00
{
2015-06-01 14:49:23 +00:00
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
MinEndDate = query.MinEndDate,
MinStartDate = query.MinStartDate,
MaxEndDate = query.MaxEndDate,
MaxStartDate = query.MaxStartDate,
ChannelIds = query.ChannelIds,
IsMovie = query.IsMovie,
2016-09-29 12:55:49 +00:00
IsSeries = query.IsSeries,
2015-07-28 12:33:30 +00:00
IsSports = query.IsSports,
2015-08-04 14:26:36 +00:00
IsKids = query.IsKids,
2016-09-29 12:55:49 +00:00
IsNews = query.IsNews,
2015-08-25 03:13:04 +00:00
Genres = query.Genres,
2017-12-03 22:15:21 +00:00
GenreIds = query.GenreIds,
2015-08-25 03:13:04 +00:00
StartIndex = query.StartIndex,
Limit = query.Limit,
2017-09-04 19:28:22 +00:00
OrderBy = query.OrderBy,
2016-06-20 04:14:47 +00:00
EnableTotalRecordCount = query.EnableTotalRecordCount,
2018-09-12 17:26:21 +00:00
TopParentIds = new[] { topFolder.Id },
Name = query.Name,
2018-09-12 17:26:21 +00:00
DtoOptions = options,
HasAired = query.HasAired,
IsAiring = query.IsAiring
2015-06-01 14:49:23 +00:00
};
2014-01-06 01:59:21 +00:00
2016-10-01 07:06:00 +00:00
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
2016-10-05 15:20:11 +00:00
var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
2016-10-01 07:06:00 +00:00
if (seriesTimer != null)
{
internalQuery.ExternalSeriesId = seriesTimer.SeriesId;
if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId))
{
// Better to return nothing than every program in the database
return new QueryResult<BaseItemDto>();
}
}
else
{
// Better to return nothing than every program in the database
return new QueryResult<BaseItemDto>();
}
}
2015-08-25 03:13:04 +00:00
var queryResult = _libraryManager.QueryItems(internalQuery);
2013-12-21 18:37:34 +00:00
2017-08-28 00:33:05 +00:00
var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
2013-11-25 20:39:23 +00:00
2015-05-31 19:12:58 +00:00
var result = new QueryResult<BaseItemDto>
2013-11-26 21:36:11 +00:00
{
Items = returnArray,
2015-08-25 03:13:04 +00:00
TotalRecordCount = queryResult.TotalRecordCount
2013-11-26 21:36:11 +00:00
};
2013-12-17 20:02:12 +00:00
2013-12-21 18:37:34 +00:00
return result;
}
2018-09-12 17:26:21 +00:00
public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
2014-01-12 15:58:47 +00:00
{
2018-09-12 17:26:21 +00:00
var user = query.User;
2015-11-21 05:01:16 +00:00
2017-10-03 18:39:37 +00:00
var topFolder = GetInternalLiveTvFolder(cancellationToken);
2016-06-20 04:14:47 +00:00
2015-11-23 16:04:57 +00:00
var internalQuery = new InternalItemsQuery(user)
2014-01-12 15:58:47 +00:00
{
2015-06-01 14:49:23 +00:00
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
IsAiring = query.IsAiring,
2018-09-12 17:26:21 +00:00
HasAired = query.HasAired,
2016-09-29 12:55:49 +00:00
IsNews = query.IsNews,
2015-06-01 14:49:23 +00:00
IsMovie = query.IsMovie,
2016-09-29 12:55:49 +00:00
IsSeries = query.IsSeries,
2015-08-04 14:26:36 +00:00
IsSports = query.IsSports,
2016-05-09 03:13:38 +00:00
IsKids = query.IsKids,
2016-05-24 07:40:45 +00:00
EnableTotalRecordCount = query.EnableTotalRecordCount,
2019-10-20 14:08:40 +00:00
OrderBy = new[] { (ItemSortBy.StartDate, SortOrder.Ascending) },
2018-09-12 17:26:21 +00:00
TopParentIds = new[] { topFolder.Id },
2017-12-05 18:30:49 +00:00
DtoOptions = options,
GenreIds = query.GenreIds
2015-06-01 14:49:23 +00:00
};
2014-01-12 15:58:47 +00:00
2016-05-24 07:40:45 +00:00
if (query.Limit.HasValue)
{
2016-06-12 05:03:52 +00:00
internalQuery.Limit = Math.Max(query.Limit.Value * 4, 200);
2016-05-24 07:40:45 +00:00
}
2017-08-19 19:43:35 +00:00
var programList = _libraryManager.QueryItems(internalQuery).Items;
var totalCount = programList.Count;
2015-04-03 16:31:56 +00:00
2019-01-13 20:37:13 +00:00
var orderedPrograms = programList.Cast<LiveTvProgram>().OrderBy(i => i.StartDate.Date);
2014-01-12 15:58:47 +00:00
2017-04-04 05:20:30 +00:00
if (query.IsAiring ?? false)
{
orderedPrograms = orderedPrograms
2018-09-12 17:26:21 +00:00
.ThenByDescending(i => GetRecommendationScore(i, user, true));
2017-04-04 05:20:30 +00:00
}
2014-01-12 15:58:47 +00:00
2017-08-19 19:43:35 +00:00
IEnumerable<BaseItem> programs = orderedPrograms;
2014-01-12 15:58:47 +00:00
if (query.Limit.HasValue)
{
2015-08-24 02:08:20 +00:00
programs = programs.Take(query.Limit.Value);
2014-01-12 15:58:47 +00:00
}
return new QueryResult<BaseItem>
{
Items = programs.ToArray(),
TotalRecordCount = totalCount
};
}
2018-09-12 17:26:21 +00:00
public QueryResult<BaseItemDto> GetRecommendedPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
{
2018-09-12 17:26:21 +00:00
if (!(query.IsAiring ?? false))
{
return GetPrograms(query, options, cancellationToken).Result;
}
2017-05-21 07:25:49 +00:00
RemoveFields(options);
2017-10-03 18:39:37 +00:00
var internalResult = GetRecommendedProgramsInternal(query, options, cancellationToken);
return new QueryResult<BaseItemDto>
{
Items = _dtoService.GetBaseItemDtos(internalResult.Items, options, query.User),
TotalRecordCount = internalResult.TotalRecordCount
};
2014-01-12 15:58:47 +00:00
}
2020-05-20 17:07:53 +00:00
private int GetRecommendationScore(LiveTvProgram program, User user, bool factorChannelWatchCount)
2014-01-12 15:58:47 +00:00
{
var score = 0;
if (program.IsLive)
{
score++;
}
if (program.IsSeries && !program.IsRepeat)
{
score++;
}
2018-09-12 17:26:21 +00:00
var channel = _libraryManager.GetItemById(program.ChannelId);
2014-01-12 15:58:47 +00:00
if (channel == null)
2014-01-12 15:58:47 +00:00
{
return score;
}
2014-01-12 15:58:47 +00:00
var channelUserdata = _userDataManager.GetUserData(user, channel);
2014-01-12 15:58:47 +00:00
if (channelUserdata.Likes.HasValue)
{
score += channelUserdata.Likes.Value ? 2 : -2;
}
2017-03-26 04:21:32 +00:00
if (channelUserdata.IsFavorite)
{
score += 3;
}
if (factorChannelWatchCount)
{
score += channelUserdata.PlayCount;
2016-05-24 07:40:45 +00:00
}
2014-01-12 15:58:47 +00:00
2016-05-24 07:40:45 +00:00
return score;
2014-01-12 15:58:47 +00:00
}
2018-09-12 17:26:21 +00:00
private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string>> programs, CancellationToken cancellationToken)
2013-12-21 18:37:34 +00:00
{
IReadOnlyList<TimerInfo> timerList = null;
IReadOnlyList<SeriesTimerInfo> seriesTimerList = null;
2018-09-12 17:26:21 +00:00
2015-11-21 05:01:16 +00:00
foreach (var programTuple in programs)
2013-12-21 18:37:34 +00:00
{
2015-11-21 05:01:16 +00:00
var program = programTuple.Item1;
2018-09-12 17:26:21 +00:00
var externalProgramId = programTuple.Item2;
string externalSeriesId = programTuple.Item3;
2015-10-28 19:40:38 +00:00
2018-09-12 17:26:21 +00:00
if (timerList == null)
2015-03-19 16:16:33 +00:00
{
2018-09-12 17:26:21 +00:00
timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
2015-03-19 16:16:33 +00:00
}
2015-11-21 05:01:16 +00:00
var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase));
2016-09-19 15:41:35 +00:00
var foundSeriesTimer = false;
2013-12-21 18:37:34 +00:00
if (timer != null)
{
2016-10-09 07:18:43 +00:00
if (timer.Status != RecordingStatus.Cancelled && timer.Status != RecordingStatus.Error)
{
2018-09-12 17:26:21 +00:00
program.TimerId = _tvDtoService.GetInternalTimerId(timer.Id);
2013-12-21 18:37:34 +00:00
2016-10-09 07:18:43 +00:00
program.Status = timer.Status.ToString();
}
2016-10-04 05:15:39 +00:00
2013-12-21 18:37:34 +00:00
if (!string.IsNullOrEmpty(timer.SeriesTimerId))
{
2018-09-12 17:26:21 +00:00
program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId)
.ToString("N", CultureInfo.InvariantCulture);
2016-09-19 15:41:35 +00:00
foundSeriesTimer = true;
2013-12-21 18:37:34 +00:00
}
}
2016-09-19 15:41:35 +00:00
if (foundSeriesTimer || string.IsNullOrWhiteSpace(externalSeriesId))
{
continue;
}
2018-09-12 17:26:21 +00:00
if (seriesTimerList == null)
2016-09-19 15:41:35 +00:00
{
2018-09-12 17:26:21 +00:00
seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
2016-09-19 15:41:35 +00:00
}
var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
2018-09-12 17:26:21 +00:00
program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id)
.ToString("N", CultureInfo.InvariantCulture);
2016-09-19 15:41:35 +00:00
}
2013-12-21 18:37:34 +00:00
}
2013-11-25 21:53:06 +00:00
}
2015-06-01 14:49:23 +00:00
internal Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
2014-02-27 16:25:04 +00:00
{
2015-06-01 14:49:23 +00:00
return RefreshChannelsInternal(progress, cancellationToken);
2014-02-27 16:25:04 +00:00
}
private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
2013-11-25 21:53:06 +00:00
{
2018-09-12 17:26:21 +00:00
await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false);
2016-05-22 17:07:30 +00:00
2017-03-13 04:49:10 +00:00
await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
2015-03-19 16:16:33 +00:00
var numComplete = 0;
2017-08-23 19:45:52 +00:00
double progressPerService = _services.Length == 0
2015-03-19 16:16:33 +00:00
? 0
: 1.0 / _services.Length;
2013-11-25 21:53:06 +00:00
2015-06-01 14:49:23 +00:00
var newChannelIdList = new List<Guid>();
var newProgramIdList = new List<Guid>();
var cleanDatabase = true;
2015-03-19 16:16:33 +00:00
foreach (var service in _services)
{
2015-03-19 16:16:33 +00:00
cancellationToken.ThrowIfCancellationRequested();
2018-12-20 12:11:26 +00:00
_logger.LogDebug("Refreshing guide from {name}", service.Name);
2015-03-19 16:16:33 +00:00
try
{
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(p * progressPerService));
2015-06-01 14:49:23 +00:00
var idList = await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
newChannelIdList.AddRange(idList.Item1);
newProgramIdList.AddRange(idList.Item2);
2015-03-19 16:16:33 +00:00
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
cleanDatabase = false;
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error refreshing channels for service");
2015-03-19 16:16:33 +00:00
}
numComplete++;
double percent = numComplete;
2017-08-23 19:45:52 +00:00
percent /= _services.Length;
2015-03-19 16:16:33 +00:00
progress.Report(100 * percent);
}
2013-11-25 21:53:06 +00:00
if (cleanDatabase)
{
2018-09-12 17:26:21 +00:00
CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken);
CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken);
}
2015-06-01 14:49:23 +00:00
2015-09-18 01:51:22 +00:00
var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
if (coreService != null)
{
await coreService.RefreshSeriesTimers(cancellationToken).ConfigureAwait(false);
await coreService.RefreshTimers(cancellationToken).ConfigureAwait(false);
2015-09-18 01:51:22 +00:00
}
2015-06-01 14:49:23 +00:00
// Load these now which will prefetch metadata
var dtoOptions = new DtoOptions();
2017-08-19 19:43:35 +00:00
var fields = dtoOptions.Fields.ToList();
fields.Remove(ItemFields.BasicSyncInfo);
2018-12-28 15:48:26 +00:00
dtoOptions.Fields = fields.ToArray();
2017-08-19 19:43:35 +00:00
2015-03-19 16:16:33 +00:00
progress.Report(100);
}
2015-06-01 17:07:55 +00:00
private async Task<Tuple<List<Guid>, List<Guid>>> RefreshChannelsInternal(ILiveTvService service, IProgress<double> progress, CancellationToken cancellationToken)
2015-03-19 16:16:33 +00:00
{
2013-11-25 21:53:06 +00:00
progress.Report(10);
var allChannelsList = (await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false))
.Select(i => new Tuple<string, ChannelInfo>(service.Name, i))
.ToList();
2013-11-25 21:53:06 +00:00
2013-12-19 21:51:32 +00:00
var list = new List<LiveTvChannel>();
2013-11-25 21:53:06 +00:00
var numComplete = 0;
2017-10-03 18:39:37 +00:00
var parentFolder = GetInternalLiveTvFolder(cancellationToken);
2013-11-25 21:53:06 +00:00
foreach (var channelInfo in allChannelsList)
2013-11-25 21:53:06 +00:00
{
2014-02-09 21:11:11 +00:00
cancellationToken.ThrowIfCancellationRequested();
2013-11-25 21:53:06 +00:00
try
{
2018-09-12 17:26:21 +00:00
var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
2013-11-25 21:53:06 +00:00
2014-01-03 04:58:22 +00:00
list.Add(item);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error getting channel information for {name}", channelInfo.Item2.Name);
2014-01-03 04:58:22 +00:00
}
numComplete++;
double percent = numComplete;
percent /= allChannelsList.Count;
progress.Report(5 * percent + 10);
}
2014-01-11 05:49:18 +00:00
2014-01-03 04:58:22 +00:00
progress.Report(15);
2014-01-11 23:07:56 +00:00
numComplete = 0;
2015-06-01 14:49:23 +00:00
var programs = new List<Guid>();
var channels = new List<Guid>();
2014-01-03 04:58:22 +00:00
2016-08-05 20:25:09 +00:00
var guideDays = GetGuideDays();
2014-01-12 16:55:38 +00:00
_logger.LogInformation("Refreshing guide with {0} days of guide data", guideDays);
2015-11-21 05:01:16 +00:00
2014-02-09 21:11:11 +00:00
cancellationToken.ThrowIfCancellationRequested();
2015-06-01 14:49:23 +00:00
foreach (var currentChannel in list)
2014-01-03 04:58:22 +00:00
{
2015-06-01 14:49:23 +00:00
channels.Add(currentChannel.Id);
2014-02-09 21:11:11 +00:00
cancellationToken.ThrowIfCancellationRequested();
2014-01-03 04:58:22 +00:00
try
{
2014-01-12 16:55:38 +00:00
var start = DateTime.UtcNow.AddHours(-1);
var end = start.AddDays(guideDays);
2014-01-12 15:58:47 +00:00
2016-09-29 12:55:49 +00:00
var isMovie = false;
var isSports = false;
var isNews = false;
var isKids = false;
var iSSeries = false;
2018-09-12 17:26:21 +00:00
var channelPrograms = (await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false)).ToList();
2013-11-25 21:53:06 +00:00
2016-10-05 15:20:11 +00:00
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
2018-09-12 17:26:21 +00:00
ChannelIds = new Guid[] { currentChannel.Id },
2017-05-21 07:25:49 +00:00
DtoOptions = new DtoOptions(true)
2016-10-05 15:20:11 +00:00
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
2016-10-06 18:55:01 +00:00
var newPrograms = new List<LiveTvProgram>();
2018-09-12 17:26:21 +00:00
var updatedPrograms = new List<BaseItem>();
2016-10-06 18:55:01 +00:00
2015-03-16 01:48:25 +00:00
foreach (var program in channelPrograms)
{
2016-10-06 18:55:01 +00:00
var programTuple = GetProgram(program, existingPrograms, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken);
var programItem = programTuple.Item1;
if (programTuple.Item2)
{
newPrograms.Add(programItem);
}
else if (programTuple.Item3)
{
updatedPrograms.Add(programItem);
}
2015-09-21 16:26:05 +00:00
2015-06-01 14:49:23 +00:00
programs.Add(programItem.Id);
2016-09-29 12:55:49 +00:00
isMovie |= program.IsMovie;
iSSeries |= program.IsSeries;
isSports |= program.IsSports;
isNews |= program.IsNews;
isKids |= program.IsKids;
2015-03-16 01:48:25 +00:00
}
2016-09-29 12:55:49 +00:00
_logger.LogDebug("Channel {0} has {1} new programs and {2} updated programs", currentChannel.Name, newPrograms.Count, updatedPrograms.Count);
2016-10-08 05:57:38 +00:00
2016-10-06 18:55:01 +00:00
if (newPrograms.Count > 0)
{
2017-11-26 04:48:12 +00:00
_libraryManager.CreateItems(newPrograms, null, cancellationToken);
2016-10-06 18:55:01 +00:00
}
2018-09-12 17:26:21 +00:00
if (updatedPrograms.Count > 0)
2016-10-06 18:55:01 +00:00
{
2018-09-12 17:26:21 +00:00
_libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
2016-10-06 18:55:01 +00:00
}
2016-09-29 12:55:49 +00:00
currentChannel.IsMovie = isMovie;
currentChannel.IsNews = isNews;
currentChannel.IsSports = isSports;
currentChannel.IsSeries = iSSeries;
2018-09-12 17:26:21 +00:00
if (isKids)
{
currentChannel.AddTag("Kids");
}
2019-09-10 20:37:53 +00:00
currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
await currentChannel.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
ForceSave = true
},
cancellationToken).ConfigureAwait(false);
2013-11-25 21:53:06 +00:00
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error getting programs for channel {name}", currentChannel.Name);
2013-11-25 21:53:06 +00:00
}
numComplete++;
double percent = numComplete / (double)allChannelsList.Count;
2013-11-25 21:53:06 +00:00
2019-09-10 20:37:53 +00:00
progress.Report((85 * percent) + 15);
2013-11-25 21:53:06 +00:00
}
2014-02-27 16:25:04 +00:00
2018-09-12 17:26:21 +00:00
progress.Report(100);
2015-06-01 17:07:55 +00:00
return new Tuple<List<Guid>, List<Guid>>(channels, programs);
2015-02-10 19:47:54 +00:00
}
2018-09-12 17:26:21 +00:00
private void CleanDatabaseInternal(Guid[] currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
{
2016-05-09 03:13:38 +00:00
var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
2015-06-01 14:49:23 +00:00
{
2017-05-21 07:25:49 +00:00
IncludeItemTypes = validTypes,
DtoOptions = new DtoOptions(false)
2017-08-19 19:43:35 +00:00
});
var numComplete = 0;
2015-06-01 14:49:23 +00:00
foreach (var itemId in list)
{
cancellationToken.ThrowIfCancellationRequested();
2018-09-12 17:26:21 +00:00
if (itemId.Equals(Guid.Empty))
{
// Somehow some invalid data got into the db. It probably predates the boundary checking
continue;
}
2015-06-01 14:49:23 +00:00
if (!currentIdList.Contains(itemId))
{
2015-06-01 14:49:23 +00:00
var item = _libraryManager.GetItemById(itemId);
2015-03-21 16:10:02 +00:00
2015-06-01 14:49:23 +00:00
if (item != null)
2015-03-21 16:10:02 +00:00
{
2019-09-10 20:37:53 +00:00
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false,
DeleteFromExternalProvider = false
},
false);
2015-03-21 16:10:02 +00:00
}
}
numComplete++;
double percent = numComplete / (double)list.Count;
2014-02-27 16:25:04 +00:00
progress.Report(100 * percent);
}
}
2015-11-21 05:01:16 +00:00
private const int MaxGuideDays = 14;
2016-08-05 20:25:09 +00:00
private double GetGuideDays()
2014-01-12 16:55:38 +00:00
{
2014-07-27 22:01:29 +00:00
var config = GetConfiguration();
if (config.GuideDays.HasValue)
2014-01-12 16:55:38 +00:00
{
2015-11-21 05:01:16 +00:00
return Math.Max(1, Math.Min(config.GuideDays.Value, MaxGuideDays));
2014-01-12 16:55:38 +00:00
}
2016-08-05 20:25:09 +00:00
return 7;
2014-01-12 16:55:38 +00:00
}
2020-05-20 17:07:53 +00:00
private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
2016-05-15 16:30:32 +00:00
{
2016-10-09 07:18:43 +00:00
if (user == null)
{
return new QueryResult<BaseItem>();
}
2018-09-12 17:26:21 +00:00
var folderIds = GetRecordingFolders(user, true)
2017-07-03 17:16:01 +00:00
.Select(i => i.Id)
2016-05-15 16:30:32 +00:00
.ToList();
2017-07-03 17:16:01 +00:00
var excludeItemTypes = new List<string>();
if (folderIds.Count == 0)
{
return new QueryResult<BaseItem>();
}
2016-09-05 05:39:14 +00:00
var includeItemTypes = new List<string>();
var genres = new List<string>();
2016-09-05 05:39:14 +00:00
if (query.IsMovie.HasValue)
{
if (query.IsMovie.Value)
{
2016-09-08 06:15:44 +00:00
includeItemTypes.Add(typeof(Movie).Name);
2016-09-05 05:39:14 +00:00
}
else
{
excludeItemTypes.Add(typeof(Movie).Name);
}
}
2016-09-05 05:39:14 +00:00
if (query.IsSeries.HasValue)
{
if (query.IsSeries.Value)
{
includeItemTypes.Add(typeof(Episode).Name);
}
else
{
excludeItemTypes.Add(typeof(Episode).Name);
}
}
if (query.IsSports ?? false)
{
genres.Add("Sports");
}
if (query.IsKids ?? false)
{
genres.Add("Kids");
genres.Add("Children");
genres.Add("Family");
}
2016-09-05 05:39:14 +00:00
2018-09-12 17:26:21 +00:00
var limit = query.Limit;
if (query.IsInProgress ?? false)
2017-08-23 19:45:52 +00:00
{
// limit = (query.Limit ?? 10) * 2;
2018-09-12 17:26:21 +00:00
limit = null;
2017-08-23 19:45:52 +00:00
2020-06-14 09:11:11 +00:00
// var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
// var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
2018-09-12 17:26:21 +00:00
2020-06-14 09:11:11 +00:00
// return new QueryResult<BaseItem>
2018-09-12 17:26:21 +00:00
//{
// Items = items,
// TotalRecordCount = items.Length
//};
dtoOptions.Fields = dtoOptions.Fields.Concat(new[] { ItemFields.Tags }).Distinct().ToArray();
2017-08-23 19:45:52 +00:00
}
2018-09-12 17:26:21 +00:00
var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
2016-05-15 16:30:32 +00:00
{
MediaTypes = new[] { MediaType.Video },
Recursive = true,
2018-12-28 15:48:26 +00:00
AncestorIds = folderIds.ToArray(),
2016-05-20 19:45:04 +00:00
IsFolder = false,
2017-01-09 17:05:34 +00:00
IsVirtualItem = false,
2018-09-12 17:26:21 +00:00
Limit = limit,
2017-03-08 06:48:07 +00:00
StartIndex = query.StartIndex,
2019-10-20 14:08:40 +00:00
OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending) },
EnableTotalRecordCount = query.EnableTotalRecordCount,
2018-12-28 15:48:26 +00:00
IncludeItemTypes = includeItemTypes.ToArray(),
ExcludeItemTypes = excludeItemTypes.ToArray(),
Genres = genres.ToArray(),
2016-10-10 18:18:28 +00:00
DtoOptions = dtoOptions
});
if (query.IsInProgress ?? false)
2013-12-28 23:09:24 +00:00
{
// TODO: Fix The co-variant conversion between Video[] and BaseItem[], this can generate runtime issues.
2018-09-12 17:26:21 +00:00
result.Items = result
.Items
.OfType<Video>()
.Where(i => !i.IsCompleteMedia)
.ToArray();
2013-12-28 23:09:24 +00:00
result.TotalRecordCount = result.Items.Count;
2013-12-21 18:37:34 +00:00
}
2018-09-12 17:26:21 +00:00
return result;
2014-08-14 13:24:30 +00:00
}
2020-05-20 17:07:53 +00:00
public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, User user = null)
2015-05-31 19:12:58 +00:00
{
2018-09-12 17:26:21 +00:00
var programTuples = new List<Tuple<BaseItemDto, string, string>>();
2017-08-01 16:45:57 +00:00
var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo);
2015-05-31 19:12:58 +00:00
2016-03-02 18:42:39 +00:00
foreach (var tuple in tuples)
2015-08-06 01:21:18 +00:00
{
2016-03-02 18:42:39 +00:00
var program = (LiveTvProgram)tuple.Item1;
var dto = tuple.Item2;
2015-08-06 01:21:18 +00:00
2016-03-02 18:42:39 +00:00
dto.StartDate = program.StartDate;
dto.EpisodeTitle = program.EpisodeTitle;
dto.IsRepeat |= program.IsRepeat;
dto.IsMovie |= program.IsMovie;
dto.IsSeries |= program.IsSeries;
dto.IsSports |= program.IsSports;
dto.IsLive |= program.IsLive;
dto.IsNews |= program.IsNews;
dto.IsKids |= program.IsKids;
dto.IsPremiere |= program.IsPremiere;
2015-10-11 16:12:53 +00:00
2017-08-01 16:45:57 +00:00
if (hasChannelInfo || hasChannelImage)
2016-03-02 18:42:39 +00:00
{
var channel = _libraryManager.GetItemById(program.ChannelId);
2016-03-02 18:42:39 +00:00
if (channel is LiveTvChannel liveChannel)
2015-10-11 16:12:53 +00:00
{
dto.ChannelName = liveChannel.Name;
dto.MediaType = liveChannel.MediaType;
dto.ChannelNumber = liveChannel.Number;
2016-03-02 18:42:39 +00:00
if (hasChannelImage && liveChannel.HasImage(ImageType.Primary))
2016-03-02 18:42:39 +00:00
{
dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(liveChannel);
2018-09-12 17:26:21 +00:00
}
}
2017-08-23 19:45:52 +00:00
}
2018-09-12 17:26:21 +00:00
programTuples.Add(new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId));
2015-05-31 18:22:51 +00:00
}
2018-09-12 17:26:21 +00:00
return AddRecordingInfo(programTuples, CancellationToken.None);
}
public ActiveRecordingInfo GetActiveRecordingInfo(string path)
{
return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
2017-08-23 19:45:52 +00:00
}
2015-05-31 18:22:51 +00:00
2020-05-20 17:07:53 +00:00
public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
2017-08-23 19:45:52 +00:00
{
var service = EmbyTV.EmbyTV.Current;
var info = activeRecordingInfo.Timer;
2018-09-12 17:26:21 +00:00
var channel = string.IsNullOrWhiteSpace(info.ChannelId) ? null : _libraryManager.GetItemById(_tvDtoService.GetInternalChannelId(service.Name, info.ChannelId));
2017-08-23 19:45:52 +00:00
dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
? null
: _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture);
2017-08-23 19:45:52 +00:00
dto.TimerId = string.IsNullOrEmpty(info.Id)
? null
2018-09-12 17:26:21 +00:00
: _tvDtoService.GetInternalTimerId(info.Id);
2017-08-23 19:45:52 +00:00
var startDate = info.StartDate;
var endDate = info.EndDate;
dto.StartDate = startDate;
dto.EndDate = endDate;
dto.Status = info.Status.ToString();
dto.IsRepeat = info.IsRepeat;
dto.EpisodeTitle = info.EpisodeTitle;
dto.IsMovie = info.IsMovie;
dto.IsSeries = info.IsSeries;
dto.IsSports = info.IsSports;
dto.IsLive = info.IsLive;
dto.IsNews = info.IsNews;
dto.IsKids = info.IsKids;
dto.IsPremiere = info.IsPremiere;
if (info.Status == RecordingStatus.InProgress)
2015-05-31 18:22:51 +00:00
{
2017-08-23 19:45:52 +00:00
startDate = info.StartDate.AddSeconds(0 - info.PrePaddingSeconds);
endDate = info.EndDate.AddSeconds(info.PostPaddingSeconds);
2015-05-31 18:22:51 +00:00
var now = DateTime.UtcNow.Ticks;
2017-08-23 19:45:52 +00:00
var start = startDate.Ticks;
var end = endDate.Ticks;
2015-05-31 18:22:51 +00:00
var pct = now - start;
2017-08-23 19:45:52 +00:00
2015-05-31 18:22:51 +00:00
pct /= end;
pct *= 100;
dto.CompletionPercentage = pct;
}
if (channel != null)
{
dto.ChannelName = channel.Name;
2015-10-16 17:06:31 +00:00
if (channel.HasImage(ImageType.Primary))
2013-12-23 03:46:03 +00:00
{
2015-05-31 18:22:51 +00:00
dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
}
}
}
2015-03-19 16:16:33 +00:00
2018-09-12 17:26:21 +00:00
public QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options)
2015-05-31 18:22:51 +00:00
{
2018-09-12 17:26:21 +00:00
var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
2015-05-31 18:22:51 +00:00
2016-08-17 19:28:43 +00:00
RemoveFields(options);
2018-09-12 17:26:21 +00:00
var internalResult = GetEmbyRecordings(query, options, user);
2017-05-21 07:25:49 +00:00
2017-09-04 19:28:22 +00:00
var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
2015-04-12 18:58:21 +00:00
2015-05-31 18:22:51 +00:00
return new QueryResult<BaseItemDto>
2013-11-26 02:53:48 +00:00
{
Items = returnArray,
2014-08-14 13:24:30 +00:00
TotalRecordCount = internalResult.TotalRecordCount
2013-11-26 02:53:48 +00:00
};
}
2013-11-26 21:36:11 +00:00
2018-09-12 17:26:21 +00:00
private async Task<QueryResult<TimerInfo>> GetTimersInternal(TimerQuery query, CancellationToken cancellationToken)
2013-11-27 19:04:19 +00:00
{
2015-03-22 23:55:33 +00:00
var tasks = _services.Select(async i =>
{
try
{
var recs = await i.GetTimersAsync(cancellationToken).ConfigureAwait(false);
return recs.Select(r => new Tuple<TimerInfo, ILiveTvService>(r, i));
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error getting recordings");
2015-03-22 23:55:33 +00:00
return new List<Tuple<TimerInfo, ILiveTvService>>();
}
});
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
var timers = results.SelectMany(i => i.ToList());
2013-11-27 19:04:19 +00:00
2016-06-19 17:41:49 +00:00
if (query.IsActive.HasValue)
{
if (query.IsActive.Value)
{
timers = timers.Where(i => i.Item1.Status == RecordingStatus.InProgress);
}
else
{
timers = timers.Where(i => i.Item1.Status != RecordingStatus.InProgress);
}
}
2016-10-04 05:15:39 +00:00
if (query.IsScheduled.HasValue)
{
if (query.IsScheduled.Value)
{
2016-10-05 07:15:29 +00:00
timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
2016-10-04 05:15:39 +00:00
}
else
{
timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
2016-10-04 05:15:39 +00:00
}
}
2013-11-27 19:04:19 +00:00
if (!string.IsNullOrEmpty(query.ChannelId))
{
2013-12-22 17:16:24 +00:00
var guid = new Guid(query.ChannelId);
2015-03-22 23:55:33 +00:00
timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
2013-11-27 19:04:19 +00:00
}
2014-01-08 05:25:21 +00:00
if (!string.IsNullOrEmpty(query.SeriesTimerId))
{
var guid = new Guid(query.SeriesTimerId);
timers = timers
2018-09-12 17:26:21 +00:00
.Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
2014-01-08 05:25:21 +00:00
}
2016-12-18 05:25:06 +00:00
if (!string.IsNullOrEmpty(query.Id))
{
timers = timers
2018-09-12 17:26:21 +00:00
.Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
}
2018-09-12 17:26:21 +00:00
var returnArray = timers
.Select(i => i.Item1)
2013-12-22 17:16:24 +00:00
.OrderBy(i => i.StartDate)
2018-09-12 17:26:21 +00:00
.ToArray();
2013-11-27 19:04:19 +00:00
2018-09-12 17:26:21 +00:00
return new QueryResult<TimerInfo>
2013-11-27 19:04:19 +00:00
{
Items = returnArray,
TotalRecordCount = returnArray.Length
};
}
2018-09-12 17:26:21 +00:00
public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
{
2018-09-12 17:26:21 +00:00
var tasks = _services.Select(async i =>
{
try
{
var recs = await i.GetTimersAsync(cancellationToken).ConfigureAwait(false);
return recs.Select(r => new Tuple<TimerInfo, ILiveTvService>(r, i));
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error getting recordings");
2018-09-12 17:26:21 +00:00
return new List<Tuple<TimerInfo, ILiveTvService>>();
}
});
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
var timers = results.SelectMany(i => i.ToList());
2018-09-12 17:26:21 +00:00
if (query.IsActive.HasValue)
{
2018-09-12 17:26:21 +00:00
if (query.IsActive.Value)
{
timers = timers.Where(i => i.Item1.Status == RecordingStatus.InProgress);
}
else
{
timers = timers.Where(i => i.Item1.Status != RecordingStatus.InProgress);
}
}
2018-09-12 17:26:21 +00:00
if (query.IsScheduled.HasValue)
2015-10-11 00:39:30 +00:00
{
2018-09-12 17:26:21 +00:00
if (query.IsScheduled.Value)
2017-10-03 18:39:37 +00:00
{
2018-09-12 17:26:21 +00:00
timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
2017-10-03 18:39:37 +00:00
}
2018-09-12 17:26:21 +00:00
else
2017-10-03 18:39:37 +00:00
{
2018-09-12 17:26:21 +00:00
timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
2017-10-03 18:39:37 +00:00
}
2015-10-11 00:39:30 +00:00
}
2018-09-12 17:26:21 +00:00
if (!string.IsNullOrEmpty(query.ChannelId))
{
var guid = new Guid(query.ChannelId);
timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
}
if (!string.IsNullOrEmpty(query.SeriesTimerId))
{
var guid = new Guid(query.SeriesTimerId);
timers = timers
.Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
}
if (!string.IsNullOrEmpty(query.Id))
{
timers = timers
.Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
}
var returnList = new List<TimerInfoDto>();
2016-02-12 04:54:00 +00:00
2018-09-12 17:26:21 +00:00
foreach (var i in timers)
2015-11-06 15:02:22 +00:00
{
2018-09-12 17:26:21 +00:00
var program = string.IsNullOrEmpty(i.Item1.ProgramId) ?
null :
_libraryManager.GetItemById(_tvDtoService.GetInternalProgramId(i.Item1.ProgramId)) as LiveTvProgram;
var channel = string.IsNullOrEmpty(i.Item1.ChannelId) ? null : _libraryManager.GetItemById(_tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
returnList.Add(_tvDtoService.GetTimerInfoDto(i.Item1, i.Item2, program, channel));
}
2015-11-06 15:02:22 +00:00
2018-09-12 17:26:21 +00:00
var returnArray = returnList
.OrderBy(i => i.StartDate)
2018-12-28 15:48:26 +00:00
.ToArray();
2015-10-11 00:39:30 +00:00
2018-09-12 17:26:21 +00:00
return new QueryResult<TimerInfoDto>
{
Items = returnArray,
TotalRecordCount = returnArray.Length
};
}
public async Task CancelTimer(string id)
{
2013-12-15 14:19:24 +00:00
var timer = await GetTimer(id, CancellationToken.None).ConfigureAwait(false);
if (timer == null)
{
2013-12-15 14:19:24 +00:00
throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
}
2015-03-12 15:51:48 +00:00
var service = GetService(timer.ServiceName);
2013-12-15 14:19:24 +00:00
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
2016-06-08 06:21:13 +00:00
2018-09-12 17:26:21 +00:00
if (!(service is EmbyTV.EmbyTV))
2016-06-08 06:21:13 +00:00
{
TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
2018-09-12 17:26:21 +00:00
}
2013-12-15 14:19:24 +00:00
}
2013-12-15 14:19:24 +00:00
public async Task CancelSeriesTimer(string id)
{
var timer = await GetSeriesTimer(id, CancellationToken.None).ConfigureAwait(false);
if (timer == null)
{
2017-02-03 20:52:56 +00:00
throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
}
2015-03-12 15:51:48 +00:00
var service = GetService(timer.ServiceName);
2013-12-15 14:19:24 +00:00
await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
2016-06-08 06:21:13 +00:00
SeriesTimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
}
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
{
2016-12-18 05:25:06 +00:00
var results = await GetTimers(new TimerQuery
{
Id = id
}, cancellationToken).ConfigureAwait(false);
2014-11-14 06:27:10 +00:00
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
}
2013-12-15 01:17:57 +00:00
public async Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken)
{
var results = await GetSeriesTimers(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
2014-11-14 06:27:10 +00:00
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
2013-12-15 01:17:57 +00:00
}
2013-12-15 14:19:24 +00:00
2016-10-01 07:06:00 +00:00
private async Task<QueryResult<SeriesTimerInfo>> GetSeriesTimersInternal(SeriesTimerQuery query, CancellationToken cancellationToken)
{
var tasks = _services.Select(async i =>
{
try
{
var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
return recs.Select(r =>
{
r.ServiceName = i.Name;
return new Tuple<SeriesTimerInfo, ILiveTvService>(r, i);
});
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error getting recordings");
2016-10-01 07:06:00 +00:00
return new List<Tuple<SeriesTimerInfo, ILiveTvService>>();
}
});
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
var timers = results.SelectMany(i => i.ToList());
if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase))
{
timers = query.SortOrder == SortOrder.Descending ?
timers.OrderBy(i => i.Item1.Priority).ThenByStringDescending(i => i.Item1.Name) :
timers.OrderByDescending(i => i.Item1.Priority).ThenByString(i => i.Item1.Name);
}
else
{
timers = query.SortOrder == SortOrder.Descending ?
timers.OrderByStringDescending(i => i.Item1.Name) :
timers.OrderByString(i => i.Item1.Name);
}
var returnArray = timers
.Select(i =>
{
return i.Item1;
})
.ToArray();
return new QueryResult<SeriesTimerInfo>
{
Items = returnArray,
TotalRecordCount = returnArray.Length
};
}
2013-12-15 01:17:57 +00:00
public async Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken)
{
2015-03-22 23:55:33 +00:00
var tasks = _services.Select(async i =>
{
try
{
var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
return recs.Select(r => new Tuple<SeriesTimerInfo, ILiveTvService>(r, i));
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error getting recordings");
2015-03-22 23:55:33 +00:00
return new List<Tuple<SeriesTimerInfo, ILiveTvService>>();
}
});
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
var timers = results.SelectMany(i => i.ToList());
2013-12-15 01:17:57 +00:00
2014-01-11 23:07:56 +00:00
if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase))
{
timers = query.SortOrder == SortOrder.Descending ?
2015-03-22 23:55:33 +00:00
timers.OrderBy(i => i.Item1.Priority).ThenByStringDescending(i => i.Item1.Name) :
timers.OrderByDescending(i => i.Item1.Priority).ThenByString(i => i.Item1.Name);
2014-01-11 23:07:56 +00:00
}
else
{
timers = query.SortOrder == SortOrder.Descending ?
2015-03-22 23:55:33 +00:00
timers.OrderByStringDescending(i => i.Item1.Name) :
timers.OrderByString(i => i.Item1.Name);
2014-01-11 23:07:56 +00:00
}
2013-12-22 18:58:51 +00:00
var returnArray = timers
2013-12-23 03:46:03 +00:00
.Select(i =>
{
string channelName = null;
2015-03-22 23:55:33 +00:00
if (!string.IsNullOrEmpty(i.Item1.ChannelId))
2013-12-23 03:46:03 +00:00
{
2015-03-22 23:55:33 +00:00
var internalChannelId = _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId);
2018-09-12 17:26:21 +00:00
var channel = _libraryManager.GetItemById(internalChannelId);
2014-01-23 18:05:41 +00:00
channelName = channel == null ? null : channel.Name;
2013-12-23 03:46:03 +00:00
}
2015-03-22 23:55:33 +00:00
return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
2013-12-23 03:46:03 +00:00
})
2013-12-15 01:17:57 +00:00
.ToArray();
return new QueryResult<SeriesTimerInfoDto>
{
Items = returnArray,
TotalRecordCount = returnArray.Length
};
}
2018-09-12 17:26:21 +00:00
public BaseItem GetLiveTvChannel(TimerInfo timer, ILiveTvService service)
{
var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, timer.ChannelId);
return _libraryManager.GetItemById(internalChannelId);
}
2020-05-20 17:07:53 +00:00
public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, User user)
2013-12-15 01:17:57 +00:00
{
2015-06-01 14:49:23 +00:00
var now = DateTime.UtcNow;
2013-12-15 01:17:57 +00:00
2018-09-12 17:26:21 +00:00
var channelIds = tuples.Select(i => i.Item2.Id).Distinct().ToArray();
2016-03-22 06:49:36 +00:00
2016-08-17 19:28:43 +00:00
var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
2015-06-01 14:49:23 +00:00
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
2016-03-22 06:49:36 +00:00
ChannelIds = channelIds,
2015-06-01 14:49:23 +00:00
MaxStartDate = now,
MinEndDate = now,
2016-03-22 06:49:36 +00:00
Limit = channelIds.Length,
2019-10-20 14:08:40 +00:00
OrderBy = new[] { (ItemSortBy.StartDate, SortOrder.Ascending) },
2018-09-12 17:26:21 +00:00
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
2017-05-21 07:25:49 +00:00
DtoOptions = options
}) : new List<BaseItem>();
2016-08-17 19:28:43 +00:00
RemoveFields(options);
var currentProgramsList = new List<BaseItem>();
2018-09-12 17:26:21 +00:00
var currentChannelsDict = new Dictionary<Guid, BaseItemDto>();
var addCurrentProgram = options.AddCurrentProgram;
2016-03-22 06:49:36 +00:00
foreach (var tuple in tuples)
{
2016-03-22 06:49:36 +00:00
var dto = tuple.Item1;
var channel = tuple.Item2;
2016-03-24 02:59:44 +00:00
dto.Number = channel.Number;
2016-05-11 17:46:44 +00:00
dto.ChannelNumber = channel.Number;
2016-03-24 02:59:44 +00:00
dto.ChannelType = channel.ChannelType;
2017-08-06 23:01:00 +00:00
currentChannelsDict[dto.Id] = dto;
if (addCurrentProgram)
2016-03-22 06:49:36 +00:00
{
2018-09-12 17:26:21 +00:00
var currentProgram = programs.FirstOrDefault(i => channel.Id.Equals(i.ChannelId));
2016-08-17 19:28:43 +00:00
if (currentProgram != null)
{
currentProgramsList.Add(currentProgram);
}
}
}
if (addCurrentProgram)
{
2017-08-28 00:33:05 +00:00
var currentProgramDtos = _dtoService.GetBaseItemDtos(currentProgramsList, options, user);
foreach (var programDto in currentProgramDtos)
{
if (currentChannelsDict.TryGetValue(programDto.ChannelId, out BaseItemDto channelDto))
{
2018-09-12 17:26:21 +00:00
channelDto.CurrentProgram = programDto;
2016-08-17 19:28:43 +00:00
}
2016-03-22 06:49:36 +00:00
}
}
}
2015-03-20 05:40:51 +00:00
private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
2013-12-17 20:02:12 +00:00
{
ILiveTvService service = null;
2014-01-23 18:05:41 +00:00
ProgramInfo programInfo = null;
if (program != null)
2014-01-23 18:05:41 +00:00
{
service = GetService(program);
2018-09-12 17:26:21 +00:00
var channel = _libraryManager.GetItemById(program.ChannelId);
2015-06-01 14:49:23 +00:00
2014-01-23 18:05:41 +00:00
programInfo = new ProgramInfo
{
Audio = program.Audio,
2018-09-12 17:26:21 +00:00
ChannelId = channel.ExternalId,
2014-01-23 18:05:41 +00:00
CommunityRating = program.CommunityRating,
EndDate = program.EndDate ?? DateTime.MinValue,
EpisodeTitle = program.EpisodeTitle,
2018-09-12 17:26:21 +00:00
Genres = program.Genres.ToList(),
Id = program.ExternalId,
2014-01-23 18:05:41 +00:00
IsHD = program.IsHD,
IsKids = program.IsKids,
IsLive = program.IsLive,
IsMovie = program.IsMovie,
IsNews = program.IsNews,
IsPremiere = program.IsPremiere,
IsRepeat = program.IsRepeat,
IsSeries = program.IsSeries,
IsSports = program.IsSports,
OriginalAirDate = program.PremiereDate,
Overview = program.Overview,
StartDate = program.StartDate,
2020-06-14 09:11:11 +00:00
// ImagePath = program.ExternalImagePath,
2014-01-23 18:05:41 +00:00
Name = program.Name,
2015-08-16 22:03:22 +00:00
OfficialRating = program.OfficialRating
2014-01-23 18:05:41 +00:00
};
2019-01-07 23:27:46 +00:00
}
if (service == null)
{
service = _services.First();
2014-01-23 18:05:41 +00:00
}
2015-03-19 16:16:33 +00:00
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
2013-12-22 18:58:51 +00:00
2016-10-11 06:46:59 +00:00
info.RecordAnyTime = true;
info.Days = new List<DayOfWeek>
{
DayOfWeek.Sunday,
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday,
DayOfWeek.Friday,
DayOfWeek.Saturday
};
info.Id = null;
2013-12-17 20:02:12 +00:00
2015-03-19 16:16:33 +00:00
return new Tuple<SeriesTimerInfo, ILiveTvService>(info, service);
}
public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(CancellationToken cancellationToken)
{
var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
2013-12-18 05:44:46 +00:00
2015-03-19 16:16:33 +00:00
var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
2013-12-18 05:44:46 +00:00
return obj;
}
public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
{
2018-09-12 17:26:21 +00:00
var program = (LiveTvProgram)_libraryManager.GetItemById(programId);
var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false);
2013-12-18 05:44:46 +00:00
var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
2015-03-19 16:16:33 +00:00
var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null);
2013-12-18 05:44:46 +00:00
2017-08-19 19:43:35 +00:00
info.Days = defaults.Item1.Days.ToArray();
2013-12-18 05:44:46 +00:00
info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
info.Name = program.Name;
2014-01-14 20:03:35 +00:00
info.ChannelId = programDto.ChannelId;
info.ChannelName = programDto.ChannelName;
2013-12-18 05:44:46 +00:00
info.StartDate = program.StartDate;
info.Name = program.Name;
info.Overview = program.Overview;
info.ProgramId = programDto.Id.ToString("N", CultureInfo.InvariantCulture);
2018-09-12 17:26:21 +00:00
info.ExternalProgramId = program.ExternalId;
2013-12-18 05:44:46 +00:00
2014-01-23 18:05:41 +00:00
if (program.EndDate.HasValue)
{
info.EndDate = program.EndDate.Value;
}
2013-12-18 05:44:46 +00:00
return info;
2013-12-17 20:02:12 +00:00
}
public async Task CreateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
{
2015-03-12 15:51:48 +00:00
var service = GetService(timer.ServiceName);
2013-12-17 20:02:12 +00:00
var info = await _tvDtoService.GetTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
2013-12-18 05:44:46 +00:00
// Set priority from default values
var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
info.Priority = defaultValues.Priority;
2016-06-08 21:04:52 +00:00
string newTimerId = null;
if (service is ISupportsNewTimerIds supportsNewTimerIds)
2016-06-08 21:04:52 +00:00
{
newTimerId = await supportsNewTimerIds.CreateTimer(info, cancellationToken).ConfigureAwait(false);
2018-09-12 17:26:21 +00:00
newTimerId = _tvDtoService.GetInternalTimerId(newTimerId);
2016-06-08 21:04:52 +00:00
}
else
{
await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
}
2016-06-08 17:10:07 +00:00
_logger.LogInformation("New recording scheduled");
2016-06-08 06:21:13 +00:00
2018-09-12 17:26:21 +00:00
if (!(service is EmbyTV.EmbyTV))
2016-06-08 06:21:13 +00:00
{
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
new TimerEventInfo(newTimerId)
2018-09-12 17:26:21 +00:00
{
ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId)
}));
2018-09-12 17:26:21 +00:00
}
2013-12-17 20:02:12 +00:00
}
public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
{
2015-03-12 15:51:48 +00:00
var service = GetService(timer.ServiceName);
2013-12-17 20:02:12 +00:00
var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
2013-12-18 05:44:46 +00:00
// Set priority from default values
var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
info.Priority = defaultValues.Priority;
2016-06-08 21:04:52 +00:00
string newTimerId = null;
if (service is ISupportsNewTimerIds supportsNewTimerIds)
2016-06-08 21:04:52 +00:00
{
newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N", CultureInfo.InvariantCulture);
2016-06-08 21:04:52 +00:00
}
else
{
await service.CreateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
}
SeriesTimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
new TimerEventInfo(newTimerId)
2016-06-08 06:21:13 +00:00
{
ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId)
}));
2013-12-17 20:02:12 +00:00
}
public async Task UpdateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
{
var info = await _tvDtoService.GetTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
2015-03-12 15:51:48 +00:00
var service = GetService(timer.ServiceName);
2013-12-17 20:02:12 +00:00
await service.UpdateTimerAsync(info, cancellationToken).ConfigureAwait(false);
}
public async Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
{
var info = await _tvDtoService.GetSeriesTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
2015-03-12 15:51:48 +00:00
var service = GetService(timer.ServiceName);
2013-12-17 20:02:12 +00:00
await service.UpdateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
2014-01-05 06:50:48 +00:00
}
public GuideInfo GetGuideInfo()
{
2015-06-01 14:49:23 +00:00
var startDate = DateTime.UtcNow;
var endDate = startDate.AddDays(GetGuideDays());
return new GuideInfo
{
StartDate = startDate,
EndDate = endDate
};
}
2014-01-12 06:31:21 +00:00
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
2019-02-02 20:45:29 +00:00
private bool _disposed = false;
2014-01-12 06:31:21 +00:00
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
2019-02-02 20:45:29 +00:00
if (_disposed)
{
return;
}
2014-01-12 06:31:21 +00:00
if (dispose)
{
2019-02-02 20:45:29 +00:00
// TODO: Dispose stuff
2014-01-12 06:31:21 +00:00
}
2019-02-02 20:45:29 +00:00
_services = null;
_listingProviders = null;
_tunerHosts = null;
_disposed = true;
2014-01-12 06:31:21 +00:00
}
2014-01-16 17:23:30 +00:00
2018-09-12 17:26:21 +00:00
private LiveTvServiceInfo[] GetServiceInfos()
2014-01-16 17:23:30 +00:00
{
2018-09-12 17:26:21 +00:00
return Services.Select(GetServiceInfo).ToArray();
2014-01-16 17:23:30 +00:00
}
private static LiveTvServiceInfo GetServiceInfo(ILiveTvService service)
2014-01-16 17:23:30 +00:00
{
2018-09-12 17:26:21 +00:00
return new LiveTvServiceInfo
2014-01-16 17:23:30 +00:00
{
Name = service.Name
};
}
2014-01-23 22:15:15 +00:00
2018-09-12 17:26:21 +00:00
public LiveTvInfo GetLiveTvInfo(CancellationToken cancellationToken)
2014-01-23 22:15:15 +00:00
{
2018-09-12 17:26:21 +00:00
var services = GetServiceInfos();
2014-01-23 22:15:15 +00:00
var info = new LiveTvInfo
{
2017-08-19 19:43:35 +00:00
Services = services,
2020-05-13 02:10:35 +00:00
IsEnabled = services.Length > 0,
EnabledUsers = _userManager.Users
.Where(IsLiveTvEnabled)
.Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
.ToArray()
2014-01-23 22:15:15 +00:00
};
return info;
}
2020-05-20 17:07:53 +00:00
private bool IsLiveTvEnabled(User user)
2014-06-23 16:05:19 +00:00
{
2020-05-13 02:10:35 +00:00
return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
2014-06-23 16:05:19 +00:00
}
2020-05-20 17:07:53 +00:00
public IEnumerable<User> GetEnabledUsers()
2014-06-10 17:36:06 +00:00
{
return _userManager.Users
2015-03-12 14:59:08 +00:00
.Where(IsLiveTvEnabled);
2014-06-10 17:36:06 +00:00
}
2014-01-23 22:15:15 +00:00
/// <summary>
/// Resets the tuner.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task ResetTuner(string id, CancellationToken cancellationToken)
{
2015-03-30 03:04:55 +00:00
var parts = id.Split(new[] { '_' }, 2);
var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), parts[0], StringComparison.OrdinalIgnoreCase));
2015-03-30 03:04:55 +00:00
if (service == null)
{
throw new ArgumentException("Service not found.");
}
return service.ResetTuner(parts[1], cancellationToken);
2014-01-23 22:15:15 +00:00
}
2014-06-07 19:46:24 +00:00
private static void RemoveFields(DtoOptions options)
2016-08-17 19:28:43 +00:00
{
2017-08-19 19:43:35 +00:00
var fields = options.Fields.ToList();
fields.Remove(ItemFields.CanDelete);
fields.Remove(ItemFields.CanDownload);
fields.Remove(ItemFields.DisplayPreferencesId);
fields.Remove(ItemFields.Etag);
2018-12-28 15:48:26 +00:00
options.Fields = fields.ToArray();
2016-08-17 19:28:43 +00:00
}
2017-10-03 18:39:37 +00:00
public Folder GetInternalLiveTvFolder(CancellationToken cancellationToken)
2014-06-07 19:46:24 +00:00
{
2017-10-13 05:44:40 +00:00
var name = _localization.GetLocalizedString("HeaderLiveTV");
2018-09-12 17:26:21 +00:00
return _libraryManager.GetNamedView(name, CollectionType.LiveTv, name);
2014-06-07 19:46:24 +00:00
}
2015-07-23 13:23:22 +00:00
2016-08-26 19:29:28 +00:00
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
2015-07-23 13:23:22 +00:00
{
2020-07-22 23:52:14 +00:00
info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.Serialize(info));
2015-07-27 16:21:18 +00:00
2015-07-23 13:23:22 +00:00
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider == null)
{
throw new ResourceNotFoundException();
}
if (provider is IConfigurableTunerHost configurable)
2016-02-19 06:20:18 +00:00
{
await configurable.Validate(info).ConfigureAwait(false);
}
2015-07-23 13:23:22 +00:00
var config = GetConfiguration();
2017-08-19 19:43:35 +00:00
var list = config.TunerHosts.ToList();
var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
2015-07-23 13:23:22 +00:00
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
2017-08-19 19:43:35 +00:00
list.Add(info);
2018-12-28 15:48:26 +00:00
config.TunerHosts = list.ToArray();
2015-07-23 13:23:22 +00:00
}
else
{
config.TunerHosts[index] = info;
}
_config.SaveConfiguration("livetv", config);
2015-07-23 17:04:54 +00:00
2016-08-26 19:29:28 +00:00
if (dataSourceChanged)
{
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
}
2015-07-25 18:11:46 +00:00
return info;
2015-07-23 13:23:22 +00:00
}
2015-07-25 17:21:10 +00:00
public async Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings)
2015-07-23 13:23:22 +00:00
{
2018-12-30 19:21:48 +00:00
// Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider
// ServerConfiguration.SaveConfiguration crashes during xml serialization for AddListingProvider
2020-07-22 23:52:14 +00:00
info = JsonSerializer.Deserialize<ListingsProviderInfo>(JsonSerializer.Serialize(info));
2015-07-23 13:23:22 +00:00
2019-01-13 20:37:13 +00:00
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
2015-07-23 13:23:22 +00:00
if (provider == null)
{
2018-12-30 12:18:38 +00:00
throw new ResourceNotFoundException(
2019-10-18 22:22:08 +00:00
string.Format(
CultureInfo.InvariantCulture,
"Couldn't find provider of type: '{0}'",
info.Type));
2015-07-23 13:23:22 +00:00
}
2015-07-25 17:21:10 +00:00
await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false);
2015-07-23 13:23:22 +00:00
LiveTvOptions config = GetConfiguration();
2015-07-23 13:23:22 +00:00
2019-01-13 20:37:13 +00:00
var list = config.ListingProviders.ToList();
2018-12-30 19:21:48 +00:00
int index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
2015-07-23 13:23:22 +00:00
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
2017-08-19 19:43:35 +00:00
list.Add(info);
2018-12-28 15:48:26 +00:00
config.ListingProviders = list.ToArray();
2015-07-23 13:23:22 +00:00
}
else
{
config.ListingProviders[index] = info;
}
_config.SaveConfiguration("livetv", config);
2015-07-23 17:04:54 +00:00
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
2015-07-27 16:21:18 +00:00
2015-07-23 13:23:22 +00:00
return info;
}
public void DeleteListingsProvider(string id)
{
var config = GetConfiguration();
2017-08-19 19:43:35 +00:00
config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
_config.SaveConfiguration("livetv", config);
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
}
2017-02-04 23:32:16 +00:00
public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelId, string providerChannelId)
{
var config = GetConfiguration();
var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
2017-02-04 23:32:16 +00:00
listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelId, StringComparison.OrdinalIgnoreCase)).ToArray();
2017-02-04 23:32:16 +00:00
if (!string.Equals(tunerChannelId, providerChannelId, StringComparison.OrdinalIgnoreCase))
{
var list = listingsProviderInfo.ChannelMappings.ToList();
list.Add(new NameValuePair
{
2017-02-04 23:32:16 +00:00
Name = tunerChannelId,
Value = providerChannelId
});
2018-12-28 15:48:26 +00:00
listingsProviderInfo.ChannelMappings = list.ToArray();
}
_config.SaveConfiguration("livetv", config);
var tunerChannels = await GetChannelsForListingsProvider(providerId, CancellationToken.None)
.ConfigureAwait(false);
var providerChannels = await GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
.ConfigureAwait(false);
2017-08-19 19:43:35 +00:00
var mappings = listingsProviderInfo.ChannelMappings;
var tunerChannelMappings =
tunerChannels.Select(i => GetTunerChannelMapping(i, mappings, providerChannels)).ToList();
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
2017-02-04 23:32:16 +00:00
return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelId, StringComparison.OrdinalIgnoreCase));
}
2017-08-19 19:43:35 +00:00
public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, NameValuePair[] mappings, List<ChannelInfo> epgChannels)
{
var result = new TunerChannelMapping
{
2017-02-04 23:32:16 +00:00
Name = tunerChannel.Name,
2017-02-23 19:13:26 +00:00
Id = tunerChannel.Id
};
2017-02-04 23:32:16 +00:00
if (!string.IsNullOrWhiteSpace(tunerChannel.Number))
{
result.Name = tunerChannel.Number + " " + result.Name;
}
2017-02-04 23:32:16 +00:00
var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, epgChannels);
if (providerChannel != null)
{
result.ProviderChannelName = providerChannel.Name;
2017-02-04 23:32:16 +00:00
result.ProviderChannelId = providerChannel.Id;
}
return result;
}
2015-08-10 19:09:10 +00:00
public Task<List<NameIdPair>> GetLineups(string providerType, string providerId, string country, string location)
2015-07-23 13:23:22 +00:00
{
var config = GetConfiguration();
2015-08-10 19:09:10 +00:00
if (string.IsNullOrWhiteSpace(providerId))
{
var provider = _listingProviders.FirstOrDefault(i => string.Equals(providerType, i.Type, StringComparison.OrdinalIgnoreCase));
2015-07-23 13:23:22 +00:00
2015-08-10 19:09:10 +00:00
if (provider == null)
{
throw new ResourceNotFoundException();
}
2015-07-23 13:23:22 +00:00
2015-08-10 19:09:10 +00:00
return provider.GetLineups(null, country, location);
2015-07-23 13:23:22 +00:00
}
2015-08-10 19:09:10 +00:00
else
{
var info = config.ListingProviders.FirstOrDefault(i => string.Equals(i.Id, providerId, StringComparison.OrdinalIgnoreCase));
2015-07-23 13:23:22 +00:00
2015-08-10 19:09:10 +00:00
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
if (provider == null)
{
throw new ResourceNotFoundException();
}
return provider.GetLineups(info, country, location);
}
2015-07-23 13:23:22 +00:00
}
2015-08-21 19:25:35 +00:00
2016-06-09 16:13:25 +00:00
public Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
{
var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
return EmbyTV.EmbyTV.Current.GetChannelsForListingsProvider(info, cancellationToken);
}
public Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken)
2016-06-08 05:24:25 +00:00
{
var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
var provider = _listingProviders.First(i => string.Equals(i.Type, info.Type, StringComparison.OrdinalIgnoreCase));
return provider.GetChannels(info, cancellationToken);
}
2017-09-08 16:13:58 +00:00
public Guid GetInternalChannelId(string serviceName, string externalId)
{
return _tvDtoService.GetInternalChannelId(serviceName, externalId);
}
2018-09-12 17:26:21 +00:00
public Guid GetInternalProgramId(string externalId)
{
return _tvDtoService.GetInternalProgramId(externalId);
}
2020-05-20 17:07:53 +00:00
public List<BaseItem> GetRecordingFolders(User user)
2018-09-12 17:26:21 +00:00
{
return GetRecordingFolders(user, false);
}
2020-05-20 17:07:53 +00:00
private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels)
2017-09-08 16:13:58 +00:00
{
2018-09-12 17:26:21 +00:00
var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => _libraryManager.FindByPath(i, true))
.Where(i => i != null)
.Where(i => i.IsVisibleStandalone(user))
.SelectMany(i => _libraryManager.GetCollectionFolders(i))
2019-02-02 11:27:06 +00:00
.GroupBy(x => x.Id)
.Select(x => x.First())
2018-09-12 17:26:21 +00:00
.OrderBy(i => i.SortName)
.ToList();
folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
2018-09-12 17:26:21 +00:00
{
UserId = user.Id,
IsRecordingsFolder = true,
RefreshLatestChannelItems = refreshChannels
}).Items);
return folders.Cast<BaseItem>().ToList();
2017-09-08 16:13:58 +00:00
}
}
2018-12-28 15:48:26 +00:00
}