From 4820fe80971c83cde97a445e45b9e0b1952b0d90 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 8 Apr 2015 10:38:02 -0400 Subject: added drawing project --- Emby.Drawing/GDI/GDIImageEncoder.cs | 233 ++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 Emby.Drawing/GDI/GDIImageEncoder.cs (limited to 'Emby.Drawing/GDI/GDIImageEncoder.cs') diff --git a/Emby.Drawing/GDI/GDIImageEncoder.cs b/Emby.Drawing/GDI/GDIImageEncoder.cs new file mode 100644 index 000000000..c6537fc43 --- /dev/null +++ b/Emby.Drawing/GDI/GDIImageEncoder.cs @@ -0,0 +1,233 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Logging; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat; + +namespace Emby.Drawing.GDI +{ + public class GDIImageEncoder : IImageEncoder + { + private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; + + public GDIImageEncoder(IFileSystem fileSystem, ILogger logger) + { + _fileSystem = fileSystem; + _logger = logger; + } + + public string[] SupportedInputFormats + { + get + { + return new[] + { + "png", + "jpeg", + "jpg", + "gif", + "bmp" + }; + } + } + + public ImageFormat[] SupportedOutputFormats + { + get + { + return new[] { ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png }; + } + } + + public ImageSize GetImageSize(string path) + { + using (var image = Image.FromFile(path)) + { + return new ImageSize + { + Width = image.Width, + Height = image.Height + }; + } + } + + public void CropWhiteSpace(string inputPath, string outputPath) + { + using (var image = (Bitmap)Image.FromFile(inputPath)) + { + using (var croppedImage = image.CropWhitespace()) + { + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + + using (var outputStream = _fileSystem.GetFileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) + { + croppedImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100); + } + } + } + } + + public void EncodeImage(string inputPath, string cacheFilePath, int width, int height, int quality, ImageProcessingOptions options) + { + var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0; + + using (var originalImage = Image.FromFile(inputPath)) + { + var newWidth = Convert.ToInt32(width); + var newHeight = Convert.ToInt32(height); + + var selectedOutputFormat = options.OutputFormat; + + // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here + // Also, Webp only supports Format32bppArgb and Format32bppRgb + var pixelFormat = selectedOutputFormat == ImageFormat.Webp + ? PixelFormat.Format32bppArgb + : PixelFormat.Format32bppPArgb; + + using (var thumbnail = new Bitmap(newWidth, newHeight, pixelFormat)) + { + // Mono throw an exeception if assign 0 to SetResolution + if (originalImage.HorizontalResolution > 0 && originalImage.VerticalResolution > 0) + { + // Preserve the original resolution + thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution); + } + + using (var thumbnailGraph = Graphics.FromImage(thumbnail)) + { + thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality; + thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; + thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; + thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; + thumbnailGraph.CompositingMode = !hasPostProcessing ? + CompositingMode.SourceCopy : + CompositingMode.SourceOver; + + SetBackgroundColor(thumbnailGraph, options); + + thumbnailGraph.DrawImage(originalImage, 0, 0, newWidth, newHeight); + + DrawIndicator(thumbnailGraph, newWidth, newHeight, options); + + var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat); + + Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); + + // Save to the cache location + using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) + { + // Save to the memory stream + thumbnail.Save(outputFormat, cacheFileStream, quality); + } + } + } + + } + } + + /// + /// Sets the color of the background. + /// + /// The graphics. + /// The options. + private void SetBackgroundColor(Graphics graphics, ImageProcessingOptions options) + { + var color = options.BackgroundColor; + + if (!string.IsNullOrEmpty(color)) + { + Color drawingColor; + + try + { + drawingColor = ColorTranslator.FromHtml(color); + } + catch + { + drawingColor = ColorTranslator.FromHtml("#" + color); + } + + graphics.Clear(drawingColor); + } + } + + /// + /// Draws the indicator. + /// + /// The graphics. + /// Width of the image. + /// Height of the image. + /// The options. + private void DrawIndicator(Graphics graphics, int imageWidth, int imageHeight, ImageProcessingOptions options) + { + if (!options.AddPlayedIndicator && !options.UnplayedCount.HasValue && options.PercentPlayed.Equals(0)) + { + return; + } + + try + { + if (options.AddPlayedIndicator) + { + var currentImageSize = new Size(imageWidth, imageHeight); + + new PlayedIndicatorDrawer().DrawPlayedIndicator(graphics, currentImageSize); + } + else if (options.UnplayedCount.HasValue) + { + var currentImageSize = new Size(imageWidth, imageHeight); + + new UnplayedCountIndicator().DrawUnplayedCountIndicator(graphics, currentImageSize, options.UnplayedCount.Value); + } + + if (options.PercentPlayed > 0) + { + var currentImageSize = new Size(imageWidth, imageHeight); + + new PercentPlayedDrawer().Process(graphics, currentImageSize, options.PercentPlayed); + } + } + catch (Exception ex) + { + _logger.ErrorException("Error drawing indicator overlay", ex); + } + } + + /// + /// Gets the output format. + /// + /// The image. + /// The output format. + /// ImageFormat. + private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, ImageFormat outputFormat) + { + switch (outputFormat) + { + case ImageFormat.Bmp: + return System.Drawing.Imaging.ImageFormat.Bmp; + case ImageFormat.Gif: + return System.Drawing.Imaging.ImageFormat.Gif; + case ImageFormat.Jpg: + return System.Drawing.Imaging.ImageFormat.Jpeg; + case ImageFormat.Png: + return System.Drawing.Imaging.ImageFormat.Png; + default: + return image.RawFormat; + } + } + + public void CreateImageCollage(ImageCollageOptions options) + { + } + + public void Dispose() + { + } + } +} -- cgit v1.2.3 From b289b4cc7f547a982b9a06e54cd2fbc893e122bd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 8 Apr 2015 11:45:30 -0400 Subject: complete gdi fallback --- Emby.Drawing/Emby.Drawing.csproj | 1 + Emby.Drawing/GDI/DynamicImageHelpers.cs | 138 +++++++++++++++++++++ Emby.Drawing/GDI/GDIImageEncoder.cs | 18 ++- Emby.Drawing/ImageMagick/StripCollageBuilder.cs | 2 +- .../Providers/IProviderManager.cs | 13 ++ MediaBrowser.Providers/Manager/ProviderManager.cs | 7 ++ .../Photos/BaseDynamicImageProvider.cs | 93 +++++--------- .../UserViews/DynamicImageProvider.cs | 12 +- 8 files changed, 215 insertions(+), 69 deletions(-) create mode 100644 Emby.Drawing/GDI/DynamicImageHelpers.cs (limited to 'Emby.Drawing/GDI/GDIImageEncoder.cs') diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 1907381e9..0e368d70e 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -48,6 +48,7 @@ Properties\SharedVersion.cs + diff --git a/Emby.Drawing/GDI/DynamicImageHelpers.cs b/Emby.Drawing/GDI/DynamicImageHelpers.cs new file mode 100644 index 000000000..c49007c5f --- /dev/null +++ b/Emby.Drawing/GDI/DynamicImageHelpers.cs @@ -0,0 +1,138 @@ +using Emby.Drawing.ImageMagick; +using MediaBrowser.Common.IO; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; + +namespace Emby.Drawing.GDI +{ + public static class DynamicImageHelpers + { + public static void CreateThumbCollage(List files, + IFileSystem fileSystem, + string file, + int width, + int height) + { + const int numStrips = 4; + files = StripCollageBuilder.ProjectPaths(files, numStrips).ToList(); + + const int rows = 1; + int cols = numStrips; + + int cellWidth = 2 * (width / 3); + int cellHeight = height; + var index = 0; + + using (var img = new Bitmap(width, height, PixelFormat.Format32bppPArgb)) + { + using (var graphics = Graphics.FromImage(img)) + { + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingMode = CompositingMode.SourceCopy; + + for (var row = 0; row < rows; row++) + { + for (var col = 0; col < cols; col++) + { + var x = col * (cellWidth / 2); + var y = row * cellHeight; + + if (files.Count > index) + { + using (var fileStream = fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true)) + { + using (var memoryStream = new MemoryStream()) + { + fileStream.CopyTo(memoryStream); + + memoryStream.Position = 0; + + using (var imgtemp = Image.FromStream(memoryStream, true, false)) + { + graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight); + } + } + } + } + + index++; + } + } + img.Save(file); + } + } + } + + public static void CreateSquareCollage(List files, + IFileSystem fileSystem, + string file, + int width, + int height) + { + files = StripCollageBuilder.ProjectPaths(files, 4).ToList(); + + const int rows = 2; + const int cols = 2; + + int singleSize = width / 2; + var index = 0; + + using (var img = new Bitmap(width, height, PixelFormat.Format32bppPArgb)) + { + using (var graphics = Graphics.FromImage(img)) + { + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingMode = CompositingMode.SourceCopy; + + for (var row = 0; row < rows; row++) + { + for (var col = 0; col < cols; col++) + { + var x = col * singleSize; + var y = row * singleSize; + + using (var fileStream = fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true)) + { + using (var memoryStream = new MemoryStream()) + { + fileStream.CopyTo(memoryStream); + + memoryStream.Position = 0; + + using (var imgtemp = Image.FromStream(memoryStream, true, false)) + { + graphics.DrawImage(imgtemp, x, y, singleSize, singleSize); + } + } + } + + index++; + } + } + img.Save(file); + } + } + } + + private static Stream GetStream(Image image) + { + var ms = new MemoryStream(); + + image.Save(ms, ImageFormat.Png); + + ms.Position = 0; + + return ms; + } + } +} diff --git a/Emby.Drawing/GDI/GDIImageEncoder.cs b/Emby.Drawing/GDI/GDIImageEncoder.cs index c6537fc43..33502c5e1 100644 --- a/Emby.Drawing/GDI/GDIImageEncoder.cs +++ b/Emby.Drawing/GDI/GDIImageEncoder.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using System.Linq; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Logging; @@ -224,6 +225,21 @@ namespace Emby.Drawing.GDI public void CreateImageCollage(ImageCollageOptions options) { + double ratio = options.Width; + ratio /= options.Height; + + if (ratio >= 1.4) + { + DynamicImageHelpers.CreateThumbCollage(options.InputPaths.ToList(), _fileSystem, options.OutputPath, options.Width, options.Height); + } + else if (ratio >= .9) + { + DynamicImageHelpers.CreateSquareCollage(options.InputPaths.ToList(), _fileSystem, options.OutputPath, options.Width, options.Height); + } + else + { + DynamicImageHelpers.CreateSquareCollage(options.InputPaths.ToList(), _fileSystem, options.OutputPath, options.Width, options.Width); + } } public void Dispose() diff --git a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs b/Emby.Drawing/ImageMagick/StripCollageBuilder.cs index d6e05a531..7cdd0077d 100644 --- a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs +++ b/Emby.Drawing/ImageMagick/StripCollageBuilder.cs @@ -69,7 +69,7 @@ namespace Emby.Drawing.ImageMagick } } - private string[] ProjectPaths(IEnumerable paths, int count) + internal static string[] ProjectPaths(IEnumerable paths, int count) { var clone = paths.ToList(); var list = new List(); diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index d40fa835f..d6fc39c5f 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -78,6 +78,19 @@ namespace MediaBrowser.Controller.Providers /// The cancellation token. /// Task. Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken); + + /// + /// Saves the image. + /// + /// The item. + /// The source. + /// Type of the MIME. + /// The type. + /// Index of the image. + /// The internal cache key. + /// The cancellation token. + /// Task. + Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken); /// /// Adds the metadata providers. diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index c9ae47ad0..01a89bf26 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -148,6 +148,13 @@ namespace MediaBrowser.Providers.Manager return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, internalCacheKey, cancellationToken); } + public Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken) + { + var fileStream = _fileSystem.GetFileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); + + return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, internalCacheKey, cancellationToken); + } + public async Task> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken) { var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders); diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs index 1e6189beb..4c9a65cf6 100644 --- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; @@ -79,56 +78,23 @@ namespace MediaBrowser.Server.Implementations.Photos string cacheKey, CancellationToken cancellationToken) { - var stream = CreateImageAsync(item, itemsWithImages, imageType, 0); + var outputPath = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid() + ".png"); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + var imageCreated = CreateImage(item, itemsWithImages, outputPath, imageType, 0); - if (stream == null) + if (!imageCreated) { return ItemUpdateType.None; } - if (stream is MemoryStream) - { - using (stream) - { - stream.Position = 0; - - await ProviderManager.SaveImage(item, stream, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false); - } - } - else - { - using (var ms = new MemoryStream()) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - - ms.Position = 0; - - await ProviderManager.SaveImage(item, ms, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false); - } - } + await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false); return ItemUpdateType.ImageUpdate; } - public async Task GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken) - { - var items = await GetItemsWithImages(item).ConfigureAwait(false); - var cacheKey = GetConfigurationCacheKey(items, item.Name); - - var result = CreateImageAsync(item, items, type, 0); - - return new DynamicImageResponse - { - HasImage = result != null, - Stream = result, - InternalCacheKey = cacheKey, - Format = ImageFormat.Png - }; - } - protected abstract Task> GetItemsWithImages(IHasImages item); - private const string Version = "20"; + private const string Version = "27"; protected string GetConfigurationCacheKey(List items, string itemName) { var parts = Version + "_" + (itemName ?? string.Empty) + "_" + @@ -137,39 +103,35 @@ namespace MediaBrowser.Server.Implementations.Photos return parts.GetMD5().ToString("N"); } - protected Stream GetThumbCollage(IHasImages primaryItem, List items) + protected void CreateThumbCollage(IHasImages primaryItem, List items, string outputPath) { - return GetThumbCollage(primaryItem, items, 960, 540, true, primaryItem.Name); + CreateCollage(primaryItem, items, outputPath, 960, 540, true, primaryItem.Name); } - protected virtual IEnumerable GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable items) + protected virtual IEnumerable GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable items) { return items .Select(i => i.GetImagePath(ImageType.Primary) ?? i.GetImagePath(ImageType.Thumb)) .Where(i => !string.IsNullOrWhiteSpace(i)); } - protected Stream GetPosterCollage(IHasImages primaryItem, List items) + protected void CreatePosterCollage(IHasImages primaryItem, List items, string outputPath) { - var path = CreateCollage(primaryItem, items, 600, 900, true, primaryItem.Name); - return FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + CreateCollage(primaryItem, items, outputPath, 600, 900, true, primaryItem.Name); } - protected Stream GetSquareCollage(IHasImages primaryItem, List items) + protected void CreateSquareCollage(IHasImages primaryItem, List items, string outputPath) { - var path = CreateCollage(primaryItem, items, 800, 800, true, primaryItem.Name); - return FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + CreateCollage(primaryItem, items, outputPath, 800, 800, true, primaryItem.Name); } - protected Stream GetThumbCollage(IHasImages primaryItem, List items, int width, int height, bool drawText, string text) + protected void CreateThumbCollage(IHasImages primaryItem, List items, string outputPath, int width, int height, bool drawText, string text) { - var path = CreateCollage(primaryItem, items, width, height, drawText, text); - return FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + CreateCollage(primaryItem, items, outputPath, width, height, drawText, text); } - private string CreateCollage(IHasImages primaryItem, List items, int width, int height, bool drawText, string text) + private void CreateCollage(IHasImages primaryItem, List items, string outputPath, int width, int height, bool drawText, string text) { - var outputPath = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid() + ".png"); Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); var options = new ImageCollageOptions @@ -182,8 +144,6 @@ namespace MediaBrowser.Server.Implementations.Photos }; ImageProcessor.CreateImageCollage(options); - - return outputPath; } public string Name @@ -191,26 +151,35 @@ namespace MediaBrowser.Server.Implementations.Photos get { return "Dynamic Image Provider"; } } - protected virtual Stream CreateImageAsync(IHasImages item, + protected virtual bool CreateImage(IHasImages item, List itemsWithImages, + string outputPath, ImageType imageType, int imageIndex) { if (itemsWithImages.Count == 0) { - return null; + return false; } if (imageType == ImageType.Thumb) { - return GetThumbCollage(item, itemsWithImages); + CreateThumbCollage(item, itemsWithImages, outputPath); + return true; } if (imageType == ImageType.Primary) { - return item is PhotoAlbum || item is Playlist ? - GetSquareCollage(item, itemsWithImages) : - GetPosterCollage(item, itemsWithImages); + if (item is PhotoAlbum || item is Playlist) + { + CreateSquareCollage(item, itemsWithImages, outputPath); + } + else + { + CreatePosterCollage(item, itemsWithImages, outputPath); + } + + return true; } throw new ArgumentException("Unexpected image type"); diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs index fc05f3169..ad8c6e414 100644 --- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -23,7 +23,8 @@ namespace MediaBrowser.Server.Implementations.UserViews private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; - public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor) + public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager) + : base(fileSystem, providerManager, applicationPaths, imageProcessor) { _userManager = userManager; _libraryManager = libraryManager; @@ -238,20 +239,21 @@ namespace MediaBrowser.Server.Implementations.UserViews return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty); } - protected override Stream CreateImageAsync(IHasImages item, List itemsWithImages, ImageType imageType, int imageIndex) + protected override bool CreateImage(IHasImages item, List itemsWithImages, string outputPath, ImageType imageType, int imageIndex) { var view = (UserView)item; if (imageType == ImageType.Primary && IsUsingCollectionStrip(view)) { if (itemsWithImages.Count == 0 && !string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) { - return null; + return false; } - return GetThumbCollage(item, itemsWithImages, 960, 540, false, item.Name); + CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540, false, item.Name); + return true; } - return base.CreateImageAsync(item, itemsWithImages, imageType, imageIndex); + return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex); } protected override IEnumerable GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable items) -- cgit v1.2.3 From b9c656e859fe28ed6a66580d1eb5577bd50264e6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 9 Apr 2015 01:20:23 -0400 Subject: added out of network bitrate limit --- Emby.Drawing/GDI/GDIImageEncoder.cs | 9 ++++-- Emby.Drawing/IImageEncoder.cs | 5 +++ Emby.Drawing/ImageMagick/ImageMagickEncoder.cs | 5 +++ MediaBrowser.Api/LiveTv/LiveTvService.cs | 6 ++-- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 37 +++++++++++++++------- MediaBrowser.Api/Subtitles/SubtitleService.cs | 4 +-- .../BaseApplicationHost.cs | 13 +++----- .../Networking/BaseNetworkManager.cs | 12 ++++--- .../Library/IMediaSourceManager.cs | 16 +++------- .../Providers/IImageEnhancer.cs | 3 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 2 +- .../Encoder/EncodingJobFactory.cs | 2 +- .../Subtitles/SubtitleEncoder.cs | 2 +- .../Configuration/ServerConfiguration.cs | 1 + MediaBrowser.Model/Session/ClientCapabilities.cs | 2 ++ MediaBrowser.Model/Users/UserPolicy.cs | 10 +++--- .../Library/MediaSourceManager.cs | 28 ++++++++++------ .../Localization/JavaScript/javascript.json | 2 +- .../Localization/Server/server.json | 8 +++-- .../Session/SessionManager.cs | 7 ++-- .../Sync/CloudSyncProfile.cs | 26 +++++++++++++-- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 1 + .../MediaBrowser.WebDashboard.csproj | 6 ++++ 24 files changed, 136 insertions(+), 73 deletions(-) (limited to 'Emby.Drawing/GDI/GDIImageEncoder.cs') diff --git a/Emby.Drawing/GDI/GDIImageEncoder.cs b/Emby.Drawing/GDI/GDIImageEncoder.cs index 33502c5e1..d968b8b5f 100644 --- a/Emby.Drawing/GDI/GDIImageEncoder.cs +++ b/Emby.Drawing/GDI/GDIImageEncoder.cs @@ -1,5 +1,4 @@ -using System.Linq; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Logging; @@ -8,6 +7,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; +using System.Linq; using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat; namespace Emby.Drawing.GDI @@ -245,5 +245,10 @@ namespace Emby.Drawing.GDI public void Dispose() { } + + public string Name + { + get { return "GDI"; } + } } } diff --git a/Emby.Drawing/IImageEncoder.cs b/Emby.Drawing/IImageEncoder.cs index 83a59a002..29261dbdb 100644 --- a/Emby.Drawing/IImageEncoder.cs +++ b/Emby.Drawing/IImageEncoder.cs @@ -44,5 +44,10 @@ namespace Emby.Drawing /// /// The options. void CreateImageCollage(ImageCollageOptions options); + /// + /// Gets the name. + /// + /// The name. + string Name { get; } } } diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index b4a00f027..3d6cdd03d 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs @@ -206,6 +206,11 @@ namespace Emby.Drawing.ImageMagick } } + public string Name + { + get { return "ImageMagick"; } + } + private bool _disposed; public void Dispose() { diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index bb6f74f36..8cb814b97 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -433,7 +433,7 @@ namespace MediaBrowser.Api.LiveTv var result = await _liveTvManager.GetPrograms(query, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task Get(GetRecommendedPrograms request) @@ -450,7 +450,7 @@ namespace MediaBrowser.Api.LiveTv var result = await _liveTvManager.GetRecommendedPrograms(query, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public object Post(GetPrograms request) @@ -473,7 +473,7 @@ namespace MediaBrowser.Api.LiveTv }, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task Get(GetRecording request) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 302ad7f29..62bee1f9b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1627,7 +1627,7 @@ namespace MediaBrowser.Api.Playback MediaSourceInfo mediaSource; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { - var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false)).ToList(); + var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index abd0278c2..6e5af261c 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Devices; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; @@ -59,23 +61,27 @@ namespace MediaBrowser.Api.Playback private readonly IMediaSourceManager _mediaSourceManager; private readonly IDeviceManager _deviceManager; private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _config; + private readonly INetworkManager _networkManager; - public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager) + public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; _libraryManager = libraryManager; + _config = config; + _networkManager = networkManager; } public async Task Get(GetPlaybackInfo request) { - var result = await GetPlaybackInfo(request.Id, request.UserId).ConfigureAwait(false); + var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false); return ToOptimizedResult(result); } public async Task Get(GetLiveMediaInfo request) { - var result = await GetPlaybackInfo(request.Id, request.UserId).ConfigureAwait(false); + var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -122,29 +128,38 @@ namespace MediaBrowser.Api.Playback public async Task Post(GetPostedPlaybackInfo request) { - var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false); var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); var profile = request.DeviceProfile; - if (profile == null) + + var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + if (caps != null) { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); - if (caps != null) + if (profile == null) { profile = caps.DeviceProfile; } } + var maxBitrate = request.MaxStreamingBitrate; + + if (_config.Configuration.RemoteClientBitrateLimit > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp)) + { + maxBitrate = Math.Min(maxBitrate ?? _config.Configuration.RemoteClientBitrateLimit, _config.Configuration.RemoteClientBitrateLimit); + } + + var info = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false); + if (profile != null) { var mediaSourceId = request.MediaSourceId; - SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); + SetDeviceSpecificData(request.Id, info, profile, authInfo, maxBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); } return ToOptimizedResult(info); } - private async Task GetPlaybackInfo(string id, string userId, string mediaSourceId = null, string liveStreamId = null) + private async Task GetPlaybackInfo(string id, string userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var result = new PlaybackInfoResponse(); @@ -153,7 +168,7 @@ namespace MediaBrowser.Api.Playback IEnumerable mediaSources; try { - mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false); + mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, supportedLiveMediaTypes, CancellationToken.None).ConfigureAwait(false); } catch (PlaybackException ex) { diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 73589d677..a70118d3c 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -136,11 +136,11 @@ namespace MediaBrowser.Api.Subtitles _providerManager = providerManager; } - public object Get(GetSubtitlePlaylist request) + public async Task Get(GetSubtitlePlaylist request) { var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); - var mediaSource = _mediaSourceManager.GetStaticMediaSource(item, request.MediaSourceId, false); + var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, false).ConfigureAwait(false); var builder = new StringBuilder(); diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index bc1b0e785..70ed5c319 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -101,12 +101,6 @@ namespace MediaBrowser.Common.Implementations /// The failed assemblies. public List FailedAssemblies { get; protected set; } - /// - /// Gets all types within all running assemblies - /// - /// All types. - public Type[] AllTypes { get; protected set; } - /// /// Gets all concrete types. /// @@ -438,9 +432,10 @@ namespace MediaBrowser.Common.Implementations Logger.Info("Loading {0}", assembly.FullName); } - AllTypes = assemblies.SelectMany(GetTypes).ToArray(); - - AllConcreteTypes = AllTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray(); + AllConcreteTypes = assemblies + .SelectMany(GetTypes) + .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType) + .ToArray(); } /// diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index 1762ed575..0fd4e2787 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -172,11 +172,11 @@ namespace MediaBrowser.Common.Implementations.Networking Uri uri; if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri)) { - var host = uri.DnsSafeHost; - Logger.Debug("Resolving host {0}", host); - try { + var host = uri.DnsSafeHost; + Logger.Debug("Resolving host {0}", host); + address = GetIpAddresses(host).FirstOrDefault(); if (address != null) @@ -186,9 +186,13 @@ namespace MediaBrowser.Common.Implementations.Networking return IsInLocalNetworkInternal(address.ToString(), false); } } + catch (InvalidOperationException) + { + // Can happen with reverse proxy or IIS url rewriting + } catch (Exception ex) { - Logger.ErrorException("Error resovling hostname {0}", ex, host); + Logger.ErrorException("Error resovling hostname", ex); } } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 5bcc5f313..a77d88049 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -43,18 +43,10 @@ namespace MediaBrowser.Controller.Library /// The identifier. /// The user identifier. /// if set to true [enable path substitution]. + /// The supported live media types. /// The cancellation token. /// IEnumerable<MediaSourceInfo>. - Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, CancellationToken cancellationToken); - - /// - /// Gets the playack media sources. - /// - /// The identifier. - /// if set to true [enable path substitution]. - /// The cancellation token. - /// Task<IEnumerable<MediaSourceInfo>>. - Task> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken); + Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken); /// /// Gets the static media sources. @@ -64,7 +56,7 @@ namespace MediaBrowser.Controller.Library /// The user. /// IEnumerable<MediaSourceInfo>. IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null); - + /// /// Gets the static media source. /// @@ -72,7 +64,7 @@ namespace MediaBrowser.Controller.Library /// The media source identifier. /// if set to true [enable path substitution]. /// MediaSourceInfo. - MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); + Task GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); /// /// Opens the media source. diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs index e5a51a56e..a43941607 100644 --- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs +++ b/MediaBrowser.Controller/Providers/IImageEnhancer.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; using System.Threading.Tasks; diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index d88adc8c6..9df69b115 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -892,7 +892,7 @@ namespace MediaBrowser.Dlna.PlayTo request.MediaSource = hasMediaSources == null ? null : - mediaSourceManager.GetStaticMediaSource(hasMediaSources, request.MediaSourceId, false); + mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, false).Result; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 8d8201074..a0f5d928d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.MediaEncoding.Encoder state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 14d3e7284..60b70ad08 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -132,7 +132,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles int subtitleStreamIndex, CancellationToken cancellationToken) { - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, false, cancellationToken).ConfigureAwait(false); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId)); diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index bc167333a..ac9bd6b08 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -208,6 +208,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableAudioArchiveFiles { get; set; } public bool EnableVideoArchiveFiles { get; set; } + public int RemoteClientBitrateLimit { get; set; } /// /// Initializes a new instance of the class. diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index 9361a60ea..25438a811 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -19,12 +19,14 @@ namespace MediaBrowser.Model.Session public bool SupportsOfflineAccess { get; set; } public DeviceProfile DeviceProfile { get; set; } + public List SupportedLiveMediaTypes { get; set; } public ClientCapabilities() { PlayableMediaTypes = new List(); SupportedCommands = new List(); SupportsPersistentIdentifier = true; + SupportedLiveMediaTypes = new List(); } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index b3c599496..d7c894587 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -39,8 +39,9 @@ namespace MediaBrowser.Model.Users public bool EnableLiveTvAccess { get; set; } public bool EnableMediaPlayback { get; set; } - public bool EnableMediaPlaybackTranscoding { get; set; } - + public bool EnableAudioPlaybackTranscoding { get; set; } + public bool EnableVideoPlaybackTranscoding { get; set; } + public bool EnableContentDeletion { get; set; } public bool EnableContentDownloading { get; set; } @@ -68,8 +69,9 @@ namespace MediaBrowser.Model.Users EnableSyncTranscoding = true; EnableMediaPlayback = true; - EnableMediaPlaybackTranscoding = true; - + EnableAudioPlaybackTranscoding = true; + EnableVideoPlaybackTranscoding = true; + EnableLiveTvManagement = true; EnableLiveTvAccess = true; diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 01efe0ab1..71fd4127b 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -129,12 +129,7 @@ namespace MediaBrowser.Server.Implementations.Library return list; } - public Task> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken) - { - return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken); - } - - public async Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, CancellationToken cancellationToken) + public async Task> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken) { var item = _libraryManager.GetItemById(id); @@ -184,9 +179,19 @@ namespace MediaBrowser.Server.Implementations.Library { if (user != null) { - if (!user.Policy.EnableMediaPlaybackTranscoding) + if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { - source.SupportsTranscoding = false; + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + source.SupportsTranscoding = false; + } + } + else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + if (!user.Policy.EnableVideoPlaybackTranscoding) + { + source.SupportsTranscoding = false; + } } } } @@ -238,9 +243,12 @@ namespace MediaBrowser.Server.Implementations.Library } } - public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) + public async Task GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) { - return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + var sources = await GetPlayackMediaSources(item.Id.ToString("N"), null, enablePathSubstitution, new[] { MediaType.Audio, MediaType.Video }, + CancellationToken.None).ConfigureAwait(false); + + return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } public IEnumerable GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null) diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index f782a1f68..4c6689bc6 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -122,7 +122,7 @@ "LabelFree": "Free", "HeaderPlaybackError": "Playback Error", "MessagePlaybackErrorNotAllowed": "You're currently not authorized to play this content. Please contact your system administrator for details.", - "MessagePlaybackErrorNoCompatibleStream": "No compatible streams are currently available. Please try again later.", + "MessagePlaybackErrorNoCompatibleStream": "No compatible streams are currently available. Please try again later or contact your system administrator for details.", "MessagePlaybackErrorRateLimitExceeded": "Your playback rate limit has been exceeded. Please contact your system administrator for details.", "MessagePlaybackErrorPlaceHolder": "The content chosen is not playable from this device.", "HeaderSelectAudio": "Select Audio", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index bf03498db..b651b72da 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1410,6 +1410,10 @@ "LabelUploadSpeedLimit": "Upload speed limit (mbps):", "OptionAllowSyncTranscoding": "Allow syncing that requires transcoding", "HeaderPlayback": "Media Playback", - "OptionAllowMediaPlaybackTranscoding": "Allow media playback that requires transcoding", - "OptionAllowMediaPlaybackTranscodingHelp": "Users will receive friendly messages when content is unplayable based on policy." + "OptionAllowAudioPlaybackTranscoding": "Allow audio playback that requires transcoding", + "OptionAllowVideoPlaybackTranscoding": "Allow video playback that requires transcoding", + "OptionAllowMediaPlaybackTranscodingHelp": "Users will receive friendly messages when content is unplayable based on policy.", + "TabStreaming": "Streaming", + "LabelRemoteClientBitrateLimit": "Remote client bitrate limit (mbps):", + "LabelRemoteClientBitrateLimitHelp": "An optional streaming bitrate limit for all remote clients. This is useful to prevent clients from requesting a higher bitrate than your connection can handle." } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 7f5033b98..112778ec8 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -305,12 +305,9 @@ namespace MediaBrowser.Server.Implementations.Session } } - private async Task GetMediaSource(IHasMediaSources item, string mediaSourceId) + private Task GetMediaSource(IHasMediaSources item, string mediaSourceId) { - var sources = await _mediaSourceManager.GetPlayackMediaSources(item.Id.ToString("N"), false, CancellationToken.None) - .ConfigureAwait(false); - - return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + return _mediaSourceManager.GetMediaSource(item, mediaSourceId, false); } /// diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 73400f834..f881a2055 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -195,17 +195,39 @@ namespace MediaBrowser.Server.Implementations.Sync } }; - var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2"; codecProfiles.Add(new CodecProfile { Type = CodecType.VideoAudio, + Codec = "ac3", Conditions = new[] { new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, - Value = maxAudioChannels, + Value = "5", + IsRequired = true + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsSecondaryAudio, + Value = "false", + IsRequired = false + } + } + }); + codecProfiles.Add(new CodecProfile + { + Type = CodecType.VideoAudio, + Codec = "ac3", + Conditions = new[] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.AudioChannels, + Value = "2", IsRequired = true }, new ProfileCondition diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 5138b157f..90af13b57 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -482,6 +482,7 @@ namespace MediaBrowser.WebDashboard.Api "selectserver.js", "serversecurity.js", "songs.js", + "streamingsettings.js", "supporterkeypage.js", "supporterpage.js", "syncactivity.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 4ad2ab685..7abe2ded9 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -150,6 +150,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -174,6 +177,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest -- cgit v1.2.3