aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Photos
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-10-29 18:01:02 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-10-29 18:01:02 -0400
commite33244d7971f0299cd21297597da6181d01631e9 (patch)
treeb11fc639998458f3b8d95b54538bd5dc0e6868bd /MediaBrowser.Server.Implementations/Photos
parent5eec770ae2ea61597f6e80877860d48f540b78e8 (diff)
improve user view images
Diffstat (limited to 'MediaBrowser.Server.Implementations/Photos')
-rw-r--r--MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs189
-rw-r--r--MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs147
-rw-r--r--MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs332
3 files changed, 346 insertions, 322 deletions
diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
new file mode 100644
index 000000000..1abefdef1
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
@@ -0,0 +1,189 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Photos
+{
+ public abstract class BaseDynamicImageProvider<T> : IHasChangeMonitor
+ where T : IHasImages
+ {
+ protected IFileSystem FileSystem { get; private set; }
+ protected IProviderManager ProviderManager { get; private set; }
+
+ protected BaseDynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager)
+ {
+ ProviderManager = providerManager;
+ FileSystem = fileSystem;
+ }
+
+ public async Task<ItemUpdateType> FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ {
+ if (!Supports(item))
+ {
+ return ItemUpdateType.None;
+ }
+
+ var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false);
+ var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false);
+
+ return primaryResult | thumbResult;
+ }
+
+ protected virtual bool Supports(IHasImages item)
+ {
+ return true;
+ }
+
+ protected abstract Task<List<BaseItem>> GetItemsWithImages(IHasImages item);
+
+ private const string Version = "3";
+ protected string GetConfigurationCacheKey(List<BaseItem> items)
+ {
+ return (Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray())).GetMD5().ToString("N");
+ }
+
+ protected async Task<ItemUpdateType> FetchAsync(IHasImages item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ {
+ var items = await GetItemsWithImages(item).ConfigureAwait(false);
+ var cacheKey = GetConfigurationCacheKey(items);
+
+ if (!HasChanged(item, imageType, cacheKey))
+ {
+ return ItemUpdateType.None;
+ }
+
+ return await FetchAsyncInternal(item, items, imageType, cacheKey, options, cancellationToken).ConfigureAwait(false);
+ }
+
+ protected async Task<ItemUpdateType> FetchAsyncInternal(IHasImages item,
+ List<BaseItem> itemsWithImages,
+ ImageType imageType,
+ string cacheKey,
+ MetadataRefreshOptions options,
+ CancellationToken cancellationToken)
+ {
+ var img = await CreateImageAsync(item, itemsWithImages, imageType, 0).ConfigureAwait(false);
+
+ if (img == null)
+ {
+ return ItemUpdateType.None;
+ }
+
+ using (var ms = new MemoryStream())
+ {
+ img.Save(ms, ImageFormat.Png);
+
+ ms.Position = 0;
+
+ await ProviderManager.SaveImage(item, ms, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false);
+ }
+
+ return ItemUpdateType.ImageUpdate;
+ }
+
+ protected Task<Image> GetThumbCollage(List<BaseItem> items)
+ {
+ return DynamicImageHelpers.GetThumbCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList(),
+ FileSystem,
+ 1600,
+ 900);
+ }
+
+ protected Task<Image> GetSquareCollage(List<BaseItem> items)
+ {
+ return DynamicImageHelpers.GetSquareCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList(),
+ FileSystem,
+ 800);
+ }
+
+ public string Name
+ {
+ get { return "Dynamic Image Provider"; }
+ }
+
+ public async Task<Image> CreateImageAsync(IHasImages item,
+ List<BaseItem> itemsWithImages,
+ ImageType imageType,
+ int imageIndex)
+ {
+ if (itemsWithImages.Count == 0)
+ {
+ return null;
+ }
+
+ return imageType == ImageType.Thumb ?
+ await GetThumbCollage(itemsWithImages).ConfigureAwait(false) :
+ await GetSquareCollage(itemsWithImages).ConfigureAwait(false);
+ }
+
+ public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+ {
+ if (!Supports(item))
+ {
+ return false;
+ }
+
+ var items = GetItemsWithImages(item).Result;
+ var cacheKey = GetConfigurationCacheKey(items);
+
+ return HasChanged(item, ImageType.Primary, cacheKey) || HasChanged(item, ImageType.Thumb, cacheKey);
+ }
+
+ protected bool HasChanged(IHasImages item, ImageType type, string cacheKey)
+ {
+ var image = item.GetImageInfo(type, 0);
+
+ if (image != null)
+ {
+ if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
+ {
+ return false;
+ }
+
+ var currentPathCacheKey = (Path.GetFileNameWithoutExtension(image.Path) ?? string.Empty).Split('_').LastOrDefault();
+
+ if (string.Equals(cacheKey, currentPathCacheKey, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected List<BaseItem> GetFinalItems(List<BaseItem> items)
+ {
+ // Rotate the images no more than once per week
+ var random = new Random(GetWeekOfYear()).Next();
+
+ return items
+ .OrderBy(i => random - items.IndexOf(i))
+ .Take(4)
+ .OrderBy(i => i.Name)
+ .ToList();
+ }
+
+ private int GetWeekOfYear()
+ {
+ var usCulture = new CultureInfo("en-US");
+ var weekNo = usCulture.Calendar.GetWeekOfYear(
+ DateTime.Now,
+ usCulture.DateTimeFormat.CalendarWeekRule,
+ usCulture.DateTimeFormat.FirstDayOfWeek);
+
+ return weekNo;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs b/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs
new file mode 100644
index 000000000..2c5cedf65
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Photos/DynamicImageHelpers.cs
@@ -0,0 +1,147 @@
+using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Photos
+{
+ public static class DynamicImageHelpers
+ {
+ public static async Task<Image> GetThumbCollage(List<string> files,
+ IFileSystem fileSystem,
+ int width,
+ int height)
+ {
+ if (files.Count < 3)
+ {
+ return await GetSingleImage(files, fileSystem).ConfigureAwait(false);
+ }
+
+ const int rows = 1;
+ const int cols = 3;
+
+ int cellWidth = 2 * (width / 3);
+ int cellHeight = height;
+ var index = 0;
+
+ 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())
+ {
+ await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
+
+ memoryStream.Position = 0;
+
+ using (var imgtemp = Image.FromStream(memoryStream, true, false))
+ {
+ graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
+ }
+ }
+ }
+ }
+
+ index++;
+ }
+ }
+ }
+
+ return img;
+ }
+
+ public static async Task<Image> GetSquareCollage(List<string> files,
+ IFileSystem fileSystem,
+ int size)
+ {
+ if (files.Count < 4)
+ {
+ return await GetSingleImage(files, fileSystem).ConfigureAwait(false);
+ }
+
+ const int rows = 2;
+ const int cols = 2;
+
+ int singleSize = size / 2;
+ var index = 0;
+
+ var img = new Bitmap(size, size, 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())
+ {
+ await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
+
+ memoryStream.Position = 0;
+
+ using (var imgtemp = Image.FromStream(memoryStream, true, false))
+ {
+ graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
+ }
+ }
+ }
+
+ index++;
+ }
+ }
+ }
+
+ return img;
+ }
+
+ private static Task<Image> GetSingleImage(List<string> files, IFileSystem fileSystem)
+ {
+ return GetImage(files[0], fileSystem);
+ }
+
+ private static async Task<Image> GetImage(string file, IFileSystem fileSystem)
+ {
+ using (var fileStream = fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+ {
+ var memoryStream = new MemoryStream();
+
+ await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
+
+ memoryStream.Position = 0;
+
+ return Image.FromStream(memoryStream, true, false);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
index 30e57106c..7c10f2767 100644
--- a/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
@@ -1,337 +1,25 @@
-using System;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Drawing.Imaging;
-using System.IO;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MoreLinq;
namespace MediaBrowser.Server.Implementations.Photos
{
- public class PhotoAlbumImageProvider : ICustomMetadataProvider<PhotoAlbum>, IHasChangeMonitor
+ public class PhotoAlbumImageProvider : BaseDynamicImageProvider<PhotoAlbum>, ICustomMetadataProvider<PhotoAlbum>
{
- private readonly IFileSystem _fileSystem;
- private readonly IProviderManager _provider;
-
- public PhotoAlbumImageProvider(IFileSystem fileSystem, IProviderManager provider)
- {
- _fileSystem = fileSystem;
- _provider = provider;
- }
-
- public async Task<ItemUpdateType> FetchAsync(PhotoAlbum item, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false);
- var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false);
-
- return primaryResult | thumbResult;
- }
-
- private Task<ItemUpdateType> FetchAsync(IHasImages item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- var items = GetItemsWithImages(item);
- var cacheKey = GetConfigurationCacheKey(items);
-
- if (!HasChanged(item, imageType, cacheKey))
- {
- return Task.FromResult(ItemUpdateType.None);
- }
-
- return FetchAsyncInternal(item, imageType, cacheKey, options, cancellationToken);
- }
-
- private async Task<ItemUpdateType> FetchAsyncInternal(IHasImages item, ImageType imageType, string cacheKey, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- var img = await CreateImageAsync(item, imageType, 0).ConfigureAwait(false);
-
- if (img == null)
- {
- return ItemUpdateType.None;
- }
-
- using (var ms = new MemoryStream())
- {
- img.Save(ms, ImageFormat.Png);
-
- ms.Position = 0;
-
- await _provider.SaveImage(item, ms, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false);
- }
-
- return ItemUpdateType.ImageUpdate;
- }
-
- private bool HasChanged(IHasImages item, ImageType type, string cacheKey)
- {
- var image = item.GetImageInfo(type, 0);
-
- if (image != null)
- {
- if (!_fileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
- {
- return false;
- }
-
- var currentPathCacheKey = (Path.GetFileNameWithoutExtension(image.Path) ?? string.Empty).Split('_').LastOrDefault();
-
- if (string.Equals(cacheKey, currentPathCacheKey, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- return true;
- }
-
- private const string Version = "3";
-
- public string GetConfigurationCacheKey(List<BaseItem> items)
- {
- return (Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray())).GetMD5().ToString("N");
- }
-
- private List<BaseItem> GetItemsWithImages(IHasImages item)
- {
- var photoAlbum = item as PhotoAlbum;
- if (photoAlbum != null)
- {
- return GetFinalItems(photoAlbum.RecursiveChildren.Where(i => i is Photo).ToList());
- }
-
- var playlist = (Playlist)item;
-
- var items = playlist.GetManageableItems()
- .Select(i =>
- {
- var subItem = i.Item2;
-
- var episode = subItem as Episode;
-
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null && series.HasImage(ImageType.Primary))
- {
- return series;
- }
- }
-
- if (subItem.HasImage(ImageType.Primary))
- {
- return subItem;
- }
-
- var parent = subItem.Parent;
-
- if (parent != null && parent.HasImage(ImageType.Primary))
- {
- if (parent is MusicAlbum)
- {
- return parent;
- }
- }
-
- return null;
- })
- .Where(i => i != null)
- .DistinctBy(i => i.Id)
- .ToList();
-
- return GetFinalItems(items);
- }
-
- private List<BaseItem> GetFinalItems(List<BaseItem> items)
- {
- // Rotate the images no more than once per day
- var random = new Random(DateTime.Now.DayOfYear).Next();
-
- return items
- .OrderBy(i => random - items.IndexOf(i))
- .Take(4)
- .OrderBy(i => i.Name)
- .ToList();
- }
-
- public async Task<Image> CreateImageAsync(IHasImages item, ImageType imageType, int imageIndex)
- {
- var items = GetItemsWithImages(item);
-
- if (items.Count == 0)
- {
- return null;
- }
-
- return imageType == ImageType.Thumb ?
- await GetThumbCollage(items).ConfigureAwait(false) :
- await GetSquareCollage(items).ConfigureAwait(false);
- }
-
- private Task<Image> GetThumbCollage(List<BaseItem> items)
- {
- return GetThumbCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList());
- }
-
- private Task<Image> GetSquareCollage(List<BaseItem> items)
- {
- return GetSquareCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList());
- }
-
- private async Task<Image> GetThumbCollage(List<string> files)
- {
- if (files.Count < 3)
- {
- return await GetSingleImage(files).ConfigureAwait(false);
- }
-
- const int rows = 1;
- const int cols = 3;
-
- const int cellWidth = 2 * (ThumbImageWidth / 3);
- const int cellHeight = ThumbImageHeight;
- var index = 0;
-
- var img = new Bitmap(ThumbImageWidth, ThumbImageHeight, 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())
- {
- await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- memoryStream.Position = 0;
-
- using (var imgtemp = Image.FromStream(memoryStream, true, false))
- {
- graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
- }
- }
- }
- }
-
- index++;
- }
- }
- }
-
- return img;
- }
-
- private const int SquareImageSize = 800;
- private const int ThumbImageWidth = 1600;
- private const int ThumbImageHeight = 900;
-
- private async Task<Image> GetSquareCollage(List<string> files)
- {
- if (files.Count < 4)
- {
- return await GetSingleImage(files).ConfigureAwait(false);
- }
-
- const int rows = 2;
- const int cols = 2;
-
- const int singleSize = SquareImageSize / 2;
- var index = 0;
-
- var img = new Bitmap(SquareImageSize, SquareImageSize, 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())
- {
- await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- memoryStream.Position = 0;
-
- using (var imgtemp = Image.FromStream(memoryStream, true, false))
- {
- graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
- }
- }
- }
-
- index++;
- }
- }
- }
-
- return img;
- }
-
- private Task<Image> GetSingleImage(List<string> files)
- {
- return GetImage(files[0]);
- }
-
- private async Task<Image> GetImage(string file)
- {
- using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true))
- {
- var memoryStream = new MemoryStream();
-
- await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- memoryStream.Position = 0;
-
- return Image.FromStream(memoryStream, true, false);
- }
- }
-
- public string Name
+ public PhotoAlbumImageProvider(IFileSystem fileSystem, IProviderManager providerManager)
+ : base(fileSystem, providerManager)
{
- get { return "Dynamic Image Provider"; }
}
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+ protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
{
- var items = GetItemsWithImages(item);
- var cacheKey = GetConfigurationCacheKey(items);
+ var photoAlbum = (PhotoAlbum)item;
+ var items = GetFinalItems(photoAlbum.RecursiveChildren.Where(i => i is Photo).ToList());
- return HasChanged(item, ImageType.Primary, cacheKey) || HasChanged(item, ImageType.Thumb, cacheKey);
+ return Task.FromResult(items);
}
}
}