aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-02-21 00:04:11 -0500
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-02-21 00:04:11 -0500
commit7cd41a6ed62c46006307add9de92e0b329bd0edc (patch)
treef8d6515c218db6be90028f3fc7bacd549a0dab8f
parenta91c676565579c0ce7f7954e89a2b57a5c6c5780 (diff)
easier user library setup
-rw-r--r--MediaBrowser.Api/Library/LibraryHelpers.cs13
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs749
-rw-r--r--MediaBrowser.Api/Library/LibraryStructureService.cs96
-rw-r--r--MediaBrowser.Api/LibraryService.cs744
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj3
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs27
-rw-r--r--MediaBrowser.Controller/Entities/User.cs84
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs2
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs3
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs12
-rw-r--r--MediaBrowser.Providers/TV/EpisodeXmlParser.cs7
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs20
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs64
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs30
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs37
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js38
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
17 files changed, 863 insertions, 1068 deletions
diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs
index 3836a0860..be9f00a61 100644
--- a/MediaBrowser.Api/Library/LibraryHelpers.cs
+++ b/MediaBrowser.Api/Library/LibraryHelpers.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
using System;
using System.IO;
using System.Linq;
@@ -27,12 +26,11 @@ namespace MediaBrowser.Api.Library
/// <param name="fileSystem">The file system.</param>
/// <param name="virtualFolderName">Name of the virtual folder.</param>
/// <param name="mediaPath">The media path.</param>
- /// <param name="user">The user.</param>
/// <param name="appPaths">The app paths.</param>
/// <exception cref="System.IO.DirectoryNotFoundException">The media folder does not exist</exception>
- public static void RemoveMediaPath(IFileSystem fileSystem, string virtualFolderName, string mediaPath, User user, IServerApplicationPaths appPaths)
+ public static void RemoveMediaPath(IFileSystem fileSystem, string virtualFolderName, string mediaPath, IServerApplicationPaths appPaths)
{
- var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath;
+ var rootFolderPath = appPaths.DefaultUserViewsPath;
var path = Path.Combine(rootFolderPath, virtualFolderName);
if (!Directory.Exists(path))
@@ -54,18 +52,17 @@ namespace MediaBrowser.Api.Library
/// <param name="fileSystem">The file system.</param>
/// <param name="virtualFolderName">Name of the virtual folder.</param>
/// <param name="path">The path.</param>
- /// <param name="user">The user.</param>
/// <param name="appPaths">The app paths.</param>
- /// <exception cref="System.ArgumentException">The path is not valid.</exception>
/// <exception cref="System.IO.DirectoryNotFoundException">The path does not exist.</exception>
- public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, User user, IServerApplicationPaths appPaths)
+ /// <exception cref="System.ArgumentException">The path is not valid.</exception>
+ public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, IServerApplicationPaths appPaths)
{
if (!Directory.Exists(path))
{
throw new DirectoryNotFoundException("The path does not exist.");
}
- var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath;
+ var rootFolderPath = appPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
var shortcutFilename = Path.GetFileNameWithoutExtension(path);
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 840a5b851..1a14646e1 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -1,12 +1,216 @@
-using MediaBrowser.Common;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
+using System.Collections;
using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Api.Library
{
+ [Route("/Items/{Id}/File", "GET")]
+ [Api(Description = "Gets the original file of an item")]
+ public class GetFile
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Videos/{Id}/Subtitle/{Index}", "GET")]
+ [Api(Description = "Gets an external subtitle file")]
+ public class GetSubtitle
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
+ public int Index { get; set; }
+ }
+
+ /// <summary>
+ /// Class GetCriticReviews
+ /// </summary>
+ [Route("/Items/{Id}/CriticReviews", "GET")]
+ [Api(Description = "Gets critic reviews for an item")]
+ public class GetCriticReviews : IReturn<QueryResult<ItemReview>>
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ /// <summary>
+ /// Skips over a given number of items within the results. Use for paging.
+ /// </summary>
+ /// <value>The start index.</value>
+ [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? StartIndex { get; set; }
+
+ /// <summary>
+ /// The maximum number of items to return
+ /// </summary>
+ /// <value>The limit.</value>
+ [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? Limit { get; set; }
+ }
+
+ /// <summary>
+ /// Class GetThemeSongs
+ /// </summary>
+ [Route("/Items/{Id}/ThemeSongs", "GET")]
+ [Api(Description = "Gets theme songs for an item")]
+ public class GetThemeSongs : IReturn<ThemeMediaResult>
+ {
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ /// <value>The user id.</value>
+ [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "InheritFromParent", Description = "Determines whether or not parent items should be searched for theme media.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool InheritFromParent { get; set; }
+ }
+
+ /// <summary>
+ /// Class GetThemeVideos
+ /// </summary>
+ [Route("/Items/{Id}/ThemeVideos", "GET")]
+ [Api(Description = "Gets theme videos for an item")]
+ public class GetThemeVideos : IReturn<ThemeMediaResult>
+ {
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ /// <value>The user id.</value>
+ [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "InheritFromParent", Description = "Determines whether or not parent items should be searched for theme media.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool InheritFromParent { get; set; }
+ }
+
+ /// <summary>
+ /// Class GetThemeVideos
+ /// </summary>
+ [Route("/Items/{Id}/ThemeMedia", "GET")]
+ [Api(Description = "Gets theme videos and songs for an item")]
+ public class GetThemeMedia : IReturn<AllThemeMediaResult>
+ {
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ /// <value>The user id.</value>
+ [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "InheritFromParent", Description = "Determines whether or not parent items should be searched for theme media.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool InheritFromParent { get; set; }
+ }
+
+ [Route("/Library/Refresh", "POST")]
+ [Api(Description = "Starts a library scan")]
+ public class RefreshLibrary : IReturnVoid
+ {
+ }
+
+ [Route("/Items/{Id}", "DELETE")]
+ [Api(Description = "Deletes an item from the library and file system")]
+ public class DeleteItem : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Items/Counts", "GET")]
+ [Api(Description = "Gets counts of various item types")]
+ public class GetItemCounts : IReturn<ItemCounts>
+ {
+ [ApiMember(Name = "UserId", Description = "Optional. Get counts from a specific user's library.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid? UserId { get; set; }
+
+ [ApiMember(Name = "IsFavorite", Description = "Optional. Get counts of favorite items", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsFavorite { get; set; }
+ }
+
+ [Route("/Items/{Id}/Ancestors", "GET")]
+ [Api(Description = "Gets all parents of an item")]
+ public class GetAncestors : IReturn<BaseItemDto[]>
+ {
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ /// <value>The user id.</value>
+ [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Items/YearIndex", "GET")]
+ [Api(Description = "Gets a year index based on an item query.")]
+ public class GetYearIndex : IReturn<List<ItemIndex>>
+ {
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ /// <value>The user id.</value>
+ [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public Guid? UserId { get; set; }
+
+ [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string IncludeItemTypes { get; set; }
+ }
+
/// <summary>
/// Class GetPhyscialPaths
/// </summary>
@@ -16,32 +220,94 @@ namespace MediaBrowser.Api.Library
{
}
+ [Route("/Library/MediaFolders", "GET")]
+ [Api(Description = "Gets all user media folders.")]
+ public class GetMediaFolders : IReturn<ItemsResult>
+ {
+
+ }
+
/// <summary>
/// Class LibraryService
/// </summary>
public class LibraryService : BaseApiService
{
/// <summary>
- /// The _app host
+ /// The _item repo
/// </summary>
- private readonly IApplicationHost _appHost;
+ private readonly IItemRepository _itemRepo;
+
private readonly ILibraryManager _libraryManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataManager;
+
+ private readonly IDtoService _dtoService;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryService" /> class.
/// </summary>
- /// <param name="appHost">The app host.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <exception cref="System.ArgumentNullException">appHost</exception>
- public LibraryService(IApplicationHost appHost, ILibraryManager libraryManager)
+ public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
+ IDtoService dtoService, IUserDataManager userDataManager)
{
- if (appHost == null)
+ _itemRepo = itemRepo;
+ _libraryManager = libraryManager;
+ _userManager = userManager;
+ _dtoService = dtoService;
+ _userDataManager = userDataManager;
+ }
+
+ public object Get(GetMediaFolders request)
+ {
+ var items = _libraryManager.GetUserRootFolder().Children.ToList();
+
+ // Get everything
+ var fields = Enum.GetNames(typeof(ItemFields))
+ .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+ .ToList();
+
+ var result = new ItemsResult
{
- throw new ArgumentNullException("appHost");
+ TotalRecordCount = items.Count,
+
+ Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields)).ToArray()
+ };
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetFile request)
+ {
+ var item = _dtoService.GetItemByDtoId(request.Id);
+ var locationType = item.LocationType;
+ if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
+ {
+ throw new ArgumentException("This command cannot be used for remote or virtual items.");
+ }
+ if (Directory.Exists(item.Path))
+ {
+ throw new ArgumentException("This command cannot be used for directories.");
}
- _appHost = appHost;
- _libraryManager = libraryManager;
+ return ToStaticFileResult(item.Path);
+ }
+
+ public object Get(GetSubtitle request)
+ {
+ var subtitleStream = _itemRepo.GetMediaStreams(new MediaStreamQuery
+ {
+
+ Index = request.Index,
+ ItemId = new Guid(request.Id),
+ Type = MediaStreamType.Subtitle
+
+ }).FirstOrDefault();
+
+ if (subtitleStream == null)
+ {
+ throw new ResourceNotFoundException();
+ }
+
+ return ToStaticFileResult(subtitleStream.Path);
}
/// <summary>
@@ -57,5 +323,466 @@ namespace MediaBrowser.Api.Library
return ToOptimizedSerializedResultUsingCache(result);
}
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ public object Get(GetAncestors request)
+ {
+ var result = GetAncestors(request);
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
+ /// <summary>
+ /// Gets the ancestors.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>Task{BaseItemDto[]}.</returns>
+ public List<BaseItemDto> GetAncestors(GetAncestors request)
+ {
+ var item = _dtoService.GetItemByDtoId(request.Id);
+
+ var baseItemDtos = new List<BaseItemDto>();
+
+ var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+
+ // Get everything
+ var fields = Enum.GetNames(typeof(ItemFields))
+ .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+ .ToList();
+
+ BaseItem parent = item.Parent;
+
+ while (parent != null)
+ {
+ if (user != null)
+ {
+ parent = TranslateParentItem(parent, user);
+ }
+
+ baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, fields, user));
+
+ parent = parent.Parent;
+ }
+
+ return baseItemDtos.ToList();
+ }
+
+ private BaseItem TranslateParentItem(BaseItem item, User user)
+ {
+ if (item.Parent is AggregateFolder)
+ {
+ return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
+ }
+
+ return item;
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ public object Get(GetCriticReviews request)
+ {
+ var result = GetCriticReviews(request);
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ public object Get(GetItemCounts request)
+ {
+ var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
+ .Where(i => i.LocationType != LocationType.Virtual)
+ .ToList();
+
+ var filteredItems = request.UserId.HasValue ? FilterItems(items, request, request.UserId.Value).ToList() : items;
+
+ var albums = filteredItems.OfType<MusicAlbum>().ToList();
+ var episodes = filteredItems.OfType<Episode>().ToList();
+ var games = filteredItems.OfType<Game>().ToList();
+ var movies = filteredItems.OfType<Movie>().ToList();
+ var musicVideos = filteredItems.OfType<MusicVideo>().ToList();
+ var adultVideos = filteredItems.OfType<AdultVideo>().ToList();
+ var boxsets = filteredItems.OfType<BoxSet>().ToList();
+ var books = filteredItems.OfType<Book>().ToList();
+ var songs = filteredItems.OfType<Audio>().ToList();
+ var series = filteredItems.OfType<Series>().ToList();
+
+ var counts = new ItemCounts
+ {
+ AlbumCount = albums.Count,
+ EpisodeCount = episodes.Count,
+ GameCount = games.Count,
+ GameSystemCount = filteredItems.OfType<GameSystem>().Count(),
+ MovieCount = movies.Count,
+ SeriesCount = series.Count,
+ SongCount = songs.Count,
+ TrailerCount = filteredItems.OfType<Trailer>().Count(),
+ MusicVideoCount = musicVideos.Count,
+ AdultVideoCount = adultVideos.Count,
+ BoxSetCount = boxsets.Count,
+ BookCount = books.Count,
+
+ UniqueTypes = items.Select(i => i.GetClientTypeName()).Distinct().ToList()
+ };
+
+ return ToOptimizedSerializedResultUsingCache(counts);
+ }
+
+ private IEnumerable<T> FilterItems<T>(IEnumerable<T> items, GetItemCounts request, Guid userId)
+ where T : BaseItem
+ {
+ if (request.IsFavorite.HasValue)
+ {
+ var val = request.IsFavorite.Value;
+
+ items = items.Where(i => _userDataManager.GetUserData(userId, i.GetUserDataKey()).IsFavorite == val);
+ }
+
+ return items;
+ }
+
+ /// <summary>
+ /// Posts the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public async void Post(RefreshLibrary request)
+ {
+ try
+ {
+ await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
+ .ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error refreshing library", ex);
+ }
+ }
+
+ /// <summary>
+ /// Deletes the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Delete(DeleteItem request)
+ {
+ var task = DeleteItem(request);
+
+ Task.WaitAll(task);
+ }
+
+ private Task DeleteItem(DeleteItem request)
+ {
+ var item = _dtoService.GetItemByDtoId(request.Id);
+
+ return _libraryManager.DeleteItem(item);
+ }
+
+ /// <summary>
+ /// Gets the critic reviews async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>Task{ItemReviewsResult}.</returns>
+ private QueryResult<ItemReview> GetCriticReviews(GetCriticReviews request)
+ {
+ var reviews = _itemRepo.GetCriticReviews(new Guid(request.Id));
+
+ var reviewsArray = reviews.ToArray();
+
+ var result = new QueryResult<ItemReview>
+ {
+ TotalRecordCount = reviewsArray.Length
+ };
+
+ if (request.StartIndex.HasValue)
+ {
+ reviewsArray = reviewsArray.Skip(request.StartIndex.Value).ToArray();
+ }
+ if (request.Limit.HasValue)
+ {
+ reviewsArray = reviewsArray.Take(request.Limit.Value).ToArray();
+ }
+
+ result.Items = reviewsArray;
+
+ return result;
+ }
+
+ public object Get(GetThemeMedia request)
+ {
+ var themeSongs = GetThemeSongs(new GetThemeSongs
+ {
+ InheritFromParent = request.InheritFromParent,
+ Id = request.Id,
+ UserId = request.UserId
+
+ });
+
+ var themeVideos = GetThemeVideos(new GetThemeVideos
+ {
+ InheritFromParent = request.InheritFromParent,
+ Id = request.Id,
+ UserId = request.UserId
+
+ });
+
+ return ToOptimizedSerializedResultUsingCache(new AllThemeMediaResult
+ {
+ ThemeSongsResult = themeSongs,
+ ThemeVideosResult = themeVideos,
+
+ SoundtrackSongsResult = GetSoundtrackSongs(request.Id, request.UserId, request.InheritFromParent)
+ });
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ public object Get(GetThemeSongs request)
+ {
+ var result = GetThemeSongs(request);
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
+ private ThemeMediaResult GetThemeSongs(GetThemeSongs request)
+ {
+ var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+
+ var item = string.IsNullOrEmpty(request.Id)
+ ? (request.UserId.HasValue
+ ? user.RootFolder
+ : (Folder)_libraryManager.RootFolder)
+ : _dtoService.GetItemByDtoId(request.Id, request.UserId);
+
+ var originalItem = item;
+
+ while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
+ {
+ item = item.Parent;
+ }
+
+ // Get everything
+ var fields = Enum.GetNames(typeof(ItemFields))
+ .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+ .ToList();
+
+ var themeSongIds = GetThemeSongIds(item);
+
+ if (themeSongIds.Count == 0 && request.InheritFromParent)
+ {
+ var album = originalItem as MusicAlbum;
+
+ if (album != null)
+ {
+ var linkedItemWithThemes = album.SoundtrackIds
+ .Select(i => _libraryManager.GetItemById(i))
+ .FirstOrDefault(i => GetThemeSongIds(i).Count > 0);
+
+ if (linkedItemWithThemes != null)
+ {
+ themeSongIds = GetThemeSongIds(linkedItemWithThemes);
+ item = linkedItemWithThemes;
+ }
+ }
+ }
+
+ var dtos = themeSongIds.Select(_libraryManager.GetItemById)
+ .OrderBy(i => i.SortName)
+ .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));
+
+ var items = dtos.ToArray();
+
+ return new ThemeMediaResult
+ {
+ Items = items,
+ TotalRecordCount = items.Length,
+ OwnerId = _dtoService.GetDtoId(item)
+ };
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ public object Get(GetThemeVideos request)
+ {
+ var result = GetThemeVideos(request);
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
+ public ThemeMediaResult GetThemeVideos(GetThemeVideos request)
+ {
+ var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+
+ var item = string.IsNullOrEmpty(request.Id)
+ ? (request.UserId.HasValue
+ ? user.RootFolder
+ : (Folder)_libraryManager.RootFolder)
+ : _dtoService.GetItemByDtoId(request.Id, request.UserId);
+
+ var originalItem = item;
+
+ while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
+ {
+ item = item.Parent;
+ }
+
+ // Get everything
+ var fields = Enum.GetNames(typeof(ItemFields))
+ .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+ .ToList();
+
+ var themeVideoIds = GetThemeVideoIds(item);
+
+ if (themeVideoIds.Count == 0 && request.InheritFromParent)
+ {
+ var album = originalItem as MusicAlbum;
+
+ if (album == null)
+ {
+ album = originalItem.Parents.OfType<MusicAlbum>().FirstOrDefault();
+ }
+
+ if (album != null)
+ {
+ var linkedItemWithThemes = album.SoundtrackIds
+ .Select(i => _libraryManager.GetItemById(i))
+ .FirstOrDefault(i => GetThemeVideoIds(i).Count > 0);
+
+ if (linkedItemWithThemes != null)
+ {
+ themeVideoIds = GetThemeVideoIds(linkedItemWithThemes);
+ item = linkedItemWithThemes;
+ }
+ }
+ }
+
+ var dtos = themeVideoIds.Select(_libraryManager.GetItemById)
+ .OrderBy(i => i.SortName)
+ .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));
+
+ var items = dtos.ToArray();
+
+ return new ThemeMediaResult
+ {
+ Items = items,
+ TotalRecordCount = items.Length,
+ OwnerId = _dtoService.GetDtoId(item)
+ };
+ }
+
+ private List<Guid> GetThemeVideoIds(BaseItem item)
+ {
+ var i = item as IHasThemeMedia;
+
+ if (i != null)
+ {
+ return i.ThemeVideoIds;
+ }
+
+ return new List<Guid>();
+ }
+
+ private List<Guid> GetThemeSongIds(BaseItem item)
+ {
+ var i = item as IHasThemeMedia;
+
+ if (i != null)
+ {
+ return i.ThemeSongIds;
+ }
+
+ return new List<Guid>();
+ }
+
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public object Get(GetYearIndex request)
+ {
+ IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager);
+
+ if (!string.IsNullOrEmpty(request.IncludeItemTypes))
+ {
+ var vals = request.IncludeItemTypes.Split(',');
+ items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+ }
+
+ var lookup = items
+ .ToLookup(i => i.ProductionYear ?? -1)
+ .OrderBy(i => i.Key)
+ .Select(i => new ItemIndex
+ {
+ ItemCount = i.Count(),
+ Name = i.Key == -1 ? string.Empty : i.Key.ToString(_usCulture)
+ })
+ .ToList();
+
+ return ToOptimizedSerializedResultUsingCache(lookup);
+ }
+
+ public ThemeMediaResult GetSoundtrackSongs(string id, Guid? userId, bool inheritFromParent)
+ {
+ var user = userId.HasValue ? _userManager.GetUserById(userId.Value) : null;
+
+ var item = string.IsNullOrEmpty(id)
+ ? (userId.HasValue
+ ? user.RootFolder
+ : (Folder)_libraryManager.RootFolder)
+ : _dtoService.GetItemByDtoId(id, userId);
+
+ // Get everything
+ var fields = Enum.GetNames(typeof(ItemFields))
+ .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+ .ToList();
+
+ var dtos = GetSoundtrackSongIds(item, inheritFromParent)
+ .Select(_libraryManager.GetItemById)
+ .OfType<MusicAlbum>()
+ .SelectMany(i => i.RecursiveChildren)
+ .OfType<Audio>()
+ .OrderBy(i => i.SortName)
+ .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));
+
+ var items = dtos.ToArray();
+
+ return new ThemeMediaResult
+ {
+ Items = items,
+ TotalRecordCount = items.Length,
+ OwnerId = _dtoService.GetDtoId(item)
+ };
+ }
+
+ private IEnumerable<Guid> GetSoundtrackSongIds(BaseItem item, bool inherit)
+ {
+ var hasSoundtracks = item as IHasSoundtracks;
+
+ if (hasSoundtracks != null)
+ {
+ return hasSoundtracks.SoundtrackIds;
+ }
+
+ if (!inherit)
+ {
+ return null;
+ }
+
+ hasSoundtracks = item.Parents.OfType<IHasSoundtracks>().FirstOrDefault();
+
+ return hasSoundtracks != null ? hasSoundtracks.SoundtrackIds : new List<Guid>();
+ }
}
}
diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs
index ff4f78c96..947da29fe 100644
--- a/MediaBrowser.Api/Library/LibraryStructureService.cs
+++ b/MediaBrowser.Api/Library/LibraryStructureService.cs
@@ -28,16 +28,9 @@ namespace MediaBrowser.Api.Library
}
[Route("/Library/VirtualFolders", "POST")]
- [Route("/Users/{UserId}/VirtualFolders", "POST")]
public class AddVirtualFolder : IReturnVoid
{
/// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
@@ -57,16 +50,9 @@ namespace MediaBrowser.Api.Library
}
[Route("/Library/VirtualFolders", "DELETE")]
- [Route("/Users/{UserId}/VirtualFolders", "DELETE")]
public class RemoveVirtualFolder : IReturnVoid
{
/// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
@@ -80,16 +66,9 @@ namespace MediaBrowser.Api.Library
}
[Route("/Library/VirtualFolders/Name", "POST")]
- [Route("/Users/{UserId}/VirtualFolders/Name", "POST")]
public class RenameVirtualFolder : IReturnVoid
{
/// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
@@ -109,16 +88,9 @@ namespace MediaBrowser.Api.Library
}
[Route("/Library/VirtualFolders/Paths", "POST")]
- [Route("/Users/{UserId}/VirtualFolders/Paths", "POST")]
public class AddMediaPath : IReturnVoid
{
/// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
@@ -138,16 +110,9 @@ namespace MediaBrowser.Api.Library
}
[Route("/Library/VirtualFolders/Paths", "DELETE")]
- [Route("/Users/{UserId}/VirtualFolders/Paths", "DELETE")]
public class RemoveMediaPath : IReturnVoid
{
/// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string UserId { get; set; }
-
- /// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
@@ -275,18 +240,7 @@ namespace MediaBrowser.Api.Library
var name = _fileSystem.GetValidFilename(request.Name);
- string rootFolderPath;
-
- if (string.IsNullOrEmpty(request.UserId))
- {
- rootFolderPath = _appPaths.DefaultUserViewsPath;
- }
- else
- {
- var user = _userManager.GetUserById(new Guid(request.UserId));
-
- rootFolderPath = user.RootFolderPath;
- }
+ var rootFolderPath = _appPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, name);
@@ -344,18 +298,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentNullException("request");
}
- string rootFolderPath;
-
- if (string.IsNullOrEmpty(request.UserId))
- {
- rootFolderPath = _appPaths.DefaultUserViewsPath;
- }
- else
- {
- var user = _userManager.GetUserById(new Guid(request.UserId));
-
- rootFolderPath = user.RootFolderPath;
- }
+ var rootFolderPath = _appPaths.DefaultUserViewsPath;
var currentPath = Path.Combine(rootFolderPath, request.Name);
var newPath = Path.Combine(rootFolderPath, request.NewName);
@@ -417,18 +360,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentNullException("request");
}
- string rootFolderPath;
-
- if (string.IsNullOrEmpty(request.UserId))
- {
- rootFolderPath = _appPaths.DefaultUserViewsPath;
- }
- else
- {
- var user = _userManager.GetUserById(new Guid(request.UserId));
-
- rootFolderPath = user.RootFolderPath;
- }
+ var rootFolderPath = _appPaths.DefaultUserViewsPath;
var path = Path.Combine(rootFolderPath, request.Name);
@@ -478,16 +410,7 @@ namespace MediaBrowser.Api.Library
try
{
- if (string.IsNullOrEmpty(request.UserId))
- {
- LibraryHelpers.AddMediaPath(_fileSystem, request.Name, request.Path, null, _appPaths);
- }
- else
- {
- var user = _userManager.GetUserById(new Guid(request.UserId));
-
- LibraryHelpers.AddMediaPath(_fileSystem, request.Name, request.Path, user, _appPaths);
- }
+ LibraryHelpers.AddMediaPath(_fileSystem, request.Name, request.Path, _appPaths);
// Need to add a delay here or directory watchers may still pick up the changes
var task = Task.Delay(1000);
@@ -524,16 +447,7 @@ namespace MediaBrowser.Api.Library
try
{
- if (string.IsNullOrEmpty(request.UserId))
- {
- LibraryHelpers.RemoveMediaPath(_fileSystem, request.Name, request.Path, null, _appPaths);
- }
- else
- {
- var user = _userManager.GetUserById(new Guid(request.UserId));
-
- LibraryHelpers.RemoveMediaPath(_fileSystem, request.Name, request.Path, user, _appPaths);
- }
+ LibraryHelpers.RemoveMediaPath(_fileSystem, request.Name, request.Path, _appPaths);
// Need to add a delay here or directory watchers may still pick up the changes
var task = Task.Delay(1000);
diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs
deleted file mode 100644
index f8b4caf94..000000000
--- a/MediaBrowser.Api/LibraryService.cs
+++ /dev/null
@@ -1,744 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using ServiceStack;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Api
-{
- [Route("/Items/{Id}/File", "GET")]
- [Api(Description = "Gets the original file of an item")]
- public class GetFile
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- [Route("/Videos/{Id}/Subtitle/{Index}", "GET")]
- [Api(Description = "Gets an external subtitle file")]
- public class GetSubtitle
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
- public int Index { get; set; }
- }
-
- /// <summary>
- /// Class GetCriticReviews
- /// </summary>
- [Route("/Items/{Id}/CriticReviews", "GET")]
- [Api(Description = "Gets critic reviews for an item")]
- public class GetCriticReviews : IReturn<QueryResult<ItemReview>>
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- /// <summary>
- /// Skips over a given number of items within the results. Use for paging.
- /// </summary>
- /// <value>The start index.</value>
- [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// The maximum number of items to return
- /// </summary>
- /// <value>The limit.</value>
- [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? Limit { get; set; }
- }
-
- /// <summary>
- /// Class GetThemeSongs
- /// </summary>
- [Route("/Items/{Id}/ThemeSongs", "GET")]
- [Api(Description = "Gets theme songs for an item")]
- public class GetThemeSongs : IReturn<ThemeMediaResult>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public Guid? UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "InheritFromParent", Description = "Determines whether or not parent items should be searched for theme media.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool InheritFromParent { get; set; }
- }
-
- /// <summary>
- /// Class GetThemeVideos
- /// </summary>
- [Route("/Items/{Id}/ThemeVideos", "GET")]
- [Api(Description = "Gets theme videos for an item")]
- public class GetThemeVideos : IReturn<ThemeMediaResult>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public Guid? UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "InheritFromParent", Description = "Determines whether or not parent items should be searched for theme media.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool InheritFromParent { get; set; }
- }
-
- /// <summary>
- /// Class GetThemeVideos
- /// </summary>
- [Route("/Items/{Id}/ThemeMedia", "GET")]
- [Api(Description = "Gets theme videos and songs for an item")]
- public class GetThemeMedia : IReturn<AllThemeMediaResult>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public Guid? UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- [ApiMember(Name = "InheritFromParent", Description = "Determines whether or not parent items should be searched for theme media.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool InheritFromParent { get; set; }
- }
-
- [Route("/Library/Refresh", "POST")]
- [Api(Description = "Starts a library scan")]
- public class RefreshLibrary : IReturnVoid
- {
- }
-
- [Route("/Items/{Id}", "DELETE")]
- [Api(Description = "Deletes an item from the library and file system")]
- public class DeleteItem : IReturnVoid
- {
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Items/Counts", "GET")]
- [Api(Description = "Gets counts of various item types")]
- public class GetItemCounts : IReturn<ItemCounts>
- {
- [ApiMember(Name = "UserId", Description = "Optional. Get counts from a specific user's library.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public Guid? UserId { get; set; }
-
- [ApiMember(Name = "IsFavorite", Description = "Optional. Get counts of favorite items", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsFavorite { get; set; }
- }
-
- [Route("/Items/{Id}/Ancestors", "GET")]
- [Api(Description = "Gets all parents of an item")]
- public class GetAncestors : IReturn<BaseItemDto[]>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public Guid? UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- [Route("/Items/YearIndex", "GET")]
- [Api(Description = "Gets a year index based on an item query.")]
- public class GetYearIndex : IReturn<List<ItemIndex>>
- {
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public Guid? UserId { get; set; }
-
- [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string IncludeItemTypes { get; set; }
- }
-
- /// <summary>
- /// Class LibraryService
- /// </summary>
- public class LibraryService : BaseApiService
- {
- /// <summary>
- /// The _item repo
- /// </summary>
- private readonly IItemRepository _itemRepo;
-
- private readonly ILibraryManager _libraryManager;
- private readonly IUserManager _userManager;
- private readonly IUserDataManager _userDataManager;
-
- private readonly IDtoService _dtoService;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LibraryService" /> class.
- /// </summary>
- public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
- IDtoService dtoService, IUserDataManager userDataManager)
- {
- _itemRepo = itemRepo;
- _libraryManager = libraryManager;
- _userManager = userManager;
- _dtoService = dtoService;
- _userDataManager = userDataManager;
- }
-
- public object Get(GetFile request)
- {
- var item = _dtoService.GetItemByDtoId(request.Id);
- var locationType = item.LocationType;
- if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
- {
- throw new ArgumentException("This command cannot be used for remote or virtual items.");
- }
- if (Directory.Exists(item.Path))
- {
- throw new ArgumentException("This command cannot be used for directories.");
- }
-
- return ToStaticFileResult(item.Path);
- }
-
- public object Get(GetSubtitle request)
- {
- var subtitleStream = _itemRepo.GetMediaStreams(new MediaStreamQuery
- {
-
- Index = request.Index,
- ItemId = new Guid(request.Id),
- Type = MediaStreamType.Subtitle
-
- }).FirstOrDefault();
-
- if (subtitleStream == null)
- {
- throw new ResourceNotFoundException();
- }
-
- return ToStaticFileResult(subtitleStream.Path);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetAncestors request)
- {
- var result = GetAncestors(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- /// <summary>
- /// Gets the ancestors.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task{BaseItemDto[]}.</returns>
- public List<BaseItemDto> GetAncestors(GetAncestors request)
- {
- var item = _dtoService.GetItemByDtoId(request.Id);
-
- var baseItemDtos = new List<BaseItemDto>();
-
- var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
-
- // Get everything
- var fields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
-
- BaseItem parent = item.Parent;
-
- while (parent != null)
- {
- if (user != null)
- {
- parent = TranslateParentItem(parent, user);
- }
-
- baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, fields, user));
-
- if (parent is UserRootFolder)
- {
- break;
- }
-
- parent = parent.Parent;
- }
-
- return baseItemDtos.ToList();
- }
-
- private BaseItem TranslateParentItem(BaseItem item, User user)
- {
- if (item.Parent is AggregateFolder)
- {
- return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
- }
-
- return item;
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetCriticReviews request)
- {
- var result = GetCriticReviews(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetItemCounts request)
- {
- var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
- .Where(i => i.LocationType != LocationType.Virtual)
- .ToList();
-
- var filteredItems = request.UserId.HasValue ? FilterItems(items, request, request.UserId.Value).ToList() : items;
-
- var albums = filteredItems.OfType<MusicAlbum>().ToList();
- var episodes = filteredItems.OfType<Episode>().ToList();
- var games = filteredItems.OfType<Game>().ToList();
- var movies = filteredItems.OfType<Movie>().ToList();
- var musicVideos = filteredItems.OfType<MusicVideo>().ToList();
- var adultVideos = filteredItems.OfType<AdultVideo>().ToList();
- var boxsets = filteredItems.OfType<BoxSet>().ToList();
- var books = filteredItems.OfType<Book>().ToList();
- var songs = filteredItems.OfType<Audio>().ToList();
- var series = filteredItems.OfType<Series>().ToList();
-
- var counts = new ItemCounts
- {
- AlbumCount = albums.Count,
- EpisodeCount = episodes.Count,
- GameCount = games.Count,
- GameSystemCount = filteredItems.OfType<GameSystem>().Count(),
- MovieCount = movies.Count,
- SeriesCount = series.Count,
- SongCount = songs.Count,
- TrailerCount = filteredItems.OfType<Trailer>().Count(),
- MusicVideoCount = musicVideos.Count,
- AdultVideoCount = adultVideos.Count,
- BoxSetCount = boxsets.Count,
- BookCount = books.Count,
-
- UniqueTypes = items.Select(i => i.GetClientTypeName()).Distinct().ToList()
- };
-
- return ToOptimizedSerializedResultUsingCache(counts);
- }
-
- private IEnumerable<T> FilterItems<T>(IEnumerable<T> items, GetItemCounts request, Guid userId)
- where T : BaseItem
- {
- if (request.IsFavorite.HasValue)
- {
- var val = request.IsFavorite.Value;
-
- items = items.Where(i => _userDataManager.GetUserData(userId, i.GetUserDataKey()).IsFavorite == val);
- }
-
- return items;
- }
-
- /// <summary>
- /// Posts the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public async void Post(RefreshLibrary request)
- {
- try
- {
- await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
- .ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error refreshing library", ex);
- }
- }
-
- /// <summary>
- /// Deletes the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- public void Delete(DeleteItem request)
- {
- var task = DeleteItem(request);
-
- Task.WaitAll(task);
- }
-
- private Task DeleteItem(DeleteItem request)
- {
- var item = _dtoService.GetItemByDtoId(request.Id);
-
- return _libraryManager.DeleteItem(item);
- }
-
- /// <summary>
- /// Gets the critic reviews async.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task{ItemReviewsResult}.</returns>
- private QueryResult<ItemReview> GetCriticReviews(GetCriticReviews request)
- {
- var reviews = _itemRepo.GetCriticReviews(new Guid(request.Id));
-
- var reviewsArray = reviews.ToArray();
-
- var result = new QueryResult<ItemReview>
- {
- TotalRecordCount = reviewsArray.Length
- };
-
- if (request.StartIndex.HasValue)
- {
- reviewsArray = reviewsArray.Skip(request.StartIndex.Value).ToArray();
- }
- if (request.Limit.HasValue)
- {
- reviewsArray = reviewsArray.Take(request.Limit.Value).ToArray();
- }
-
- result.Items = reviewsArray;
-
- return result;
- }
-
- public object Get(GetThemeMedia request)
- {
- var themeSongs = GetThemeSongs(new GetThemeSongs
- {
- InheritFromParent = request.InheritFromParent,
- Id = request.Id,
- UserId = request.UserId
-
- });
-
- var themeVideos = GetThemeVideos(new GetThemeVideos
- {
- InheritFromParent = request.InheritFromParent,
- Id = request.Id,
- UserId = request.UserId
-
- });
-
- return ToOptimizedSerializedResultUsingCache(new AllThemeMediaResult
- {
- ThemeSongsResult = themeSongs,
- ThemeVideosResult = themeVideos,
-
- SoundtrackSongsResult = GetSoundtrackSongs(request.Id, request.UserId, request.InheritFromParent)
- });
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetThemeSongs request)
- {
- var result = GetThemeSongs(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- private ThemeMediaResult GetThemeSongs(GetThemeSongs request)
- {
- var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
-
- var item = string.IsNullOrEmpty(request.Id)
- ? (request.UserId.HasValue
- ? user.RootFolder
- : (Folder)_libraryManager.RootFolder)
- : _dtoService.GetItemByDtoId(request.Id, request.UserId);
-
- var originalItem = item;
-
- while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
- {
- item = item.Parent;
- }
-
- // Get everything
- var fields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
-
- var themeSongIds = GetThemeSongIds(item);
-
- if (themeSongIds.Count == 0 && request.InheritFromParent)
- {
- var album = originalItem as MusicAlbum;
-
- if (album != null)
- {
- var linkedItemWithThemes = album.SoundtrackIds
- .Select(i => _libraryManager.GetItemById(i))
- .FirstOrDefault(i => GetThemeSongIds(i).Count > 0);
-
- if (linkedItemWithThemes != null)
- {
- themeSongIds = GetThemeSongIds(linkedItemWithThemes);
- item = linkedItemWithThemes;
- }
- }
- }
-
- var dtos = themeSongIds.Select(_libraryManager.GetItemById)
- .OrderBy(i => i.SortName)
- .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));
-
- var items = dtos.ToArray();
-
- return new ThemeMediaResult
- {
- Items = items,
- TotalRecordCount = items.Length,
- OwnerId = _dtoService.GetDtoId(item)
- };
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetThemeVideos request)
- {
- var result = GetThemeVideos(request);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
- public ThemeMediaResult GetThemeVideos(GetThemeVideos request)
- {
- var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
-
- var item = string.IsNullOrEmpty(request.Id)
- ? (request.UserId.HasValue
- ? user.RootFolder
- : (Folder)_libraryManager.RootFolder)
- : _dtoService.GetItemByDtoId(request.Id, request.UserId);
-
- var originalItem = item;
-
- while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
- {
- item = item.Parent;
- }
-
- // Get everything
- var fields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
-
- var themeVideoIds = GetThemeVideoIds(item);
-
- if (themeVideoIds.Count == 0 && request.InheritFromParent)
- {
- var album = originalItem as MusicAlbum;
-
- if (album == null)
- {
- album = originalItem.Parents.OfType<MusicAlbum>().FirstOrDefault();
- }
-
- if (album != null)
- {
- var linkedItemWithThemes = album.SoundtrackIds
- .Select(i => _libraryManager.GetItemById(i))
- .FirstOrDefault(i => GetThemeVideoIds(i).Count > 0);
-
- if (linkedItemWithThemes != null)
- {
- themeVideoIds = GetThemeVideoIds(linkedItemWithThemes);
- item = linkedItemWithThemes;
- }
- }
- }
-
- var dtos = themeVideoIds.Select(_libraryManager.GetItemById)
- .OrderBy(i => i.SortName)
- .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));
-
- var items = dtos.ToArray();
-
- return new ThemeMediaResult
- {
- Items = items,
- TotalRecordCount = items.Length,
- OwnerId = _dtoService.GetDtoId(item)
- };
- }
-
- private List<Guid> GetThemeVideoIds(BaseItem item)
- {
- var i = item as IHasThemeMedia;
-
- if (i != null)
- {
- return i.ThemeVideoIds;
- }
-
- return new List<Guid>();
- }
-
- private List<Guid> GetThemeSongIds(BaseItem item)
- {
- var i = item as IHasThemeMedia;
-
- if (i != null)
- {
- return i.ThemeSongIds;
- }
-
- return new List<Guid>();
- }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public object Get(GetYearIndex request)
- {
- IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager);
-
- if (!string.IsNullOrEmpty(request.IncludeItemTypes))
- {
- var vals = request.IncludeItemTypes.Split(',');
- items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
- }
-
- var lookup = items
- .ToLookup(i => i.ProductionYear ?? -1)
- .OrderBy(i => i.Key)
- .Select(i => new ItemIndex
- {
- ItemCount = i.Count(),
- Name = i.Key == -1 ? string.Empty : i.Key.ToString(_usCulture)
- })
- .ToList();
-
- return ToOptimizedSerializedResultUsingCache(lookup);
- }
-
- public ThemeMediaResult GetSoundtrackSongs(string id, Guid? userId, bool inheritFromParent)
- {
- var user = userId.HasValue ? _userManager.GetUserById(userId.Value) : null;
-
- var item = string.IsNullOrEmpty(id)
- ? (userId.HasValue
- ? user.RootFolder
- : (Folder)_libraryManager.RootFolder)
- : _dtoService.GetItemByDtoId(id, userId);
-
- // Get everything
- var fields = Enum.GetNames(typeof(ItemFields))
- .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
-
- var dtos = GetSoundtrackSongIds(item, inheritFromParent)
- .Select(_libraryManager.GetItemById)
- .OfType<MusicAlbum>()
- .SelectMany(i => i.RecursiveChildren)
- .OfType<Audio>()
- .OrderBy(i => i.SortName)
- .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));
-
- var items = dtos.ToArray();
-
- return new ThemeMediaResult
- {
- Items = items,
- TotalRecordCount = items.Length,
- OwnerId = _dtoService.GetDtoId(item)
- };
- }
-
- private IEnumerable<Guid> GetSoundtrackSongIds(BaseItem item, bool inherit)
- {
- var hasSoundtracks = item as IHasSoundtracks;
-
- if (hasSoundtracks != null)
- {
- return hasSoundtracks.SoundtrackIds;
- }
-
- if (!inherit)
- {
- return null;
- }
-
- hasSoundtracks = item.Parents.OfType<IHasSoundtracks>().FirstOrDefault();
-
- return hasSoundtracks != null ? hasSoundtracks.SoundtrackIds : new List<Guid>();
- }
- }
-}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index f8d0ed125..c95df7db3 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -83,10 +83,9 @@
<Compile Include="InstantMixService.cs" />
<Compile Include="ItemRefreshService.cs" />
<Compile Include="ItemUpdateService.cs" />
- <Compile Include="LibraryService.cs" />
+ <Compile Include="Library\LibraryService.cs" />
<Compile Include="Library\FileOrganizationService.cs" />
<Compile Include="Library\LibraryHelpers.cs" />
- <Compile Include="Library\LibraryService.cs" />
<Compile Include="Library\LibraryStructureService.cs" />
<Compile Include="LiveTv\LiveTvService.cs" />
<Compile Include="LocalizationService.cs" />
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index b9b6dd517..6472b988a 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -277,6 +277,19 @@ namespace MediaBrowser.Controller.Entities
get { return GetRecursiveChildren(); }
}
+ public override bool IsVisible(User user)
+ {
+ if (this is ICollectionFolder)
+ {
+ if (user.Configuration.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ return base.IsVisible(user);
+ }
+
private List<BaseItem> LoadChildrenInternal()
{
return LoadChildren().ToList();
@@ -762,15 +775,15 @@ namespace MediaBrowser.Controller.Entities
{
list.Add(child);
}
- }
-
- if (recursive && child.IsFolder)
- {
- var folder = (Folder)child;
- if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
+ if (recursive && child.IsFolder)
{
- hasLinkedChildren = true;
+ var folder = (Folder)child;
+
+ if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
+ {
+ hasLinkedChildren = true;
+ }
}
}
}
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index 66ef8c7dc..d5fc3172c 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -20,35 +20,6 @@ namespace MediaBrowser.Controller.Entities
public static IXmlSerializer XmlSerializer { get; set; }
/// <summary>
- /// Gets the root folder path.
- /// </summary>
- /// <value>The root folder path.</value>
- [IgnoreDataMember]
- public string RootFolderPath
- {
- get
- {
- var path = Configuration.UseCustomLibrary ? GetRootFolderPath(Name) : ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
-
- Directory.CreateDirectory(path);
-
- return path;
- }
- }
-
- /// <summary>
- /// Gets the root folder path based on a given username
- /// </summary>
- /// <param name="username">The username.</param>
- /// <returns>System.String.</returns>
- private string GetRootFolderPath(string username)
- {
- var safeFolderName = FileSystem.GetValidFilename(username);
-
- return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.RootFolderPath, safeFolderName);
- }
-
- /// <summary>
/// Gets or sets the password.
/// </summary>
/// <value>The password.</value>
@@ -98,23 +69,15 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// The _root folder
- /// </summary>
- private UserRootFolder _rootFolder;
- /// <summary>
/// Gets the root folder.
/// </summary>
/// <value>The root folder.</value>
[IgnoreDataMember]
- public UserRootFolder RootFolder
+ public Folder RootFolder
{
get
{
- return _rootFolder ?? (LibraryManager.GetUserRootFolder(RootFolderPath));
- }
- private set
- {
- _rootFolder = value;
+ return LibraryManager.GetUserRootFolder();
}
}
@@ -166,22 +129,6 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// Reloads the root media folder
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public async Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken)
- {
- Logger.Info("Validating media library for {0}", Name);
- await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await RootFolder.ValidateChildren(progress, cancellationToken).ConfigureAwait(false);
- }
-
- /// <summary>
/// Renames the user.
/// </summary>
/// <param name="newName">The new name.</param>
@@ -215,29 +162,10 @@ namespace MediaBrowser.Controller.Entities
{
Directory.CreateDirectory(newConfigDirectory);
}
-
- var customLibraryPath = GetRootFolderPath(Name);
-
- // Move the root folder path if using a custom library
- if (Directory.Exists(customLibraryPath))
- {
- var newRootFolderPath = GetRootFolderPath(newName);
- if (Directory.Exists(newRootFolderPath))
- {
- Directory.Delete(newRootFolderPath, true);
- }
- Directory.Move(customLibraryPath, newRootFolderPath);
- }
}
Name = newName;
- // Force these to be lazy loaded again
- RootFolder = null;
-
- // Kick off a task to validate the media library
- Task.Run(() => ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
-
return RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = true,
@@ -318,16 +246,8 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("config");
}
- var customLibraryChanged = config.UseCustomLibrary != Configuration.UseCustomLibrary;
-
Configuration = config;
SaveConfiguration(serializer);
-
- // Force these to be lazy loaded again
- if (customLibraryChanged)
- {
- RootFolder = null;
- }
}
}
}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 9a8d3e7f4..1829e10c7 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Entities
if (string.Equals("default", Name, System.StringComparison.OrdinalIgnoreCase))
{
- Name = "Default Media Library";
+ Name = "Media Folders";
hasChanges = true;
}
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 0b1ac9137..747ae48ad 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -202,9 +202,8 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the user root folder.
/// </summary>
- /// <param name="userRootPath">The user root path.</param>
/// <returns>UserRootFolder.</returns>
- UserRootFolder GetUserRootFolder(string userRootPath);
+ Folder GetUserRootFolder();
/// <summary>
/// Creates the item.
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index c306c7775..fa2554e05 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -17,12 +17,6 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value><c>true</c> if items with no rating info should be blocked; otherwise, <c>false</c>.</value>
public bool BlockNotRated { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [use custom library].
- /// </summary>
- /// <value><c>true</c> if [use custom library]; otherwise, <c>false</c>.</value>
- public bool UseCustomLibrary { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is administrator.
@@ -71,7 +65,9 @@ namespace MediaBrowser.Model.Configuration
public bool EnableLiveTvAccess { get; set; }
public bool EnableMediaPlayback { get; set; }
-
+
+ public string[] BlockedMediaFolders { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="UserConfiguration" /> class.
/// </summary>
@@ -84,6 +80,8 @@ namespace MediaBrowser.Model.Configuration
EnableLiveTvManagement = true;
EnableMediaPlayback = true;
EnableLiveTvAccess = true;
+
+ BlockedMediaFolders = new string[] { };
}
}
}
diff --git a/MediaBrowser.Providers/TV/EpisodeXmlParser.cs b/MediaBrowser.Providers/TV/EpisodeXmlParser.cs
index ee78c3777..2b2d607ca 100644
--- a/MediaBrowser.Providers/TV/EpisodeXmlParser.cs
+++ b/MediaBrowser.Providers/TV/EpisodeXmlParser.cs
@@ -23,9 +23,12 @@ namespace MediaBrowser.Providers.TV
{
}
+ private string _xmlPath;
+
public void Fetch(Episode item, List<LocalImageInfo> images, string metadataFile, CancellationToken cancellationToken)
{
_imagesFound = images;
+ _xmlPath = metadataFile;
Fetch(item, metadataFile, cancellationToken);
}
@@ -75,8 +78,8 @@ namespace MediaBrowser.Providers.TV
// even though it's actually using the metadata folder.
filename = Path.GetFileName(filename);
- var parentFolder = Path.GetDirectoryName(item.Path);
- filename = Path.Combine(parentFolder, "metadata", filename);
+ var parentFolder = Path.GetDirectoryName(_xmlPath);
+ filename = Path.Combine(parentFolder, filename);
var file = new FileInfo(filename);
if (file.Exists)
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 2928363e3..36a8d3526 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -283,26 +283,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
return new[] { user.RootFolder as T };
}
- // Need to find what user collection folder this belongs to
- if (item.Parent is AggregateFolder)
- {
- if (item.LocationType == LocationType.FileSystem)
- {
- return collections.Where(i => i.PhysicalLocations.Contains(item.Path)).Cast<T>();
- }
- }
-
- // If it's a user root, return it only if it's the right one
- if (item is UserRootFolder)
- {
- if (item.Id == user.RootFolder.Id)
- {
- return new[] { item };
- }
-
- return new T[] { };
- }
-
// Return it only if it's in the user's library
if (includeIfNotFound || allRecursiveChildren.ContainsKey(item.Id))
{
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index c2044476a..5bc1ff45a 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -153,12 +153,6 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
- /// <summary>
- /// The _user root folders
- /// </summary>
- private readonly ConcurrentDictionary<string, UserRootFolder> _userRootFolders =
- new ConcurrentDictionary<string, UserRootFolder>();
-
private readonly IFileSystem _fileSystem;
/// <summary>
@@ -586,7 +580,7 @@ namespace MediaBrowser.Server.Implementations.Library
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
-
+
// Need to remove subpaths that may have been resolved from shortcuts
// Example: if \\server\movies exists, then strip out \\server\movies\action
if (isPhysicalRoot)
@@ -701,20 +695,18 @@ namespace MediaBrowser.Server.Implementations.Library
return rootFolder;
}
- /// <summary>
- /// Gets the user root folder.
- /// </summary>
- /// <param name="userRootPath">The user root path.</param>
- /// <returns>UserRootFolder.</returns>
- public UserRootFolder GetUserRootFolder(string userRootPath)
+ private UserRootFolder _userRootFolder;
+ public Folder GetUserRootFolder()
{
- return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ??
- (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)));
- }
+ if (_userRootFolder == null)
+ {
+ var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
- public Person GetPersonSync(string name)
- {
- return GetItemByName<Person>(ConfigurationManager.ApplicationPaths.PeoplePath, name);
+ _userRootFolder = RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ??
+ (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath));
+ }
+
+ return _userRootFolder;
}
/// <summary>
@@ -1001,7 +993,7 @@ namespace MediaBrowser.Server.Implementations.Library
// Just run the scheduled task so that the user can see it
_taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
}
-
+
/// <summary>
/// Validates the media library internal.
/// </summary>
@@ -1035,19 +1027,15 @@ namespace MediaBrowser.Server.Implementations.Library
progress.Report(1);
- foreach (var folder in _userManager.Users.Select(u => u.RootFolder).Distinct())
- {
- await ValidateCollectionFolders(folder, cancellationToken).ConfigureAwait(false);
- }
+ var userRoot = GetUserRootFolder();
+ await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+
+ await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(), recursive: false).ConfigureAwait(false);
progress.Report(2);
var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct => progress.Report(2 + pct * .13));
-
- innerProgress = new ActionableProgress<double>();
-
innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73));
// Now validate the entire media library
@@ -1119,22 +1107,6 @@ namespace MediaBrowser.Server.Implementations.Library
}
/// <summary>
- /// Validates only the collection folders for a User and goes no further
- /// </summary>
- /// <param name="userRootFolder">The user root folder.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task ValidateCollectionFolders(UserRootFolder userRootFolder, CancellationToken cancellationToken)
- {
- _logger.Info("Validating collection folders within {0}", userRootFolder.Path);
- await userRootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await userRootFolder.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(), recursive: false).ConfigureAwait(false);
- }
-
- /// <summary>
/// Gets the default view.
/// </summary>
/// <returns>IEnumerable{VirtualFolderInfo}.</returns>
@@ -1150,7 +1122,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>IEnumerable{VirtualFolderInfo}.</returns>
public IEnumerable<VirtualFolderInfo> GetVirtualFolders(User user)
{
- return GetView(user.RootFolderPath);
+ return GetDefaultVirtualFolders();
}
/// <summary>
@@ -1399,7 +1371,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
await _providerManagerFactory().SaveMetadata(item, updateReason).ConfigureAwait(false);
}
-
+
item.DateLastSaved = DateTime.UtcNow;
_logger.Debug("Saving {0} to database.", item.Path ?? item.Name);
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index ce76dd21b..8654a26ce 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -332,29 +332,15 @@ namespace MediaBrowser.Server.Implementations.Library
await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);
- if (user.Configuration.UseCustomLibrary)
+ var path = user.ConfigurationFilePath;
+
+ try
+ {
+ File.Delete(path);
+ }
+ catch (IOException ex)
{
- var path = user.RootFolderPath;
-
- try
- {
- Directory.Delete(path, true);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting directory {0}", ex, path);
- }
-
- path = user.ConfigurationFilePath;
-
- try
- {
- File.Delete(path);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting file {0}", ex, path);
- }
+ _logger.ErrorException("Error deleting file {0}", ex, path);
}
// Force this to be lazy loaded again
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 85aeeb129..895a8d9b9 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -254,6 +254,14 @@ namespace MediaBrowser.ServerApplication
{
try
{
+ MigrateUserFolders();
+ }
+ catch (IOException ex)
+ {
+ }
+
+ try
+ {
File.Delete(Path.Combine(ApplicationPaths.PluginsPath, "MBPhoto.dll"));
}
catch (IOException)
@@ -319,6 +327,35 @@ namespace MediaBrowser.ServerApplication
});
}
+ private void MigrateUserFolders()
+ {
+ var rootPath = ApplicationPaths.RootFolderPath;
+
+ var folders = new DirectoryInfo(rootPath).EnumerateDirectories("*", SearchOption.TopDirectoryOnly).Where(i => !string.Equals(i.Name, "default", StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ foreach (var folder in folders)
+ {
+ MigrateUserFolder(folder);
+ }
+ }
+
+ private void MigrateUserFolder(DirectoryInfo folder)
+ {
+ var foldersInDefault = new DirectoryInfo(ApplicationPaths.DefaultUserViewsPath).EnumerateDirectories("*", SearchOption.TopDirectoryOnly).ToList();
+
+ var foldersInUserView = folder.EnumerateDirectories("*", SearchOption.TopDirectoryOnly).ToList();
+
+ var foldersToMove = foldersInUserView.Where(i => !foldersInDefault.Any(f => string.Equals(f.Name, i.Name, StringComparison.OrdinalIgnoreCase))).ToList();
+
+ foreach (var folderToMove in foldersToMove)
+ {
+ folderToMove.MoveTo(Path.Combine(ApplicationPaths.DefaultUserViewsPath, folderToMove.Name));
+ }
+
+ Directory.Delete(folder.FullName, true);
+ }
+
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index 375aacbce..97c26ec78 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -1300,7 +1300,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Gets the virtual folder for a view. Specify a userId to get a user view, or omit for the default view.
+ * Gets the virtual folder list
*/
self.getVirtualFolders = function (userId) {
@@ -1477,16 +1477,16 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Removes a virtual folder from either the default view or a user view
+ * Removes a virtual folder
* @param {String} name
*/
- self.removeVirtualFolder = function (name, userId, refreshLibrary) {
+ self.removeVirtualFolder = function (name, refreshLibrary) {
if (!name) {
throw new Error("null name");
}
- var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/VirtualFolders";
+ var url = "Library/VirtualFolders";
url = self.getUrl(url, {
refreshLibrary: refreshLibrary ? true : false,
@@ -1500,10 +1500,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Adds a virtual folder to either the default view or a user view
+ * Adds a virtual folder
* @param {String} name
*/
- self.addVirtualFolder = function (name, type, userId, refreshLibrary) {
+ self.addVirtualFolder = function (name, type, refreshLibrary) {
if (!name) {
throw new Error("null name");
@@ -1518,7 +1518,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
options.refreshLibrary = refreshLibrary ? true : false;
options.name = name;
- var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/VirtualFolders";
+ var url = "Library/VirtualFolders";
url = self.getUrl(url, options);
@@ -1529,18 +1529,16 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Renames a virtual folder, within either the default view or a user view
+ * Renames a virtual folder
* @param {String} name
*/
- self.renameVirtualFolder = function (name, newName, userId, refreshLibrary) {
+ self.renameVirtualFolder = function (name, newName, refreshLibrary) {
if (!name) {
throw new Error("null name");
}
- var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/VirtualFolders";
-
- url += "/Name";
+ var url = "Library/VirtualFolders/Name";
url = self.getUrl(url, {
refreshLibrary: refreshLibrary ? true : false,
@@ -1555,10 +1553,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Adds an additional mediaPath to an existing virtual folder, within either the default view or a user view
+ * Adds an additional mediaPath to an existing virtual folder
* @param {String} name
*/
- self.addMediaPath = function (virtualFolderName, mediaPath, userId, refreshLibrary) {
+ self.addMediaPath = function (virtualFolderName, mediaPath, refreshLibrary) {
if (!virtualFolderName) {
throw new Error("null virtualFolderName");
@@ -1568,9 +1566,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
throw new Error("null mediaPath");
}
- var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/VirtualFolders";
-
- url += "/Paths";
+ var url = "Library/VirtualFolders/Paths";
url = self.getUrl(url, {
refreshLibrary: refreshLibrary ? true : false,
@@ -1585,10 +1581,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Removes a media path from a virtual folder, within either the default view or a user view
+ * Removes a media path from a virtual folder
* @param {String} name
*/
- self.removeMediaPath = function (virtualFolderName, mediaPath, userId, refreshLibrary) {
+ self.removeMediaPath = function (virtualFolderName, mediaPath, refreshLibrary) {
if (!virtualFolderName) {
throw new Error("null virtualFolderName");
@@ -1598,9 +1594,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
throw new Error("null mediaPath");
}
- var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/VirtualFolders";
-
- url += "/Paths";
+ var url = "Library/VirtualFolders/Paths";
url = self.getUrl(url, {
refreshLibrary: refreshLibrary ? true : false,
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 4957f3563..0bdea523e 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.244" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.245" targetFramework="net45" />
</packages> \ No newline at end of file