diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 6205c5f70d..dc271c5571 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -588,6 +588,10 @@ namespace MediaBrowser.Dlna.Ssdp { } + catch (Exception) + { + // If called while shutting down, seeing a NullReferenceException inside EndReceive + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index c88f88c6e4..7e55d77d7a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -232,7 +232,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - _channelCache = list; + _channelCache = list.ToList(); return list; } @@ -520,9 +520,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { if (!IsListingProviderEnabledForTuner(provider.Item2, channel.TunerHostId)) { + _logger.Debug("Skipping getting programs for channel {0}-{1} from {2}-{3}, because it's not enabled for this tuner.", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty); continue; } + _logger.Debug("Getting programs for channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty); + var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channel.Number, channel.Name, startDateUtc, endDateUtc, cancellationToken) .ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 4499432290..70638a8bdd 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -28,8 +28,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings private const string ApiUrl = "https://json.schedulesdirect.org/20141201"; - private readonly ConcurrentDictionary _channelPair = - new ConcurrentDictionary(); + private readonly Dictionary> _channelPairingCache = + new Dictionary>(StringComparer.OrdinalIgnoreCase); public SchedulesDirect(ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IApplicationHost appHost) { @@ -68,29 +68,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings if (string.IsNullOrWhiteSpace(token)) { + _logger.Warn("SchedulesDirect token is empty, returning empty program list"); return programsInfo; } if (string.IsNullOrWhiteSpace(info.ListingsId)) { + _logger.Warn("ListingsId is null, returning empty program list"); return programsInfo; } - var httpOptions = new HttpRequestOptions() - { - Url = ApiUrl + "/schedules", - UserAgent = UserAgent, - CancellationToken = cancellationToken, - // The data can be large so give it some extra time - TimeoutMs = 60000, - LogErrorResponseBody = true - }; - - httpOptions.RequestHeaders["token"] = token; - var dates = GetScheduleRequestDates(startDateUtc, endDateUtc); - ScheduleDirect.Station station = GetStation(channelNumber, channelName); + ScheduleDirect.Station station = GetStation(info.ListingsId, channelNumber, channelName); if (station == null) { @@ -113,13 +103,26 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings var requestString = _jsonSerializer.SerializeToString(requestList); _logger.Debug("Request string for schedules is: " + requestString); + + var httpOptions = new HttpRequestOptions() + { + Url = ApiUrl + "/schedules", + UserAgent = UserAgent, + CancellationToken = cancellationToken, + // The data can be large so give it some extra time + TimeoutMs = 60000, + LogErrorResponseBody = true + }; + + httpOptions.RequestHeaders["token"] = token; + httpOptions.RequestContent = requestString; using (var response = await Post(httpOptions, true, info).ConfigureAwait(false)) { StreamReader reader = new StreamReader(response.Content); string responseString = reader.ReadToEnd(); var dailySchedules = _jsonSerializer.DeserializeFromString>(responseString); - _logger.Debug("Found " + dailySchedules.Count() + " programs on " + channelNumber + " ScheduleDirect"); + _logger.Debug("Found " + dailySchedules.Count + " programs on " + channelNumber + " ScheduleDirect"); httpOptions = new HttpRequestOptions() { @@ -176,23 +179,77 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings return programsInfo; } - private ScheduleDirect.Station GetStation(string channelNumber, string channelName) + private readonly object _channelCacheLock = new object(); + private ScheduleDirect.Station GetStation(string listingsId, string channelNumber, string channelName) { - ScheduleDirect.Station station; + lock (_channelCacheLock) + { + Dictionary channelPair; + if (_channelPairingCache.TryGetValue(listingsId, out channelPair)) + { + ScheduleDirect.Station station; + + if (channelPair.TryGetValue(channelNumber, out station)) + { + return station; + } - if (_channelPair.TryGetValue(channelNumber, out station)) + if (string.IsNullOrWhiteSpace(channelName)) + { + return null; + } + + channelName = NormalizeName(channelName); + + return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase)); + } + + return null; + } + } + + private void AddToChannelPairCache(string listingsId, string channelNumber, ScheduleDirect.Station schChannel) + { + lock (_channelCacheLock) { - return station; + Dictionary cache; + if (_channelPairingCache.TryGetValue(listingsId, out cache)) + { + cache[channelNumber] = schChannel; + } + else + { + cache = new Dictionary(); + cache[channelNumber] = schChannel; + _channelPairingCache[listingsId] = cache; + } } + } - if (string.IsNullOrWhiteSpace(channelName)) + private void ClearPairCache(string listingsId) + { + lock (_channelCacheLock) { - return null; + Dictionary cache; + if (_channelPairingCache.TryGetValue(listingsId, out cache)) + { + cache.Clear(); + } } + } - channelName = NormalizeName(channelName); + private int GetChannelPairCacheCount(string listingsId) + { + lock (_channelCacheLock) + { + Dictionary cache; + if (_channelPairingCache.TryGetValue(listingsId, out cache)) + { + return cache.Count; + } - return _channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase)); + return 0; + } } private string NormalizeName(string value) @@ -203,7 +260,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings public async Task AddMetadata(ListingsProviderInfo info, List channels, CancellationToken cancellationToken) { - if (string.IsNullOrWhiteSpace(info.ListingsId)) + var listingsId = info.ListingsId; + if (string.IsNullOrWhiteSpace(listingsId)) { throw new Exception("ListingsId required"); } @@ -215,11 +273,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings throw new Exception("token required"); } - _channelPair.Clear(); + ClearPairCache(listingsId); var httpOptions = new HttpRequestOptions() { - Url = ApiUrl + "/lineups/" + info.ListingsId, + Url = ApiUrl + "/lineups/" + listingsId, UserAgent = UserAgent, CancellationToken = cancellationToken, LogErrorResponseBody = true, @@ -232,7 +290,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings using (var response = await Get(httpOptions, true, info).ConfigureAwait(false)) { var root = _jsonSerializer.DeserializeFromStream(response); - _logger.Info("Found " + root.map.Count() + " channels on the lineup on ScheduleDirect"); + _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect"); _logger.Info("Mapping Stations to Channel"); foreach (ScheduleDirect.Map map in root.map) { @@ -251,13 +309,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings _logger.Debug("Found channel: " + channelNumber + " in Schedules Direct"); var schChannel = root.stations.FirstOrDefault(item => item.stationID == map.stationID); - _channelPair.TryAdd(channelNumber, schChannel); + AddToChannelPairCache(listingsId, channelNumber, schChannel); } - _logger.Info("Added " + _channelPair.Count + " channels to the dictionary"); + _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary"); foreach (ChannelInfo channel in channels) { - var station = GetStation(channel.Number, channel.Name); + var station = GetStation(listingsId, channel.Number, channel.Name); if (station != null) {