diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index c95df7db31..6e214f960c 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -123,7 +123,6 @@ - diff --git a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs deleted file mode 100644 index 1c32082164..0000000000 --- a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs +++ /dev/null @@ -1,264 +0,0 @@ -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using ServiceStack; -using System; -using System.Threading; -using System.Threading.Tasks; -using ServiceStack.Text.Controller; - -namespace MediaBrowser.Api.UserLibrary -{ - /// - /// Class MarkItemByNameFavorite - /// - [Route("/Users/{UserId}/Favorites/Artists/{Name}", "POST")] - [Route("/Users/{UserId}/Favorites/Persons/{Name}", "POST")] - [Route("/Users/{UserId}/Favorites/Studios/{Name}", "POST")] - [Route("/Users/{UserId}/Favorites/Genres/{Name}", "POST")] - [Route("/Users/{UserId}/Favorites/MusicGenres/{Name}", "POST")] - [Route("/Users/{UserId}/Favorites/GameGenres/{Name}", "POST")] - [Api(Description = "Marks something as a favorite")] - public class MarkItemByNameFavorite : IReturn - { - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public Guid UserId { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - } - - /// - /// Class UnmarkItemByNameFavorite - /// - [Route("/Users/{UserId}/Favorites/Artists/{Name}", "DELETE")] - [Route("/Users/{UserId}/Favorites/Persons/{Name}", "DELETE")] - [Route("/Users/{UserId}/Favorites/Studios/{Name}", "DELETE")] - [Route("/Users/{UserId}/Favorites/Genres/{Name}", "DELETE")] - [Route("/Users/{UserId}/Favorites/MusicGenres/{Name}", "DELETE")] - [Route("/Users/{UserId}/Favorites/GameGenres/{Name}", "DELETE")] - [Api(Description = "Unmarks something as a favorite")] - public class UnmarkItemByNameFavorite : IReturn - { - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public Guid UserId { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Name { get; set; } - } - - /// - /// Class UpdateItemByNameRating - /// - [Route("/Users/{UserId}/Ratings/Artists/{Name}", "POST")] - [Route("/Users/{UserId}/Ratings/Persons/{Name}", "POST")] - [Route("/Users/{UserId}/Ratings/Studios/{Name}", "POST")] - [Route("/Users/{UserId}/Ratings/Genres/{Name}", "POST")] - [Route("/Users/{UserId}/Ratings/MusicGenres/{Name}", "POST")] - [Api(Description = "Updates a user's rating for an item")] - public class UpdateItemByNameRating : IReturn - { - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public Guid UserId { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Name { get; set; } - - /// - /// Gets or sets a value indicating whether this is likes. - /// - /// true if likes; otherwise, false. - [ApiMember(Name = "Likes", Description = "Whether the user likes the item or not. true/false", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] - public bool Likes { get; set; } - } - - /// - /// Class DeleteItemByNameRating - /// - [Route("/Users/{UserId}/Ratings/Artists/{Name}", "DELETE")] - [Route("/Users/{UserId}/Ratings/Persons/{Name}", "DELETE")] - [Route("/Users/{UserId}/Ratings/Studios/{Name}", "DELETE")] - [Route("/Users/{UserId}/Ratings/Genres/{Name}", "DELETE")] - [Route("/Users/{UserId}/Ratings/MusicGenres/{Name}", "DELETE")] - [Route("/Users/{UserId}/Ratings/GameGenres/{Name}", "DELETE")] - [Api(Description = "Deletes a user's saved personal rating for an item")] - public class DeleteItemByNameRating : IReturn - { - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public Guid UserId { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Name { get; set; } - } - - /// - /// Class ItemByNameUserDataService - /// - public class ItemByNameUserDataService : BaseApiService - { - /// - /// The user data repository - /// - protected readonly IUserDataManager UserDataRepository; - - /// - /// The library manager - /// - protected readonly ILibraryManager LibraryManager; - private readonly IDtoService _dtoService; - - /// - /// Initializes a new instance of the class. - /// - /// The user data repository. - /// The library manager. - public ItemByNameUserDataService(IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService) - { - UserDataRepository = userDataRepository; - LibraryManager = libraryManager; - _dtoService = dtoService; - } - - /// - /// Posts the specified request. - /// - /// The request. - public object Post(MarkItemByNameFavorite request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(3); - - var task = MarkFavorite(request.UserId, type, request.Name, true); - - return ToOptimizedResult(task.Result); - } - - /// - /// Posts the specified request. - /// - /// The request. - public object Post(UpdateItemByNameRating request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(3); - - var task = MarkLike(request.UserId, type, request.Name, request.Likes); - - return ToOptimizedResult(task.Result); - } - - /// - /// Deletes the specified request. - /// - /// The request. - public object Delete(UnmarkItemByNameFavorite request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(3); - - var task = MarkFavorite(request.UserId, type, request.Name, false); - - return ToOptimizedResult(task.Result); - } - - /// - /// Deletes the specified request. - /// - /// The request. - public object Delete(DeleteItemByNameRating request) - { - var pathInfo = PathInfo.Parse(Request.PathInfo); - var type = pathInfo.GetArgumentValue(3); - - var task = MarkLike(request.UserId, type, request.Name, null); - - return ToOptimizedResult(task.Result); - } - - /// - /// Marks the favorite. - /// - /// The user id. - /// The type. - /// The name. - /// if set to true [is favorite]. - /// Task. - protected async Task MarkFavorite(Guid userId, string type, string name, bool isFavorite) - { - var item = GetItemByName(name, type, LibraryManager); - - var key = item.GetUserDataKey(); - - // Get the user data for this item - var data = UserDataRepository.GetUserData(userId, key); - - // Set favorite status - data.IsFavorite = isFavorite; - - await UserDataRepository.SaveUserData(userId, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None).ConfigureAwait(false); - - data = UserDataRepository.GetUserData(userId, key); - - return _dtoService.GetUserItemDataDto(data); - } - - /// - /// Marks the like. - /// - /// The user id. - /// The type. - /// The name. - /// if set to true [likes]. - /// Task. - protected async Task MarkLike(Guid userId, string type, string name, bool? likes) - { - var item = GetItemByName(name, type, LibraryManager); - - var key = item.GetUserDataKey(); - - // Get the user data for this item - var data = UserDataRepository.GetUserData(userId, key); - - data.Likes = likes; - - await UserDataRepository.SaveUserData(userId, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None).ConfigureAwait(false); - - data = UserDataRepository.GetUserData(userId, key); - - return _dtoService.GetUserItemDataDto(data); - } - } -} diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 6472b988a2..b56abcc805 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -424,7 +424,7 @@ namespace MediaBrowser.Controller.Entities { BaseItem currentChild; - if (currentChildren.TryGetValue(child.Id, out currentChild)) + if (currentChildren.TryGetValue(child.Id, out currentChild) && child.IsInMixedFolder == currentChild.IsInMixedFolder) { var currentChildLocationType = currentChild.LocationType; if (currentChildLocationType != LocationType.Remote && @@ -433,7 +433,6 @@ namespace MediaBrowser.Controller.Entities currentChild.DateModified = child.DateModified; } - currentChild.IsInMixedFolder = child.IsInMixedFolder; currentChild.IsOffline = false; } else diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 1e54e2dae5..d4d2a81240 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -818,15 +818,6 @@ namespace MediaBrowser.Model.ApiClient /// name string GetPersonImageUrl(string name, ImageOptions options); - /// - /// Gets the year image URL. - /// - /// The item. - /// The options. - /// System.String. - /// item - string GetYearImageUrl(BaseItemDto item, ImageOptions options); - /// /// Gets an image url that can be used to download an image from the api /// diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 773ad354c6..7610f72490 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -119,13 +119,13 @@ namespace MediaBrowser.Providers.Manager var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true); - await _providerManager.SaveImage((BaseItem)item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } else { var mimeType = "image/" + response.Format.ToString().ToLower(); - await _providerManager.SaveImage((BaseItem)item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; @@ -325,7 +325,7 @@ namespace MediaBrowser.Providers.Manager { var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); - await _providerManager.SaveImage((BaseItem)item, response.Content, response.ContentType, type, null, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, response.Content, response.ContentType, type, null, cancellationToken).ConfigureAwait(false); result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; break; @@ -362,7 +362,7 @@ namespace MediaBrowser.Providers.Manager { var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); - await _providerManager.SaveImage((BaseItem)item, response.Content, response.ContentType, imageType, null, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, response.Content, response.ContentType, imageType, null, cancellationToken).ConfigureAwait(false); result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; break; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index e79fa65fb5..044973064e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -183,7 +183,10 @@ namespace MediaBrowser.Providers.MediaInfo audio.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); // Several different forms of retaildate - audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date"); + audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "date"); // If we don't have a ProductionYear try and get it from PremiereDate if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index f55fc4cf56..551b417cb4 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -516,8 +516,6 @@ namespace MediaBrowser.Providers.MediaInfo var path = mount == null ? item.Path : mount.MountedPath; var dvd = new Dvd(path); - item.RunTimeTicks = dvd.Titles.Select(GetRuntime).Max(); - var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault(); uint? titleNumber = null; @@ -525,6 +523,7 @@ namespace MediaBrowser.Providers.MediaInfo if (primaryTitle != null) { titleNumber = primaryTitle.TitleNumber; + item.RunTimeTicks = GetRuntime(primaryTitle); } item.PlayableStreamFileNames = GetPrimaryPlaylistVobFiles(item, mount, titleNumber) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 5bc1ff45a6..b59e95c727 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -279,7 +279,7 @@ namespace MediaBrowser.Server.Implementations.Library if (ibnPathChanged) { - _itemsByName.Clear(); + RemoveItemsByNameFromCache(); } var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName; @@ -302,6 +302,32 @@ namespace MediaBrowser.Server.Implementations.Library }); } + private void RemoveItemsByNameFromCache() + { + RemoveItemsFromCache(i => i is Person); + RemoveItemsFromCache(i => i is Year); + RemoveItemsFromCache(i => i is Genre); + RemoveItemsFromCache(i => i is MusicGenre); + RemoveItemsFromCache(i => i is GameGenre); + RemoveItemsFromCache(i => i is Studio); + RemoveItemsFromCache(i => + { + var artist = i as MusicArtist; + return artist != null && artist.IsAccessedByName; + }); + } + + private void RemoveItemsFromCache(Func remove) + { + var items = _libraryItemsCache.ToList().Where(i => remove(i.Value)).ToList(); + + foreach (var item in items) + { + BaseItem value; + _libraryItemsCache.TryRemove(item.Key, out value); + } + } + /// /// Updates the season zero names. /// @@ -378,28 +404,17 @@ namespace MediaBrowser.Server.Implementations.Library /// The item. private void UpdateItemInLibraryCache(BaseItem item) { - if (item is IItemByName) - { - var hasDualAccess = item as IHasDualAccess; - if (hasDualAccess != null) - { - if (hasDualAccess.IsAccessedByName) - { - return; - } - } - else - { - return; - } - } - RegisterItem(item); } public void RegisterItem(BaseItem item) { - LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; }); + RegisterItem(item.Id, item); + } + + private void RegisterItem(Guid id, BaseItem item) + { + LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; }); } public async Task DeleteItem(BaseItem item, DeleteOptions options) @@ -580,7 +595,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) @@ -790,11 +805,6 @@ namespace MediaBrowser.Server.Implementations.Library return GetItemByName(ConfigurationManager.ApplicationPaths.ArtistsPath, name); } - /// - /// The images by name item cache - /// - private readonly ConcurrentDictionary _itemsByName = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private T GetItemByName(string path, string name) where T : BaseItem, new() { @@ -812,24 +822,26 @@ namespace MediaBrowser.Server.Implementations.Library string subFolderPrefix = null; - if (typeof(T) == typeof(Person) && ConfigurationManager.Configuration.EnablePeoplePrefixSubFolders) + var type = typeof(T); + + if (type == typeof(Person) && ConfigurationManager.Configuration.EnablePeoplePrefixSubFolders) { subFolderPrefix = validFilename.Substring(0, 1); } - var key = string.IsNullOrEmpty(subFolderPrefix) ? + var fullPath = string.IsNullOrEmpty(subFolderPrefix) ? Path.Combine(path, validFilename) : Path.Combine(path, subFolderPrefix, validFilename); + var id = fullPath.GetMBId(type); + BaseItem obj; - if (!_itemsByName.TryGetValue(key, out obj)) + if (!_libraryItemsCache.TryGetValue(id, out obj)) { - var tuple = CreateItemByName(key, name); + obj = CreateItemByName(fullPath, name, id); - obj = tuple.Item2; - - _itemsByName.AddOrUpdate(key, obj, (keyName, oldValue) => obj); + RegisterItem(id, obj); } return obj as T; @@ -843,7 +855,7 @@ namespace MediaBrowser.Server.Implementations.Library /// The name. /// Task{``0}. /// Path not created: + path - private Tuple CreateItemByName(string path, string name) + private T CreateItemByName(string path, string name, Guid id) where T : BaseItem, new() { var isArtist = typeof(T) == typeof(MusicArtist); @@ -856,7 +868,7 @@ namespace MediaBrowser.Server.Implementations.Library if (existing != null) { - return new Tuple(false, existing); + return existing; } } @@ -877,10 +889,6 @@ namespace MediaBrowser.Server.Implementations.Library isNew = true; } - var type = typeof(T); - - var id = path.GetMBId(type); - var item = isNew ? null : RetrieveItem(id) as T; if (item == null) @@ -893,7 +901,6 @@ namespace MediaBrowser.Server.Implementations.Library DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo), Path = path }; - isNew = true; } if (isArtist) @@ -901,7 +908,7 @@ namespace MediaBrowser.Server.Implementations.Library (item as MusicArtist).IsAccessedByName = true; } - return new Tuple(isNew, item); + return item; } ///