diff options
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<UserView>.</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<UserView>.</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 "$(SolutionDir)..\Deploy\MBServer.zip" "$(SolutionDir)..\Deploy\MBServer_%(CurrentAssembly.Version).zip" /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">
|
