aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj7
-rw-r--r--MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj7
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs9
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs11
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs147
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs19
-rw-r--r--MediaBrowser.Controller/Library/IUserViewManager.cs2
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj7
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj7
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj7
-rw-r--r--MediaBrowser.Model/Entities/CollectionType.cs5
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj7
-rw-r--r--MediaBrowser.Providers/FolderImages/DefaultImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Folders/UserViewMetadataService.cs33
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs2
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj8
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs107
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs22
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json7
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj10
-rw-r--r--MediaBrowser.Server.Implementations/Music/MusicDynamicImageProvider.cs90
-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
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs291
-rw-r--r--MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj7
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj7
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs2
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj19
29 files changed, 792 insertions, 725 deletions
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index b233629e1..5d8a24f9a 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -172,6 +172,13 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
index d71f2954b..90c9f6942 100644
--- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
+++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
@@ -124,6 +124,13 @@
xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
)</PostBuildEvent>
</PropertyGroup>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 82971ac5c..d82ef3f56 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -373,6 +373,15 @@ namespace MediaBrowser.Controller.Entities
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
}
+ public static string GetInternalMetadataPathForId(Guid id)
+ {
+ var idString = id.ToString("N");
+
+ var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
+
+ return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
+ }
+
/// <summary>
/// Creates the name of the sort.
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index eb08a6f2f..c313966a5 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -12,12 +12,21 @@ namespace MediaBrowser.Controller.Entities
public string ViewType { get; set; }
public Guid ParentId { get; set; }
+ public Guid? UserId { get; set; }
+
public static ITVSeriesManager TVSeriesManager;
public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
{
+ var parent = this as Folder;
+
+ if (ParentId != Guid.Empty)
+ {
+ parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
+ }
+
return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, CollectionManager)
- .GetUserItems(this, ViewType, query);
+ .GetUserItems(parent, ViewType, query);
}
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index bf6cac87e..ff3b5920e 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -1,5 +1,4 @@
-using System.IO;
-using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
@@ -15,6 +14,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.Entities
return GetResult(result);
}
- case CollectionType.LiveTvChannels:
+ case SpecialFolder.LiveTvChannels:
{
var result = await _liveTvManager.GetInternalChannels(new LiveTvChannelQuery
{
@@ -76,7 +76,7 @@ namespace MediaBrowser.Controller.Entities
return GetResult(result);
}
- case CollectionType.LiveTvNowPlaying:
+ case SpecialFolder.LiveTvNowPlaying:
{
var result = await _liveTvManager.GetRecommendedProgramsInternal(new RecommendedProgramQuery
{
@@ -89,7 +89,7 @@ namespace MediaBrowser.Controller.Entities
return GetResult(result);
}
- case CollectionType.LiveTvRecordingGroups:
+ case SpecialFolder.LiveTvRecordingGroups:
{
var result = await _liveTvManager.GetInternalRecordings(new RecordingQuery
{
@@ -128,85 +128,85 @@ namespace MediaBrowser.Controller.Entities
case CollectionType.Movies:
return await GetMovieFolders(parent, user, query).ConfigureAwait(false);
- case CollectionType.GameGenres:
+ case SpecialFolder.GameGenres:
return GetGameGenres(parent, user, query);
- case CollectionType.GameSystems:
+ case SpecialFolder.GameSystems:
return GetGameSystems(parent, user, query);
- case CollectionType.LatestGames:
+ case SpecialFolder.LatestGames:
return GetLatestGames(parent, user, query);
- case CollectionType.RecentlyPlayedGames:
+ case SpecialFolder.RecentlyPlayedGames:
return GetRecentlyPlayedGames(parent, user, query);
- case CollectionType.GameFavorites:
+ case SpecialFolder.GameFavorites:
return GetFavoriteGames(parent, user, query);
- case CollectionType.TvShowSeries:
+ case SpecialFolder.TvShowSeries:
return GetTvSeries(parent, user, query);
- case CollectionType.TvGenres:
+ case SpecialFolder.TvGenres:
return GetTvGenres(parent, user, query);
- case CollectionType.TvResume:
+ case SpecialFolder.TvResume:
return GetTvResume(parent, user, query);
- case CollectionType.TvNextUp:
+ case SpecialFolder.TvNextUp:
return GetTvNextUp(parent, query);
- case CollectionType.TvLatest:
+ case SpecialFolder.TvLatest:
return GetTvLatest(parent, user, query);
- case CollectionType.MovieFavorites:
+ case SpecialFolder.MovieFavorites:
return GetFavoriteMovies(parent, user, query);
- case CollectionType.MovieLatest:
+ case SpecialFolder.MovieLatest:
return GetMovieLatest(parent, user, query);
- case CollectionType.MovieGenres:
+ case SpecialFolder.MovieGenres:
return GetMovieGenres(parent, user, query);
- case CollectionType.MovieResume:
+ case SpecialFolder.MovieResume:
return GetMovieResume(parent, user, query);
- case CollectionType.MovieMovies:
+ case SpecialFolder.MovieMovies:
return GetMovieMovies(parent, user, query);
- case CollectionType.MovieCollections:
+ case SpecialFolder.MovieCollections:
return GetMovieCollections(parent, user, query);
- case CollectionType.MusicLatest:
+ case SpecialFolder.MusicLatest:
return GetMusicLatest(parent, user, query);
- case CollectionType.MusicAlbums:
+ case SpecialFolder.MusicAlbums:
return GetMusicAlbums(parent, user, query);
- case CollectionType.MusicAlbumArtists:
+ case SpecialFolder.MusicAlbumArtists:
return GetMusicAlbumArtists(parent, user, query);
- case CollectionType.MusicArtists:
+ case SpecialFolder.MusicArtists:
return GetMusicArtists(parent, user, query);
- case CollectionType.MusicSongs:
+ case SpecialFolder.MusicSongs:
return GetMusicSongs(parent, user, query);
- case CollectionType.TvFavoriteEpisodes:
+ case SpecialFolder.TvFavoriteEpisodes:
return GetFavoriteEpisodes(parent, user, query);
- case CollectionType.TvFavoriteSeries:
+ case SpecialFolder.TvFavoriteSeries:
return GetFavoriteSeries(parent, user, query);
- case CollectionType.MusicFavorites:
+ case SpecialFolder.MusicFavorites:
return await GetMusicFavorites(parent, user, query).ConfigureAwait(false);
- case CollectionType.MusicFavoriteAlbums:
+ case SpecialFolder.MusicFavoriteAlbums:
return GetFavoriteAlbums(parent, user, query);
- case CollectionType.MusicFavoriteArtists:
+ case SpecialFolder.MusicFavoriteArtists:
return GetFavoriteArtists(parent, user, query);
- case CollectionType.MusicFavoriteSongs:
+ case SpecialFolder.MusicFavoriteSongs:
return GetFavoriteSongs(parent, user, query);
default:
@@ -228,15 +228,13 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>();
- var category = "music";
-
- list.Add(await GetUserView(category, CollectionType.MusicLatest, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MusicAlbums, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MusicSongs, user, "3", parent).ConfigureAwait(false));
- //list.Add(await GetUserView(CollectionType.MusicArtists, user, "3", parent).ConfigureAwait(false));
- //list.Add(await GetUserView(CollectionType.MusicGenres, user, "5", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MusicFavorites, user, "6", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicLatest, user, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "3", parent).ConfigureAwait(false));
+ //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "3", parent).ConfigureAwait(false));
+ //list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "5", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "6", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -245,11 +243,9 @@ namespace MediaBrowser.Controller.Entities
{
var list = new List<BaseItem>();
- var category = "music";
-
- list.Add(await GetUserView(category, CollectionType.MusicFavoriteAlbums, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MusicFavoriteArtists, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MusicFavoriteSongs, user, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavoriteAlbums, user, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavoriteArtists, user, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavoriteSongs, user, "2", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -353,13 +349,11 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>();
- var category = "movies";
-
- list.Add(await GetUserView(category, CollectionType.MovieResume, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MovieLatest, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MovieMovies, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MovieCollections, user, "3", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.MovieFavorites, user, "4", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieResume, user, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieLatest, user, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieMovies, user, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieCollections, user, "3", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieFavorites, user, "4", parent).ConfigureAwait(false));
//list.Add(await GetUserView(CollectionType.MovieGenres, user, "5", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
@@ -461,15 +455,13 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>();
- var category = "tvshows";
-
- list.Add(await GetUserView(category, CollectionType.TvResume, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.TvNextUp, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.TvLatest, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.TvShowSeries, user, "3", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.TvFavoriteSeries, user, "4", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.TvFavoriteEpisodes, user, "5", parent).ConfigureAwait(false));
- //list.Add(await GetUserView(CollectionType.TvGenres, user, "5", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvResume, user, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvNextUp, user, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvLatest, user, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvShowSeries, user, "3", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvFavoriteSeries, user, "4", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvFavoriteEpisodes, user, "5", parent).ConfigureAwait(false));
+ //list.Add(await GetUserView(SpecialFolder.TvGenres, user, "5", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -483,13 +475,11 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>();
- var category = "games";
-
- list.Add(await GetUserView(category, CollectionType.LatestGames, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.GameFavorites, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(category, CollectionType.GameSystems, user, "3", parent).ConfigureAwait(false));
- //list.Add(await GetUserView(CollectionType.GameGenres, user, "4", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.LatestGames, user, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.GameFavorites, user, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.GameSystems, user, "3", parent).ConfigureAwait(false));
+ //list.Add(await GetUserView(SpecialFolder.GameGenres, user, "4", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -1491,25 +1481,20 @@ namespace MediaBrowser.Controller.Entities
{
var list = new List<BaseItem>();
- list.Add(await _userViewManager.GetUserView("livetv", CollectionType.LiveTvNowPlaying, user, "0", CancellationToken.None).ConfigureAwait(false));
- list.Add(await _userViewManager.GetUserView("livetv", CollectionType.LiveTvChannels, user, string.Empty, CancellationToken.None).ConfigureAwait(false));
- list.Add(await _userViewManager.GetUserView("livetv", CollectionType.LiveTvRecordingGroups, user, string.Empty, CancellationToken.None).ConfigureAwait(false));
+ var parent = user.RootFolder;
+
+ list.Add(await GetUserView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, parent).ConfigureAwait(false));
return list;
}
- private async Task<UserView> GetUserView(string category, string type, User user, string sortName, Folder parent)
+ private async Task<UserView> GetUserView(string type, User user, string sortName, Folder parent)
{
- var view = await _userViewManager.GetUserView(category, type, user, sortName, CancellationToken.None)
+ var view = await _userViewManager.GetUserView(parent.Id.ToString("N"), type, user, sortName, CancellationToken.None)
.ConfigureAwait(false);
- if (parent.Id != view.ParentId)
- {
- view.ParentId = parent.Id;
- await view.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None)
- .ConfigureAwait(false);
- }
-
return view;
}
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index f93564882..be43f4527 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -325,15 +325,21 @@ namespace MediaBrowser.Controller.Library
IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items);
/// <summary>
- /// Gets the named folder.
+ /// Gets the special folder.
/// </summary>
+ /// <param name="user">The user.</param>
/// <param name="name">The name.</param>
- /// <param name="category">The category.</param>
+ /// <param name="parentId">The parent identifier.</param>
/// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{Folder}.</returns>
- Task<UserView> GetNamedView(string name, string category, string viewType, string sortName, CancellationToken cancellationToken);
+ /// <returns>Task&lt;UserView&gt;.</returns>
+ Task<UserView> GetSpecialFolder(User user,
+ string name,
+ string parentId,
+ string viewType,
+ string sortName,
+ CancellationToken cancellationToken);
/// <summary>
/// Gets the named view.
@@ -343,6 +349,9 @@ namespace MediaBrowser.Controller.Library
/// <param name="sortName">Name of the sort.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;UserView&gt;.</returns>
- Task<UserView> GetNamedView(string name, string viewType, string sortName, CancellationToken cancellationToken);
+ Task<UserView> GetNamedView(string name,
+ string viewType,
+ string sortName,
+ CancellationToken cancellationToken);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs
index 5b6189837..12dc9c0b2 100644
--- a/MediaBrowser.Controller/Library/IUserViewManager.cs
+++ b/MediaBrowser.Controller/Library/IUserViewManager.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Library
{
Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken);
- Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken);
+ Task<UserView> GetUserView(string type, string sortName, CancellationToken cancellationToken);
Task<UserView> GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken);
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 232f36ebf..5e90b9938 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -369,6 +369,13 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
<PreBuildEvent>
</PreBuildEvent>
</PropertyGroup>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 8c970eac2..259459d04 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -91,6 +91,13 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index 627b7a266..df34b295d 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -1124,6 +1124,13 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\portable\" /y /d /r /i
)</PostBuildEvent>
</PropertyGroup>
<Import Project="Fody.targets" />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs
index aaeeafa53..9cd7999b0 100644
--- a/MediaBrowser.Model/Entities/CollectionType.cs
+++ b/MediaBrowser.Model/Entities/CollectionType.cs
@@ -25,7 +25,10 @@
public const string LiveTv = "livetv";
public const string Playlists = "playlists";
public const string Folders = "folders";
+ }
+ public static class SpecialFolder
+ {
public const string LiveTvNowPlaying = "LiveTvNowPlaying";
public const string LiveTvChannels = "LiveTvChannels";
public const string LiveTvRecordingGroups = "LiveTvRecordingGroups";
@@ -44,7 +47,7 @@
public const string MovieCollections = "MovieCollections";
public const string MovieFavorites = "MovieFavorites";
public const string MovieGenres = "MovieGenres";
-
+
public const string LatestGames = "LatestGames";
public const string RecentlyPlayedGames = "RecentlyPlayedGames";
public const string GameSystems = "GameSystems";
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 24bf2c93c..14309ef18 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -428,6 +428,13 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i
)</PostBuildEvent>
</PropertyGroup>
<Import Project="Fody.targets" />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Providers/FolderImages/DefaultImageProvider.cs b/MediaBrowser.Providers/FolderImages/DefaultImageProvider.cs
index d1ff4f92c..8ddbbba7d 100644
--- a/MediaBrowser.Providers/FolderImages/DefaultImageProvider.cs
+++ b/MediaBrowser.Providers/FolderImages/DefaultImageProvider.cs
@@ -112,7 +112,14 @@ namespace MediaBrowser.Providers.FolderImages
public bool Supports(IHasImages item)
{
- return item is UserView || item is ICollectionFolder;
+ var view = item as UserView;
+
+ if (view != null)
+ {
+ return !view.UserId.HasValue;
+ }
+
+ return item is ICollectionFolder;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
new file mode 100644
index 000000000..97ac78a82
--- /dev/null
+++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
@@ -0,0 +1,33 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Providers.Manager;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.Folders
+{
+ public class UserViewMetadataService : MetadataService<UserView, ItemLookupInfo>
+ {
+ public UserViewMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+ {
+ }
+
+ /// <summary>
+ /// Merges the specified source.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="lockedFields">The locked fields.</param>
+ /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
+ /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
+ protected override void MergeData(UserView source, UserView target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index e89c282ca..6bb3a2dd3 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -468,7 +468,7 @@ namespace MediaBrowser.Providers.Manager
// Give it a dummy path just so that it looks like a file system item
var dummy = new T()
{
- Path = "C:\\",
+ Path = BaseItem.GetInternalMetadataPathForId(Guid.NewGuid()),
// Dummy this up to fool the local trailer check
Parent = new Folder()
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 0740cee29..5f897fa78 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -85,6 +85,7 @@
<Compile Include="FolderImages\DefaultImageProvider.cs" />
<Compile Include="Folders\FolderMetadataService.cs" />
<Compile Include="Channels\AudioChannelItemMetadataService.cs" />
+ <Compile Include="Folders\UserViewMetadataService.cs" />
<Compile Include="GameGenres\GameGenreMetadataService.cs" />
<Compile Include="Channels\VideoChannelItemMetadataService.cs" />
<Compile Include="Games\GameMetadataService.cs" />
@@ -216,6 +217,13 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 91e2b3697..5fbdf9815 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -530,8 +530,8 @@ namespace MediaBrowser.Server.Implementations.Library
return item;
}
- public BaseItem ResolvePath(FileSystemInfo fileInfo,
- Folder parent = null,
+ public BaseItem ResolvePath(FileSystemInfo fileInfo,
+ Folder parent = null,
string collectionType = null)
{
return ResolvePath(fileInfo, new DirectoryService(_logger), parent, collectionType);
@@ -1190,7 +1190,7 @@ namespace MediaBrowser.Server.Implementations.Library
return item;
}
-
+
/// <summary>
/// Gets the intros.
/// </summary>
@@ -1508,27 +1508,22 @@ namespace MediaBrowser.Server.Implementations.Library
return collectionTypes.Count == 1 ? collectionTypes[0] : null;
}
- public Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken)
- {
- return GetNamedView(name, null, type, sortName, cancellationToken);
- }
-
- public async Task<UserView> GetNamedView(string name, string category, string type, string sortName, CancellationToken cancellationToken)
+ public async Task<UserView> GetNamedView(string name,
+ string type,
+ string sortName,
+ CancellationToken cancellationToken)
{
var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
"views");
- if (!string.IsNullOrWhiteSpace(category))
- {
- path = Path.Combine(path, _fileSystem.GetValidFilename(category));
- }
-
path = Path.Combine(path, _fileSystem.GetValidFilename(type));
var id = (path + "_namedview_" + name).GetMBId(typeof(UserView));
var item = GetItemById(id) as UserView;
+ var refresh = false;
+
if (item == null ||
!string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
{
@@ -1546,7 +1541,89 @@ namespace MediaBrowser.Server.Implementations.Library
await CreateItem(item, cancellationToken).ConfigureAwait(false);
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ refresh = true;
+ }
+
+ if (!refresh && item != null)
+ {
+ refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24;
+ }
+
+ if (refresh)
+ {
+ await item.RefreshMetadata(new MetadataRefreshOptions
+ {
+ ForceSave = true
+
+ }, cancellationToken).ConfigureAwait(false);
+ }
+
+ return item;
+ }
+
+ public async Task<UserView> GetSpecialFolder(User user,
+ string name,
+ string parentId,
+ string viewType,
+ string sortName,
+ CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException("name");
+ }
+
+ if (string.IsNullOrWhiteSpace(parentId))
+ {
+ throw new ArgumentNullException("parentId");
+ }
+
+ if (string.IsNullOrWhiteSpace(viewType))
+ {
+ throw new ArgumentNullException("viewType");
+ }
+
+ var id = ("7_namedview_" + name + user.Id.ToString("N") + parentId).GetMBId(typeof(UserView));
+
+ var path = BaseItem.GetInternalMetadataPathForId(id);
+
+ var item = GetItemById(id) as UserView;
+
+ var refresh = false;
+
+ if (item == null)
+ {
+ Directory.CreateDirectory(path);
+
+ item = new UserView
+ {
+ Path = path,
+ Id = id,
+ DateCreated = DateTime.UtcNow,
+ Name = name,
+ ViewType = viewType,
+ ForcedSortName = sortName,
+ UserId = user.Id,
+ ParentId = new Guid(parentId)
+ };
+
+ await CreateItem(item, cancellationToken).ConfigureAwait(false);
+
+ refresh = true;
+ }
+
+ if (!refresh && item != null)
+ {
+ refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24;
+ }
+
+ if (refresh)
+ {
+ await item.RefreshMetadata(new MetadataRefreshOptions
+ {
+ ForceSave = true
+
+ }, cancellationToken).ConfigureAwait(false);
}
return item;
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index 768c29ce0..d2d435414 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -68,24 +68,24 @@ namespace MediaBrowser.Server.Implementations.Library
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) ||
foldersWithViewTypes.Any(i => string.IsNullOrWhiteSpace(i.CollectionType)))
{
- list.Add(await GetUserView(CollectionType.TvShows, user, string.Empty, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.TvShows, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) ||
foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)))
{
- list.Add(await GetUserView(CollectionType.Music, user, string.Empty, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Music, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) ||
foldersWithViewTypes.Any(i => string.IsNullOrWhiteSpace(i.CollectionType)))
{
- list.Add(await GetUserView(CollectionType.Movies, user, string.Empty, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Movies, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase)))
{
- list.Add(await GetUserView(CollectionType.Games, user, string.Empty, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Games, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (user.Configuration.DisplayCollectionsView &&
@@ -93,7 +93,7 @@ namespace MediaBrowser.Server.Implementations.Library
.Except(standaloneFolders)
.SelectMany(i => i.GetRecursiveChildren(user, false)).OfType<BoxSet>().Any())
{
- list.Add(await GetUserView(CollectionType.BoxSets, user, string.Empty, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.BoxSets, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)))
@@ -103,7 +103,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (user.Configuration.DisplayFoldersView)
{
- list.Add(await GetUserView(CollectionType.Folders, user, "zz_" + CollectionType.Folders, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Folders, "zz_" + CollectionType.Folders, cancellationToken).ConfigureAwait(false));
}
if (query.IncludeExternalContent)
@@ -148,16 +148,18 @@ namespace MediaBrowser.Server.Implementations.Library
.ThenBy(i => i.SortName);
}
- public Task<UserView> GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken)
+ public Task<UserView> GetUserView(string parentId, string type, User user, string sortName, CancellationToken cancellationToken)
{
var name = _localizationManager.GetLocalizedString("ViewType" + type);
- return _libraryManager.GetNamedView(name, category, type, sortName, cancellationToken);
+ return _libraryManager.GetSpecialFolder(user, name, parentId, type, sortName, cancellationToken);
}
- public Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken)
+ public Task<UserView> GetUserView(string type, string sortName, CancellationToken cancellationToken)
{
- return GetUserView(null, type, user, sortName, cancellationToken);
+ var name = _localizationManager.GetLocalizedString("ViewType" + type);
+
+ return _libraryManager.GetNamedView(name, type, sortName, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 31ac4806a..4e2913540 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -830,7 +830,7 @@
"HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client",
"ButtonDismiss": "Dismiss",
"ButtonTakeTheTour": "Take the tour",
- "ButtonEditOtherUserPreferences": "Edit this user's profile and personal preferences.",
+ "ButtonEditOtherUserPreferences": "Edit this user's profile, password and personal preferences.",
"LabelChannelStreamQuality": "Preferred internet stream quality:",
"LabelChannelStreamQualityHelp": "In a low bandwidth environment, limiting quality can help ensure a smooth streaming experience.",
"OptionBestAvailableStreamQuality": "Best available",
@@ -1260,5 +1260,8 @@
"OptionDisableUserPreferences": "Disable access to user preferences",
"OptionDisableUserPreferencesHelp": "If enabled, only administrators will be able to configure user profile images, passwords, and language preferences.",
"HeaderSelectServer": "Select Server",
- "MessageNoServersAvailableToConnect": "No servers are available to connect to. If you've been invited to share a server, make sure to confirm it by clicking the link in the email."
+ "MessageNoServersAvailableToConnect": "No servers are available to connect to. If you've been invited to share a server, make sure to confirm it by clicking the link in the email.",
+ "TitleNewUser": "New User",
+ "ButtonConfigurePassword": "Configure Password",
+ "HeaderDashboardUserPassword": "User passwords are managed within each user's personal profile settings."
}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 38d5eb072..ee5528bac 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -226,6 +226,7 @@
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
<Compile Include="MediaEncoder\EncodingManager.cs" />
+ <Compile Include="Music\MusicDynamicImageProvider.cs" />
<Compile Include="News\NewsEntryPoint.cs" />
<Compile Include="News\NewsService.cs" />
<Compile Include="Notifications\CoreNotificationTypes.cs" />
@@ -240,6 +241,8 @@
<Compile Include="Persistence\SqliteProviderInfoRepository.cs" />
<Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" />
<Compile Include="Persistence\TypeMapper.cs" />
+ <Compile Include="Photos\BaseDynamicImageProvider.cs" />
+ <Compile Include="Photos\DynamicImageHelpers.cs" />
<Compile Include="Playlists\ManualPlaylistsFolder.cs" />
<Compile Include="Photos\PhotoAlbumImageProvider.cs" />
<Compile Include="Playlists\PlaylistImageProvider.cs" />
@@ -515,6 +518,13 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/MediaBrowser.Server.Implementations/Music/MusicDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Music/MusicDynamicImageProvider.cs
new file mode 100644
index 000000000..846357529
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Music/MusicDynamicImageProvider.cs
@@ -0,0 +1,90 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Server.Implementations.Photos;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Music
+{
+ public class MusicDynamicImageProvider : BaseDynamicImageProvider<UserView>, ICustomMetadataProvider<UserView>
+ {
+ private readonly IUserManager _userManager;
+
+ public MusicDynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IUserManager userManager)
+ : base(fileSystem, providerManager)
+ {
+ _userManager = userManager;
+ }
+
+ protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
+ {
+ var view = (UserView)item;
+
+ if (!view.UserId.HasValue)
+ {
+ return new List<BaseItem>();
+ }
+
+ var result = await view.GetItems(new InternalItemsQuery
+ {
+ User = _userManager.GetUserById(view.UserId.Value)
+
+ }).ConfigureAwait(false);
+
+ return GetFinalItems(result.Items.Where(i => i.HasImage(ImageType.Primary)).ToList());
+ }
+
+ protected override bool Supports(IHasImages item)
+ {
+ var view = item as UserView;
+
+ if (view != null && view.UserId.HasValue)
+ {
+ var supported = new[]
+ {
+ SpecialFolder.TvFavoriteEpisodes,
+ SpecialFolder.TvFavoriteSeries,
+ SpecialFolder.TvGenres,
+ SpecialFolder.TvLatest,
+ SpecialFolder.TvNextUp,
+ SpecialFolder.TvResume,
+ SpecialFolder.TvShowSeries,
+
+ SpecialFolder.MovieCollections,
+ SpecialFolder.MovieFavorites,
+ SpecialFolder.MovieGenres,
+ SpecialFolder.MovieLatest,
+ SpecialFolder.MovieMovies,
+ SpecialFolder.MovieResume,
+
+ SpecialFolder.GameFavorites,
+ SpecialFolder.GameGenres,
+ SpecialFolder.GameSystems,
+ SpecialFolder.LatestGames,
+ SpecialFolder.RecentlyPlayedGames,
+
+ SpecialFolder.MusicArtists,
+ SpecialFolder.MusicAlbumArtists,
+ SpecialFolder.MusicAlbums,
+ SpecialFolder.MusicGenres,
+ SpecialFolder.MusicLatest,
+ SpecialFolder.MusicSongs,
+ SpecialFolder.MusicFavorites,
+ SpecialFolder.MusicFavoriteArtists,
+ SpecialFolder.MusicFavoriteAlbums,
+ SpecialFolder.MusicFavoriteSongs
+ };
+
+ return supported.Contains(view.ViewType, StringComparer.OrdinalIgnoreCase) &&
+ _userManager.GetUserById(view.UserId.Value) != null;
+ }
+
+ return false;
+ }
+ }
+}
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);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs
index 54c100120..aa10e6635 100644
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -1,123 +1,26 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
+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 MediaBrowser.Server.Implementations.Photos;
using MoreLinq;
-using System;
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;
namespace MediaBrowser.Server.Implementations.Playlists
{
- public class PlaylistImageProvider : ICustomMetadataProvider<Playlist>, IHasChangeMonitor
+ public class PlaylistImageProvider : BaseDynamicImageProvider<Playlist>, ICustomMetadataProvider<Playlist>
{
- private readonly IFileSystem _fileSystem;
- private readonly IProviderManager _provider;
-
- public PlaylistImageProvider(IFileSystem fileSystem, IProviderManager provider)
- {
- _fileSystem = fileSystem;
- _provider = provider;
- }
-
- public async Task<ItemUpdateType> FetchAsync(Playlist 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;
- }
-
- 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)
+ public PlaylistImageProvider(IFileSystem fileSystem, IProviderManager providerManager) : base(fileSystem, providerManager)
{
- return (Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray())).GetMD5().ToString("N");
}
- private List<BaseItem> GetItemsWithImages(IHasImages item)
+ protected override Task<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()
@@ -157,189 +60,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
.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
- {
- get { return "Dynamic Image Provider"; }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
- {
- var items = GetItemsWithImages(item);
- var cacheKey = GetConfigurationCacheKey(items);
-
- return HasChanged(item, ImageType.Primary, cacheKey) || HasChanged(item, ImageType.Thumb, cacheKey);
+ return Task.FromResult(GetFinalItems(items));
}
}
}
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
index de9d5b01e..8cf64517d 100644
--- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
+++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
@@ -189,4 +189,11 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
</Project> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 555e46099..a8917356e 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -266,4 +266,11 @@ del "$(SolutionDir)..\Deploy\MBServer.zip"
</GetAssemblyIdentity>
<Exec Command="copy &quot;$(SolutionDir)..\Deploy\MBServer.zip&quot; &quot;$(SolutionDir)..\Deploy\MBServer_%(CurrentAssembly.Version).zip&quot; /y" Condition="'$(ConfigurationName)' == 'Release'" />
</Target>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
</Project> \ No newline at end of file
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index e835f6d49..0a6e48ea9 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -458,7 +458,9 @@ namespace MediaBrowser.WebDashboard.Api
"tvstudios.js",
"tvupcoming.js",
"useredit.js",
+ "usernew.js",
"myprofile.js",
+ "userpassword.js",
"userprofilespage.js",
"userparentalcontrol.js",
"userlibraryaccess.js",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index b657acc65..2863f1a4a 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -89,6 +89,12 @@
<Content Include="dashboard-ui\scripts\selectserver.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\usernew.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\scripts\userpassword.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\selectserver.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1592,9 +1598,15 @@
<Content Include="dashboard-ui\userlibraryaccess.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\usernew.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\userparentalcontrol.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\userpassword.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\wizardservice.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -2285,6 +2297,13 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">