aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs')
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs377
1 files changed, 138 insertions, 239 deletions
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index dbae6d03e..47e862b9f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -17,7 +17,6 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using System;
@@ -54,10 +53,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly ConcurrentDictionary<string, LiveStreamData> _openStreams =
new ConcurrentDictionary<string, LiveStreamData>();
- private List<Guid> _channelIdList = new List<Guid>();
- private Dictionary<Guid, LiveTvProgram> _programs;
- private readonly ConcurrentDictionary<Guid, bool> _refreshedPrograms = new ConcurrentDictionary<Guid, bool>();
-
public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager)
{
_config = config;
@@ -108,44 +103,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
}
- private readonly object _programsDataLock = new object();
- private Dictionary<Guid, LiveTvProgram> GetProgramsDictionary()
- {
- if (_programs == null)
- {
- lock (_programsDataLock)
- {
- if (_programs == null)
- {
- var dict = new Dictionary<Guid, LiveTvProgram>();
-
- foreach (var item in _itemRepo.GetItemsOfType(typeof(LiveTvProgram))
- .Cast<LiveTvProgram>()
- .ToList())
- {
- dict[item.Id] = item;
- }
-
- _programs = dict;
- }
- }
- }
-
- return _programs;
- }
-
- private IEnumerable<LiveTvProgram> GetPrograms()
- {
- return GetProgramsDictionary().Values;
- }
-
public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- var channels = _channelIdList.Select(_libraryManager.GetItemById)
- .Where(i => i != null)
- .OfType<LiveTvChannel>();
+ var channels = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(LiveTvChannel).Name }
+
+ }).Items.Cast<LiveTvChannel>();
if (user != null)
{
@@ -258,9 +224,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var returnList = new List<ChannelInfoDto>();
+ var now = DateTime.UtcNow;
+
+ var programs = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ MaxStartDate = now,
+ MinEndDate = now
+
+ }).Items.Cast<LiveTvProgram>().OrderBy(i => i.StartDate).ToList();
+
foreach (var channel in internalResult.Items)
{
- var currentProgram = GetCurrentProgram(channel.ExternalId);
+ var channelIdString = channel.Id.ToString("N");
+ var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString, StringComparison.OrdinalIgnoreCase));
returnList.Add(_tvDtoService.GetChannelInfoDto(channel, currentProgram, user));
}
@@ -286,34 +263,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
internal LiveTvProgram GetInternalProgram(string id)
{
- var guid = new Guid(id);
-
- LiveTvProgram obj = null;
-
- GetProgramsDictionary().TryGetValue(guid, out obj);
-
- if (obj != null)
- {
- RefreshIfNeeded(obj);
- }
- return obj;
- }
-
- private void RefreshIfNeeded(LiveTvProgram program)
- {
- if (!_refreshedPrograms.ContainsKey(program.Id))
- {
- _refreshedPrograms.TryAdd(program.Id, true);
- _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions());
- }
+ return _libraryManager.GetItemById(id) as LiveTvProgram;
}
- private void RefreshIfNeeded(IEnumerable<LiveTvProgram> programs)
+ internal LiveTvProgram GetInternalProgram(Guid id)
{
- foreach (var program in programs)
- {
- RefreshIfNeeded(program);
- }
+ return _libraryManager.GetItemById(id) as LiveTvProgram;
}
public async Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
@@ -598,14 +553,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return item;
}
- private async Task<LiveTvProgram> GetProgram(ProgramInfo info, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
+ private async Task<LiveTvProgram> GetProgram(ProgramInfo info, string channelId, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
{
var id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
var item = _libraryManager.GetItemById(id) as LiveTvProgram;
+ var isNew = false;
if (item == null)
{
+ isNew = true;
item = new LiveTvProgram
{
Name = info.Name,
@@ -619,8 +576,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.ServiceName = serviceName;
item.Audio = info.Audio;
- item.ExternalChannelId = info.ChannelId;
- item.CommunityRating = info.CommunityRating;
+ item.ChannelId = channelId;
+ item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
item.EndDate = info.EndDate;
item.EpisodeTitle = info.EpisodeTitle;
item.ExternalId = info.Id;
@@ -636,8 +593,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.IsSeries = info.IsSeries;
item.IsSports = info.IsSports;
item.Name = info.Name;
- item.OfficialRating = info.OfficialRating;
- item.Overview = info.Overview;
+ item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
+ item.Overview = item.Overview ?? info.Overview;
item.OriginalAirDate = info.OriginalAirDate;
item.ProviderImagePath = info.ImagePath;
item.ProviderImageUrl = info.ImageUrl;
@@ -647,7 +604,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.ProductionYear = info.ProductionYear;
item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate;
- await item.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ if (isNew)
+ {
+ await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ }
+
+ _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions());
return item;
}
@@ -721,17 +687,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return recording;
}
- private LiveTvChannel GetChannel(LiveTvProgram program)
- {
- var programChannelId = program.ExternalChannelId;
-
- if (string.IsNullOrWhiteSpace(programChannelId)) return null;
-
- var internalProgramChannelId = _tvDtoService.GetInternalChannelId(program.ServiceName, programChannelId);
-
- return GetInternalChannel(internalProgramChannelId);
- }
-
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
{
var program = GetInternalProgram(id);
@@ -745,64 +700,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
{
- IEnumerable<LiveTvProgram> programs = GetPrograms();
-
- if (query.MinEndDate.HasValue)
+ var internalQuery = new InternalItemsQuery
{
- var val = query.MinEndDate.Value;
-
- programs = programs.Where(i => i.EndDate.HasValue && i.EndDate.Value >= val);
- }
-
- if (query.MinStartDate.HasValue)
- {
- var val = query.MinStartDate.Value;
-
- programs = programs.Where(i => i.StartDate >= val);
- }
-
- if (query.MaxEndDate.HasValue)
- {
- var val = query.MaxEndDate.Value;
-
- programs = programs.Where(i => i.EndDate.HasValue && i.EndDate.Value <= val);
- }
-
- if (query.MaxStartDate.HasValue)
- {
- var val = query.MaxStartDate.Value;
-
- programs = programs.Where(i => i.StartDate <= val);
- }
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ MinEndDate = query.MinEndDate,
+ MinStartDate = query.MinStartDate,
+ MaxEndDate = query.MaxEndDate,
+ MaxStartDate = query.MaxStartDate,
+ ChannelIds = query.ChannelIds,
+ IsMovie = query.IsMovie,
+ IsSports = query.IsSports
+ };
if (query.HasAired.HasValue)
{
- var val = query.HasAired.Value;
- programs = programs.Where(i => i.HasAired == val);
- }
-
- if (query.ChannelIds.Length > 0)
- {
- var guids = query.ChannelIds.Select(i => new Guid(i)).ToList();
-
- programs = programs.Where(i =>
+ if (query.HasAired.Value)
{
- var programChannelId = i.ExternalChannelId;
-
- var service = GetService(i);
- var internalProgramChannelId = _tvDtoService.GetInternalChannelId(service.Name, programChannelId);
-
- return guids.Contains(internalProgramChannelId);
- });
+ internalQuery.MaxEndDate = DateTime.UtcNow;
+ }
+ else
+ {
+ internalQuery.MinEndDate = DateTime.UtcNow;
+ }
}
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- if (user != null)
- {
- // Avoid implicitly captured closure
- var currentUser = user;
- programs = programs.Where(i => i.IsVisible(currentUser));
- }
+ IEnumerable<LiveTvProgram> programs = _libraryManager.GetItems(internalQuery).Items.Cast<LiveTvProgram>();
// Apply genre filter
if (query.Genres.Length > 0)
@@ -810,14 +732,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
programs = programs.Where(p => p.Genres.Any(g => query.Genres.Contains(g, StringComparer.OrdinalIgnoreCase)));
}
- if (query.IsMovie.HasValue)
- {
- programs = programs.Where(p => p.IsMovie == query.IsMovie);
- }
-
- if (query.IsSports.HasValue)
+ var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
+ if (user != null)
{
- programs = programs.Where(p => p.IsSports == query.IsSports);
+ // Avoid implicitly captured closure
+ var currentUser = user;
+ programs = programs.Where(i => i.IsVisible(currentUser));
}
programs = _libraryManager.Sort(programs, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending)
@@ -840,8 +760,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
.Select(i => _dtoService.GetBaseItemDto(i, new DtoOptions(), user))
.ToArray();
- RefreshIfNeeded(programList);
-
await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
var result = new QueryResult<BaseItemDto>
@@ -855,35 +773,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken)
{
- IEnumerable<LiveTvProgram> programs = GetPrograms();
-
- var user = _userManager.GetUserById(query.UserId);
-
- // Avoid implicitly captured closure
- var currentUser = user;
- programs = programs.Where(i => i.IsVisible(currentUser));
-
- if (query.IsAiring.HasValue)
+ var internalQuery = new InternalItemsQuery
{
- var val = query.IsAiring.Value;
- programs = programs.Where(i => i.IsAiring == val);
- }
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ IsAiring = query.IsAiring,
+ IsMovie = query.IsMovie,
+ IsSports = query.IsSports
+ };
if (query.HasAired.HasValue)
{
- var val = query.HasAired.Value;
- programs = programs.Where(i => i.HasAired == val);
+ if (query.HasAired.Value)
+ {
+ internalQuery.MaxEndDate = DateTime.UtcNow;
+ }
+ else
+ {
+ internalQuery.MinEndDate = DateTime.UtcNow;
+ }
}
- if (query.IsMovie.HasValue)
- {
- programs = programs.Where(p => p.IsMovie == query.IsMovie.Value);
- }
+ IEnumerable<LiveTvProgram> programs = _libraryManager.GetItems(internalQuery).Items.Cast<LiveTvProgram>();
- if (query.IsSports.HasValue)
- {
- programs = programs.Where(p => p.IsSports == query.IsSports.Value);
- }
+ var user = _userManager.GetUserById(query.UserId);
+
+ // Avoid implicitly captured closure
+ var currentUser = user;
+ programs = programs.Where(i => i.IsVisible(currentUser));
var programList = programs.ToList();
@@ -904,8 +820,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
programList = programs.ToList();
- RefreshIfNeeded(programList);
-
var returnArray = programList.ToArray();
var result = new QueryResult<LiveTvProgram>
@@ -952,8 +866,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
score++;
}
- var internalChannelId = _tvDtoService.GetInternalChannelId(program.ServiceName, program.ExternalChannelId);
- var channel = GetInternalChannel(internalChannelId);
+ var channel = GetInternalChannel(program.ChannelId);
var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey());
@@ -1048,17 +961,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
}
- internal async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
+ internal Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
{
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(p * .9));
- await RefreshChannelsInternal(innerProgress, cancellationToken).ConfigureAwait(false);
-
- innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(90 + (p * .1)));
- await CleanDatabaseInternal(progress, cancellationToken).ConfigureAwait(false);
-
- RefreshIfNeeded(GetPrograms().ToList());
+ return RefreshChannelsInternal(progress, cancellationToken);
}
private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
@@ -1068,6 +973,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
? 0
: 1 / _services.Count;
+ var newChannelIdList = new List<Guid>();
+ var newProgramIdList = new List<Guid>();
+
foreach (var service in _services)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -1077,7 +985,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(p * progressPerService));
- await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
+ var idList = await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
+
+ newChannelIdList.AddRange(idList.Item1);
+ newProgramIdList.AddRange(idList.Item2);
}
catch (OperationCanceledException)
{
@@ -1095,10 +1006,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(100 * percent);
}
+ await CleanDatabaseInternal(newChannelIdList, typeof(LiveTvChannel).Name, progress, cancellationToken).ConfigureAwait(false);
+ await CleanDatabaseInternal(newProgramIdList, typeof(LiveTvProgram).Name, progress, cancellationToken).ConfigureAwait(false);
+
+ // Load these now which will prefetch metadata
+ var dtoOptions = new DtoOptions();
+ dtoOptions.Fields.Remove(ItemFields.SyncInfo);
+ await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
+
progress.Report(100);
}
- private async Task RefreshChannelsInternal(ILiveTvService service, IProgress<double> progress, CancellationToken cancellationToken)
+ private async Task<Tuple<List<Guid>,List<Guid>>> RefreshChannelsInternal(ILiveTvService service, IProgress<double> progress, CancellationToken cancellationToken)
{
progress.Report(10);
@@ -1137,23 +1056,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(5 * percent + 10);
}
- _channelIdList = list.Select(i => i.Id).ToList();
progress.Report(15);
numComplete = 0;
- var programs = new List<LiveTvProgram>();
+ var programs = new List<Guid>();
+ var channels = new List<Guid>();
var guideDays = GetGuideDays(list.Count);
cancellationToken.ThrowIfCancellationRequested();
- foreach (var item in list)
+ foreach (var currentChannel in list)
{
+ channels.Add(currentChannel.Id);
cancellationToken.ThrowIfCancellationRequested();
- // Avoid implicitly captured closure
- var currentChannel = item;
-
try
{
var start = DateTime.UtcNow.AddHours(-1);
@@ -1161,9 +1078,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
+ var channelId = currentChannel.Id.ToString("N");
+
foreach (var program in channelPrograms)
{
- programs.Add(await GetProgram(program, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false));
+ var programItem = await GetProgram(program, channelId, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false);
+ programs.Add(programItem.Id);
}
}
catch (OperationCanceledException)
@@ -1181,44 +1101,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(80 * percent + 10);
}
-
- lock (_programsDataLock)
- {
- _programs = programs.ToDictionary(i => i.Id);
- }
-
- _refreshedPrograms.Clear();
- progress.Report(90);
-
- // Load these now which will prefetch metadata
- var dtoOptions = new DtoOptions();
- dtoOptions.Fields.Remove(ItemFields.SyncInfo);
- await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
progress.Report(100);
- }
- private Task CleanDatabaseInternal(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return DeleteOldPrograms(GetProgramsDictionary().Keys.ToList(), progress, cancellationToken);
+ return new Tuple<List<Guid>,List<Guid>>(channels, programs);
}
- private async Task DeleteOldPrograms(List<Guid> currentIdList, IProgress<double> progress, CancellationToken cancellationToken)
+ private async Task CleanDatabaseInternal(List<Guid> currentIdList, string typeName, IProgress<double> progress, CancellationToken cancellationToken)
{
- var list = _itemRepo.GetItemIdsOfType(typeof(LiveTvProgram)).ToList();
+ var list = _itemRepo.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeName }
+
+ }).Items.ToList();
var numComplete = 0;
- foreach (var programId in list)
+ foreach (var itemId in list)
{
cancellationToken.ThrowIfCancellationRequested();
- if (!currentIdList.Contains(programId))
+ if (!currentIdList.Contains(itemId))
{
- var program = _libraryManager.GetItemById(programId);
+ var item = _libraryManager.GetItemById(itemId);
- if (program != null)
+ if (item != null)
{
- await _libraryManager.DeleteItem(program).ConfigureAwait(false);
+ await _libraryManager.DeleteItem(item).ConfigureAwait(false);
}
}
@@ -1358,11 +1266,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var program = (LiveTvProgram)item;
var service = GetService(program);
- var channel = string.IsNullOrEmpty(program.ExternalChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, program.ExternalChannelId));
+ var channel = GetInternalChannel(program.ChannelId);
dto.Id = _tvDtoService.GetInternalProgramId(service.Name, program.ExternalId).ToString("N");
- dto.ChannelId = _tvDtoService.GetInternalChannelId(service.Name, program.ExternalChannelId).ToString("N");
+ dto.ChannelId = channel.Id.ToString("N");
dto.StartDate = program.StartDate;
dto.IsRepeat = program.IsRepeat;
@@ -1676,29 +1584,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var channel = GetInternalChannel(id);
- var currentProgram = GetCurrentProgram(channel.ExternalId);
-
- var dto = _tvDtoService.GetChannelInfoDto(channel, currentProgram, user);
+ var now = DateTime.UtcNow;
- return dto;
- }
+ var programs = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ ChannelIds = new[] { id },
+ MaxStartDate = now,
+ MinEndDate = now,
+ Limit = 1
- private LiveTvProgram GetCurrentProgram(string externalChannelId)
- {
- var now = DateTime.UtcNow;
+ }).Items.Cast<LiveTvProgram>();
- var program = GetPrograms()
- .Where(i => string.Equals(externalChannelId, i.ExternalChannelId, StringComparison.OrdinalIgnoreCase))
+ var currentProgram = programs
.OrderBy(i => i.StartDate)
- .SkipWhile(i => now >= (i.EndDate ?? DateTime.MinValue))
.FirstOrDefault();
- if (program != null)
- {
- RefreshIfNeeded(program);
- }
+ var dto = _tvDtoService.GetChannelInfoDto(channel, currentProgram, user);
- return program;
+ return dto;
}
private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
@@ -1711,10 +1615,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (program != null)
{
+ var channel = GetInternalChannel(program.ChannelId);
+
programInfo = new ProgramInfo
{
Audio = program.Audio,
- ChannelId = program.ExternalChannelId,
+ ChannelId = channel.ExternalId,
CommunityRating = program.CommunityRating,
EndDate = program.EndDate ?? DateTime.MinValue,
EpisodeTitle = program.EpisodeTitle,
@@ -1990,15 +1896,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public GuideInfo GetGuideInfo()
{
- var programs = GetPrograms().OrderBy(i => i.StartDate).ToList();
-
- var startDate = programs.Count == 0 ?
- DateTime.MinValue :
- programs[0].StartDate;
-
- var endDate = programs.Count == 0 ?
- DateTime.MinValue :
- programs[programs.Count - 1].StartDate;
+ var startDate = DateTime.UtcNow;
+ var endDate = startDate.AddDays(14);
return new GuideInfo
{