diff options
Diffstat (limited to 'Emby.Server.Implementations')
21 files changed, 377 insertions, 374 deletions
diff --git a/Emby.Server.Implementations/Connect/ConnectManager.cs b/Emby.Server.Implementations/Connect/ConnectManager.cs index 7e6755f6a..8aac2a8c4 100644 --- a/Emby.Server.Implementations/Connect/ConnectManager.cs +++ b/Emby.Server.Implementations/Connect/ConnectManager.cs @@ -995,7 +995,7 @@ namespace Emby.Server.Implementations.Connect 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) { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index c158f2e51..4a4a1a6bf 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -257,7 +257,6 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "SeriesId", "GUID", existingColumnNames); AddColumn(db, "TypedBaseItems", "SeriesSortName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ExternalSeriesId", "Text", existingColumnNames); - AddColumn(db, "TypedBaseItems", "ShortOverview", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Tagline", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "Keywords", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "ProviderIds", "Text", existingColumnNames); @@ -466,7 +465,6 @@ namespace Emby.Server.Implementations.Data "InheritedParentalRatingValue", "InheritedTags", "ExternalSeriesId", - "ShortOverview", "Tagline", "Keywords", "ProviderIds", @@ -598,7 +596,6 @@ namespace Emby.Server.Implementations.Data "SeriesId", "SeriesSortName", "ExternalSeriesId", - "ShortOverview", "Tagline", "Keywords", "ProviderIds", @@ -1038,7 +1035,6 @@ namespace Emby.Server.Implementations.Data } saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId); - saveItemStatement.TryBind("@ShortOverview", item.ShortOverview); saveItemStatement.TryBind("@Tagline", item.Tagline); if (item.Keywords.Count > 0) @@ -1893,15 +1889,6 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.ShortOverview)) - { - if (!reader.IsDBNull(index)) - { - item.ShortOverview = reader.GetString(index); - } - index++; - } - if (query.HasField(ItemFields.Taglines)) { if (!reader.IsDBNull(index)) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index f866c34de..9c50ad5da 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1052,11 +1052,6 @@ namespace Emby.Server.Implementations.Dto dto.OriginalTitle = item.OriginalTitle; } - if (fields.Contains(ItemFields.ShortOverview)) - { - dto.ShortOverview = item.ShortOverview; - } - if (fields.Contains(ItemFields.ParentId)) { var displayParentId = item.DisplayParentId; diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index e3cd96894..195d24b21 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -309,8 +309,8 @@ <Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project> <Name>SocketHttpListener.Portable</Name> </ProjectReference> - <Reference Include="Emby.XmlTv, Version=1.0.6193.39741, Culture=neutral, processorArchitecture=MSIL"> - <HintPath>..\packages\Emby.XmlTv.1.0.3\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath> + <Reference Include="Emby.XmlTv, Version=1.0.6241.4924, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Emby.XmlTv.1.0.5\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="MediaBrowser.Naming, Version=1.0.6201.24431, Culture=neutral, processorArchitecture=MSIL"> diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index c59a22884..de3a1664e 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1927,11 +1927,18 @@ namespace Emby.Server.Implementations.Library return ItemRepository.RetrieveItem(id); } - public IEnumerable<Folder> GetCollectionFolders(BaseItem item) + public List<Folder> GetCollectionFolders(BaseItem item) { - while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) + while (item != null) { - item = item.GetParent(); + var parent = item.GetParent(); + + if (parent == null || parent is AggregateFolder) + { + break; + } + + item = parent; } if (item == null) @@ -1941,7 +1948,8 @@ namespace Emby.Server.Implementations.Library return GetUserRootFolder().Children .OfType<Folder>() - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)); + .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)) + .ToList(); } public LibraryOptions GetLibraryOptions(BaseItem item) @@ -2619,18 +2627,6 @@ namespace Emby.Server.Implementations.Library } } - foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) - { - if (!string.IsNullOrWhiteSpace(map.From)) - { - var substitutionResult = SubstitutePathInternal(path, map.From, map.To); - if (substitutionResult.Item2) - { - return substitutionResult.Item1; - } - } - } - return path; } @@ -2764,7 +2760,6 @@ namespace Emby.Server.Implementations.Library 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) { foreach (var url in image.Path.Split('|')) @@ -2773,7 +2768,7 @@ namespace Emby.Server.Implementations.Library { _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); diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 2971405b9..6cf201990 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -74,20 +74,21 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return new MusicArtist(); } - if (_config.Configuration.EnableSimpleArtistDetection) - { - return null; - } + return null; + //if (_config.Configuration.EnableSimpleArtistDetection) + //{ + // return null; + //} - // Avoid mis-identifying top folders - if (args.Parent.IsRoot) return null; + //// Avoid mis-identifying top folders + //if (args.Parent.IsRoot) return null; - var directoryService = args.DirectoryService; + //var directoryService = args.DirectoryService; - var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); + //var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); - // If we contain an album assume we are an artist folder - return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null; + //// If we contain an album assume we are an artist folder + //return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null; } } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 2a4cc49b7..cf37366fb 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -64,6 +64,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV episode.SeasonId = season.Id; episode.SeasonName = season.Name; } + + // Assume season 1 if there's no season folder and a season number could not be determined + if (season == null && !episode.ParentIndexNumber.HasValue && (episode.IndexNumber.HasValue || episode.PremiereDate.HasValue)) + { + episode.ParentIndexNumber = 1; + } } return episode; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index e6c88aa1a..a47a3322e 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.Library return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2); })); - var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).Select(i => new SearchHintInfo + var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).ThenBy(i => i.Item1.SortName).Select(i => new SearchHintInfo { Item = i.Item1, MatchedTerm = i.Item2 diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 5e0b4ff34..89ef87c8e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -393,7 +393,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { try { - await provider.Item1.AddMetadata(provider.Item2, enabledChannels, cancellationToken).ConfigureAwait(false); + await AddMetadata(provider.Item1, provider.Item2, enabledChannels, enableCache, cancellationToken).ConfigureAwait(false); } catch (NotSupportedException) { @@ -409,6 +409,120 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV 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) { var list = new List<ChannelInfo>(); @@ -663,7 +777,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV existingTimer.ProductionYear = updatedTimer.ProductionYear; existingTimer.ProgramId = updatedTimer.ProgramId; existingTimer.SeasonNumber = updatedTimer.SeasonNumber; - existingTimer.ShortOverview = updatedTimer.ShortOverview; existingTimer.StartDate = updatedTimer.StartDate; existingTimer.ShowId = updatedTimer.ShowId; } @@ -846,49 +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); - var channelMappings = GetChannelMappings(provider.Item2); - var channelNumber = channel.Number; - string mappedChannelNumber; - if (channelMappings.TryGetValue(channelNumber, out mappedChannelNumber)) - { - _logger.Debug("Found mapped channel on provider {0}. Tuner channel number: {1}, Mapped channel number: {2}", provider.Item1.Name, channelNumber, mappedChannelNumber); - channelNumber = mappedChannelNumber; - } + var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false); - var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channelNumber, channel.Name, startDateUtc, endDateUtc, cancellationToken) - .ConfigureAwait(false); + List<ProgramInfo> programs; - var list = programs.ToList(); + if (epgChannel == null) + { + programs = new List<ProgramInfo>(); + } + else + { + programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken) + .ConfigureAwait(false)).ToList(); + } // 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; } - if (list.Count > 0) + if (programs.Count > 0) { - SaveEpgDataForChannel(channelId, list); + SaveEpgDataForChannel(channelId, programs); - return list; + return programs; } } 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() { return GetConfiguration().ListingProviders @@ -1755,7 +1856,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Name = timer.Name, HomePageUrl = timer.HomePageUrl, - ShortOverview = timer.ShortOverview, Overview = timer.Overview, Genres = timer.Genres, CommunityRating = timer.CommunityRating, @@ -1959,11 +2059,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV writer.WriteElementString("genre", genre); } - if (!string.IsNullOrWhiteSpace(item.ShortOverview)) - { - writer.WriteElementString("outline", item.ShortOverview); - } - if (!string.IsNullOrWhiteSpace(item.HomePageUrl)) { writer.WriteElementString("website", item.HomePageUrl); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index beb08cc25..1f739b3c6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -154,7 +154,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks); var inputModifiers = "-fflags +genpts -async 1 -vsync -1"; - var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\""; + var mapArgs = string.Equals(OutputFormat, "mkv", StringComparison.OrdinalIgnoreCase) ? "-map 0" : "-sn"; + // temporary + mapArgs = "-sn"; + var commandLineArgs = "-i \"{0}\"{4} " + mapArgs + " {2} -map_metadata -1 -threads 0 {3} -y \"{1}\""; long startTimeTicks = 0; //if (mediaSource.DateLiveStreamOpened.HasValue) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 84f802d76..17de93a3c 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -61,7 +61,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV timerInfo.HomePageUrl = programInfo.HomePageUrl; timerInfo.CommunityRating = programInfo.CommunityRating; timerInfo.Overview = programInfo.Overview; - timerInfo.ShortOverview = programInfo.ShortOverview; timerInfo.OfficialRating = programInfo.OfficialRating; timerInfo.IsRepeat = programInfo.IsRepeat; timerInfo.SeriesId = programInfo.SeriesId; diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 46b914232..1b7a1c8c6 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -15,6 +15,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Extensions; namespace Emby.Server.Implementations.LiveTv.Listings { @@ -60,8 +61,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings return dates; } - public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, 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>(); var token = await GetToken(info, cancellationToken).ConfigureAwait(false); @@ -80,15 +89,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings var dates = GetScheduleRequestDates(startDateUtc, endDateUtc); - ScheduleDirect.Station station = GetStation(info.ListingsId, channelNumber, channelName); - - if (station == null) - { - _logger.Info("No Schedules Direct Station found for channel {0} with name {1}", channelNumber, channelName); - return programsInfo; - } - - string stationID = station.stationID; + string stationID = channelId; _logger.Info("Channel Station ID is: " + stationID); List<ScheduleDirect.RequestScheduleForChannel> requestList = @@ -122,7 +123,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings StreamReader reader = new StreamReader(response.Content); string responseString = reader.ReadToEnd(); 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() { @@ -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; } - private readonly object _channelCacheLock = new object(); - private ScheduleDirect.Station GetStation(string listingsId, string channelNumber, string channelName) + private string GetChannelNumber(ScheduleDirect.Map map) { - lock (_channelCacheLock) - { - Dictionary<string, ScheduleDirect.Station> channelPair; - 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; - } - } + var channelNumber = map.logicalChannelNumber; - if (!string.IsNullOrWhiteSpace(channelNumber)) - { - return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase)); - } - } - - return null; - } - } - - private void AddToChannelPairCache(string listingsId, string channelNumber, ScheduleDirect.Station schChannel) - { - 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) + if (string.IsNullOrWhiteSpace(channelNumber)) { - Dictionary<string, ScheduleDirect.Station> cache; - if (_channelPairingCache.TryGetValue(listingsId, out cache)) - { - cache.Clear(); - } + channelNumber = map.channel; } - } - - private int GetChannelPairCacheCount(string listingsId) - { - lock (_channelCacheLock) + if (string.IsNullOrWhiteSpace(channelNumber)) { - Dictionary<string, ScheduleDirect.Station> cache; - if (_channelPairingCache.TryGetValue(listingsId, out cache)) - { - return cache.Count; - } - - return 0; + channelNumber = map.atscMajor + "." + map.atscMinor; } - } + channelNumber = channelNumber.TrimStart('0'); - private string NormalizeName(string value) - { - return value.Replace(" ", string.Empty).Replace("-", string.Empty); + return channelNumber; } - 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) + private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details) { //_logger.Debug("Show type is: " + (details.showType ?? "No ShowType")); DateTime startAt = GetDate(programInfo.airDateTime); @@ -386,7 +228,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ProgramAudio audioType = ProgramAudio.Stereo; 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) { @@ -422,7 +264,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings var info = new ProgramInfo { - ChannelId = channel, + ChannelId = channelId, Id = newID, StartDate = startAt, EndDate = endAt, @@ -479,7 +321,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } else if (details.descriptions.description100 != null) { - info.ShortOverview = details.descriptions.description100[0].description; + info.Overview = details.descriptions.description100[0].description; } } @@ -969,8 +811,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings throw new Exception("ListingsId required"); } - await AddMetadata(info, new List<ChannelInfo>(), cancellationToken).ConfigureAwait(false); - var token = await GetToken(info, cancellationToken); if (string.IsNullOrWhiteSpace(token)) @@ -997,39 +837,81 @@ namespace Emby.Server.Implementations.LiveTv.Listings var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response); _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect"); _logger.Info("Mapping Stations to Channel"); + + var allStations = root.stations ?? new List<ScheduleDirect.Station>(); + foreach (ScheduleDirect.Map map in root.map) { - var channelNumber = map.logicalChannelNumber; - - if (string.IsNullOrWhiteSpace(channelNumber)) + var channelNumber = GetChannelNumber(map); + + var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase)); + if (station == null) { - channelNumber = map.channel; - } - if (string.IsNullOrWhiteSpace(channelNumber)) - { - channelNumber = map.atscMajor + "." + map.atscMinor; + station = new ScheduleDirect.Station + { + stationID = map.stationID + }; } - channelNumber = channelNumber.TrimStart('0'); var name = channelNumber; - var station = GetStation(listingsId, channelNumber, null); - if (station != null && !string.IsNullOrWhiteSpace(station.name)) - { - name = station.name; - } - - list.Add(new ChannelInfo + var channelInfo = new ChannelInfo { Number = channelNumber, 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; } + 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 Token diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 57307aa73..d7803f9e3 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -106,8 +106,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings return cacheFile; } - public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, 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)) { var length = endDateUtc - startDateUtc; @@ -120,7 +125,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false); 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)); } @@ -139,7 +144,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings StartDate = GetDate(p.StartDate), Name = p.Title, Overview = p.Description, - ShortOverview = p.Description, ProductionYear = !p.CopyrightDate.HasValue ? (int?)null : p.CopyrightDate.Value.Year, SeasonNumber = p.Episode == null ? null : p.Episode.Series, IsSeries = p.Episode != null, @@ -153,10 +157,29 @@ namespace Emby.Server.Implementations.LiveTv.Listings HasImage = p.Icon != null && !String.IsNullOrEmpty(p.Icon.Source), OfficialRating = p.Rating != null && !String.IsNullOrEmpty(p.Rating.Value) ? p.Rating.Value : null, CommunityRating = p.StarRating.HasValue ? p.StarRating.Value : (float?)null, - SeriesId = p.Episode != null ? p.Title.GetMD5().ToString("N") : null, - ShowId = ((p.Title ?? string.Empty) + (episodeTitle ?? string.Empty)).GetMD5().ToString("N") + SeriesId = p.Episode != null ? p.Title.GetMD5().ToString("N") : null }; + if (!string.IsNullOrWhiteSpace(p.ProgramId)) + { + programInfo.ShowId = p.ProgramId; + } + else + { + var uniqueString = (p.Title ?? string.Empty) + (episodeTitle ?? string.Empty) + (p.IceTvEpisodeNumber ?? string.Empty); + + if (programInfo.SeasonNumber.HasValue) + { + uniqueString = "-" + programInfo.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture); + } + if (programInfo.EpisodeNumber.HasValue) + { + uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture); + } + + programInfo.ShowId = uniqueString.GetMD5().ToString("N"); + } + if (programInfo.IsMovie) { programInfo.IsSeries = false; @@ -176,28 +199,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings 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) { // Assume all urls are valid. check files for existence diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index ff76f6bef..e59a8f93c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2112,7 +2112,7 @@ namespace Emby.Server.Implementations.LiveTv if (timer == null) { - throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id)); + throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id)); } var service = GetService(timer.ServiceName); @@ -2884,20 +2884,20 @@ namespace Emby.Server.Implementations.LiveTv _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 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(); list.Add(new NameValuePair { - Name = tunerChannelNumber, - Value = providerChannelNumber + Name = tunerChannelId, + Value = providerChannelId }); listingsProviderInfo.ChannelMappings = list.ToArray(); } @@ -2917,31 +2917,33 @@ namespace Emby.Server.Implementations.LiveTv _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 { - Name = channel.Number + " " + channel.Name, - Number = channel.Number + Name = tunerChannel.Name, + Id = tunerChannel.TunerChannelId }; - var mapping = mappings.FirstOrDefault(i => string.Equals(i.Name, channel.Number, StringComparison.OrdinalIgnoreCase)); - var providerChannelNumber = channel.Number; + if (!string.IsNullOrWhiteSpace(tunerChannel.Number)) + { + result.Name = tunerChannel.Number + " " + result.Name; + } - if (mapping != null) + if (string.IsNullOrWhiteSpace(result.Id)) { - providerChannelNumber = mapping.Value; + result.Id = tunerChannel.Id; } - var providerChannel = providerChannels.FirstOrDefault(i => string.Equals(i.Number, providerChannelNumber, StringComparison.OrdinalIgnoreCase)); + var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, epgChannels); if (providerChannel != null) { - result.ProviderChannelNumber = providerChannel.Number; result.ProviderChannelName = providerChannel.Name; + result.ProviderChannelId = providerChannel.Id; } return result; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 5e191ada9..34d0dd853 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -76,6 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var channels = new List<M3UChannel>(); string line; string extInf = ""; + while ((line = reader.ReadLine()) != null) { line = line.Trim(); @@ -111,6 +112,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts extInf = ""; } } + return channels; } @@ -134,9 +136,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts channel.Name = GetChannelName(extInf, attributes); 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 = channelId; } return channel; @@ -172,9 +176,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts numberString = numberString.Trim(); } - if (string.IsNullOrWhiteSpace(numberString) || - string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) || - string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase)) + if (!IsValidChannelNumber(numberString)) { string value; if (attributes.TryGetValue("tvg-id", out value)) @@ -192,9 +194,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts numberString = numberString.Trim(); } - if (string.IsNullOrWhiteSpace(numberString) || - string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) || - string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase)) + if (!IsValidChannelNumber(numberString)) { string value; if (attributes.TryGetValue("channel-id", out value)) @@ -208,9 +208,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts numberString = numberString.Trim(); } - if (string.IsNullOrWhiteSpace(numberString) || - string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) || - string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase)) + if (!IsValidChannelNumber(numberString)) { numberString = null; } @@ -225,8 +223,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last()); - double value; - if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) + if (!IsValidChannelNumber(numberString)) { numberString = null; } @@ -236,6 +233,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts 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) { var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -281,6 +296,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts 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) { var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs index 7b88be19c..a7e1b3cf3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,10 +12,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { public class MulticastStream { - private readonly List<QueueStream> _outputStreams = new List<QueueStream>(); + private readonly ConcurrentDictionary<Guid,QueueStream> _outputStreams = new ConcurrentDictionary<Guid, QueueStream>(); private const int BufferSize = 81920; private CancellationToken _cancellationToken; private readonly ILogger _logger; + private readonly ConcurrentQueue<byte[]> _sharedBuffer = new ConcurrentQueue<byte[]>(); public MulticastStream(ILogger logger) { @@ -35,17 +37,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { byte[] copy = new byte[bytesRead]; Buffer.BlockCopy(buffer, 0, copy, 0, bytesRead); - - List<QueueStream> streams = null; - lock (_outputStreams) + _sharedBuffer.Enqueue(copy); + + while (_sharedBuffer.Count > 3000) { - streams = _outputStreams.ToList(); + byte[] bytes; + _sharedBuffer.TryDequeue(out bytes); } - foreach (var stream in streams) + var allStreams = _outputStreams.ToList(); + foreach (var stream in allStreams) { - stream.Queue(copy); + stream.Value.Queue(copy); } if (onStarted != null) @@ -70,11 +74,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts OnFinished = OnFinished }; - lock (_outputStreams) + var initial = _sharedBuffer.ToList(); + var list = new List<byte>(); + + foreach (var bytes in initial) { - _outputStreams.Add(result); + list.AddRange(bytes); } + _logger.Info("QueueStream started with {0} initial bytes", list.Count); + + result.Queue(list.ToArray()); + + _outputStreams.TryAdd(result.Id, result); + result.Start(_cancellationToken); return result.TaskCompletion.Task; @@ -82,10 +95,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public void RemoveOutputStream(QueueStream stream) { - lock (_outputStreams) - { - _outputStreams.Remove(stream); - } + QueueStream removed; + _outputStreams.TryRemove(stream.Id, out removed); } private void OnFinished(QueueStream queueStream) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs index bd6f31906..7b48ce21a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs @@ -19,7 +19,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public Action<QueueStream> OnFinished { get; set; } private readonly ILogger _logger; - private bool _isActive; + public Guid Id = Guid.NewGuid(); public QueueStream(Stream outputStream, ILogger logger) { @@ -30,10 +30,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public void Queue(byte[] bytes) { - if (_isActive) - { - _queue.Enqueue(bytes); - } + _queue.Enqueue(bytes); } public void Start(CancellationToken cancellationToken) @@ -59,10 +56,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { - while (!cancellationToken.IsCancellationRequested) + while (true) { - _isActive = true; - var bytes = Dequeue(); if (bytes != null) { @@ -73,9 +68,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts await Task.Delay(50, cancellationToken).ConfigureAwait(false); } } - - TaskCompletion.TrySetResult(true); - _logger.Debug("QueueStream complete"); } catch (OperationCanceledException) { @@ -89,8 +81,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts } finally { - _isActive = false; - if (OnFinished != null) { OnFinished(this); diff --git a/Emby.Server.Implementations/Migrations/GuideMigration.cs b/Emby.Server.Implementations/Migrations/GuideMigration.cs index 71286b282..99b2942dc 100644 --- a/Emby.Server.Implementations/Migrations/GuideMigration.cs +++ b/Emby.Server.Implementations/Migrations/GuideMigration.cs @@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Migrations public async Task Run() { - var name = "GuideRefresh2"; + var name = "GuideRefresh3"; if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase)) { diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 6bf412525..9dfaa102a 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -144,12 +144,19 @@ namespace Emby.Server.Implementations.TV // If viewing all next up for all series, remove first episodes // But if that returns empty, keep those first episodes (avoid completely empty view) var alwaysEnableFirstEpisode = !string.IsNullOrWhiteSpace(request.SeriesId); + var anyFound = false; return allNextUp .Where(i => { if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue) { + anyFound = true; + return true; + } + + if (!anyFound && i.Item1 == DateTime.MinValue) + { return true; } diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 79a1e4640..691ff0699 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Events; using MediaBrowser.Model.Net; @@ -246,7 +247,7 @@ namespace Emby.Server.Implementations.Udp try { - await _udpClient.SendAsync(bytes, bytes.Length, remoteEndPoint).ConfigureAwait(false); + await _udpClient.SendAsync(bytes, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false); _logger.Info("Udp message sent to {0}", remoteEndPoint); } diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config index fcce67b33..5249577e6 100644 --- a/Emby.Server.Implementations/packages.config +++ b/Emby.Server.Implementations/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Emby.XmlTv" version="1.0.3" targetFramework="portable45-net45+win8" /> + <package id="Emby.XmlTv" version="1.0.5" targetFramework="portable45-net45+win8" /> <package id="MediaBrowser.Naming" version="1.0.4" targetFramework="portable45-net45+win8" /> <package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" /> <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="portable45-net45+win8" /> |
