diff options
| author | Eric Reed <ebr@mediabrowser3.com> | 2015-10-16 18:36:41 -0400 |
|---|---|---|
| committer | Eric Reed <ebr@mediabrowser3.com> | 2015-10-16 18:36:41 -0400 |
| commit | 8ef0596f0fa9fe0259bd1f706a92d4eab1bc4c93 (patch) | |
| tree | 90c952932ccc958055cad2500ab4764ec281ad67 /MediaBrowser.Server.Implementations | |
| parent | 1261f51412fe6c3ddab12f615026b2c4ecc4b62b (diff) | |
| parent | d1195257aa6ffa8086ce6461183442ffa28365f3 (diff) | |
Merge branch 'dev' of https://github.com/MediaBrowser/Emby into dev
Diffstat (limited to 'MediaBrowser.Server.Implementations')
10 files changed, 145 insertions, 74 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs index 55e6e0103..7cffa58db 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Server.Implementations.Channels { @@ -35,24 +36,9 @@ namespace MediaBrowser.Server.Implementations.Channels if (!string.IsNullOrEmpty(channelItem.ExternalImagePath)) { - var options = new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = channelItem.ExternalImagePath - }; - - var response = await _httpClient.GetResponse(options).ConfigureAwait(false); - - if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) - { - imageResponse.HasImage = true; - imageResponse.Stream = response.Content; - imageResponse.SetFormatFromMimeType(response.ContentType); - } - else - { - _logger.Error("Provider did not return an image content type."); - } + imageResponse.Path = channelItem.ExternalImagePath; + imageResponse.Protocol = MediaProtocol.Http; + imageResponse.HasImage = true; } return imageResponse; diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 5c0b5e5b2..97867d6a2 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1743,7 +1743,7 @@ namespace MediaBrowser.Server.Implementations.Dto { var imageInfo = item.GetImageInfo(ImageType.Primary, 0); - if (imageInfo == null) + if (imageInfo == null || !imageInfo.IsLocalFile) { return; } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 0156a46a9..4ccfca1bf 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2354,5 +2354,17 @@ namespace MediaBrowser.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) + { + _logger.Debug("ConvertImageToLocal item {0}", item.Id); + + await _providerManagerFactory().SaveImage(item, image.Path, _dynamicImageResourcePool, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); + + await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); + + return item.GetImageInfo(image.Type, imageIndex); + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3ec64a017..0d4960795 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -491,6 +491,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { + var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + + result.Item2.Release(); + + return result.Item1; + } + catch (Exception e) + { + _logger.ErrorException("Error getting channel stream", e); + } + } + + throw new ApplicationException("Tuner not found."); + } + + private async Task<Tuple<MediaSourceInfo, SemaphoreSlim>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + { + _logger.Info("Streaming Channel " + channelId); + + foreach (var hostInstance in _liveTvManager.TunerHosts) + { + try + { return await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); } catch (Exception e) @@ -653,40 +676,56 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - var mediaStreamInfo = await GetChannelStream(timer.ChannelId, null, CancellationToken.None); - - // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg - await Task.Delay(3000, cancellationToken).ConfigureAwait(false); + var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None); + var mediaStreamInfo = result.Item1; + var isResourceOpen = true; - var duration = recordingEndDate - DateTime.UtcNow; - - HttpRequestOptions httpRequestOptions = new HttpRequestOptions() + // Unfortunately due to the semaphore we have to have a nested try/finally + try { - Url = mediaStreamInfo.Path - }; + // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg + await Task.Delay(3000, cancellationToken).ConfigureAwait(false); + + var duration = recordingEndDate - DateTime.UtcNow; - recording.Path = recordPath; - recording.Status = RecordingStatus.InProgress; - recording.DateLastUpdated = DateTime.UtcNow; - _recordingProvider.Update(recording); + HttpRequestOptions httpRequestOptions = new HttpRequestOptions() + { + Url = mediaStreamInfo.Path + }; + + recording.Path = recordPath; + recording.Status = RecordingStatus.InProgress; + recording.DateLastUpdated = DateTime.UtcNow; + _recordingProvider.Update(recording); + + _logger.Info("Beginning recording."); + + httpRequestOptions.BufferContent = false; + var durationToken = new CancellationTokenSource(duration); + var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + httpRequestOptions.CancellationToken = linkedToken; + _logger.Info("Writing file to path: " + recordPath); + using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET")) + { + using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + result.Item2.Release(); + isResourceOpen = false; - _logger.Info("Beginning recording."); + await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); + } + } - httpRequestOptions.BufferContent = false; - var durationToken = new CancellationTokenSource(duration); - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - httpRequestOptions.CancellationToken = linkedToken; - _logger.Info("Writing file to path: " + recordPath); - using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET")) + recording.Status = RecordingStatus.Completed; + _logger.Info("Recording completed"); + } + finally { - using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + if (isResourceOpen) { - await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); + result.Item2.Release(); } } - - recording.Status = RecordingStatus.Completed; - _logger.Info("Recording completed"); } catch (OperationCanceledException) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 65125da66..538a963fd 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1446,7 +1446,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { dto.ChannelName = channel.Name; - if (!string.IsNullOrEmpty(channel.PrimaryImagePath)) + if (channel.HasImage(ImageType.Primary)) { dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); } @@ -1512,7 +1512,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { dto.ChannelName = channel.Name; - if (!string.IsNullOrEmpty(channel.PrimaryImagePath)) + if (channel.HasImage(ImageType.Primary)) { dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs index ba9ce0db5..e0d90d349 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Server.Implementations.LiveTv { @@ -40,24 +41,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv { if (liveTvItem.ExternalImagePath.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { - var options = new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = liveTvItem.ExternalImagePath - }; - - var response = await _httpClient.GetResponse(options).ConfigureAwait(false); - - if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) - { - imageResponse.HasImage = true; - imageResponse.Stream = response.Content; - imageResponse.SetFormatFromMimeType(response.ContentType); - } - else - { - _logger.Error("Provider did not return an image content type."); - } + imageResponse.Path = liveTvItem.ExternalImagePath; + imageResponse.Protocol = MediaProtocol.Http; + imageResponse.HasImage = true; } else { diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index d8d91c2f9..3fb1d9661 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.LiveTv { - class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey + public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey { private readonly ILiveTvManager _liveTvManager; private readonly IConfigurationManager _config; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 616d01a32..d811152c2 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -141,7 +141,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts protected abstract Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken); - public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) + public async Task<Tuple<MediaSourceInfo, SemaphoreSlim>> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) { if (IsValidChannelId(channelId)) { @@ -173,9 +173,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts try { var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); - - await AddMediaInfo(stream, false, cancellationToken).ConfigureAwait(false); - return stream; + var resourcePool = GetLock(host.Url); + + await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + return new Tuple<MediaSourceInfo, SemaphoreSlim>(stream, resourcePool); } catch (Exception ex) { @@ -187,7 +188,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts throw new LiveTvConflictException(); } - private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) + /// <summary> + /// The _semaphoreLocks + /// </summary> + private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>(StringComparer.OrdinalIgnoreCase); + /// <summary> + /// Gets the lock. + /// </summary> + /// <param name="url">The filename.</param> + /// <returns>System.Object.</returns> + private SemaphoreSlim GetLock(string url) + { + return _semaphoreLocks.GetOrAdd(url, key => new SemaphoreSlim(1, 1)); + } + + private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + { + await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + await AddMediaInfoInternal(mediaSource, isAudio, cancellationToken).ConfigureAwait(false); + + // Leave the resource locked. it will be released upstream + } + catch (Exception) + { + // Release the resource if there's some kind of failure. + resourcePool.Release(); + + throw; + } + } + + private async Task AddMediaInfoInternal(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) { var originalRuntime = mediaSource.RunTimeTicks; diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs index 6259c61af..60b8c00bd 100644 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Progress; +using MediaBrowser.Common.Progress; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -17,7 +16,7 @@ using MediaBrowser.Controller.Entities.Audio; namespace MediaBrowser.Server.Implementations.Persistence { - class CleanDatabaseScheduledTask : IScheduledTask + public class CleanDatabaseScheduledTask : IScheduledTask { private readonly ILibraryManager _libraryManager; private readonly IItemRepository _itemRepo; diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs index 37ee2b319..b07b5b8c4 100644 --- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs @@ -125,7 +125,22 @@ namespace MediaBrowser.Server.Implementations.Photos protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items) { return items - .Select(i => i.GetImagePath(ImageType.Primary) ?? i.GetImagePath(ImageType.Thumb)) + .Select(i => + { + var image = i.GetImageInfo(ImageType.Primary, 0); + + if (image != null && image.IsLocalFile) + { + return image.Path; + } + image = i.GetImageInfo(ImageType.Thumb, 0); + + if (image != null && image.IsLocalFile) + { + return image.Path; + } + return null; + }) .Where(i => !string.IsNullOrWhiteSpace(i)); } |
