update live tv database

This commit is contained in:
Luke Pulverenti 2015-06-01 10:49:23 -04:00
parent 68e64feafd
commit f2abd8ba39
20 changed files with 587 additions and 303 deletions

View File

@ -340,7 +340,7 @@ namespace MediaBrowser.Api
// We can really reduce the timeout for apps that are using the newer api // We can really reduce the timeout for apps that are using the newer api
if (!string.IsNullOrWhiteSpace(job.PlaySessionId)) if (!string.IsNullOrWhiteSpace(job.PlaySessionId))
{ {
timerDuration = 60000; timerDuration = 120000;
} }
} }

View File

@ -879,7 +879,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (!EnableSplitTranscoding(state)) if (!EnableSplitTranscoding(state))
{ {
args += " -copyts"; //args += " -copyts";
} }
return args; return args;
@ -910,11 +910,11 @@ namespace MediaBrowser.Api.Playback.Hls
//toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime); //toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime);
toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks); toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks);
} }
}
if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0) if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
{ {
timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture); timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
}
} }
var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty; var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
@ -959,6 +959,7 @@ namespace MediaBrowser.Api.Playback.Hls
private bool EnableSplitTranscoding(StreamState state) private bool EnableSplitTranscoding(StreamState state)
{ {
return false;
if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase)) if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase))
{ {
return false; return false;

View File

@ -42,7 +42,14 @@ namespace MediaBrowser.Api.Playback
var options = GetOptions(); var options = GetOptions();
if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdInSeconds)) var threshold = options.ThrottleThresholdInSeconds;
if (!options.EnableThrottling)
{
threshold *= 2;
}
if (IsThrottleAllowed(_job, threshold))
{ {
PauseTranscoding(); PauseTranscoding();
} }
@ -56,7 +63,7 @@ namespace MediaBrowser.Api.Playback
{ {
if (!_isPaused) if (!_isPaused)
{ {
_logger.Debug("Sending pause command to ffmpeg"); //_logger.Debug("Sending pause command to ffmpeg");
try try
{ {
@ -74,7 +81,7 @@ namespace MediaBrowser.Api.Playback
{ {
if (_isPaused) if (_isPaused)
{ {
_logger.Debug("Sending unpause command to ffmpeg"); //_logger.Debug("Sending unpause command to ffmpeg");
try try
{ {

View File

@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.Channels
{ {
public string ExternalId { get; set; } public string ExternalId { get; set; }
public string ChannelId { get; set; }
public string DataVersion { get; set; } public string DataVersion { get; set; }
public ChannelItemType ChannelItemType { get; set; } public ChannelItemType ChannelItemType { get; set; }

View File

@ -12,7 +12,6 @@ namespace MediaBrowser.Controller.Channels
{ {
public string ExternalId { get; set; } public string ExternalId { get; set; }
public string ChannelId { get; set; }
public string DataVersion { get; set; } public string DataVersion { get; set; }
public ChannelItemType ChannelItemType { get; set; } public ChannelItemType ChannelItemType { get; set; }

View File

@ -16,7 +16,6 @@ namespace MediaBrowser.Controller.Channels
{ {
public string ExternalId { get; set; } public string ExternalId { get; set; }
public string ChannelId { get; set; }
public string DataVersion { get; set; } public string DataVersion { get; set; }
public ChannelItemType ChannelItemType { get; set; } public ChannelItemType ChannelItemType { get; set; }

View File

@ -59,6 +59,12 @@ namespace MediaBrowser.Controller.Entities
public List<ItemImageInfo> ImageInfos { get; set; } public List<ItemImageInfo> ImageInfos { get; set; }
/// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
[IgnoreDataMember] [IgnoreDataMember]
public virtual bool SupportsAddingToPlaylist public virtual bool SupportsAddingToPlaylist
{ {

View File

@ -0,0 +1,9 @@

namespace MediaBrowser.Controller.Entities
{
public interface IHasProgramAttributes
{
bool IsMovie { get; set; }
bool IsSports { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace MediaBrowser.Controller.Entities
{
public interface IHasStartDate
{
DateTime StartDate { get; set; }
}
}

View File

@ -73,6 +73,17 @@ namespace MediaBrowser.Controller.Entities
public string[] Tags { get; set; } public string[] Tags { get; set; }
public string[] OfficialRatings { get; set; } public string[] OfficialRatings { get; set; }
public DateTime? MinStartDate { get; set; }
public DateTime? MaxStartDate { get; set; }
public DateTime? MinEndDate { get; set; }
public DateTime? MaxEndDate { get; set; }
public bool? IsAiring { get; set; }
public bool? IsMovie { get; set; }
public bool? IsSports { get; set; }
public string[] ChannelIds { get; set; }
public InternalItemsQuery() public InternalItemsQuery()
{ {
Tags = new string[] { }; Tags = new string[] { };
@ -89,6 +100,7 @@ namespace MediaBrowser.Controller.Entities
Years = new int[] { }; Years = new int[] { };
PersonTypes = new string[] { }; PersonTypes = new string[] { };
PersonIds = new string[] { }; PersonIds = new string[] { };
ChannelIds = new string[] { };
} }
} }
} }

View File

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Library namespace MediaBrowser.Controller.Library
{ {
@ -132,6 +133,13 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem GetItemById(Guid id); BaseItem GetItemById(Guid id);
/// <summary>
/// Gets the items.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>QueryResult&lt;BaseItem&gt;.</returns>
QueryResult<BaseItem> GetItems(InternalItemsQuery query);
/// <summary> /// <summary>
/// Gets the memory item by identifier. /// Gets the memory item by identifier.
/// </summary> /// </summary>

View File

@ -1,5 +1,4 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
@ -7,12 +6,10 @@ using MediaBrowser.Model.Users;
using System; using System;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
public class LiveTvProgram : BaseItem, ILiveTvItem, IHasLookupInfo<LiveTvProgramLookupInfo> public class LiveTvProgram : BaseItem, ILiveTvItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
{ {
/// <summary> /// <summary>
/// Gets the user data key. /// Gets the user data key.
@ -28,12 +25,6 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
public string ExternalId { get; set; } public string ExternalId { get; set; }
/// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
public string ExternalChannelId { get; set; }
/// <summary> /// <summary>
/// Gets or sets the original air date. /// Gets or sets the original air date.
/// </summary> /// </summary>
@ -204,15 +195,6 @@ namespace MediaBrowser.Controller.LiveTv
return "Program"; return "Program";
} }
public override Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
{
DateLastSaved = DateTime.UtcNow;
// Avoid library manager and keep out of in-memory cache
// Not great that this class has to know about that, but we'll improve that later.
return ItemRepository.SaveItem(this, cancellationToken);
}
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)
{ {
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);

View File

@ -149,10 +149,12 @@
<Compile Include="Entities\IHasOriginalTitle.cs" /> <Compile Include="Entities\IHasOriginalTitle.cs" />
<Compile Include="Entities\IHasPreferredMetadataLanguage.cs" /> <Compile Include="Entities\IHasPreferredMetadataLanguage.cs" />
<Compile Include="Entities\IHasProductionLocations.cs" /> <Compile Include="Entities\IHasProductionLocations.cs" />
<Compile Include="Entities\IHasProgramAttributes.cs" />
<Compile Include="Entities\IHasScreenshots.cs" /> <Compile Include="Entities\IHasScreenshots.cs" />
<Compile Include="Entities\IHasSeries.cs" /> <Compile Include="Entities\IHasSeries.cs" />
<Compile Include="Entities\IHasShortOverview.cs" /> <Compile Include="Entities\IHasShortOverview.cs" />
<Compile Include="Entities\IHasSpecialFeatures.cs" /> <Compile Include="Entities\IHasSpecialFeatures.cs" />
<Compile Include="Entities\IHasStartDate.cs" />
<Compile Include="Entities\IHasTaglines.cs" /> <Compile Include="Entities\IHasTaglines.cs" />
<Compile Include="Entities\IHasTags.cs" /> <Compile Include="Entities\IHasTags.cs" />
<Compile Include="Entities\IHasThemeMedia.cs" /> <Compile Include="Entities\IHasThemeMedia.cs" />

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Persistence namespace MediaBrowser.Controller.Persistence
{ {
@ -102,13 +103,6 @@ namespace MediaBrowser.Controller.Persistence
/// <returns>IEnumerable{ChildDefinition}.</returns> /// <returns>IEnumerable{ChildDefinition}.</returns>
IEnumerable<Guid> GetChildren(Guid parentId); IEnumerable<Guid> GetChildren(Guid parentId);
/// <summary>
/// Gets the type of the items of.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>IEnumerable{Guid}.</returns>
IEnumerable<Guid> GetItemIdsOfType(Type type);
/// <summary> /// <summary>
/// Saves the children. /// Saves the children.
/// </summary> /// </summary>
@ -135,11 +129,24 @@ namespace MediaBrowser.Controller.Persistence
Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken); Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the type of the items of. /// Gets the item ids.
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="query">The query.</param>
/// <returns>IEnumerable&lt;BaseItem&gt;.</returns> /// <returns>IEnumerable&lt;Guid&gt;.</returns>
IEnumerable<BaseItem> GetItemsOfType(Type type); QueryResult<Guid> GetItemIds(InternalItemsQuery query);
/// <summary>
/// Gets the items.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>QueryResult&lt;BaseItem&gt;.</returns>
QueryResult<BaseItem> GetItems(InternalItemsQuery query);
/// <summary>
/// Gets the item ids list.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>List&lt;Guid&gt;.</returns>
List<Guid> GetItemIdsList(InternalItemsQuery query);
} }
} }

View File

@ -1,21 +1,20 @@
using System.Linq; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels; using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Movies;
using MediaBrowser.Providers.TV;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -23,7 +22,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.Omdb namespace MediaBrowser.Providers.Omdb
{ {
public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>, public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>,
IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<ChannelVideoItem, ChannelItemLookupInfo> IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<ChannelVideoItem, ChannelItemLookupInfo>, IRemoteMetadataProvider<LiveTvProgram, LiveTvProgramLookupInfo>
{ {
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -48,6 +47,16 @@ namespace MediaBrowser.Providers.Omdb
return GetSearchResults(searchInfo, "movie", cancellationToken); return GetSearchResults(searchInfo, "movie", cancellationToken);
} }
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(LiveTvProgramLookupInfo searchInfo, CancellationToken cancellationToken)
{
if (!searchInfo.IsMovie)
{
return Task.FromResult<IEnumerable<RemoteSearchResult>>(new List<RemoteSearchResult>());
}
return GetSearchResults(searchInfo, "movie", cancellationToken);
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken)
{ {
var list = new List<RemoteSearchResult>(); var list = new List<RemoteSearchResult>();
@ -169,13 +178,22 @@ namespace MediaBrowser.Providers.Omdb
return result; return result;
} }
public Task<MetadataResult<LiveTvProgram>> GetMetadata(LiveTvProgramLookupInfo info, CancellationToken cancellationToken)
{
if (!info.IsMovie)
{
return Task.FromResult(new MetadataResult<LiveTvProgram>());
}
return GetMovieResult<LiveTvProgram>(info, cancellationToken);
}
public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken) public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken)
{ {
return GetMovieResult<Movie>(info, cancellationToken); return GetMovieResult<Movie>(info, cancellationToken);
} }
private async Task<MetadataResult<T>> GetMovieResult<T>(ItemLookupInfo info, CancellationToken cancellationToken) private async Task<MetadataResult<T>> GetMovieResult<T>(ItemLookupInfo info, CancellationToken cancellationToken)
where T : Video, new() where T : BaseItem, new()
{ {
var result = new MetadataResult<T> var result = new MetadataResult<T>
{ {

View File

@ -84,7 +84,7 @@ namespace MediaBrowser.Server.Implementations.IO
// This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called.
// Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds // Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds
// But if we make this delay too high, we risk missing legitimate changes // But if we make this delay too high, we risk missing legitimate changes
await Task.Delay(10000).ConfigureAwait(false); await Task.Delay(15000).ConfigureAwait(false);
string val; string val;
_tempIgnoredPaths.TryRemove(path, out val); _tempIgnoredPaths.TryRemove(path, out val);

View File

@ -16,6 +16,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Naming.Audio; using MediaBrowser.Naming.Audio;
using MediaBrowser.Naming.Common; using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.TV; using MediaBrowser.Naming.TV;
@ -1209,6 +1210,18 @@ namespace MediaBrowser.Server.Implementations.Library
return item; return item;
} }
public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
{
var result = ItemRepository.GetItemIdsList(query);
var items = result.Select(GetItemById).ToArray();
return new QueryResult<BaseItem>
{
Items = items
};
}
/// <summary> /// <summary>
/// Gets the intros. /// Gets the intros.
/// </summary> /// </summary>

View File

@ -17,7 +17,6 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
@ -54,10 +53,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly ConcurrentDictionary<string, LiveStreamData> _openStreams = private readonly ConcurrentDictionary<string, LiveStreamData> _openStreams =
new ConcurrentDictionary<string, LiveStreamData>(); 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) 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; _config = config;
@ -108,44 +103,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>(); _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) public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
{ {
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
var channels = _channelIdList.Select(_libraryManager.GetItemById) var channels = _libraryManager.GetItems(new InternalItemsQuery
.Where(i => i != null) {
.OfType<LiveTvChannel>(); IncludeItemTypes = new[] { typeof(LiveTvChannel).Name }
}).Items.Cast<LiveTvChannel>();
if (user != null) if (user != null)
{ {
@ -258,9 +224,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var returnList = new List<ChannelInfoDto>(); 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) 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)); returnList.Add(_tvDtoService.GetChannelInfoDto(channel, currentProgram, user));
} }
@ -286,34 +263,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
internal LiveTvProgram GetInternalProgram(string id) internal LiveTvProgram GetInternalProgram(string id)
{ {
var guid = new Guid(id); return _libraryManager.GetItemById(id) as LiveTvProgram;
LiveTvProgram obj = null;
GetProgramsDictionary().TryGetValue(guid, out obj);
if (obj != null)
{
RefreshIfNeeded(obj);
}
return obj;
} }
private void RefreshIfNeeded(LiveTvProgram program) internal LiveTvProgram GetInternalProgram(Guid id)
{ {
if (!_refreshedPrograms.ContainsKey(program.Id)) return _libraryManager.GetItemById(id) as LiveTvProgram;
{
_refreshedPrograms.TryAdd(program.Id, true);
_providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions());
}
}
private void RefreshIfNeeded(IEnumerable<LiveTvProgram> programs)
{
foreach (var program in programs)
{
RefreshIfNeeded(program);
}
} }
public async Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken) public async Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
@ -598,14 +553,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return item; 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 id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
var item = _libraryManager.GetItemById(id) as LiveTvProgram; var item = _libraryManager.GetItemById(id) as LiveTvProgram;
var isNew = false;
if (item == null) if (item == null)
{ {
isNew = true;
item = new LiveTvProgram item = new LiveTvProgram
{ {
Name = info.Name, Name = info.Name,
@ -619,8 +576,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.ServiceName = serviceName; item.ServiceName = serviceName;
item.Audio = info.Audio; item.Audio = info.Audio;
item.ExternalChannelId = info.ChannelId; item.ChannelId = channelId;
item.CommunityRating = info.CommunityRating; item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
item.EndDate = info.EndDate; item.EndDate = info.EndDate;
item.EpisodeTitle = info.EpisodeTitle; item.EpisodeTitle = info.EpisodeTitle;
item.ExternalId = info.Id; item.ExternalId = info.Id;
@ -636,8 +593,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.IsSeries = info.IsSeries; item.IsSeries = info.IsSeries;
item.IsSports = info.IsSports; item.IsSports = info.IsSports;
item.Name = info.Name; item.Name = info.Name;
item.OfficialRating = info.OfficialRating; item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
item.Overview = info.Overview; item.Overview = item.Overview ?? info.Overview;
item.OriginalAirDate = info.OriginalAirDate; item.OriginalAirDate = info.OriginalAirDate;
item.ProviderImagePath = info.ImagePath; item.ProviderImagePath = info.ImagePath;
item.ProviderImageUrl = info.ImageUrl; item.ProviderImageUrl = info.ImageUrl;
@ -647,7 +604,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.ProductionYear = info.ProductionYear; item.ProductionYear = info.ProductionYear;
item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate; 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; return item;
} }
@ -721,17 +687,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return recording; 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) public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
{ {
var program = GetInternalProgram(id); var program = GetInternalProgram(id);
@ -745,55 +700,36 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken) public async Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
{ {
IEnumerable<LiveTvProgram> programs = GetPrograms(); var internalQuery = new InternalItemsQuery
if (query.MinEndDate.HasValue)
{ {
var val = query.MinEndDate.Value; IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
MinEndDate = query.MinEndDate,
programs = programs.Where(i => i.EndDate.HasValue && i.EndDate.Value >= val); MinStartDate = query.MinStartDate,
} MaxEndDate = query.MaxEndDate,
MaxStartDate = query.MaxStartDate,
if (query.MinStartDate.HasValue) ChannelIds = query.ChannelIds,
{ IsMovie = query.IsMovie,
var val = query.MinStartDate.Value; IsSports = query.IsSports
};
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);
}
if (query.HasAired.HasValue) if (query.HasAired.HasValue)
{ {
var val = query.HasAired.Value; if (query.HasAired.Value)
programs = programs.Where(i => i.HasAired == val); {
internalQuery.MaxEndDate = DateTime.UtcNow;
}
else
{
internalQuery.MinEndDate = DateTime.UtcNow;
}
} }
if (query.ChannelIds.Length > 0) IEnumerable<LiveTvProgram> programs = _libraryManager.GetItems(internalQuery).Items.Cast<LiveTvProgram>();
// Apply genre filter
if (query.Genres.Length > 0)
{ {
var guids = query.ChannelIds.Select(i => new Guid(i)).ToList(); programs = programs.Where(p => p.Genres.Any(g => query.Genres.Contains(g, StringComparer.OrdinalIgnoreCase)));
programs = programs.Where(i =>
{
var programChannelId = i.ExternalChannelId;
var service = GetService(i);
var internalProgramChannelId = _tvDtoService.GetInternalChannelId(service.Name, programChannelId);
return guids.Contains(internalProgramChannelId);
});
} }
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
@ -804,22 +740,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
programs = programs.Where(i => i.IsVisible(currentUser)); programs = programs.Where(i => i.IsVisible(currentUser));
} }
// Apply genre filter
if (query.Genres.Length > 0)
{
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)
{
programs = programs.Where(p => p.IsSports == query.IsSports);
}
programs = _libraryManager.Sort(programs, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending) programs = _libraryManager.Sort(programs, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending)
.Cast<LiveTvProgram>(); .Cast<LiveTvProgram>();
@ -840,8 +760,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
.Select(i => _dtoService.GetBaseItemDto(i, new DtoOptions(), user)) .Select(i => _dtoService.GetBaseItemDto(i, new DtoOptions(), user))
.ToArray(); .ToArray();
RefreshIfNeeded(programList);
await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false); await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
var result = new QueryResult<BaseItemDto> var result = new QueryResult<BaseItemDto>
@ -855,7 +773,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken) public async Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken)
{ {
IEnumerable<LiveTvProgram> programs = GetPrograms(); var internalQuery = new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
IsAiring = query.IsAiring,
IsMovie = query.IsMovie,
IsSports = query.IsSports
};
if (query.HasAired.HasValue)
{
if (query.HasAired.Value)
{
internalQuery.MaxEndDate = DateTime.UtcNow;
}
else
{
internalQuery.MinEndDate = DateTime.UtcNow;
}
}
IEnumerable<LiveTvProgram> programs = _libraryManager.GetItems(internalQuery).Items.Cast<LiveTvProgram>();
var user = _userManager.GetUserById(query.UserId); var user = _userManager.GetUserById(query.UserId);
@ -863,28 +801,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var currentUser = user; var currentUser = user;
programs = programs.Where(i => i.IsVisible(currentUser)); programs = programs.Where(i => i.IsVisible(currentUser));
if (query.IsAiring.HasValue)
{
var val = query.IsAiring.Value;
programs = programs.Where(i => i.IsAiring == val);
}
if (query.HasAired.HasValue)
{
var val = query.HasAired.Value;
programs = programs.Where(i => i.HasAired == val);
}
if (query.IsMovie.HasValue)
{
programs = programs.Where(p => p.IsMovie == query.IsMovie.Value);
}
if (query.IsSports.HasValue)
{
programs = programs.Where(p => p.IsSports == query.IsSports.Value);
}
var programList = programs.ToList(); var programList = programs.ToList();
var genres = programList.SelectMany(i => i.Genres) var genres = programList.SelectMany(i => i.Genres)
@ -904,8 +820,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
programList = programs.ToList(); programList = programs.ToList();
RefreshIfNeeded(programList);
var returnArray = programList.ToArray(); var returnArray = programList.ToArray();
var result = new QueryResult<LiveTvProgram> var result = new QueryResult<LiveTvProgram>
@ -952,8 +866,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
score++; score++;
} }
var internalChannelId = _tvDtoService.GetInternalChannelId(program.ServiceName, program.ExternalChannelId); var channel = GetInternalChannel(program.ChannelId);
var channel = GetInternalChannel(internalChannelId);
var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey()); 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>(); return RefreshChannelsInternal(progress, cancellationToken);
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());
} }
private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken) private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
@ -1068,6 +973,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
? 0 ? 0
: 1 / _services.Count; : 1 / _services.Count;
var newChannelIdList = new List<Guid>();
var newProgramIdList = new List<Guid>();
foreach (var service in _services) foreach (var service in _services)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -1077,7 +985,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var innerProgress = new ActionableProgress<double>(); var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(p * progressPerService)); 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) catch (OperationCanceledException)
{ {
@ -1095,10 +1006,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(100 * percent); 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); 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); progress.Report(10);
@ -1137,23 +1056,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(5 * percent + 10); progress.Report(5 * percent + 10);
} }
_channelIdList = list.Select(i => i.Id).ToList();
progress.Report(15); progress.Report(15);
numComplete = 0; numComplete = 0;
var programs = new List<LiveTvProgram>(); var programs = new List<Guid>();
var channels = new List<Guid>();
var guideDays = GetGuideDays(list.Count); var guideDays = GetGuideDays(list.Count);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
foreach (var item in list) foreach (var currentChannel in list)
{ {
channels.Add(currentChannel.Id);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// Avoid implicitly captured closure
var currentChannel = item;
try try
{ {
var start = DateTime.UtcNow.AddHours(-1); 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 channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
var channelId = currentChannel.Id.ToString("N");
foreach (var program in channelPrograms) 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) catch (OperationCanceledException)
@ -1181,44 +1101,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(80 * percent + 10); 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); progress.Report(100);
return new Tuple<List<Guid>,List<Guid>>(channels, programs);
} }
private Task CleanDatabaseInternal(IProgress<double> progress, CancellationToken cancellationToken) private async Task CleanDatabaseInternal(List<Guid> currentIdList, string typeName, IProgress<double> progress, CancellationToken cancellationToken)
{ {
return DeleteOldPrograms(GetProgramsDictionary().Keys.ToList(), progress, cancellationToken); var list = _itemRepo.GetItemIds(new InternalItemsQuery
} {
IncludeItemTypes = new[] { typeName }
private async Task DeleteOldPrograms(List<Guid> currentIdList, IProgress<double> progress, CancellationToken cancellationToken) }).Items.ToList();
{
var list = _itemRepo.GetItemIdsOfType(typeof(LiveTvProgram)).ToList();
var numComplete = 0; var numComplete = 0;
foreach (var programId in list) foreach (var itemId in list)
{ {
cancellationToken.ThrowIfCancellationRequested(); 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 program = (LiveTvProgram)item;
var service = GetService(program); 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.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.StartDate = program.StartDate;
dto.IsRepeat = program.IsRepeat; dto.IsRepeat = program.IsRepeat;
@ -1676,31 +1584,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{ {
var channel = GetInternalChannel(id); var channel = GetInternalChannel(id);
var currentProgram = GetCurrentProgram(channel.ExternalId); var now = DateTime.UtcNow;
var programs = _libraryManager.GetItems(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
ChannelIds = new[] { id },
MaxStartDate = now,
MinEndDate = now,
Limit = 1
}).Items.Cast<LiveTvProgram>();
var currentProgram = programs
.OrderBy(i => i.StartDate)
.FirstOrDefault();
var dto = _tvDtoService.GetChannelInfoDto(channel, currentProgram, user); var dto = _tvDtoService.GetChannelInfoDto(channel, currentProgram, user);
return dto; return dto;
} }
private LiveTvProgram GetCurrentProgram(string externalChannelId)
{
var now = DateTime.UtcNow;
var program = GetPrograms()
.Where(i => string.Equals(externalChannelId, i.ExternalChannelId, StringComparison.OrdinalIgnoreCase))
.OrderBy(i => i.StartDate)
.SkipWhile(i => now >= (i.EndDate ?? DateTime.MinValue))
.FirstOrDefault();
if (program != null)
{
RefreshIfNeeded(program);
}
return program;
}
private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null) private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
{ {
var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ? var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ?
@ -1711,10 +1615,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (program != null) if (program != null)
{ {
var channel = GetInternalChannel(program.ChannelId);
programInfo = new ProgramInfo programInfo = new ProgramInfo
{ {
Audio = program.Audio, Audio = program.Audio,
ChannelId = program.ExternalChannelId, ChannelId = channel.ExternalId,
CommunityRating = program.CommunityRating, CommunityRating = program.CommunityRating,
EndDate = program.EndDate ?? DateTime.MinValue, EndDate = program.EndDate ?? DateTime.MinValue,
EpisodeTitle = program.EpisodeTitle, EpisodeTitle = program.EpisodeTitle,
@ -1990,15 +1896,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public GuideInfo GetGuideInfo() public GuideInfo GetGuideInfo()
{ {
var programs = GetPrograms().OrderBy(i => i.StartDate).ToList(); var startDate = DateTime.UtcNow;
var endDate = startDate.AddDays(14);
var startDate = programs.Count == 0 ?
DateTime.MinValue :
programs[0].StartDate;
var endDate = programs.Count == 0 ?
DateTime.MinValue :
programs[programs.Count - 1].StartDate;
return new GuideInfo return new GuideInfo
{ {

View File

@ -70,7 +70,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{ {
try try
{ {
var response = await service.GetProgramImageAsync(liveTvItem.ExternalId, liveTvItem.ExternalChannelId, cancellationToken).ConfigureAwait(false); var channel = _liveTvManager.GetInternalChannel(liveTvItem.ChannelId);
var response = await service.GetProgramImageAsync(liveTvItem.ExternalId, channel.ExternalId, cancellationToken).ConfigureAwait(false);
if (response != null) if (response != null)
{ {

View File

@ -1,12 +1,15 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -126,6 +129,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.RunQueries(queries, _logger); _connection.RunQueries(queries, _logger);
_connection.AddColumn(_logger, "TypedBaseItems", "StartDate", "DATETIME");
_connection.AddColumn(_logger, "TypedBaseItems", "EndDate", "DATETIME");
_connection.AddColumn(_logger, "TypedBaseItems", "ChannelId", "Text");
_connection.AddColumn(_logger, "TypedBaseItems", "IsMovie", "BIT");
_connection.AddColumn(_logger, "TypedBaseItems", "IsSports", "BIT");
PrepareStatements(); PrepareStatements();
_mediaStreamsRepository.Initialize(); _mediaStreamsRepository.Initialize();
@ -143,10 +152,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
private void PrepareStatements() private void PrepareStatements()
{ {
_saveItemCommand = _connection.CreateCommand(); _saveItemCommand = _connection.CreateCommand();
_saveItemCommand.CommandText = "replace into TypedBaseItems (guid, type, data) values (@1, @2, @3)"; _saveItemCommand.CommandText = "replace into TypedBaseItems (guid, type, data, StartDate, EndDate, ChannelId, IsMovie, IsSports) values (@1, @2, @3, @4, @5, @6, @7, @8)";
_saveItemCommand.Parameters.Add(_saveItemCommand, "@1"); _saveItemCommand.Parameters.Add(_saveItemCommand, "@1");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@2"); _saveItemCommand.Parameters.Add(_saveItemCommand, "@2");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@3"); _saveItemCommand.Parameters.Add(_saveItemCommand, "@3");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@4");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@5");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@6");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@7");
_saveItemCommand.Parameters.Add(_saveItemCommand, "@8");
_deleteChildrenCommand = _connection.CreateCommand(); _deleteChildrenCommand = _connection.CreateCommand();
_deleteChildrenCommand.CommandText = "delete from ChildrenIds where ParentId=@ParentId"; _deleteChildrenCommand.CommandText = "delete from ChildrenIds where ParentId=@ParentId";
@ -155,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_deleteItemCommand = _connection.CreateCommand(); _deleteItemCommand = _connection.CreateCommand();
_deleteItemCommand.CommandText = "delete from TypedBaseItems where guid=@Id"; _deleteItemCommand.CommandText = "delete from TypedBaseItems where guid=@Id";
_deleteItemCommand.Parameters.Add(_deleteItemCommand, "@Id"); _deleteItemCommand.Parameters.Add(_deleteItemCommand, "@Id");
_saveChildrenCommand = _connection.CreateCommand(); _saveChildrenCommand = _connection.CreateCommand();
_saveChildrenCommand.CommandText = "replace into ChildrenIds (ParentId, ItemId) values (@ParentId, @ItemId)"; _saveChildrenCommand.CommandText = "replace into ChildrenIds (ParentId, ItemId) values (@ParentId, @ItemId)";
_saveChildrenCommand.Parameters.Add(_saveChildrenCommand, "@ParentId"); _saveChildrenCommand.Parameters.Add(_saveChildrenCommand, "@ParentId");
@ -200,7 +214,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
CheckDisposed(); CheckDisposed();
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
IDbTransaction transaction = null; IDbTransaction transaction = null;
@ -217,6 +231,31 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemCommand.GetParameter(1).Value = item.GetType().FullName; _saveItemCommand.GetParameter(1).Value = item.GetType().FullName;
_saveItemCommand.GetParameter(2).Value = _jsonSerializer.SerializeToBytes(item); _saveItemCommand.GetParameter(2).Value = _jsonSerializer.SerializeToBytes(item);
var hasStartDate = item as IHasStartDate;
if (hasStartDate != null)
{
_saveItemCommand.GetParameter(3).Value = hasStartDate.StartDate;
}
else
{
_saveItemCommand.GetParameter(3).Value = null;
}
_saveItemCommand.GetParameter(4).Value = item.EndDate;
_saveItemCommand.GetParameter(5).Value = item.ChannelId;
var hasProgramAttributes = item as IHasProgramAttributes;
if (hasProgramAttributes != null)
{
_saveItemCommand.GetParameter(6).Value = hasProgramAttributes.IsMovie;
_saveItemCommand.GetParameter(7).Value = hasProgramAttributes.IsSports;
}
else
{
_saveItemCommand.GetParameter(6).Value = null;
_saveItemCommand.GetParameter(7).Value = null;
}
_saveItemCommand.Transaction = transaction; _saveItemCommand.Transaction = transaction;
_saveItemCommand.ExecuteNonQuery(); _saveItemCommand.ExecuteNonQuery();
@ -254,7 +293,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_writeLock.Release(); _writeLock.Release();
} }
} }
/// <summary> /// <summary>
/// Internal retrieve from items or users table /// Internal retrieve from items or users table
/// </summary> /// </summary>
@ -270,7 +309,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
CheckDisposed(); CheckDisposed();
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select type,data from TypedBaseItems where guid = @guid"; cmd.CommandText = "select type,data from TypedBaseItems where guid = @guid";
@ -467,7 +506,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
CheckDisposed(); CheckDisposed();
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select ItemId from ChildrenIds where ParentId = @ParentId"; cmd.CommandText = "select ItemId from ChildrenIds where ParentId = @ParentId";
@ -492,7 +531,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
CheckDisposed(); CheckDisposed();
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select type,data from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)"; cmd.CommandText = "select type,data from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)";
@ -544,6 +583,279 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
} }
public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
CheckDisposed();
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select type,data from TypedBaseItems";
var whereClauses = GetWhereClauses(query, cmd, false);
var whereTextWithoutPaging = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
whereClauses = GetWhereClauses(query, cmd, true);
var whereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
cmd.CommandText += whereText;
if (query.Limit.HasValue)
{
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
}
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging;
var list = new List<BaseItem>();
var count = 0;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
{
list.Add(GetItem(reader));
}
if (reader.NextResult() && reader.Read())
{
count = reader.GetInt32(0);
}
}
return new QueryResult<BaseItem>()
{
Items = list.ToArray(),
TotalRecordCount = count
};
}
}
public List<Guid> GetItemIdsList(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
CheckDisposed();
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select guid from TypedBaseItems";
var whereClauses = GetWhereClauses(query, cmd, false);
whereClauses = GetWhereClauses(query, cmd, true);
var whereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
cmd.CommandText += whereText;
if (query.Limit.HasValue)
{
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
}
var list = new List<Guid>();
_logger.Debug(cmd.CommandText);
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
{
list.Add(reader.GetGuid(0));
}
}
return list;
}
}
public QueryResult<Guid> GetItemIds(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
CheckDisposed();
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select guid from TypedBaseItems";
var whereClauses = GetWhereClauses(query, cmd, false);
var whereTextWithoutPaging = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
whereClauses = GetWhereClauses(query, cmd, true);
var whereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
cmd.CommandText += whereText;
if (query.Limit.HasValue)
{
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
}
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging;
var list = new List<Guid>();
var count = 0;
_logger.Debug(cmd.CommandText);
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
{
list.Add(reader.GetGuid(0));
}
if (reader.NextResult() && reader.Read())
{
count = reader.GetInt32(0);
}
}
return new QueryResult<Guid>()
{
Items = list.ToArray(),
TotalRecordCount = count
};
}
}
private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, bool addPaging)
{
var whereClauses = new List<string>();
if (query.IsMovie.HasValue)
{
whereClauses.Add("IsMovie=@IsMovie");
cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie;
}
if (query.IsSports.HasValue)
{
whereClauses.Add("IsSports=@IsSports");
cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports;
}
if (query.IncludeItemTypes.Length == 1)
{
whereClauses.Add("type=@type");
cmd.Parameters.Add(cmd, "@type", DbType.String).Value = MapIncludeItemType(query.IncludeItemTypes[0]);
}
if (query.IncludeItemTypes.Length > 1)
{
var inClause = string.Join(",", query.IncludeItemTypes.Select(i => "'" + MapIncludeItemType(i) + "'").ToArray());
whereClauses.Add(string.Format("type in ({0})", inClause));
}
if (query.ChannelIds.Length == 1)
{
whereClauses.Add("ChannelId=@ChannelId");
cmd.Parameters.Add(cmd, "@ChannelId", DbType.String).Value = query.ChannelIds[0];
}
if (query.ChannelIds.Length > 1)
{
var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i + "'").ToArray());
whereClauses.Add(string.Format("ChannelId in ({0})", inClause));
}
if (query.MinEndDate.HasValue)
{
whereClauses.Add("EndDate>=@MinEndDate");
cmd.Parameters.Add(cmd, "@MinEndDate", DbType.Date).Value = query.MinEndDate.Value;
}
if (query.MaxEndDate.HasValue)
{
whereClauses.Add("EndDate<=@MaxEndDate");
cmd.Parameters.Add(cmd, "@MaxEndDate", DbType.Date).Value = query.MaxEndDate.Value;
}
if (query.MinStartDate.HasValue)
{
whereClauses.Add("StartDate>=@MinStartDate");
cmd.Parameters.Add(cmd, "@MinStartDate", DbType.Date).Value = query.MinStartDate.Value;
}
if (query.MaxStartDate.HasValue)
{
whereClauses.Add("StartDate<=@MaxStartDate");
cmd.Parameters.Add(cmd, "@MaxStartDate", DbType.Date).Value = query.MaxStartDate.Value;
}
if (query.IsAiring.HasValue)
{
if (query.IsAiring.Value)
{
whereClauses.Add("StartDate<=@MaxStartDate");
cmd.Parameters.Add(cmd, "@MaxStartDate", DbType.Date).Value = DateTime.UtcNow;
whereClauses.Add("EndDate>=@MinEndDate");
cmd.Parameters.Add(cmd, "@MinEndDate", DbType.Date).Value = DateTime.UtcNow;
}
else
{
whereClauses.Add("(StartDate>@IsAiringDate OR EndDate < @IsAiringDate)");
cmd.Parameters.Add(cmd, "@IsAiringDate", DbType.Date).Value = DateTime.UtcNow;
}
}
if (addPaging)
{
if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
{
var pagingWhereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM TypedBaseItems {0} ORDER BY DateCreated DESC LIMIT {1})",
pagingWhereText,
query.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
}
}
return whereClauses;
}
// Not crazy about having this all the way down here, but at least it's in one place
readonly Dictionary<string, string> _types = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{typeof(LiveTvProgram).Name, typeof(LiveTvProgram).FullName},
{typeof(LiveTvChannel).Name, typeof(LiveTvChannel).FullName}
};
private string MapIncludeItemType(string value)
{
string result;
if (_types.TryGetValue(value, out result))
{
return result;
}
return value;
}
public IEnumerable<Guid> GetItemIdsOfType(Type type) public IEnumerable<Guid> GetItemIdsOfType(Type type)
{ {
if (type == null) if (type == null)
@ -577,7 +889,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
CheckDisposed(); CheckDisposed();
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
IDbTransaction transaction = null; IDbTransaction transaction = null;
@ -595,7 +907,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_deleteItemCommand.GetParameter(0).Value = id; _deleteItemCommand.GetParameter(0).Value = id;
_deleteItemCommand.Transaction = transaction; _deleteItemCommand.Transaction = transaction;
_deleteItemCommand.ExecuteNonQuery(); _deleteItemCommand.ExecuteNonQuery();
transaction.Commit(); transaction.Commit();
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@ -642,7 +954,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
CheckDisposed(); CheckDisposed();
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
IDbTransaction transaction = null; IDbTransaction transaction = null;