aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Library
diff options
context:
space:
mode:
author7illusions <z@7illusions.com>2014-08-30 19:06:58 +0200
committer7illusions <z@7illusions.com>2014-08-30 19:06:58 +0200
commit66ad1699e22029b605e17735e8d9450285d8748a (patch)
treeffc92c88d24850b2f82b6b3a8bdd904a2ccc77a5 /MediaBrowser.Server.Implementations/Library
parent34bc54263e886aae777a3537dc50a6535b51330a (diff)
parent9d36f518182bc075c19d78084870f5115fa62d1e (diff)
Merge pull request #1 from MediaBrowser/master
Update to latest
Diffstat (limited to 'MediaBrowser.Server.Implementations/Library')
-rw-r--r--MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs19
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs19
-rw-r--r--MediaBrowser.Server.Implementations/Library/MusicManager.cs14
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs135
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs39
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs26
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs38
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs35
-rw-r--r--MediaBrowser.Server.Implementations/Library/SearchEngine.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserDataManager.cs37
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs129
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs97
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs103
17 files changed, 593 insertions, 166 deletions
diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index 17118f4b4..7b58dd7c4 100644
--- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -14,21 +14,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
- /// <summary>
- /// Any folder named in this list will be ignored - can be added to at runtime for extensibility
- /// </summary>
- private static readonly Dictionary<string, string> IgnoreFolders = new List<string>
- {
- "metadata",
- "ps3_update",
- "ps3_vprm",
- "extrafanart",
- "extrathumbs",
- ".actors",
- ".wd_tv"
-
- }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
-
private readonly IFileSystem _fileSystem;
public CoreResolutionIgnoreRule(IFileSystem fileSystem)
@@ -78,7 +63,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (args.IsDirectory)
{
// Ignore any folders in our list
- if (IgnoreFolders.ContainsKey(filename))
+ if (EntityResolutionHelper.IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
{
return true;
}
@@ -105,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (args.Parent != null)
{
// Don't resolve these into audio files
- if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(filename))
+ if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(filename))
{
return true;
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 64e0a6662..297d5e032 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -906,7 +906,7 @@ namespace MediaBrowser.Server.Implementations.Library
// Ensure the location is available.
Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
- return new PeopleValidator(this, _logger).ValidatePeople(cancellationToken, new MetadataRefreshOptions(), progress);
+ return new PeopleValidator(this, _logger, ConfigurationManager).ValidatePeople(cancellationToken, progress);
}
/// <summary>
@@ -1157,7 +1157,7 @@ namespace MediaBrowser.Server.Implementations.Library
private string GetCollectionType(string path)
{
return new DirectoryInfo(path).EnumerateFiles("*.collection", SearchOption.TopDirectoryOnly)
- .Select(i => Path.GetFileNameWithoutExtension(i.FullName))
+ .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
.FirstOrDefault();
}
@@ -1486,23 +1486,22 @@ namespace MediaBrowser.Server.Implementations.Library
public async Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken)
{
- var id = "namedview_3_" + name;
- var guid = id.GetMD5();
+ var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
+ "views",
+ _fileSystem.GetValidFilename(type));
- var item = GetItemById(guid) as UserView;
+ var id = (path + "_namedview_" + name).GetMBId(typeof(UserView));
+
+ var item = GetItemById(id) as UserView;
if (item == null)
{
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
- "views",
- _fileSystem.GetValidFilename(type));
-
Directory.CreateDirectory(Path.GetDirectoryName(path));
item = new UserView
{
Path = path,
- Id = guid,
+ Id = id,
DateCreated = DateTime.UtcNow,
Name = name,
ViewType = type,
diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs
index 9d5826454..7ffbab860 100644
--- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs
@@ -18,15 +18,20 @@ namespace MediaBrowser.Server.Implementations.Library
public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user)
{
- return GetInstantMixFromGenres(item.Genres, user);
+ var list = new List<Audio>
+ {
+ item
+ };
+
+ return list.Concat(GetInstantMixFromGenres(item.Genres, user));
}
public IEnumerable<Audio> GetInstantMixFromArtist(string name, User user)
{
var artist = _libraryManager.GetArtist(name);
- var genres = _libraryManager.RootFolder
- .RecursiveChildren
+ var genres = user.RootFolder
+ .GetRecursiveChildren(user)
.OfType<Audio>()
.Where(i => i.HasArtist(name))
.SelectMany(i => i.Genres)
@@ -39,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.Library
public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user)
{
var genres = item
- .RecursiveChildren
+ .GetRecursiveChildren(user, true)
.OfType<Audio>()
.SelectMany(i => i.Genres)
.Concat(item.Genres)
@@ -57,6 +62,7 @@ namespace MediaBrowser.Server.Implementations.Library
return inputItems
.OfType<Audio>()
.Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
+ .Where(i => i.Item2 > 0)
.OrderByDescending(i => i.Item2)
.ThenBy(i => Guid.NewGuid())
.Select(i => i.Item1)
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 28d476971..1f9dc56f9 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
@@ -6,6 +7,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
@@ -17,6 +19,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
{
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+
+ public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -45,17 +56,19 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
return null;
}
-
+
var collectionType = args.GetCollectionType();
+ var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music,
+ StringComparison.OrdinalIgnoreCase);
+
// If there's a collection type and it's not music, don't allow it.
- if (!string.IsNullOrEmpty(collectionType) &&
- !string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+ if (!isMusicMediaFolder)
{
return null;
}
-
- return IsMusicAlbum(args) ? new MusicAlbum() : null;
+
+ return IsMusicAlbum(args, isMusicMediaFolder) ? new MusicAlbum() : null;
}
@@ -63,50 +76,29 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// Determine if the supplied file data points to a music album
/// </summary>
/// <param name="path">The path.</param>
+ /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param>
/// <param name="directoryService">The directory service.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
/// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
- public static bool IsMusicAlbum(string path, IDirectoryService directoryService)
+ public static bool IsMusicAlbum(string path, bool isMusicMediaFolder, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
{
- // If list contains at least 2 audio files or at least one and no video files consider it to contain music
- var foundAudio = 0;
-
- foreach (var file in directoryService.GetFiles(path))
- {
- var fullName = file.FullName;
-
- if (EntityResolutionHelper.IsAudioFile(fullName))
- {
- // Don't resolve these into audio files
- if (string.Equals(Path.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(fullName))
- {
- continue;
- }
-
- foundAudio++;
- }
- if (foundAudio >= 2)
- {
- return true;
- }
- if (EntityResolutionHelper.IsVideoFile(fullName)) return false;
- }
-
- // or a single audio file and no video files
- return foundAudio > 0;
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), isMusicMediaFolder, true, directoryService, logger, fileSystem);
}
/// <summary>
/// Determine if the supplied resolve args should be considered a music album
/// </summary>
/// <param name="args">The args.</param>
+ /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param>
/// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
- public static bool IsMusicAlbum(ItemResolveArgs args)
+ private bool IsMusicAlbum(ItemResolveArgs args, bool isMusicMediaFolder)
{
// Args points to an album if parent is an Artist folder or it directly contains music
if (args.IsDirectory)
{
//if (args.Parent is MusicArtist) return true; //saves us from testing children twice
- if (ContainsMusic(args.FileSystemChildren)) return true;
+ if (ContainsMusic(args.FileSystemChildren, isMusicMediaFolder, true, args.DirectoryService, _logger, _fileSystem)) return true;
}
return false;
@@ -116,27 +108,86 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// Determine if the supplied list contains what we should consider music
/// </summary>
/// <param name="list">The list.</param>
+ /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param>
+ /// <param name="allowSubfolders">if set to <c>true</c> [allow subfolders].</param>
+ /// <param name="directoryService">The directory service.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
/// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
- private static bool ContainsMusic(IEnumerable<FileSystemInfo> list)
+ private static bool ContainsMusic(IEnumerable<FileSystemInfo> list,
+ bool isMusicMediaFolder,
+ bool allowSubfolders,
+ IDirectoryService directoryService,
+ ILogger logger,
+ IFileSystem fileSystem)
{
// If list contains at least 2 audio files or at least one and no video files consider it to contain music
var foundAudio = 0;
- foreach (var file in list)
+ var discSubfolderCount = 0;
+
+ foreach (var fileSystemInfo in list)
{
- var fullName = file.FullName;
+ if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ {
+ if (isMusicMediaFolder && allowSubfolders && IsAlbumSubfolder(fileSystemInfo, true, directoryService, logger, fileSystem))
+ {
+ discSubfolderCount++;
+ }
+ if (!IsAdditionalSubfolderAllowed(fileSystemInfo))
+ {
+ return false;
+ }
+ }
+
+ var fullName = fileSystemInfo.FullName;
+
+ if (EntityResolutionHelper.IsAudioFile(fullName))
+ {
+ // Don't resolve these into audio files
+ if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename))
+ {
+ continue;
+ }
+
+ foundAudio++;
+ }
+ else if (EntityResolutionHelper.IsVideoFile(fullName)) return false;
+ else if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) return false;
- if (EntityResolutionHelper.IsAudioFile(fullName)) foundAudio++;
if (foundAudio >= 2)
{
return true;
}
- if (EntityResolutionHelper.IsVideoFile(fullName)) return false;
- if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) return false;
}
// or a single audio file and no video files
- return foundAudio > 0;
+ return foundAudio > 0 || discSubfolderCount > 0;
+ }
+
+ private static bool IsAlbumSubfolder(FileSystemInfo directory, bool isMusicMediaFolder, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
+ {
+ var path = directory.FullName;
+
+ if (IsMultiDiscFolder(path))
+ {
+ logger.Debug("Found multi-disc folder: " + path);
+
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), isMusicMediaFolder, false, directoryService, logger, fileSystem);
+ }
+
+ return false;
+ }
+
+ public static bool IsMultiDiscFolder(string path)
+ {
+ return EntityResolutionHelper.IsMultiDiscAlbumFolder(path);
+ }
+
+ private static bool IsAdditionalSubfolderAllowed(FileSystemInfo directory)
+ {
+ // Resolver will ignore them based on rules engine
+ return true;
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 4fa97fc9d..2417d5dcb 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -1,9 +1,11 @@
-using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Linq;
@@ -15,6 +17,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+
+ public MusicArtistResolver(ILogger logger, IFileSystem fileSystem)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -51,9 +62,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
var collectionType = args.GetCollectionType();
+ var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music,
+ StringComparison.OrdinalIgnoreCase);
+
// If there's a collection type and it's not music, it can't be a series
- if (!string.IsNullOrEmpty(collectionType) &&
- !string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+ if (!isMusicMediaFolder)
{
return null;
}
@@ -61,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
var directoryService = args.DirectoryService;
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
+ return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, isMusicMediaFolder, directoryService, _logger, _fileSystem)) ? new MusicArtist() : null;
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
index 594277ef7..166465f72 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using System;
@@ -12,6 +13,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
/// </summary>
public class FolderResolver : FolderResolver<Folder>
{
+ private readonly IFileSystem _fileSystem;
+
+ public FolderResolver(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -69,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
}
})
- .Select(i => Path.GetFileNameWithoutExtension(i.FullName))
+ .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
.FirstOrDefault();
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
index 10ee3586d..b483f7c42 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using System;
@@ -11,6 +12,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
/// </summary>
public class LocalTrailerResolver : BaseVideoResolver<Trailer>
{
+ private readonly IFileSystem _fileSystem;
+
+ public LocalTrailerResolver(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -33,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
}
// Support xbmc local trailer convention, but only when looking for local trailers (hence the parent == null check)
- if (args.Parent == null && Path.GetFileNameWithoutExtension(args.Path).EndsWith(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase))
+ if (args.Parent == null && _fileSystem.GetFileNameWithoutExtension(args.Path).EndsWith(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase))
{
return base.Resolve(args);
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 905e6e676..215cff22f 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@@ -6,11 +7,11 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using MediaBrowser.Model.Logging;
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
{
@@ -22,12 +23,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
private readonly IServerApplicationPaths _applicationPaths;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
- public MovieResolver(IServerApplicationPaths appPaths, ILibraryManager libraryManager, ILogger logger)
+ public MovieResolver(IServerApplicationPaths appPaths, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
{
_applicationPaths = appPaths;
_libraryManager = libraryManager;
_logger = logger;
+ _fileSystem = fileSystem;
}
/// <summary>
@@ -79,29 +82,29 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
{
if (string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<Trailer>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService, false, false);
+ return FindMovie<Trailer>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, false, false);
}
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService, false, false);
+ return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, false, false);
}
if (string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<AdultVideo>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService, true, false);
+ return FindMovie<AdultVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, false);
}
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService, true, false);
+ return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, false);
}
if (string.IsNullOrEmpty(collectionType) ||
string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService, true, true);
+ return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, true);
}
return null;
@@ -187,7 +190,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
/// <param name="directoryService">The directory service.</param>
/// <param name="supportMultiFileItems">if set to <c>true</c> [support multi file items].</param>
/// <returns>Movie.</returns>
- private T FindMovie<T>(string path, Folder parent, IEnumerable<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, bool supportMultiFileItems, bool supportsMultipleSources)
+ private T FindMovie<T>(string path, Folder parent, List<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, bool supportMultiFileItems, bool supportsMultipleSources)
where T : Video, new()
{
var movies = new List<T>();
@@ -407,7 +410,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(filenamePrefix))
{
- if (sortedMovies.All(i => Path.GetFileNameWithoutExtension(i.Path).StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase)))
+ if (sortedMovies.All(i => _fileSystem.GetFileNameWithoutExtension(i.Path).StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase)))
{
firstMovie.HasLocalAlternateVersions = true;
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
new file mode 100644
index 000000000..2fcfd7086
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
@@ -0,0 +1,39 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ public class PhotoAlbumResolver : FolderResolver<PhotoAlbum>
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Trailer.</returns>
+ protected override PhotoAlbum Resolve(ItemResolveArgs args)
+ {
+ // Must be an image file within a photo collection
+ if (!args.IsRoot && args.IsDirectory && string.Equals(args.GetCollectionType(), CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
+ {
+ if (HasPhotos(args))
+ {
+ return new PhotoAlbum
+ {
+ Path = args.Path
+ };
+ }
+ }
+
+ return null;
+ }
+
+ private static bool HasPhotos(ItemResolveArgs args)
+ {
+ return args.FileSystemChildren.Any(i => ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory) && PhotoResolver.IsImageFile(i.FullName));
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index cba7aba9a..60e7edfdd 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -1,24 +1,14 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
using System;
+using System.IO;
using System.Linq;
namespace MediaBrowser.Server.Implementations.Library.Resolvers
{
public class PhotoResolver : ItemResolver<Photo>
{
- private readonly IServerApplicationPaths _applicationPaths;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PhotoResolver" /> class.
- /// </summary>
- /// <param name="applicationPaths">The application paths.</param>
- public PhotoResolver(IServerApplicationPaths applicationPaths)
- {
- _applicationPaths = applicationPaths;
- }
-
/// <summary>
/// Resolves the specified args.
/// </summary>
@@ -27,7 +17,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
protected override Photo Resolve(ItemResolveArgs args)
{
// Must be an image file within a photo collection
- if (!args.IsDirectory && IsImageFile(args.Path) && string.Equals(args.GetCollectionType(), "photos", StringComparison.OrdinalIgnoreCase))
+ if (!args.IsDirectory && IsImageFile(args.Path) && string.Equals(args.GetCollectionType(), CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
{
return new Photo
{
@@ -39,10 +29,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
}
protected static string[] ImageExtensions = { ".tiff", ".jpeg", ".jpg", ".png", ".aiff" };
- protected bool IsImageFile(string path)
+ internal static bool IsImageFile(string path)
{
- return !path.EndsWith("folder.jpg", StringComparison.OrdinalIgnoreCase)
- && ImageExtensions.Any(p => path.EndsWith(p, StringComparison.OrdinalIgnoreCase));
+ var filename = Path.GetFileName(path);
+
+ return !string.Equals(filename, "folder.jpg", StringComparison.OrdinalIgnoreCase)
+ && ImageExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
new file mode 100644
index 000000000..7eff53ce1
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -0,0 +1,38 @@
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Playlists;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ public class PlaylistResolver : FolderResolver<Playlist>
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>BoxSet.</returns>
+ protected override Playlist Resolve(ItemResolveArgs args)
+ {
+ // It's a boxset if all of the following conditions are met:
+ // Is a Directory
+ // Contains [playlist] in the path
+ if (args.IsDirectory)
+ {
+ var filename = Path.GetFileName(args.Path);
+
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+
+ if (filename.IndexOf("[playlist]", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return new Playlist { Path = args.Path };
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 6b376d3b4..d3aad582a 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -1,9 +1,11 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using System;
using System.IO;
@@ -14,6 +16,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class SeriesResolver : FolderResolver<Series>
{
+ private readonly IFileSystem _fileSystem;
+ private readonly ILogger _logger;
+
+ public SeriesResolver(IFileSystem fileSystem, ILogger logger)
+ {
+ _fileSystem = fileSystem;
+ _logger = logger;
+ }
+
/// <summary>
/// Gets the priority.
/// </summary>
@@ -49,32 +60,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
var collectionType = args.GetCollectionType();
+ var isTvShowsFolder = string.Equals(collectionType, CollectionType.TvShows,
+ StringComparison.OrdinalIgnoreCase);
+
// If there's a collection type and it's not tv, it can't be a series
if (!string.IsNullOrEmpty(collectionType) &&
- !string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) &&
+ !isTvShowsFolder &&
!string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
{
return null;
}
-
- // It's a Series if any of the following conditions are met:
- // series.xml exists
- // [tvdbid= is present in the path
- // TVUtils.IsSeriesFolder returns true
- var filename = Path.GetFileName(args.Path);
-
- if (string.IsNullOrEmpty(filename))
- {
- return null;
- }
- // Without these movies that have the name season in them could cause the parent folder to be resolved as a series
- if (filename.IndexOf("[tmdbid=", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return null;
- }
-
- if (args.ContainsMetaFileByName("series.xml") || filename.IndexOf("[tvdbid=", StringComparison.OrdinalIgnoreCase) != -1 || TVUtils.IsSeriesFolder(args.Path, collectionType == CollectionType.TvShows, args.FileSystemChildren, args.DirectoryService))
+ if (TVUtils.IsSeriesFolder(args.Path, isTvShowsFolder, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger))
{
return new Series();
}
diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
index 4c65fad68..6faa72b81 100644
--- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
+++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
var user = _userManager.GetUserById(new Guid(query.UserId));
- inputItems = user.RootFolder.GetRecursiveChildren(user, null);
+ inputItems = user.RootFolder.GetRecursiveChildren(user, true);
}
diff --git a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs
index 79f126511..d3030f31f 100644
--- a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
@@ -125,5 +126,41 @@ namespace MediaBrowser.Server.Implementations.Library
{
return userId + key;
}
+
+ public UserItemDataDto GetUserDataDto(IHasUserData item, User user)
+ {
+ var userData = GetUserData(user.Id, item.GetUserDataKey());
+ var dto = GetUserItemDataDto(userData);
+
+ item.FillUserDataDtoValues(dto, userData, user);
+
+ return dto;
+ }
+
+ /// <summary>
+ /// Converts a UserItemData to a DTOUserItemData
+ /// </summary>
+ /// <param name="data">The data.</param>
+ /// <returns>DtoUserItemData.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ private UserItemDataDto GetUserItemDataDto(UserItemData data)
+ {
+ if (data == null)
+ {
+ throw new ArgumentNullException("data");
+ }
+
+ return new UserItemDataDto
+ {
+ IsFavorite = data.IsFavorite,
+ Likes = data.Likes,
+ PlaybackPositionTicks = data.PlaybackPositionTicks,
+ PlayCount = data.PlayCount,
+ Rating = data.Rating,
+ Played = data.Played,
+ LastPlayedDate = data.LastPlayedDate,
+ Key = data.Key
+ };
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index f9d7479ce..94cc61240 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -1,14 +1,20 @@
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Server.Implementations.Security;
using System;
using System.Collections.Generic;
using System.IO;
@@ -47,20 +53,29 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
/// <value>The user repository.</value>
private IUserRepository UserRepository { get; set; }
+ public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
private readonly IXmlSerializer _xmlSerializer;
+ private readonly INetworkManager _networkManager;
+
+ private readonly Func<IImageProcessor> _imageProcessorFactory;
+ private readonly Func<IDtoService> _dtoServiceFactory;
+
/// <summary>
/// Initializes a new instance of the <see cref="UserManager" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userRepository">The user repository.</param>
- public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer)
+ public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory)
{
_logger = logger;
UserRepository = userRepository;
_xmlSerializer = xmlSerializer;
+ _networkManager = networkManager;
+ _imageProcessorFactory = imageProcessorFactory;
+ _dtoServiceFactory = dtoServiceFactory;
ConfigurationManager = configurationManager;
Users = new List<User>();
}
@@ -118,28 +133,26 @@ namespace MediaBrowser.Server.Implementations.Library
Users = await LoadUsers().ConfigureAwait(false);
}
- /// <summary>
- /// Authenticates a User and returns a result indicating whether or not it succeeded
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="password">The password.</param>
- /// <returns>Task{System.Boolean}.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- public async Task<bool> AuthenticateUser(User user, string password)
+ public async Task<bool> AuthenticateUser(string username, string password, string remoteEndPoint)
{
- if (user == null)
+ if (string.IsNullOrWhiteSpace(username))
{
- throw new ArgumentNullException("user");
+ throw new ArgumentNullException("username");
}
+ var user = Users.First(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
+
if (user.Configuration.IsDisabled)
{
- throw new UnauthorizedAccessException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
+ throw new AuthenticationException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
}
- var existingPasswordString = string.IsNullOrEmpty(user.Password) ? GetSha1String(string.Empty) : user.Password;
+ var success = string.Equals(GetPasswordHash(user), password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
- var success = string.Equals(existingPasswordString, password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+ {
+ success = string.Equals(GetLocalPasswordHash(user), password.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ }
// Update LastActivityDate and LastLoginDate, then save
if (success)
@@ -153,6 +166,25 @@ namespace MediaBrowser.Server.Implementations.Library
return success;
}
+ private string GetPasswordHash(User user)
+ {
+ return string.IsNullOrEmpty(user.Password)
+ ? GetSha1String(string.Empty)
+ : user.Password;
+ }
+
+ private string GetLocalPasswordHash(User user)
+ {
+ return string.IsNullOrEmpty(user.LocalPassword)
+ ? GetSha1String(string.Empty)
+ : user.LocalPassword;
+ }
+
+ private bool IsPasswordEmpty(string passwordHash)
+ {
+ return string.Equals(passwordHash, GetSha1String(string.Empty), StringComparison.OrdinalIgnoreCase);
+ }
+
/// <summary>
/// Gets the sha1 string.
/// </summary>
@@ -192,6 +224,65 @@ namespace MediaBrowser.Server.Implementations.Library
return users;
}
+ public UserDto GetUserDto(User user, string remoteEndPoint = null)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException("user");
+ }
+
+ var passwordHash = GetPasswordHash(user);
+
+ var hasConfiguredDefaultPassword = !IsPasswordEmpty(passwordHash);
+
+ var hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
+ !IsPasswordEmpty(GetLocalPasswordHash(user)) :
+ hasConfiguredDefaultPassword;
+
+ var dto = new UserDto
+ {
+ Id = user.Id.ToString("N"),
+ Name = user.Name,
+ HasPassword = hasPassword,
+ HasConfiguredPassword = hasConfiguredDefaultPassword,
+ LastActivityDate = user.LastActivityDate,
+ LastLoginDate = user.LastLoginDate,
+ Configuration = user.Configuration
+ };
+
+ var image = user.GetImageInfo(ImageType.Primary, 0);
+
+ if (image != null)
+ {
+ dto.PrimaryImageTag = GetImageCacheTag(user, image);
+
+ try
+ {
+ _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
+ }
+ catch (Exception ex)
+ {
+ // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
+ _logger.ErrorException("Error generating PrimaryImageAspectRatio for {0}", ex, user.Name);
+ }
+ }
+
+ return dto;
+ }
+
+ private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
+ {
+ try
+ {
+ return _imageProcessorFactory().GetImageCacheTag(item, image);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting {0} image info for {1}", ex, image.Type, image.Path);
+ return null;
+ }
+ }
+
/// <summary>
/// Refreshes metadata for each user
/// </summary>
@@ -278,7 +369,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <exception cref="System.ArgumentException"></exception>
public async Task<User> CreateUser(string name)
{
- if (string.IsNullOrEmpty(name))
+ if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("name");
}
@@ -386,16 +477,18 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="user">The user.</param>
/// <param name="newPassword">The new password.</param>
/// <returns>Task.</returns>
- public Task ChangePassword(User user, string newPassword)
+ public async Task ChangePassword(User user, string newPassword)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
- user.Password = string.IsNullOrEmpty(newPassword) ? string.Empty : GetSha1String(newPassword);
+ user.Password = string.IsNullOrEmpty(newPassword) ? GetSha1String(string.Empty) : GetSha1String(newPassword);
+
+ await UpdateUser(user).ConfigureAwait(false);
- return UpdateUser(user);
+ EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index fc4b9eb4c..63aa3764c 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -7,11 +9,14 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
+using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -27,8 +32,10 @@ namespace MediaBrowser.Server.Implementations.Library
private readonly IChannelManager _channelManager;
private readonly ILiveTvManager _liveTvManager;
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly IPlaylistManager _playlists;
- public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IFileSystem fileSystem, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager)
+ public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IFileSystem fileSystem, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager, IServerApplicationPaths appPaths, IPlaylistManager playlists)
{
_libraryManager = libraryManager;
_localizationManager = localizationManager;
@@ -36,6 +43,8 @@ namespace MediaBrowser.Server.Implementations.Library
_userManager = userManager;
_channelManager = channelManager;
_liveTvManager = liveTvManager;
+ _appPaths = appPaths;
+ _playlists = playlists;
}
public async Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken)
@@ -62,40 +71,58 @@ namespace MediaBrowser.Server.Implementations.Library
if (recursiveChildren.OfType<Series>().Any())
{
- list.Add(await GetUserView(CollectionType.TvShows, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.TvShows, user, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (recursiveChildren.OfType<MusicAlbum>().Any() ||
recursiveChildren.OfType<MusicVideo>().Any())
{
- list.Add(await GetUserView(CollectionType.Music, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Music, user, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (recursiveChildren.OfType<Movie>().Any())
{
- list.Add(await GetUserView(CollectionType.Movies, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Movies, user, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (recursiveChildren.OfType<Game>().Any())
{
- list.Add(await GetUserView(CollectionType.Games, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.Games, user, string.Empty, cancellationToken).ConfigureAwait(false));
}
- if (recursiveChildren.OfType<BoxSet>().Any())
+ if (user.Configuration.DisplayCollectionsView &&
+ recursiveChildren.OfType<BoxSet>().Any())
{
- list.Add(await GetUserView(CollectionType.BoxSets, user, cancellationToken).ConfigureAwait(false));
+ list.Add(await GetUserView(CollectionType.BoxSets, user, string.Empty, cancellationToken).ConfigureAwait(false));
+ }
+
+ if (recursiveChildren.OfType<Playlist>().Any())
+ {
+ list.Add(_playlists.GetPlaylistsFolder(user.Id.ToString("N")));
+ }
+
+ if (user.Configuration.DisplayFoldersView)
+ {
+ list.Add(await GetUserView(CollectionType.Folders, user, "zz_" + CollectionType.Folders, cancellationToken).ConfigureAwait(false));
}
if (query.IncludeExternalContent)
{
var channelResult = await _channelManager.GetChannels(new ChannelQuery
{
- Limit = 0,
UserId = query.UserId
}, cancellationToken).ConfigureAwait(false);
- if (channelResult.TotalRecordCount > 0)
+ var channels = channelResult.Items;
+
+ var embeddedChannels = channels
+ .Where(i => user.Configuration.DisplayChannelsWithinViews.Contains(i.Id))
+ .ToList();
+
+ list.AddRange(embeddedChannels.Select(i => _channelManager.GetChannel(i.Id)));
+
+ if (channels.Length > embeddedChannels.Count)
{
list.Add(await _channelManager.GetInternalChannelFolder(query.UserId, cancellationToken).ConfigureAwait(false));
}
@@ -106,14 +133,58 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
- return list.OrderBy(i => i.SortName);
+ var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
+
+ var orders = user.Configuration.OrderedViews.ToList();
+
+ return list
+ .OrderBy(i =>
+ {
+ var index = orders.IndexOf(i.Id.ToString("N"));
+
+ return index == -1 ? int.MaxValue : index;
+ })
+ .ThenBy(sorted.IndexOf)
+ .ThenBy(i => i.SortName);
}
- private Task<UserView> GetUserView(string type, User user, CancellationToken cancellationToken)
+ public Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken)
{
var name = _localizationManager.GetLocalizedString("ViewType" + type);
- return _libraryManager.GetNamedView(name, type, string.Empty, cancellationToken);
+ return _libraryManager.GetNamedView(name, type, sortName, cancellationToken);
+ }
+
+ public async Task<SpecialFolder> GetSpecialFolder(string name, SpecialFolderType type, string itemType, CancellationToken cancellationToken)
+ {
+ var path = Path.Combine(_appPaths.ItemsByNamePath,
+ "specialfolders",
+ _fileSystem.GetValidFilename(name));
+
+ var id = (path + "_specialfolder_" + name).GetMBId(typeof(SpecialFolder));
+
+ var item = _libraryManager.GetItemById(id) as SpecialFolder;
+
+ if (item == null)
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+ item = new SpecialFolder
+ {
+ Path = path,
+ Id = id,
+ DateCreated = DateTime.UtcNow,
+ Name = name,
+ SpecialFolderType = type,
+ ItemTypeName = itemType
+ };
+
+ await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ }
+
+ return item;
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
index 05c972a4e..059ad2481 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -1,8 +1,13 @@
using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -23,46 +28,128 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// </summary>
private readonly ILogger _logger;
+ private readonly IServerConfigurationManager _config;
+
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidator" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public PeopleValidator(ILibraryManager libraryManager, ILogger logger)
+ public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config)
{
_libraryManager = libraryManager;
_logger = logger;
+ _config = config;
+ }
+
+ private bool DownloadMetadata(PersonInfo i, PeopleMetadataOptions options)
+ {
+ if (i.IsType(PersonType.Actor))
+ {
+ return options.DownloadActorMetadata;
+ }
+ if (i.IsType(PersonType.Director))
+ {
+ return options.DownloadDirectorMetadata;
+ }
+ if (i.IsType(PersonType.Composer))
+ {
+ return options.DownloadComposerMetadata;
+ }
+ if (i.IsType(PersonType.Writer))
+ {
+ return options.DownloadWriterMetadata;
+ }
+ if (i.IsType(PersonType.Producer))
+ {
+ return options.DownloadProducerMetadata;
+ }
+ if (i.IsType(PersonType.GuestStar))
+ {
+ return options.DownloadGuestStarMetadata;
+ }
+
+ return options.DownloadOtherPeopleMetadata;
+ }
+
+ private IEnumerable<PersonInfo> GetPeopleToValidate(BaseItem item, PeopleMetadataOptions options)
+ {
+ return item.People.Where(i =>
+ {
+ if (i.IsType(PersonType.Actor))
+ {
+ return options.DownloadActorMetadata;
+ }
+ if (i.IsType(PersonType.Director))
+ {
+ return options.DownloadDirectorMetadata;
+ }
+ if (i.IsType(PersonType.Composer))
+ {
+ return options.DownloadComposerMetadata;
+ }
+ if (i.IsType(PersonType.Writer))
+ {
+ return options.DownloadWriterMetadata;
+ }
+ if (i.IsType(PersonType.Producer))
+ {
+ return options.DownloadProducerMetadata;
+ }
+ if (i.IsType(PersonType.GuestStar))
+ {
+ return options.DownloadGuestStarMetadata;
+ }
+
+ return options.DownloadOtherPeopleMetadata;
+ });
}
/// <summary>
/// Validates the people.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="options">The options.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task.</returns>
- public async Task ValidatePeople(CancellationToken cancellationToken, MetadataRefreshOptions options, IProgress<double> progress)
+ public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(pct => progress.Report(pct * .15));
+ var peopleOptions = _config.Configuration.PeopleMetadataOptions;
+
var people = _libraryManager.RootFolder.GetRecursiveChildren()
- .SelectMany(c => c.People)
+ .SelectMany(i => i.People)
.Where(i => !string.IsNullOrWhiteSpace(i.Name))
- .Select(i => i.Name)
- .Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
- var numComplete = 0;
+ var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
foreach (var person in people)
{
+ bool current;
+ if (!dict.TryGetValue(person.Name, out current) || !current)
+ {
+ dict[person.Name] = DownloadMetadata(person, peopleOptions);
+ }
+ }
+
+ var numComplete = 0;
+
+ foreach (var person in dict)
+ {
cancellationToken.ThrowIfCancellationRequested();
try
{
- var item = _libraryManager.GetPerson(person);
+ var item = _libraryManager.GetPerson(person.Key);
+
+ var options = new MetadataRefreshOptions
+ {
+ MetadataRefreshMode = person.Value ? MetadataRefreshMode.Default : MetadataRefreshMode.ValidationOnly,
+ ImageRefreshMode = person.Value ? ImageRefreshMode.Default : ImageRefreshMode.ValidationOnly
+ };
await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
}