Merge pull request #2440 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2017-02-04 18:34:41 -05:00 committed by GitHub
commit 77b0cabfee
21 changed files with 319 additions and 363 deletions

View file

@ -995,7 +995,7 @@ namespace Emby.Server.Implementations.Connect
if (changed) if (changed)
{ {
await _providerManager.SaveImage(user, imageUrl, null, ImageType.Primary, null, CancellationToken.None).ConfigureAwait(false); await _providerManager.SaveImage(user, imageUrl, ImageType.Primary, null, CancellationToken.None).ConfigureAwait(false);
await user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) await user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
{ {

View file

@ -2760,7 +2760,6 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.UpdatePeople(item.Id, people); return ItemRepository.UpdatePeople(item.Id, people);
} }
private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1, 1);
public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex) public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex)
{ {
foreach (var url in image.Path.Split('|')) foreach (var url in image.Path.Split('|'))
@ -2769,7 +2768,7 @@ namespace Emby.Server.Implementations.Library
{ {
_logger.Debug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url); _logger.Debug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url);
await _providerManagerFactory().SaveImage(item, url, _dynamicImageResourcePool, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); await _providerManagerFactory().SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
var newImage = item.GetImageInfo(image.Type, imageIndex); var newImage = item.GetImageInfo(image.Type, imageIndex);

View file

@ -393,7 +393,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
try try
{ {
await provider.Item1.AddMetadata(provider.Item2, enabledChannels, cancellationToken).ConfigureAwait(false); await AddMetadata(provider.Item1, provider.Item2, enabledChannels, enableCache, cancellationToken).ConfigureAwait(false);
} }
catch (NotSupportedException) catch (NotSupportedException)
{ {
@ -409,6 +409,120 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return list; return list;
} }
private async Task AddMetadata(IListingsProvider provider, ListingsProviderInfo info, List<ChannelInfo> tunerChannels, bool enableCache, CancellationToken cancellationToken)
{
var epgChannels = await GetEpgChannels(provider, info, enableCache, cancellationToken).ConfigureAwait(false);
foreach (var tunerChannel in tunerChannels)
{
var epgChannel = GetEpgChannelFromTunerChannel(info, tunerChannel, epgChannels);
if (epgChannel != null)
{
if (!string.IsNullOrWhiteSpace(epgChannel.Name))
{
tunerChannel.Name = epgChannel.Name;
}
}
}
}
private readonly ConcurrentDictionary<string, List<ChannelInfo>> _epgChannels =
new ConcurrentDictionary<string, List<ChannelInfo>>(StringComparer.OrdinalIgnoreCase);
private async Task<List<ChannelInfo>> GetEpgChannels(IListingsProvider provider, ListingsProviderInfo info, bool enableCache, CancellationToken cancellationToken)
{
List<ChannelInfo> result;
if (!enableCache || !_epgChannels.TryGetValue(info.Id, out result))
{
result = await provider.GetChannels(info, cancellationToken).ConfigureAwait(false);
_epgChannels.AddOrUpdate(info.Id, result, (k, v) => result);
}
return result;
}
private async Task<ChannelInfo> GetEpgChannelFromTunerChannel(IListingsProvider provider, ListingsProviderInfo info, ChannelInfo tunerChannel, CancellationToken cancellationToken)
{
var epgChannels = await GetEpgChannels(provider, info, true, cancellationToken).ConfigureAwait(false);
return GetEpgChannelFromTunerChannel(info, tunerChannel, epgChannels);
}
private string GetMappedChannel(string channelId, List<NameValuePair> mappings)
{
foreach (NameValuePair mapping in mappings)
{
if (StringHelper.EqualsIgnoreCase(mapping.Name, channelId))
{
return mapping.Value;
}
}
return channelId;
}
private ChannelInfo GetEpgChannelFromTunerChannel(ListingsProviderInfo info, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
{
return GetEpgChannelFromTunerChannel(info.ChannelMappings.ToList(), tunerChannel, epgChannels);
}
public ChannelInfo GetEpgChannelFromTunerChannel(List<NameValuePair> mappings, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
{
if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
{
var tunerChannelId = GetMappedChannel(tunerChannel.TunerChannelId, mappings);
if (string.IsNullOrWhiteSpace(tunerChannelId))
{
tunerChannelId = tunerChannel.TunerChannelId;
}
var channel = epgChannels.FirstOrDefault(i => string.Equals(tunerChannelId, i.Id, StringComparison.OrdinalIgnoreCase));
if (channel != null)
{
return channel;
}
}
if (!string.IsNullOrWhiteSpace(tunerChannel.Number))
{
var tunerChannelNumber = GetMappedChannel(tunerChannel.Number, mappings);
if (string.IsNullOrWhiteSpace(tunerChannelNumber))
{
tunerChannelNumber = tunerChannel.Number;
}
var channel = epgChannels.FirstOrDefault(i => string.Equals(tunerChannelNumber, i.Number, StringComparison.OrdinalIgnoreCase));
if (channel != null)
{
return channel;
}
}
if (!string.IsNullOrWhiteSpace(tunerChannel.Name))
{
var normalizedName = NormalizeName(tunerChannel.Name);
var channel = epgChannels.FirstOrDefault(i => string.Equals(normalizedName, NormalizeName(i.Name ?? string.Empty), StringComparison.OrdinalIgnoreCase));
if (channel != null)
{
return channel;
}
}
return null;
}
private string NormalizeName(string value)
{
return value.Replace(" ", string.Empty).Replace("-", string.Empty);
}
public async Task<List<ChannelInfo>> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken) public async Task<List<ChannelInfo>> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken)
{ {
var list = new List<ChannelInfo>(); var list = new List<ChannelInfo>();
@ -845,54 +959,37 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.Debug("Getting programs for channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty); _logger.Debug("Getting programs for channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
var channelMappings = GetChannelMappings(provider.Item2); var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false);
var channelNumber = channel.Number;
var tunerChannelId = channel.TunerChannelId;
if (!string.IsNullOrWhiteSpace(channelNumber)) List<ProgramInfo> programs;
if (epgChannel == null)
{ {
string mappedChannelNumber; programs = new List<ProgramInfo>();
if (channelMappings.TryGetValue(channelNumber, out mappedChannelNumber)) }
{ else
_logger.Debug("Found mapped channel on provider {0}. Tuner channel number: {1}, Mapped channel number: {2}", provider.Item1.Name, channelNumber, mappedChannelNumber); {
channelNumber = mappedChannelNumber; programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
} .ConfigureAwait(false)).ToList();
} }
var programs = await provider.Item1.GetProgramsAsync(provider.Item2, tunerChannelId, channelNumber, channel.Name, startDateUtc, endDateUtc, cancellationToken)
.ConfigureAwait(false);
var list = programs.ToList();
// Replace the value that came from the provider with a normalized value // Replace the value that came from the provider with a normalized value
foreach (var program in list) foreach (var program in programs)
{ {
program.ChannelId = channelId; program.ChannelId = channelId;
} }
if (list.Count > 0) if (programs.Count > 0)
{ {
SaveEpgDataForChannel(channelId, list); SaveEpgDataForChannel(channelId, programs);
return list; return programs;
} }
} }
return new List<ProgramInfo>(); return new List<ProgramInfo>();
} }
private Dictionary<string, string> GetChannelMappings(ListingsProviderInfo info)
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var mapping in info.ChannelMappings)
{
dict[mapping.Name] = mapping.Value;
}
return dict;
}
private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders() private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders()
{ {
return GetConfiguration().ListingProviders return GetConfiguration().ListingProviders

View file

@ -15,6 +15,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.LiveTv.Listings namespace Emby.Server.Implementations.LiveTv.Listings
{ {
@ -60,8 +61,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return dates; return dates;
} }
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(channelId))
{
throw new ArgumentNullException("channelId");
}
// Normalize incoming input
channelId = channelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I');
List<ProgramInfo> programsInfo = new List<ProgramInfo>(); List<ProgramInfo> programsInfo = new List<ProgramInfo>();
var token = await GetToken(info, cancellationToken).ConfigureAwait(false); var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
@ -80,15 +89,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc); var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
ScheduleDirect.Station station = GetStation(info.ListingsId, channelNumber, channelName); string stationID = channelId;
if (station == null)
{
_logger.Info("No Schedules Direct Station found for channel {0} with name {1}", channelNumber, channelName);
return programsInfo;
}
string stationID = station.stationID;
_logger.Info("Channel Station ID is: " + stationID); _logger.Info("Channel Station ID is: " + stationID);
List<ScheduleDirect.RequestScheduleForChannel> requestList = List<ScheduleDirect.RequestScheduleForChannel> requestList =
@ -122,7 +123,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
StreamReader reader = new StreamReader(response.Content); StreamReader reader = new StreamReader(response.Content);
string responseString = reader.ReadToEnd(); string responseString = reader.ReadToEnd();
var dailySchedules = _jsonSerializer.DeserializeFromString<List<ScheduleDirect.Day>>(responseString); var dailySchedules = _jsonSerializer.DeserializeFromString<List<ScheduleDirect.Day>>(responseString);
_logger.Debug("Found " + dailySchedules.Count + " programs on " + channelNumber + " ScheduleDirect"); _logger.Debug("Found " + dailySchedules.Count + " programs on " + stationID + " ScheduleDirect");
httpOptions = new HttpRequestOptions() httpOptions = new HttpRequestOptions()
{ {
@ -180,7 +181,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
} }
} }
programsInfo.Add(GetProgram(channelNumber, schedule, programDict[schedule.programID])); programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
} }
} }
} }
@ -202,183 +203,24 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return 0; return 0;
} }
private readonly object _channelCacheLock = new object(); private string GetChannelNumber(ScheduleDirect.Map map)
private ScheduleDirect.Station GetStation(string listingsId, string channelNumber, string channelName)
{ {
lock (_channelCacheLock) var channelNumber = map.logicalChannelNumber;
if (string.IsNullOrWhiteSpace(channelNumber))
{ {
Dictionary<string, ScheduleDirect.Station> channelPair; channelNumber = map.channel;
if (_channelPairingCache.TryGetValue(listingsId, out channelPair))
{
ScheduleDirect.Station station;
if (!string.IsNullOrWhiteSpace(channelNumber) && channelPair.TryGetValue(channelNumber, out station))
{
return station;
}
if (!string.IsNullOrWhiteSpace(channelName))
{
channelName = NormalizeName(channelName);
var result = channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
if (result != null)
{
return result;
}
}
if (!string.IsNullOrWhiteSpace(channelNumber))
{
return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase));
}
}
return null;
} }
if (string.IsNullOrWhiteSpace(channelNumber))
{
channelNumber = map.atscMajor + "." + map.atscMinor;
}
channelNumber = channelNumber.TrimStart('0');
return channelNumber;
} }
private void AddToChannelPairCache(string listingsId, string channelNumber, ScheduleDirect.Station schChannel) private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details)
{
lock (_channelCacheLock)
{
Dictionary<string, ScheduleDirect.Station> cache;
if (_channelPairingCache.TryGetValue(listingsId, out cache))
{
cache[channelNumber] = schChannel;
}
else
{
cache = new Dictionary<string, ScheduleDirect.Station>();
cache[channelNumber] = schChannel;
_channelPairingCache[listingsId] = cache;
}
}
}
private void ClearPairCache(string listingsId)
{
lock (_channelCacheLock)
{
Dictionary<string, ScheduleDirect.Station> cache;
if (_channelPairingCache.TryGetValue(listingsId, out cache))
{
cache.Clear();
}
}
}
private int GetChannelPairCacheCount(string listingsId)
{
lock (_channelCacheLock)
{
Dictionary<string, ScheduleDirect.Station> cache;
if (_channelPairingCache.TryGetValue(listingsId, out cache))
{
return cache.Count;
}
return 0;
}
}
private string NormalizeName(string value)
{
return value.Replace(" ", string.Empty).Replace("-", string.Empty);
}
public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels,
CancellationToken cancellationToken)
{
var listingsId = info.ListingsId;
if (string.IsNullOrWhiteSpace(listingsId))
{
throw new Exception("ListingsId required");
}
var token = await GetToken(info, cancellationToken);
if (string.IsNullOrWhiteSpace(token))
{
throw new Exception("token required");
}
ClearPairCache(listingsId);
var httpOptions = new HttpRequestOptions()
{
Url = ApiUrl + "/lineups/" + listingsId,
UserAgent = UserAgent,
CancellationToken = cancellationToken,
LogErrorResponseBody = true,
// The data can be large so give it some extra time
TimeoutMs = 60000
};
httpOptions.RequestHeaders["token"] = token;
using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
{
var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
foreach (ScheduleDirect.Map map in root.map)
{
var channelNumber = map.logicalChannelNumber;
if (string.IsNullOrWhiteSpace(channelNumber))
{
channelNumber = map.channel;
}
if (string.IsNullOrWhiteSpace(channelNumber))
{
channelNumber = map.atscMajor + "." + map.atscMinor;
}
channelNumber = channelNumber.TrimStart('0');
_logger.Debug("Found channel: " + channelNumber + " in Schedules Direct");
var schChannel = (root.stations ?? new List<ScheduleDirect.Station>()).FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
if (schChannel != null)
{
AddToChannelPairCache(listingsId, channelNumber, schChannel);
}
else
{
AddToChannelPairCache(listingsId, channelNumber, new ScheduleDirect.Station
{
stationID = map.stationID
});
}
}
foreach (ChannelInfo channel in channels)
{
var station = GetStation(listingsId, channel.Number, channel.Name);
if (station != null)
{
if (station.logo != null)
{
channel.ImageUrl = station.logo.URL;
channel.HasImage = true;
}
if (!string.IsNullOrWhiteSpace(station.name))
{
channel.Name = station.name;
}
}
else
{
_logger.Info("Schedules Direct doesnt have data for channel: " + channel.Number + " " + channel.Name);
}
}
}
}
private ProgramInfo GetProgram(string channel, ScheduleDirect.Program programInfo,
ScheduleDirect.ProgramDetails details)
{ {
//_logger.Debug("Show type is: " + (details.showType ?? "No ShowType")); //_logger.Debug("Show type is: " + (details.showType ?? "No ShowType"));
DateTime startAt = GetDate(programInfo.airDateTime); DateTime startAt = GetDate(programInfo.airDateTime);
@ -386,7 +228,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ProgramAudio audioType = ProgramAudio.Stereo; ProgramAudio audioType = ProgramAudio.Stereo;
bool repeat = programInfo.@new == null; bool repeat = programInfo.@new == null;
string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channel; string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channelId;
if (programInfo.audioProperties != null) if (programInfo.audioProperties != null)
{ {
@ -422,7 +264,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var info = new ProgramInfo var info = new ProgramInfo
{ {
ChannelId = channel, ChannelId = channelId,
Id = newID, Id = newID,
StartDate = startAt, StartDate = startAt,
EndDate = endAt, EndDate = endAt,
@ -969,8 +811,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
throw new Exception("ListingsId required"); throw new Exception("ListingsId required");
} }
await AddMetadata(info, new List<ChannelInfo>(), cancellationToken).ConfigureAwait(false);
var token = await GetToken(info, cancellationToken); var token = await GetToken(info, cancellationToken);
if (string.IsNullOrWhiteSpace(token)) if (string.IsNullOrWhiteSpace(token))
@ -997,39 +837,81 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response); var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(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"); _logger.Info("Mapping Stations to Channel");
var allStations = root.stations ?? new List<ScheduleDirect.Station>();
foreach (ScheduleDirect.Map map in root.map) foreach (ScheduleDirect.Map map in root.map)
{ {
var channelNumber = map.logicalChannelNumber; var channelNumber = GetChannelNumber(map);
if (string.IsNullOrWhiteSpace(channelNumber)) var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
if (station == null)
{ {
channelNumber = map.channel; station = new ScheduleDirect.Station
{
stationID = map.stationID
};
} }
if (string.IsNullOrWhiteSpace(channelNumber))
{
channelNumber = map.atscMajor + "." + map.atscMinor;
}
channelNumber = channelNumber.TrimStart('0');
var name = channelNumber; var name = channelNumber;
var station = GetStation(listingsId, channelNumber, null);
if (station != null && !string.IsNullOrWhiteSpace(station.name)) var channelInfo = new ChannelInfo
{
name = station.name;
}
list.Add(new ChannelInfo
{ {
Number = channelNumber, Number = channelNumber,
Name = name Name = name
}); };
if (station != null)
{
if (!string.IsNullOrWhiteSpace(station.name))
{
channelInfo.Name = station.name;
}
channelInfo.Id = station.stationID;
channelInfo.CallSign = station.callsign;
if (station.logo != null)
{
channelInfo.ImageUrl = station.logo.URL;
channelInfo.HasImage = true;
}
}
list.Add(channelInfo);
} }
} }
return list; return list;
} }
private ScheduleDirect.Station GetStation(List<ScheduleDirect.Station> allStations, string channelNumber, string channelName)
{
if (!string.IsNullOrWhiteSpace(channelName))
{
channelName = NormalizeName(channelName);
var result = allStations.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
if (result != null)
{
return result;
}
}
if (!string.IsNullOrWhiteSpace(channelNumber))
{
return allStations.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase));
}
return null;
}
private string NormalizeName(string value)
{
return value.Replace(" ", string.Empty).Replace("-", string.Empty);
}
public class ScheduleDirect public class ScheduleDirect
{ {
public class Token public class Token

View file

@ -106,8 +106,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return cacheFile; return cacheFile;
} }
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(channelId))
{
throw new ArgumentNullException("channelId");
}
if (!await EmbyTV.EmbyTVRegistration.Instance.EnableXmlTv().ConfigureAwait(false)) if (!await EmbyTV.EmbyTVRegistration.Instance.EnableXmlTv().ConfigureAwait(false))
{ {
var length = endDateUtc - startDateUtc; var length = endDateUtc - startDateUtc;
@ -120,7 +125,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false); var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
var reader = new XmlTvReader(path, GetLanguage()); var reader = new XmlTvReader(path, GetLanguage());
var results = reader.GetProgrammes(channelNumber, startDateUtc, endDateUtc, cancellationToken); var results = reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken);
return results.Select(p => GetProgramInfo(p, info)); return results.Select(p => GetProgramInfo(p, info));
} }
@ -194,28 +199,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return date; return date;
} }
public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken)
{
// Add the channel image url
var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
var reader = new XmlTvReader(path, GetLanguage());
var results = reader.GetChannels().ToList();
if (channels != null)
{
foreach (var c in channels)
{
var channelNumber = info.GetMappedChannel(c.Number);
var match = results.FirstOrDefault(r => string.Equals(r.Id, channelNumber, StringComparison.OrdinalIgnoreCase));
if (match != null && match.Icon != null && !String.IsNullOrEmpty(match.Icon.Source))
{
c.ImageUrl = match.Icon.Source;
}
}
}
}
public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings) public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
{ {
// Assume all urls are valid. check files for existence // Assume all urls are valid. check files for existence

View file

@ -2884,20 +2884,20 @@ namespace Emby.Server.Implementations.LiveTv
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>(); _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
} }
public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber) public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelId, string providerChannelId)
{ {
var config = GetConfiguration(); var config = GetConfiguration();
var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase)); var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelNumber, StringComparison.OrdinalIgnoreCase)).ToArray(); listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelId, StringComparison.OrdinalIgnoreCase)).ToArray();
if (!string.Equals(tunerChannelNumber, providerChannelNumber, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(tunerChannelId, providerChannelId, StringComparison.OrdinalIgnoreCase))
{ {
var list = listingsProviderInfo.ChannelMappings.ToList(); var list = listingsProviderInfo.ChannelMappings.ToList();
list.Add(new NameValuePair list.Add(new NameValuePair
{ {
Name = tunerChannelNumber, Name = tunerChannelId,
Value = providerChannelNumber Value = providerChannelId
}); });
listingsProviderInfo.ChannelMappings = list.ToArray(); listingsProviderInfo.ChannelMappings = list.ToArray();
} }
@ -2917,31 +2917,33 @@ namespace Emby.Server.Implementations.LiveTv
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>(); _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
return tunerChannelMappings.First(i => string.Equals(i.Number, tunerChannelNumber, StringComparison.OrdinalIgnoreCase)); return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelId, StringComparison.OrdinalIgnoreCase));
} }
public TunerChannelMapping GetTunerChannelMapping(ChannelInfo channel, List<NameValuePair> mappings, List<ChannelInfo> providerChannels) public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, List<NameValuePair> mappings, List<ChannelInfo> epgChannels)
{ {
var result = new TunerChannelMapping var result = new TunerChannelMapping
{ {
Name = channel.Number + " " + channel.Name, Name = tunerChannel.Name,
Number = channel.Number Id = tunerChannel.TunerChannelId
}; };
var mapping = mappings.FirstOrDefault(i => string.Equals(i.Name, channel.Number, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrWhiteSpace(tunerChannel.Number))
var providerChannelNumber = channel.Number;
if (mapping != null)
{ {
providerChannelNumber = mapping.Value; result.Name = tunerChannel.Number + " " + result.Name;
} }
var providerChannel = providerChannels.FirstOrDefault(i => string.Equals(i.Number, providerChannelNumber, StringComparison.OrdinalIgnoreCase)); if (string.IsNullOrWhiteSpace(result.Id))
{
result.Id = tunerChannel.Id;
}
var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, epgChannels);
if (providerChannel != null) if (providerChannel != null)
{ {
result.ProviderChannelNumber = providerChannel.Number;
result.ProviderChannelName = providerChannel.Name; result.ProviderChannelName = providerChannel.Name;
result.ProviderChannelId = providerChannel.Id;
} }
return result; return result;

View file

@ -113,17 +113,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
} }
} }
var startingNumber = 1;
foreach (var channel in channels)
{
if (!string.IsNullOrWhiteSpace(channel.Number))
{
continue;
}
channel.Number = startingNumber.ToString(CultureInfo.InvariantCulture);
startingNumber++;
}
return channels; return channels;
} }
@ -147,10 +136,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
channel.Name = GetChannelName(extInf, attributes); channel.Name = GetChannelName(extInf, attributes);
channel.Number = GetChannelNumber(extInf, attributes, mediaUrl); channel.Number = GetChannelNumber(extInf, attributes, mediaUrl);
if (attributes.TryGetValue("tvg-id", out value)) var channelId = GetTunerChannelId(attributes);
if (!string.IsNullOrWhiteSpace(channelId))
{ {
channel.Id = value; channel.Id = channelId;
channel.TunerChannelId = value; channel.TunerChannelId = channelId;
} }
return channel; return channel;
@ -186,9 +176,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
numberString = numberString.Trim(); numberString = numberString.Trim();
} }
if (string.IsNullOrWhiteSpace(numberString) || if (!IsValidChannelNumber(numberString))
string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
{ {
string value; string value;
if (attributes.TryGetValue("tvg-id", out value)) if (attributes.TryGetValue("tvg-id", out value))
@ -206,9 +194,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
numberString = numberString.Trim(); numberString = numberString.Trim();
} }
if (string.IsNullOrWhiteSpace(numberString) || if (!IsValidChannelNumber(numberString))
string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
{ {
string value; string value;
if (attributes.TryGetValue("channel-id", out value)) if (attributes.TryGetValue("channel-id", out value))
@ -222,9 +208,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
numberString = numberString.Trim(); numberString = numberString.Trim();
} }
if (string.IsNullOrWhiteSpace(numberString) || if (!IsValidChannelNumber(numberString))
string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
{ {
numberString = null; numberString = null;
} }
@ -239,8 +223,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last()); numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
double value; if (!IsValidChannelNumber(numberString))
if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
{ {
numberString = null; numberString = null;
} }
@ -250,6 +233,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return numberString; return numberString;
} }
private bool IsValidChannelNumber(string numberString)
{
if (string.IsNullOrWhiteSpace(numberString) ||
string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
{
return false;
}
double value;
if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
{
return false;
}
return true;
}
private string GetChannelName(string extInf, Dictionary<string, string> attributes) private string GetChannelName(string extInf, Dictionary<string, string> attributes)
{ {
var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
@ -295,6 +296,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return name; return name;
} }
private string GetTunerChannelId(Dictionary<string, string> attributes)
{
string result;
attributes.TryGetValue("tvg-id", out result);
if (string.IsNullOrWhiteSpace(result))
{
attributes.TryGetValue("channel-id", out result);
}
return result;
}
private Dictionary<string, string> ParseExtInf(string line, out string remaining) private Dictionary<string, string> ParseExtInf(string line, out string remaining)
{ {
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

View file

@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Migrations
public async Task Run() public async Task Run()
{ {
var name = "GuideRefresh2"; var name = "GuideRefresh3";
if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase)) if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase))
{ {

View file

@ -210,7 +210,7 @@ namespace MediaBrowser.Api.Images
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task DownloadRemoteImage(BaseItem item, BaseDownloadRemoteImage request) private async Task DownloadRemoteImage(BaseItem item, BaseDownloadRemoteImage request)
{ {
await _providerManager.SaveImage(item, request.ImageUrl, null, request.Type, null, CancellationToken.None).ConfigureAwait(false); await _providerManager.SaveImage(item, request.ImageUrl, request.Type, null, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
} }

View file

@ -640,8 +640,8 @@ namespace MediaBrowser.Api.LiveTv
{ {
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ProviderId { get; set; } public string ProviderId { get; set; }
public string TunerChannelNumber { get; set; } public string TunerChannelId { get; set; }
public string ProviderChannelNumber { get; set; } public string ProviderChannelId { get; set; }
} }
public class ChannelMappingOptions public class ChannelMappingOptions
@ -765,7 +765,7 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Post(SetChannelMapping request) public async Task<object> Post(SetChannelMapping request)
{ {
return await _liveTvManager.SetChannelMapping(request.ProviderId, request.TunerChannelNumber, request.ProviderChannelNumber).ConfigureAwait(false); return await _liveTvManager.SetChannelMapping(request.ProviderId, request.TunerChannelId, request.ProviderChannelId).ConfigureAwait(false);
} }
public async Task<object> Get(GetChannelMappingOptions request) public async Task<object> Get(GetChannelMappingOptions request)
@ -791,7 +791,7 @@ namespace MediaBrowser.Api.LiveTv
ProviderChannels = providerChannels.Select(i => new NameIdPair ProviderChannels = providerChannels.Select(i => new NameIdPair
{ {
Name = i.Name, Name = i.Name,
Id = i.Number Id = i.TunerChannelId
}).ToList(), }).ToList(),

View file

@ -788,7 +788,7 @@ namespace MediaBrowser.Controller.Entities
query.IsVirtualUnaired, query.IsVirtualUnaired,
query.IsUnaired); query.IsUnaired);
if (collapseBoxSetItems) if (collapseBoxSetItems && user != null)
{ {
items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager); items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager);
} }
@ -1119,13 +1119,11 @@ namespace MediaBrowser.Controller.Entities
InternalItemsQuery query, InternalItemsQuery query,
ILibraryManager libraryManager, bool enableSorting) ILibraryManager libraryManager, bool enableSorting)
{ {
var user = query.User;
items = items.DistinctBy(i => i.GetPresentationUniqueKey(), StringComparer.OrdinalIgnoreCase); items = items.DistinctBy(i => i.GetPresentationUniqueKey(), StringComparer.OrdinalIgnoreCase);
if (query.SortBy.Length > 0) if (query.SortBy.Length > 0)
{ {
items = libraryManager.Sort(items, user, query.SortBy, query.SortOrder); items = libraryManager.Sort(items, query.User, query.SortBy, query.SortOrder);
} }
var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray(); var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();

View file

@ -27,6 +27,8 @@ namespace MediaBrowser.Controller.LiveTv
public string TunerChannelId { get; set; } public string TunerChannelId { get; set; }
public string CallSign { get; set; }
/// <summary> /// <summary>
/// Gets or sets the tuner host identifier. /// Gets or sets the tuner host identifier.
/// </summary> /// </summary>

View file

@ -11,8 +11,7 @@ namespace MediaBrowser.Controller.LiveTv
{ {
string Name { get; } string Name { get; }
string Type { get; } string Type { get; }
Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken); Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken);
Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings); Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings);
Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location); Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location);
Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken); Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken);

View file

@ -3,8 +3,8 @@
public class TunerChannelMapping public class TunerChannelMapping
{ {
public string Name { get; set; } public string Name { get; set; }
public string Number { get; set; }
public string ProviderChannelNumber { get; set; }
public string ProviderChannelName { get; set; } public string ProviderChannelName { get; set; }
public string ProviderChannelId { get; set; }
public string Id { get; set; }
} }
} }

View file

@ -47,12 +47,11 @@ namespace MediaBrowser.Controller.Providers
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="url">The URL.</param> /// <param name="url">The URL.</param>
/// <param name="resourcePool">The resource pool.</param>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="imageIndex">Index of the image.</param> /// <param name="imageIndex">Index of the image.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveImage(IHasImages item, string url, SemaphoreSlim resourcePool, ImageType type, int? imageIndex, CancellationToken cancellationToken); Task SaveImage(IHasImages item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Saves the image. /// Saves the image.

View file

@ -96,17 +96,5 @@ namespace MediaBrowser.Model.LiveTv
EnableAllTuners = true; EnableAllTuners = true;
ChannelMappings = new NameValuePair[] {}; ChannelMappings = new NameValuePair[] {};
} }
public string GetMappedChannel(string channelNumber)
{
foreach (NameValuePair mapping in ChannelMappings)
{
if (StringHelper.EqualsIgnoreCase(mapping.Name, channelNumber))
{
return mapping.Value;
}
}
return channelNumber;
}
} }
} }

View file

@ -16,7 +16,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Manager namespace MediaBrowser.Providers.Manager
@ -234,6 +233,7 @@ namespace MediaBrowser.Providers.Manager
return retryPath; return retryPath;
} }
private SemaphoreSlim _imageSaveSemaphore = new SemaphoreSlim(1, 1);
/// <summary> /// <summary>
/// Saves the image to location. /// Saves the image to location.
/// </summary> /// </summary>
@ -247,11 +247,13 @@ namespace MediaBrowser.Providers.Manager
var parentFolder = Path.GetDirectoryName(path); var parentFolder = Path.GetDirectoryName(path);
_libraryMonitor.ReportFileSystemChangeBeginning(path); await _imageSaveSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
_libraryMonitor.ReportFileSystemChangeBeginning(parentFolder);
try try
{ {
_libraryMonitor.ReportFileSystemChangeBeginning(path);
_libraryMonitor.ReportFileSystemChangeBeginning(parentFolder);
_fileSystem.CreateDirectory(Path.GetDirectoryName(path)); _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
// If the file is currently hidden we'll have to remove that or the save will fail // If the file is currently hidden we'll have to remove that or the save will fail
@ -283,6 +285,8 @@ namespace MediaBrowser.Providers.Manager
} }
finally finally
{ {
_imageSaveSemaphore.Release();
_libraryMonitor.ReportFileSystemChangeComplete(path, false); _libraryMonitor.ReportFileSystemChangeComplete(path, false);
_libraryMonitor.ReportFileSystemChangeComplete(parentFolder, false); _libraryMonitor.ReportFileSystemChangeComplete(parentFolder, false);
} }

View file

@ -253,7 +253,7 @@ namespace MediaBrowser.Providers.Manager
{ {
try try
{ {
await ProviderManager.SaveImage(personEntity, imageUrl, null, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
return; return;
} }
catch (Exception ex) catch (Exception ex)

View file

@ -123,12 +123,11 @@ namespace MediaBrowser.Providers.Manager
return Task.FromResult(ItemUpdateType.None); return Task.FromResult(ItemUpdateType.None);
} }
public async Task SaveImage(IHasImages item, string url, SemaphoreSlim resourcePool, ImageType type, int? imageIndex, CancellationToken cancellationToken) public async Task SaveImage(IHasImages item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{ {
var response = await _httpClient.GetResponse(new HttpRequestOptions var response = await _httpClient.GetResponse(new HttpRequestOptions
{ {
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
ResourcePool = resourcePool,
Url = url, Url = url,
BufferContent = false BufferContent = false

View file

@ -127,14 +127,7 @@ namespace MediaBrowser.Providers.Omdb
} }
} }
using (var stream = await _httpClient.Get(new HttpRequestOptions using (var stream = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
Url = url,
ResourcePool = OmdbProvider.ResourcePool,
CancellationToken = cancellationToken,
BufferContent = true
}).ConfigureAwait(false))
{ {
var resultList = new List<SearchResult>(); var resultList = new List<SearchResult>();

View file

@ -296,14 +296,7 @@ namespace MediaBrowser.Providers.Omdb
var url = string.Format("https://www.omdbapi.com/?i={0}&plot=full&tomatoes=true&r=json", imdbParam); var url = string.Format("https://www.omdbapi.com/?i={0}&plot=full&tomatoes=true&r=json", imdbParam);
using (var stream = await _httpClient.Get(new HttpRequestOptions using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
Url = url,
ResourcePool = ResourcePool,
CancellationToken = cancellationToken,
BufferContent = true
}).ConfigureAwait(false))
{ {
var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream); var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
_fileSystem.CreateDirectory(Path.GetDirectoryName(path)); _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
@ -337,14 +330,7 @@ namespace MediaBrowser.Providers.Omdb
var url = string.Format("https://www.omdbapi.com/?i={0}&season={1}&detail=full", imdbParam, seasonId); var url = string.Format("https://www.omdbapi.com/?i={0}&season={1}&detail=full", imdbParam, seasonId);
using (var stream = await _httpClient.Get(new HttpRequestOptions using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
Url = url,
ResourcePool = ResourcePool,
CancellationToken = cancellationToken,
BufferContent = true
}).ConfigureAwait(false))
{ {
var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream); var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream);
_fileSystem.CreateDirectory(Path.GetDirectoryName(path)); _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
@ -354,6 +340,17 @@ namespace MediaBrowser.Providers.Omdb
return path; return path;
} }
public static Task<Stream> GetOmdbResponse(IHttpClient httpClient, string url, CancellationToken cancellationToken)
{
return httpClient.Get(new HttpRequestOptions
{
Url = url,
ResourcePool = ResourcePool,
CancellationToken = cancellationToken,
BufferContent = true
});
}
internal string GetDataFilePath(string imdbId) internal string GetDataFilePath(string imdbId)
{ {
if (string.IsNullOrEmpty(imdbId)) if (string.IsNullOrEmpty(imdbId))