aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/LiveTv
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/LiveTv')
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs2
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs21
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs11
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs257
4 files changed, 194 insertions, 97 deletions
diff --git a/MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs
index 56e92f82c4..bed9458ecf 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs
@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
return new ITaskTrigger[]
{
- new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
+ new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
};
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
new file mode 100644
index 0000000000..57d1d79e16
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
@@ -0,0 +1,21 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.LiveTv;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Server.Implementations.LiveTv
+{
+ public class LiveTvConfigurationFactory : IConfigurationFactory
+ {
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new List<ConfigurationStore>
+ {
+ new ConfigurationStore
+ {
+ ConfigurationType = typeof(LiveTvOptions),
+ Key = "livetv"
+ }
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 412b2e7bd0..9c69e656d7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
@@ -23,15 +22,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly IUserDataManager _userDataManager;
private readonly IDtoService _dtoService;
- private readonly IItemRepository _itemRepo;
- public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger, IItemRepository itemRepo)
+ public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger)
{
_dtoService = dtoService;
_userDataManager = userDataManager;
_imageProcessor = imageProcessor;
_logger = logger;
- _itemRepo = itemRepo;
}
public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel)
@@ -249,7 +246,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (user != null)
{
- dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, recording.GetUserDataKey()));
+ dto.UserData = _userDataManager.GetUserDataDto(recording, user);
dto.PlayAccess = recording.GetPlayAccess(user);
}
@@ -322,7 +319,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (user != null)
{
- dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey()));
+ dto.UserData = _userDataManager.GetUserDataDto(info, user);
dto.PlayAccess = info.GetPlayAccess(user);
}
@@ -401,7 +398,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (user != null)
{
- dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, item.GetUserDataKey()));
+ dto.UserData = _userDataManager.GetUserDataDto(item, user);
dto.PlayAccess = item.GetPlayAccess(user);
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 467bedcf13..d072217af4 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
@@ -40,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager;
private readonly ITaskManager _taskManager;
- private readonly IJsonSerializer _json;
+ private readonly IJsonSerializer _jsonSerializer;
private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localization;
@@ -49,8 +50,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
- private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams =
- new ConcurrentDictionary<string, LiveStreamInfo>();
+ private readonly ConcurrentDictionary<string, LiveStreamData> _openStreams =
+ new ConcurrentDictionary<string, LiveStreamData>();
private List<Guid> _channelIdList = new List<Guid>();
private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
@@ -58,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1);
- public LiveTvManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, IJsonSerializer json, ILocalizationManager localization)
+ public LiveTvManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer)
{
_config = config;
_fileSystem = fileSystem;
@@ -67,12 +68,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_userManager = userManager;
_libraryManager = libraryManager;
_taskManager = taskManager;
- _json = json;
_localization = localization;
+ _jsonSerializer = jsonSerializer;
_dtoService = dtoService;
_userDataManager = userDataManager;
- _tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
+ _tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger);
}
/// <summary>
@@ -86,6 +87,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public ILiveTvService ActiveService { get; private set; }
+ private LiveTvOptions GetConfiguration()
+ {
+ return _config.GetConfiguration<LiveTvOptions>("livetv");
+ }
+
/// <summary>
/// Adds the parts.
/// </summary>
@@ -94,7 +100,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
_services.AddRange(services);
- SetActiveService(_config.Configuration.LiveTvOptions.ActiveService);
+ SetActiveService(GetConfiguration().ActiveService);
}
private void SetActiveService(string name)
@@ -125,7 +131,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
}
- public async Task<QueryResult<ChannelInfoDto>> GetChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
+ public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
@@ -214,9 +220,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv
allEnumerable = allEnumerable.Take(query.Limit.Value);
}
+ var result = new QueryResult<LiveTvChannel>
+ {
+ Items = allEnumerable.ToArray(),
+ TotalRecordCount = allChannels.Count
+ };
+
+ return result;
+ }
+
+ public async Task<QueryResult<ChannelInfoDto>> GetChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
+ {
+ var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
+
+ var internalResult = await GetInternalChannels(query, cancellationToken).ConfigureAwait(false);
+
var returnList = new List<ChannelInfoDto>();
- foreach (var channel in allEnumerable)
+ foreach (var channel in internalResult.Items)
{
var currentProgram = await GetCurrentProgram(channel.ExternalId, cancellationToken).ConfigureAwait(false);
@@ -226,7 +247,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var result = new QueryResult<ChannelInfoDto>
{
Items = returnList.ToArray(),
- TotalRecordCount = allChannels.Count
+ TotalRecordCount = internalResult.TotalRecordCount
};
return result;
@@ -257,12 +278,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return obj;
}
- private async Task RefreshIfNeeded(IEnumerable<LiveTvProgram> programs, CancellationToken cancellationToken)
+ private Task RefreshIfNeeded(IEnumerable<LiveTvProgram> programs, CancellationToken cancellationToken)
{
- foreach (var program in programs)
+ var list = programs.ToList();
+
+ Task.Run(async () =>
{
- await RefreshIfNeeded(program, cancellationToken).ConfigureAwait(false);
- }
+ foreach (var program in list)
+ {
+ await RefreshIfNeeded(program, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ }, cancellationToken);
+
+ return Task.FromResult(true);
}
private async Task RefreshIfNeeded(LiveTvProgram program, CancellationToken cancellationToken)
@@ -272,9 +301,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return;
}
- await program.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-
_refreshedPrograms.TryAdd(program.Id, true);
+
+ await program.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
public async Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
@@ -292,65 +321,53 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<LiveStreamInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
{
- await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var service = ActiveService;
-
- var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
-
- var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
-
- var result = await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
-
- Sanitize(result);
-
- _logger.Debug("Live stream info: " + _json.SerializeToString(result));
-
- if (!string.IsNullOrEmpty(result.Id))
- {
- _openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
- }
-
- return result;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting recording stream", ex);
-
- throw;
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
+ return await GetLiveStream(id, false, cancellationToken).ConfigureAwait(false);
}
public async Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken)
{
+ return await GetLiveStream(id, true, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task<LiveStreamInfo> GetLiveStream(string id, bool isChannel, CancellationToken cancellationToken)
+ {
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var service = ActiveService;
+ LiveStreamInfo info;
- var channel = GetInternalChannel(id);
-
- _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
+ if (isChannel)
+ {
+ var channel = GetInternalChannel(id);
+ _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
- var result = await service.GetChannelStream(channel.ExternalId, cancellationToken).ConfigureAwait(false);
+ info = await service.GetChannelStream(channel.ExternalId, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
+ var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
- Sanitize(result);
+ _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.Id);
+ info = await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
+ }
- _logger.Debug("Live stream info: " + _json.SerializeToString(result));
+ _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));
+ Sanitize(info);
- if (!string.IsNullOrEmpty(result.Id))
+ var data = new LiveStreamData
{
- _openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
- }
+ Info = info,
+ ConsumerCount = 1,
+ IsChannel = isChannel,
+ ItemId = id
+ };
- return result;
+ _openStreams.AddOrUpdate(info.Id, data, (key, i) => data);
+
+ return info;
}
catch (Exception ex)
{
@@ -458,13 +475,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
item.ChannelType = channelInfo.ChannelType;
- item.ProviderImageUrl = channelInfo.ImageUrl;
- item.HasProviderImage = channelInfo.HasImage;
- item.ProviderImagePath = channelInfo.ImagePath;
item.ExternalId = channelInfo.Id;
item.ServiceName = serviceName;
item.Number = channelInfo.Number;
+ var replaceImages = new List<ImageType>();
+
+ if (!string.Equals(item.ProviderImageUrl, channelInfo.ImageUrl, StringComparison.OrdinalIgnoreCase))
+ {
+ isNew = true;
+ replaceImages.Add(ImageType.Primary);
+ }
+ if (!string.Equals(item.ProviderImagePath, channelInfo.ImagePath, StringComparison.OrdinalIgnoreCase))
+ {
+ isNew = true;
+ replaceImages.Add(ImageType.Primary);
+ }
+
+ item.ProviderImageUrl = channelInfo.ImageUrl;
+ item.HasProviderImage = channelInfo.HasImage;
+ item.ProviderImagePath = channelInfo.ImagePath;
+
if (string.IsNullOrEmpty(item.Name))
{
item.Name = channelInfo.Name;
@@ -472,7 +503,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
await item.RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = isNew
+ ForceSave = isNew,
+ ReplaceImages = replaceImages.Distinct().ToList()
}, cancellationToken);
@@ -560,24 +592,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv
};
}
- if (!string.IsNullOrEmpty(info.Path))
- {
- item.Path = info.Path;
- }
- else if (!string.IsNullOrEmpty(info.Url))
- {
- item.Path = info.Url;
- }
-
isNew = true;
}
item.RecordingInfo = info;
item.ServiceName = serviceName;
+ var originalPath = item.Path;
+
+ if (!string.IsNullOrEmpty(info.Path))
+ {
+ item.Path = info.Path;
+ }
+ else if (!string.IsNullOrEmpty(info.Url))
+ {
+ item.Path = info.Url;
+ }
+
+ var pathChanged = !string.Equals(originalPath, item.Path);
+
await item.RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = isNew
+ ForceSave = isNew || pathChanged
}, cancellationToken);
@@ -1010,9 +1046,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private double GetGuideDays(int channelCount)
{
- if (_config.Configuration.LiveTvOptions.GuideDays.HasValue)
+ var config = GetConfiguration();
+
+ if (config.GuideDays.HasValue)
{
- return _config.Configuration.LiveTvOptions.GuideDays.Value;
+ return config.GuideDays.Value;
}
var programsPerDay = channelCount * 48;
@@ -1032,15 +1070,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return channels.Select(i => new Tuple<string, ChannelInfo>(service.Name, i));
}
- public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
+ public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken)
{
var service = ActiveService;
if (service == null)
{
- return new QueryResult<RecordingInfoDto>
+ return new QueryResult<BaseItem>
{
- Items = new RecordingInfoDto[] { }
+ Items = new BaseItem[] { }
};
}
@@ -1125,7 +1163,30 @@ namespace MediaBrowser.Server.Implementations.LiveTv
entities = entities.Take(query.Limit.Value);
}
- var returnArray = entities
+ return new QueryResult<BaseItem>
+ {
+ Items = entities.Cast<BaseItem>().ToArray(),
+ TotalRecordCount = entityList.Count
+ };
+ }
+
+ public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
+ {
+ var service = ActiveService;
+
+ if (service == null)
+ {
+ return new QueryResult<RecordingInfoDto>
+ {
+ Items = new RecordingInfoDto[] { }
+ };
+ }
+
+ var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
+
+ var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false);
+
+ var returnArray = internalResult.Items.Cast<ILiveTvRecording>()
.Select(i =>
{
var channel = string.IsNullOrEmpty(i.RecordingInfo.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, i.RecordingInfo.ChannelId));
@@ -1136,7 +1197,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return new QueryResult<RecordingInfoDto>
{
Items = returnArray,
- TotalRecordCount = entityList.Count
+ TotalRecordCount = internalResult.TotalRecordCount
};
}
@@ -1597,20 +1658,38 @@ namespace MediaBrowser.Server.Implementations.LiveTv
};
}
+ class LiveStreamData
+ {
+ internal LiveStreamInfo Info;
+ internal int ConsumerCount;
+ internal string ItemId;
+ internal bool IsChannel;
+ }
+
public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
- var service = ActiveService;
-
- _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id);
-
try
{
- await service.CloseLiveStream(id, cancellationToken).ConfigureAwait(false);
+ var service = ActiveService;
- LiveStreamInfo removed;
- _openStreams.TryRemove(id, out removed);
+ LiveStreamData data;
+ if (_openStreams.TryGetValue(id, out data))
+ {
+ if (data.ConsumerCount > 1)
+ {
+ data.ConsumerCount--;
+ _logger.Info("Decrementing live stream client count.");
+ return;
+ }
+
+ }
+ _openStreams.TryRemove(id, out data);
+
+ _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id);
+
+ await service.CloseLiveStream(id, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1662,7 +1741,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
foreach (var stream in _openStreams.Values.ToList())
{
- var task = CloseLiveStream(stream.Id, CancellationToken.None);
+ var task = CloseLiveStream(stream.Info.Id, CancellationToken.None);
Task.WaitAll(task);
}