This commit is contained in:
Eric Reed 2015-10-16 18:36:41 -04:00
commit 8ef0596f0f
29 changed files with 382 additions and 154 deletions

View File

@ -18,6 +18,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using Emby.Drawing.Common; using Emby.Drawing.Common;
using MediaBrowser.Controller.Library;
namespace Emby.Drawing namespace Emby.Drawing
{ {
@ -53,18 +54,20 @@ namespace Emby.Drawing
private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationPaths _appPaths;
private readonly IImageEncoder _imageEncoder; private readonly IImageEncoder _imageEncoder;
private readonly SemaphoreSlim _imageProcessingSemaphore; private readonly SemaphoreSlim _imageProcessingSemaphore;
private readonly Func<ILibraryManager> _libraryManager;
public ImageProcessor(ILogger logger, public ImageProcessor(ILogger logger,
IServerApplicationPaths appPaths, IServerApplicationPaths appPaths,
IFileSystem fileSystem, IFileSystem fileSystem,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IImageEncoder imageEncoder, IImageEncoder imageEncoder,
int maxConcurrentImageProcesses) int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager)
{ {
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_imageEncoder = imageEncoder; _imageEncoder = imageEncoder;
_libraryManager = libraryManager;
_appPaths = appPaths; _appPaths = appPaths;
ImageEnhancers = new List<IImageEnhancer>(); ImageEnhancers = new List<IImageEnhancer>();
@ -158,7 +161,14 @@ namespace Emby.Drawing
throw new ArgumentNullException("options"); throw new ArgumentNullException("options");
} }
var originalImagePath = options.Image.Path; var originalImage = options.Image;
if (!originalImage.IsLocalFile)
{
originalImage = await _libraryManager().ConvertImageToLocal(options.Item, originalImage, options.ImageIndex).ConfigureAwait(false);
}
var originalImagePath = originalImage.Path;
if (options.HasDefaultOptions(originalImagePath) && options.Enhancers.Count == 0 && !options.CropWhiteSpace) if (options.HasDefaultOptions(originalImagePath) && options.Enhancers.Count == 0 && !options.CropWhiteSpace)
{ {
@ -166,7 +176,7 @@ namespace Emby.Drawing
return originalImagePath; return originalImagePath;
} }
var dateModified = options.Image.DateModified; var dateModified = originalImage.DateModified;
if (options.CropWhiteSpace) if (options.CropWhiteSpace)
{ {
@ -181,7 +191,7 @@ namespace Emby.Drawing
var tuple = await GetEnhancedImage(new ItemImageInfo var tuple = await GetEnhancedImage(new ItemImageInfo
{ {
DateModified = dateModified, DateModified = dateModified,
Type = options.Image.Type, Type = originalImage.Type,
Path = originalImagePath Path = originalImagePath
}, options.Item, options.ImageIndex, options.Enhancers).ConfigureAwait(false); }, options.Item, options.ImageIndex, options.Enhancers).ConfigureAwait(false);
@ -360,21 +370,16 @@ namespace Emby.Drawing
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower()); return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower());
} }
/// <summary>
/// Gets the size of the image.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>ImageSize.</returns>
public ImageSize GetImageSize(string path)
{
return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
}
public ImageSize GetImageSize(ItemImageInfo info) public ImageSize GetImageSize(ItemImageInfo info)
{ {
return GetImageSize(info.Path, info.DateModified, false); return GetImageSize(info.Path, info.DateModified, false);
} }
public ImageSize GetImageSize(string path)
{
return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
}
/// <summary> /// <summary>
/// Gets the size of the image. /// Gets the size of the image.
/// </summary> /// </summary>
@ -800,7 +805,6 @@ namespace Emby.Drawing
return false; return false;
} }
}); });
} }

View File

@ -318,12 +318,15 @@ namespace MediaBrowser.Api.Images
int? height = null; int? height = null;
try try
{
if (info.IsLocalFile)
{ {
var size = _imageProcessor.GetImageSize(info); var size = _imageProcessor.GetImageSize(info);
width = Convert.ToInt32(size.Width); width = Convert.ToInt32(size.Width);
height = Convert.ToInt32(size.Height); height = Convert.ToInt32(size.Height);
} }
}
catch catch
{ {

View File

@ -124,7 +124,7 @@ namespace MediaBrowser.Api.Social
Task.WaitAll(task); Task.WaitAll(task);
} }
public object Get(GetShareImage request) public async Task<object> Get(GetShareImage request)
{ {
var share = _sharingManager.GetShareInfo(request.Id); var share = _sharingManager.GetShareInfo(request.Id);
@ -142,10 +142,24 @@ namespace MediaBrowser.Api.Social
var image = item.GetImageInfo(ImageType.Primary, 0); var image = item.GetImageInfo(ImageType.Primary, 0);
if (image != null) if (image != null)
{
if (image.IsLocalFile)
{ {
return ToStaticFileResult(image.Path); return ToStaticFileResult(image.Path);
} }
try
{
// Don't fail the request over this
var updatedImage = await _libraryManager.ConvertImageToLocal(item, image, 0).ConfigureAwait(false);
return ToStaticFileResult(updatedImage.Path);
}
catch
{
}
}
// Grab a dlna icon if nothing else is available // Grab a dlna icon if nothing else is available
using (var response = _dlnaManager.GetIcon("logo240.jpg")) using (var response = _dlnaManager.GetIcon("logo240.jpg"))
{ {

View File

@ -28,16 +28,16 @@ namespace MediaBrowser.Controller.Drawing
/// <summary> /// <summary>
/// Gets the size of the image. /// Gets the size of the image.
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="info">The information.</param>
/// <returns>ImageSize.</returns> /// <returns>ImageSize.</returns>
ImageSize GetImageSize(string path); ImageSize GetImageSize(ItemImageInfo info);
/// <summary> /// <summary>
/// Gets the size of the image. /// Gets the size of the image.
/// </summary> /// </summary>
/// <param name="info">The information.</param> /// <param name="path">The path.</param>
/// <returns>ImageSize.</returns> /// <returns>ImageSize.</returns>
ImageSize GetImageSize(ItemImageInfo info); ImageSize GetImageSize(string path);
/// <summary> /// <summary>
/// Adds the parts. /// Adds the parts.

View File

@ -597,7 +597,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the custom rating. /// Gets or sets the custom rating.
/// </summary> /// </summary>
/// <value>The custom rating.</value> /// <value>The custom rating.</value>
[IgnoreDataMember] //[IgnoreDataMember]
public string CustomRating { get; set; } public string CustomRating { get; set; }
/// <summary> /// <summary>
@ -628,7 +628,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the community rating. /// Gets or sets the community rating.
/// </summary> /// </summary>
/// <value>The community rating.</value> /// <value>The community rating.</value>
[IgnoreDataMember] //[IgnoreDataMember]
public float? CommunityRating { get; set; } public float? CommunityRating { get; set; }
/// <summary> /// <summary>
@ -654,7 +654,7 @@ namespace MediaBrowser.Controller.Entities
/// This could be episode number, album track number, etc. /// This could be episode number, album track number, etc.
/// </summary> /// </summary>
/// <value>The index number.</value> /// <value>The index number.</value>
[IgnoreDataMember] //[IgnoreDataMember]
public int? IndexNumber { get; set; } public int? IndexNumber { get; set; }
/// <summary> /// <summary>
@ -1432,6 +1432,23 @@ namespace MediaBrowser.Controller.Entities
return GetImageInfo(type, imageIndex) != null; return GetImageInfo(type, imageIndex) != null;
} }
public void SetImage(ItemImageInfo image, int index)
{
if (image.Type == ImageType.Chapter)
{
throw new ArgumentException("Cannot set chapter images using SetImagePath");
}
var existingImage = GetImageInfo(image.Type, index);
if (existingImage != null)
{
ImageInfos.Remove(existingImage);
}
ImageInfos.Add(image);
}
public void SetImagePath(ImageType type, int index, FileSystemMetadata file) public void SetImagePath(ImageType type, int index, FileSystemMetadata file)
{ {
if (type == ImageType.Chapter) if (type == ImageType.Chapter)
@ -1473,6 +1490,8 @@ namespace MediaBrowser.Controller.Entities
// Remove it from the item // Remove it from the item
RemoveImage(info); RemoveImage(info);
if (info.IsLocalFile)
{
// Delete the source file // Delete the source file
var currentFile = new FileInfo(info.Path); var currentFile = new FileInfo(info.Path);
@ -1486,6 +1505,7 @@ namespace MediaBrowser.Controller.Entities
FileSystem.DeleteFile(currentFile.FullName); FileSystem.DeleteFile(currentFile.FullName);
} }
}
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
} }
@ -1505,11 +1525,16 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public bool ValidateImages(IDirectoryService directoryService) public bool ValidateImages(IDirectoryService directoryService)
{ {
var allDirectories = ImageInfos.Select(i => System.IO.Path.GetDirectoryName(i.Path)).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); var allFiles = ImageInfos
var allFiles = allDirectories.SelectMany(directoryService.GetFiles).Select(i => i.FullName).ToList(); .Where(i => i.IsLocalFile)
.Select(i => System.IO.Path.GetDirectoryName(i.Path))
.Distinct(StringComparer.OrdinalIgnoreCase)
.SelectMany(directoryService.GetFiles)
.Select(i => i.FullName)
.ToList();
var deletedImages = ImageInfos var deletedImages = ImageInfos
.Where(image => !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase)) .Where(image => image.IsLocalFile && !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
.ToList(); .ToList();
if (deletedImages.Count > 0) if (deletedImages.Count > 0)
@ -1618,17 +1643,20 @@ namespace MediaBrowser.Controller.Entities
imageAdded = true; imageAdded = true;
} }
else else
{
if (existing.IsLocalFile)
{ {
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage); existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
} }
} }
}
if (imageAdded || images.Count != existingImages.Count) if (imageAdded || images.Count != existingImages.Count)
{ {
var newImagePaths = images.Select(i => i.FullName).ToList(); var newImagePaths = images.Select(i => i.FullName).ToList();
var deleted = existingImages var deleted = existingImages
.Where(i => !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !FileSystem.FileExists(i.Path)) .Where(i => i.IsLocalFile && !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !FileSystem.FileExists(i.Path))
.ToList(); .ToList();
ImageInfos = ImageInfos.Except(deleted).ToList(); ImageInfos = ImageInfos.Except(deleted).ToList();
@ -1679,6 +1707,12 @@ namespace MediaBrowser.Controller.Entities
return Task.FromResult(true); return Task.FromResult(true);
} }
if (!info1.IsLocalFile || !info2.IsLocalFile)
{
// TODO: Not supported yet
return Task.FromResult(true);
}
var path1 = info1.Path; var path1 = info1.Path;
var path2 = info2.Path; var path2 = info2.Path;

View File

@ -2,9 +2,11 @@
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -191,6 +193,21 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
void RemoveImage(ItemImageInfo image); void RemoveImage(ItemImageInfo image);
/// <summary>
/// Updates to repository.
/// </summary>
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary>
/// Sets the image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="index">The index.</param>
void SetImage(ItemImageInfo image, int index);
} }
public static class HasImagesExtensions public static class HasImagesExtensions

View File

@ -36,14 +36,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The date last refreshed.</value> /// <value>The date last refreshed.</value>
DateTime DateLastRefreshed { get; set; } DateTime DateLastRefreshed { get; set; }
/// <summary>
/// Updates to repository.
/// </summary>
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// This is called before any metadata refresh and returns true or false indicating if changes were made /// This is called before any metadata refresh and returns true or false indicating if changes were made
/// </summary> /// </summary>

View File

@ -29,6 +29,13 @@ namespace MediaBrowser.Controller.Entities
{ {
get get
{ {
if (Path != null)
{
if (Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
return true; return true;
} }
} }

View File

@ -534,5 +534,14 @@ namespace MediaBrowser.Controller.Library
/// <param name="to">To.</param> /// <param name="to">To.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string SubstitutePath(string path, string from, string to); string SubstitutePath(string path, string from, string to);
/// <summary>
/// Converts the image to local.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="image">The image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task.</returns>
Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dto; using System;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="streamId">The stream identifier.</param> /// <param name="streamId">The stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns> /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken); Task<Tuple<MediaSourceInfo,SemaphoreSlim>> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the channel stream media sources. /// Gets the channel stream media sources.
/// </summary> /// </summary>

View File

@ -1,12 +1,14 @@
using System; using System;
using System.IO; using System.IO;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Controller.Providers namespace MediaBrowser.Controller.Providers
{ {
public class DynamicImageResponse public class DynamicImageResponse
{ {
public string Path { get; set; } public string Path { get; set; }
public MediaProtocol Protocol { get; set; }
public Stream Stream { get; set; } public Stream Stream { get; set; }
public ImageFormat Format { get; set; } public ImageFormat Format { get; set; }
public bool HasImage { get; set; } public bool HasImage { get; set; }

View File

@ -226,11 +226,15 @@ namespace MediaBrowser.Model.Configuration
public bool EnableDateLastRefresh { get; set; } public bool EnableDateLastRefresh { get; set; }
public string[] Migrations { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
/// </summary> /// </summary>
public ServerConfiguration() public ServerConfiguration()
{ {
Migrations = new string[] {};
ImageSavingConvention = ImageSavingConvention.Compatible; ImageSavingConvention = ImageSavingConvention.Compatible;
PublicPort = 8096; PublicPort = 8096;
PublicHttpsPort = 8920; PublicHttpsPort = 8920;

View File

@ -136,7 +136,7 @@ namespace MediaBrowser.Providers.Manager
source = memoryStream; source = memoryStream;
var currentPath = GetCurrentImagePath(item, type, index); var currentImage = GetCurrentImage(item, type, index);
using (source) using (source)
{ {
@ -160,8 +160,10 @@ namespace MediaBrowser.Providers.Manager
SetImagePath(item, type, imageIndex, paths[0]); SetImagePath(item, type, imageIndex, paths[0]);
// Delete the current path // Delete the current path
if (!string.IsNullOrEmpty(currentPath) && !paths.Contains(currentPath, StringComparer.OrdinalIgnoreCase)) if (currentImage != null && currentImage.IsLocalFile && !paths.Contains(currentImage.Path, StringComparer.OrdinalIgnoreCase))
{ {
var currentPath = currentImage.Path;
_libraryMonitor.ReportFileSystemChangeBeginning(currentPath); _libraryMonitor.ReportFileSystemChangeBeginning(currentPath);
try try
@ -301,9 +303,9 @@ namespace MediaBrowser.Providers.Manager
/// or /// or
/// imageIndex /// imageIndex
/// </exception> /// </exception>
private string GetCurrentImagePath(IHasImages item, ImageType type, int imageIndex) private ItemImageInfo GetCurrentImage(IHasImages item, ImageType type, int imageIndex)
{ {
return item.GetImagePath(type, imageIndex); return item.GetImageInfo(type, imageIndex);
} }
/// <summary> /// <summary>

View File

@ -17,6 +17,7 @@ using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Providers.Manager namespace MediaBrowser.Providers.Manager
{ {
@ -137,6 +138,18 @@ namespace MediaBrowser.Providers.Manager
if (response.HasImage) if (response.HasImage)
{ {
if (!string.IsNullOrEmpty(response.Path)) if (!string.IsNullOrEmpty(response.Path))
{
if (response.Protocol == MediaProtocol.Http)
{
_logger.Debug("Setting image url into item {0}", item.Id);
item.SetImage(new ItemImageInfo
{
Path = response.Path,
Type = imageType
}, 0);
}
else
{ {
var mimeType = MimeTypes.GetMimeType(response.Path); var mimeType = MimeTypes.GetMimeType(response.Path);
@ -144,6 +157,7 @@ namespace MediaBrowser.Providers.Manager
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false); await _providerManager.SaveImage(item, stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false);
} }
}
else else
{ {
var mimeType = "image/" + response.Format.ToString().ToLower(); var mimeType = "image/" + response.Format.ToString().ToLower();

View File

@ -22,13 +22,15 @@ namespace MediaBrowser.Providers.Music
var image = album.GetRecursiveChildren() var image = album.GetRecursiveChildren()
.OfType<Audio>() .OfType<Audio>()
.Select(i => i.GetImagePath(type)) .Select(i => i.GetImageInfo(type, 0))
.FirstOrDefault(i => !string.IsNullOrEmpty(i)); .FirstOrDefault(i => i != null && i.IsLocalFile);
var imagePath = image == null ? null : image.Path;
return Task.FromResult(new DynamicImageResponse return Task.FromResult(new DynamicImageResponse
{ {
Path = image, Path = imagePath,
HasImage = !string.IsNullOrEmpty(image) HasImage = !string.IsNullOrEmpty(imagePath)
}); });
} }

View File

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Server.Implementations.Channels namespace MediaBrowser.Server.Implementations.Channels
{ {
@ -35,24 +36,9 @@ namespace MediaBrowser.Server.Implementations.Channels
if (!string.IsNullOrEmpty(channelItem.ExternalImagePath)) if (!string.IsNullOrEmpty(channelItem.ExternalImagePath))
{ {
var options = new HttpRequestOptions imageResponse.Path = channelItem.ExternalImagePath;
{ imageResponse.Protocol = MediaProtocol.Http;
CancellationToken = cancellationToken,
Url = channelItem.ExternalImagePath
};
var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
imageResponse.HasImage = true; imageResponse.HasImage = true;
imageResponse.Stream = response.Content;
imageResponse.SetFormatFromMimeType(response.ContentType);
}
else
{
_logger.Error("Provider did not return an image content type.");
}
} }
return imageResponse; return imageResponse;

View File

@ -1743,7 +1743,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
var imageInfo = item.GetImageInfo(ImageType.Primary, 0); var imageInfo = item.GetImageInfo(ImageType.Primary, 0);
if (imageInfo == null) if (imageInfo == null || !imageInfo.IsLocalFile)
{ {
return; return;
} }

View File

@ -2354,5 +2354,17 @@ namespace MediaBrowser.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)
{
_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);
}
} }
} }

View File

@ -487,6 +487,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{ {
_logger.Info("Streaming Channel " + channelId); _logger.Info("Streaming Channel " + channelId);
foreach (var hostInstance in _liveTvManager.TunerHosts)
{
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) foreach (var hostInstance in _liveTvManager.TunerHosts)
{ {
try try
@ -653,8 +676,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
try try
{ {
var mediaStreamInfo = await GetChannelStream(timer.ChannelId, null, CancellationToken.None); var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None);
var mediaStreamInfo = result.Item1;
var isResourceOpen = true;
// Unfortunately due to the semaphore we have to have a nested try/finally
try
{
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
await Task.Delay(3000, cancellationToken).ConfigureAwait(false); await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
@ -681,6 +709,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{ {
using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
{ {
result.Item2.Release();
isResourceOpen = false;
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
} }
} }
@ -688,6 +719,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recording.Status = RecordingStatus.Completed; recording.Status = RecordingStatus.Completed;
_logger.Info("Recording completed"); _logger.Info("Recording completed");
} }
finally
{
if (isResourceOpen)
{
result.Item2.Release();
}
}
}
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_logger.Info("Recording stopped"); _logger.Info("Recording stopped");

View File

@ -1446,7 +1446,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{ {
dto.ChannelName = channel.Name; dto.ChannelName = channel.Name;
if (!string.IsNullOrEmpty(channel.PrimaryImagePath)) if (channel.HasImage(ImageType.Primary))
{ {
dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
} }
@ -1512,7 +1512,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{ {
dto.ChannelName = channel.Name; dto.ChannelName = channel.Name;
if (!string.IsNullOrEmpty(channel.PrimaryImagePath)) if (channel.HasImage(ImageType.Primary))
{ {
dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
} }

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Server.Implementations.LiveTv namespace MediaBrowser.Server.Implementations.LiveTv
{ {
@ -40,24 +41,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{ {
if (liveTvItem.ExternalImagePath.StartsWith("http", StringComparison.OrdinalIgnoreCase)) if (liveTvItem.ExternalImagePath.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{ {
var options = new HttpRequestOptions imageResponse.Path = liveTvItem.ExternalImagePath;
{ imageResponse.Protocol = MediaProtocol.Http;
CancellationToken = cancellationToken,
Url = liveTvItem.ExternalImagePath
};
var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
imageResponse.HasImage = true; imageResponse.HasImage = true;
imageResponse.Stream = response.Content;
imageResponse.SetFormatFromMimeType(response.ContentType);
}
else
{
_logger.Error("Provider did not return an image content type.");
}
} }
else else
{ {

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv namespace MediaBrowser.Server.Implementations.LiveTv
{ {
class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey
{ {
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly IConfigurationManager _config; private readonly IConfigurationManager _config;

View File

@ -141,7 +141,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
protected abstract Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken); 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)) if (IsValidChannelId(channelId))
{ {
@ -173,9 +173,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
try try
{ {
var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
var resourcePool = GetLock(host.Url);
await AddMediaInfo(stream, false, cancellationToken).ConfigureAwait(false); await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false);
return stream; return new Tuple<MediaSourceInfo, SemaphoreSlim>(stream, resourcePool);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -187,7 +188,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
throw new LiveTvConflictException(); 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; var originalRuntime = mediaSource.RunTimeTicks;

View File

@ -1,5 +1,4 @@
using MediaBrowser.Common.IO; using MediaBrowser.Common.Progress;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -17,7 +16,7 @@ using MediaBrowser.Controller.Entities.Audio;
namespace MediaBrowser.Server.Implementations.Persistence namespace MediaBrowser.Server.Implementations.Persistence
{ {
class CleanDatabaseScheduledTask : IScheduledTask public class CleanDatabaseScheduledTask : IScheduledTask
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;

View File

@ -125,7 +125,22 @@ namespace MediaBrowser.Server.Implementations.Photos
protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items) protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items)
{ {
return 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)); .Where(i => !string.IsNullOrWhiteSpace(i));
} }

View File

@ -333,18 +333,18 @@ namespace MediaBrowser.Server.Startup.Common
}); });
LogManager.RemoveConsoleOutput(); LogManager.RemoveConsoleOutput();
PerformPostInitMigrations();
} }
public override async Task Init(IProgress<double> progress) public override Task Init(IProgress<double> progress)
{ {
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
PerformPreInitMigrations(); PerformPreInitMigrations();
await base.Init(progress).ConfigureAwait(false); return base.Init(progress);
PerformPostInitMigrations();
} }
private void PerformPreInitMigrations() private void PerformPreInitMigrations()
@ -362,7 +362,10 @@ namespace MediaBrowser.Server.Startup.Common
private void PerformPostInitMigrations() private void PerformPostInitMigrations()
{ {
var migrations = new List<IVersionMigration>(); var migrations = new List<IVersionMigration>
{
new Release5767(ServerConfigurationManager, TaskManager)
};
foreach (var task in migrations) foreach (var task in migrations)
{ {
@ -563,7 +566,7 @@ namespace MediaBrowser.Server.Startup.Common
int.TryParse(_startupOptions.GetOption("-imagethreads"), NumberStyles.Any, CultureInfo.InvariantCulture, out maxConcurrentImageProcesses); int.TryParse(_startupOptions.GetOption("-imagethreads"), NumberStyles.Any, CultureInfo.InvariantCulture, out maxConcurrentImageProcesses);
} }
return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, GetImageEncoder(), maxConcurrentImageProcesses); return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, GetImageEncoder(), maxConcurrentImageProcesses, () => LibraryManager);
} }
private IImageEncoder GetImageEncoder() private IImageEncoder GetImageEncoder()

View File

@ -72,6 +72,7 @@
<Compile Include="INativeApp.cs" /> <Compile Include="INativeApp.cs" />
<Compile Include="MbLinkShortcutHandler.cs" /> <Compile Include="MbLinkShortcutHandler.cs" />
<Compile Include="Migrations\IVersionMigration.cs" /> <Compile Include="Migrations\IVersionMigration.cs" />
<Compile Include="Migrations\Release5767.cs" />
<Compile Include="Migrations\RenameXmlOptions.cs" /> <Compile Include="Migrations\RenameXmlOptions.cs" />
<Compile Include="NativeEnvironment.cs" /> <Compile Include="NativeEnvironment.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -0,0 +1,47 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Server.Implementations.LiveTv;
using MediaBrowser.Server.Implementations.Persistence;
using MediaBrowser.Server.Implementations.ScheduledTasks;
namespace MediaBrowser.Server.Startup.Common.Migrations
{
public class Release5767 : IVersionMigration
{
private readonly IServerConfigurationManager _config;
private readonly ITaskManager _taskManager;
public Release5767(IServerConfigurationManager config, ITaskManager taskManager)
{
_config = config;
_taskManager = taskManager;
}
public void Run()
{
var name = "5767";
if (_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase))
{
return;
}
Task.Run(async () =>
{
await Task.Delay(3000).ConfigureAwait(false);
_taskManager.QueueScheduledTask<RefreshChannelsScheduledTask>();
_taskManager.QueueScheduledTask<CleanDatabaseScheduledTask>();
_taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
});
var list = _config.Configuration.Migrations.ToList();
list.Add(name);
_config.Configuration.Migrations = list.ToArray();
_config.SaveConfiguration();
}
}
}

View File

@ -884,11 +884,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
{ {
writer.WriteStartElement("art"); writer.WriteStartElement("art");
var poster = item.PrimaryImagePath; var image = item.GetImageInfo(ImageType.Primary, 0);
if (!string.IsNullOrEmpty(poster)) if (image != null && image.IsLocalFile)
{ {
writer.WriteElementString("poster", GetPathToSave(item.PrimaryImagePath, libraryManager, config)); writer.WriteElementString("poster", GetPathToSave(image.Path, libraryManager, config));
} }
foreach (var backdrop in item.GetImages(ImageType.Backdrop)) foreach (var backdrop in item.GetImages(ImageType.Backdrop))
@ -985,10 +985,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
try try
{ {
var personEntity = libraryManager.GetPerson(person.Name); var personEntity = libraryManager.GetPerson(person.Name);
var image = personEntity.GetImageInfo(ImageType.Primary, 0);
if (!string.IsNullOrEmpty(personEntity.PrimaryImagePath)) if (image != null && image.IsLocalFile)
{ {
writer.WriteElementString("thumb", GetPathToSave(personEntity.PrimaryImagePath, libraryManager, config)); writer.WriteElementString("thumb", GetPathToSave(image.Path, libraryManager, config));
} }
} }
catch (Exception) catch (Exception)