Merge branch 'dev' of https://github.com/MediaBrowser/Emby into dev
This commit is contained in:
commit
8ef0596f0f
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -319,10 +319,13 @@ namespace MediaBrowser.Api.Images
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var size = _imageProcessor.GetImageSize(info);
|
if (info.IsLocalFile)
|
||||||
|
{
|
||||||
|
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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -143,7 +143,21 @@ namespace MediaBrowser.Api.Social
|
||||||
|
|
||||||
if (image != null)
|
if (image != null)
|
||||||
{
|
{
|
||||||
return ToStaticFileResult(image.Path);
|
if (image.IsLocalFile)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
|
@ -25,13 +25,6 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
/// <value>The image enhancers.</value>
|
/// <value>The image enhancers.</value>
|
||||||
IEnumerable<IImageEnhancer> ImageEnhancers { get; }
|
IEnumerable<IImageEnhancer> ImageEnhancers { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>ImageSize.</returns>
|
|
||||||
ImageSize GetImageSize(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the size of the image.
|
/// Gets the size of the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -39,6 +32,13 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
/// <returns>ImageSize.</returns>
|
/// <returns>ImageSize.</returns>
|
||||||
ImageSize GetImageSize(ItemImageInfo info);
|
ImageSize GetImageSize(ItemImageInfo info);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>ImageSize.</returns>
|
||||||
|
ImageSize GetImageSize(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the parts.
|
/// Adds the parts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -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,18 +1490,21 @@ namespace MediaBrowser.Controller.Entities
|
||||||
// Remove it from the item
|
// Remove it from the item
|
||||||
RemoveImage(info);
|
RemoveImage(info);
|
||||||
|
|
||||||
// Delete the source file
|
if (info.IsLocalFile)
|
||||||
var currentFile = new FileInfo(info.Path);
|
|
||||||
|
|
||||||
// Deletion will fail if the file is hidden so remove the attribute first
|
|
||||||
if (currentFile.Exists)
|
|
||||||
{
|
{
|
||||||
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
// Delete the source file
|
||||||
{
|
var currentFile = new FileInfo(info.Path);
|
||||||
currentFile.Attributes &= ~FileAttributes.Hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystem.DeleteFile(currentFile.FullName);
|
// Deletion will fail if the file is hidden so remove the attribute first
|
||||||
|
if (currentFile.Exists)
|
||||||
|
{
|
||||||
|
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||||
|
{
|
||||||
|
currentFile.Attributes &= ~FileAttributes.Hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
@ -1619,7 +1644,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
if (existing.IsLocalFile)
|
||||||
|
{
|
||||||
|
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1628,7 +1656,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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<MediaSourceInfo>.</returns>
|
/// <returns>Task<MediaSourceInfo>.</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>
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -138,11 +139,24 @@ namespace MediaBrowser.Providers.Manager
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(response.Path))
|
if (!string.IsNullOrEmpty(response.Path))
|
||||||
{
|
{
|
||||||
var mimeType = MimeTypes.GetMimeType(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
|
||||||
|
|
||||||
var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true);
|
}, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var mimeType = MimeTypes.GetMimeType(response.Path);
|
||||||
|
|
||||||
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false);
|
var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true);
|
||||||
|
|
||||||
|
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -391,7 +405,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var existing = item.GetImageInfo(type, 0);
|
var existing = item.GetImageInfo(type, 0);
|
||||||
if (existing != null && !_fileSystem.FileExists(existing.Path))
|
if (existing != null && !_fileSystem.FileExists(existing.Path))
|
||||||
{
|
{
|
||||||
item.RemoveImage(existing);
|
item.RemoveImage(existing);
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|
|
@ -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)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
imageResponse.HasImage = true;
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageResponse;
|
return imageResponse;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,40 +676,56 @@ 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;
|
||||||
|
|
||||||
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
|
// Unfortunately due to the semaphore we have to have a nested try/finally
|
||||||
await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
|
try
|
||||||
|
|
||||||
var duration = recordingEndDate - DateTime.UtcNow;
|
|
||||||
|
|
||||||
HttpRequestOptions httpRequestOptions = new HttpRequestOptions()
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
recording.Path = recordPath;
|
var duration = recordingEndDate - DateTime.UtcNow;
|
||||||
recording.Status = RecordingStatus.InProgress;
|
|
||||||
recording.DateLastUpdated = DateTime.UtcNow;
|
|
||||||
_recordingProvider.Update(recording);
|
|
||||||
|
|
||||||
_logger.Info("Beginning recording.");
|
HttpRequestOptions httpRequestOptions = new HttpRequestOptions()
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
|
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;
|
||||||
|
|
||||||
|
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recording.Status = RecordingStatus.Completed;
|
||||||
|
_logger.Info("Recording completed");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (isResourceOpen)
|
||||||
|
{
|
||||||
|
result.Item2.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recording.Status = RecordingStatus.Completed;
|
|
||||||
_logger.Info("Recording completed");
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
imageResponse.HasImage = true;
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
|
||||||
return stream;
|
await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false);
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
47
MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs
Normal file
47
MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user