diff --git a/MediaBrowser.Api/AlbumsService.cs b/MediaBrowser.Api/AlbumsService.cs index 7ffe8b600f..17eec73d38 100644 --- a/MediaBrowser.Api/AlbumsService.cs +++ b/MediaBrowser.Api/AlbumsService.cs @@ -29,12 +29,14 @@ namespace MediaBrowser.Api /// The _library manager /// private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; - public AlbumsService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager) + public AlbumsService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; + _itemRepo = itemRepo; } /// @@ -45,6 +47,7 @@ namespace MediaBrowser.Api public object Get(GetSimilarAlbums request) { var result = SimilarItemsHelper.GetSimilarItems(_userManager, + _itemRepo, _libraryManager, _userDataRepository, Logger, diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs index 634e79de83..fea34ec19a 100644 --- a/MediaBrowser.Api/DisplayPreferencesService.cs +++ b/MediaBrowser.Api/DisplayPreferencesService.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Serialization; using ServiceStack.ServiceHost; @@ -43,7 +43,7 @@ namespace MediaBrowser.Api /// /// The _display preferences manager /// - private readonly IDisplayPreferencesManager _displayPreferencesManager; + private readonly IDisplayPreferencesRepository _displayPreferencesManager; /// /// The _json serializer /// @@ -54,7 +54,7 @@ namespace MediaBrowser.Api /// /// The json serializer. /// The display preferences manager. - public DisplayPreferencesService(IJsonSerializer jsonSerializer, IDisplayPreferencesManager displayPreferencesManager) + public DisplayPreferencesService(IJsonSerializer jsonSerializer, IDisplayPreferencesRepository displayPreferencesManager) { _jsonSerializer = jsonSerializer; _displayPreferencesManager = displayPreferencesManager; @@ -66,9 +66,9 @@ namespace MediaBrowser.Api /// The request. public object Get(GetDisplayPreferences request) { - var task = _displayPreferencesManager.GetDisplayPreferences(request.Id); + var result = _displayPreferencesManager.GetDisplayPreferences(request.Id); - return ToOptimizedResult(task.Result); + return ToOptimizedResult(result); } /// diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 7dc19a9372..49c24fe512 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -33,17 +33,21 @@ namespace MediaBrowser.Api /// private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The user manager. /// The user data repository. /// The library manager. - public GamesService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager) + /// The item repo. + public GamesService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; + _itemRepo = itemRepo; } /// @@ -54,6 +58,7 @@ namespace MediaBrowser.Api public object Get(GetSimilarGames request) { var result = SimilarItemsHelper.GetSimilarItems(_userManager, + _itemRepo, _libraryManager, _userDataRepository, Logger, diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 56a1e1e170..673593d824 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -8,6 +8,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -291,6 +292,8 @@ namespace MediaBrowser.Api.Images private readonly IProviderManager _providerManager; + private readonly IItemRepository _itemRepo; + /// /// Initializes a new instance of the class. /// @@ -298,12 +301,13 @@ namespace MediaBrowser.Api.Images /// The library manager. /// The app paths. /// The provider manager. - public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager) + public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo) { _userManager = userManager; _libraryManager = libraryManager; _appPaths = appPaths; _providerManager = providerManager; + _itemRepo = itemRepo; } /// @@ -404,7 +408,7 @@ namespace MediaBrowser.Api.Images { index = 0; - foreach (var chapter in video.Chapters) + foreach (var chapter in _itemRepo.GetChapters(video.Id)) { if (!string.IsNullOrEmpty(chapter.ImagePath)) { diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index f1338c44b2..ef4602b8f6 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -429,7 +429,7 @@ namespace MediaBrowser.Api .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) .ToList(); - var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository); + var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo); var items = _itemRepo.RetrieveItems private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// /// Initializes a new instance of the class. /// /// The user manager. /// The user data repository. /// The library manager. - public MoviesService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager) + public MoviesService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; + _itemRepo = itemRepo; } /// @@ -62,6 +65,7 @@ namespace MediaBrowser.Api public object Get(GetSimilarMovies request) { var result = SimilarItemsHelper.GetSimilarItems(_userManager, + _itemRepo, _libraryManager, _userDataRepository, Logger, diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index b6a7c72046..1c4c3d70e8 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; @@ -47,8 +48,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// The library manager. /// The iso manager. /// The media encoder. - public AudioService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder) + public AudioService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 961c8770c9..c4f258d8aa 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using System; @@ -23,9 +24,12 @@ namespace MediaBrowser.Api.Playback.Progressive /// public abstract class BaseProgressiveStreamingService : BaseStreamingService { - protected BaseProgressiveStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder) : + protected readonly IItemRepository ItemRepository; + + protected BaseProgressiveStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepository) : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder) { + ItemRepository = itemRepository; } /// @@ -304,7 +308,7 @@ namespace MediaBrowser.Api.Playback.Progressive } } - return new ImageService(UserManager, LibraryManager, ApplicationPaths, null) + return new ImageService(UserManager, LibraryManager, ApplicationPaths, null, ItemRepository) { Logger = Logger, RequestContext = RequestContext, diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 80ea77d8e6..742fba1c34 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using ServiceStack.ServiceHost; using System; using System.IO; @@ -59,8 +60,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// The library manager. /// The iso manager. /// The media encoder. - public VideoService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder) + public VideoService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo) { } diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index c96fc504f0..06bdfe49b9 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -71,6 +71,7 @@ namespace MediaBrowser.Api /// Gets the similar items. /// /// The user manager. + /// The item repository. /// The library manager. /// The user data repository. /// The logger. @@ -78,7 +79,7 @@ namespace MediaBrowser.Api /// The include in search. /// The get similarity score. /// ItemsResult. - internal static ItemsResult GetSimilarItems(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, ILogger logger, BaseGetSimilarItems request, Func includeInSearch, Func getSimilarityScore) + internal static ItemsResult GetSimilarItems(IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataRepository userDataRepository, ILogger logger, BaseGetSimilarItems request, Func includeInSearch, Func getSimilarityScore) { var user = request.UserId.HasValue ? userManager.GetUserById(request.UserId.Value) : null; @@ -88,7 +89,7 @@ namespace MediaBrowser.Api var fields = request.GetItemFields().ToList(); - var dtoBuilder = new DtoBuilder(logger, libraryManager, userDataRepository); + var dtoBuilder = new DtoBuilder(logger, libraryManager, userDataRepository, itemRepository); var inputItems = user == null ? libraryManager.RootFolder.RecursiveChildren diff --git a/MediaBrowser.Api/TrailersService.cs b/MediaBrowser.Api/TrailersService.cs index bc6313de06..777aced076 100644 --- a/MediaBrowser.Api/TrailersService.cs +++ b/MediaBrowser.Api/TrailersService.cs @@ -34,17 +34,20 @@ namespace MediaBrowser.Api /// private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// /// Initializes a new instance of the class. /// /// The user manager. /// The user data repository. /// The library manager. - public TrailersService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager) + public TrailersService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; + _itemRepo = itemRepo; } /// @@ -55,6 +58,7 @@ namespace MediaBrowser.Api public object Get(GetSimilarTrailers request) { var result = SimilarItemsHelper.GetSimilarItems(_userManager, + _itemRepo, _libraryManager, _userDataRepository, Logger, diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 09bdc05479..3f3259171e 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -90,17 +90,20 @@ namespace MediaBrowser.Api /// private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// /// Initializes a new instance of the class. /// /// The user manager. /// The user data repository. /// The library manager. - public TvShowsService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager) + public TvShowsService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; + _itemRepo = itemRepo; } /// @@ -111,6 +114,7 @@ namespace MediaBrowser.Api public object Get(GetSimilarShows request) { var result = SimilarItemsHelper.GetSimilarItems(_userManager, + _itemRepo, _libraryManager, _userDataRepository, Logger, @@ -239,7 +243,7 @@ namespace MediaBrowser.Api /// Task. private Task GetItemDtos(IEnumerable pagedItems, User user, List fields) { - var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository); + var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo); return Task.WhenAll(pagedItems.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user))); } diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index fc981d0ded..7a027a0527 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -69,13 +69,14 @@ namespace MediaBrowser.Api.UserLibrary public class ArtistsService : BaseItemsByNameService { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The user manager. /// The library manager. /// The user data repository. - public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + /// The item repo. + public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + : base(userManager, libraryManager, userDataRepository, itemRepo) { } @@ -103,7 +104,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository); if (request.UserId.HasValue) { diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 65ec74bcf5..b93d339ced 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -29,6 +29,7 @@ namespace MediaBrowser.Api.UserLibrary /// protected readonly ILibraryManager LibraryManager; protected readonly IUserDataRepository UserDataRepository; + protected readonly IItemRepository ItemRepository; /// /// Initializes a new instance of the class. @@ -36,11 +37,12 @@ namespace MediaBrowser.Api.UserLibrary /// The user manager. /// The library manager. /// The user data repository. - protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) + protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepository) { UserManager = userManager; LibraryManager = libraryManager; UserDataRepository = userDataRepository; + ItemRepository = itemRepository; } /// @@ -265,8 +267,8 @@ namespace MediaBrowser.Api.UserLibrary return null; } - var dto = user == null ? await new DtoBuilder(Logger, LibraryManager, UserDataRepository).GetBaseItemDto(item, fields).ConfigureAwait(false) : - await new DtoBuilder(Logger, LibraryManager, UserDataRepository).GetBaseItemDto(item, fields, user).ConfigureAwait(false); + var dto = user == null ? await new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository).GetBaseItemDto(item, fields).ConfigureAwait(false) : + await new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository).GetBaseItemDto(item, fields, user).ConfigureAwait(false); if (fields.Contains(ItemFields.ItemCounts)) { diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index d44394c4fd..7c49501abd 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -69,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary /// public class GenresService : BaseItemsByNameService { - public GenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + public GenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + : base(userManager, libraryManager, userDataRepository, itemRepo) { } @@ -98,7 +98,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository); if (request.UserId.HasValue) { diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index a4ed0396ef..b96b94823e 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -204,6 +204,8 @@ namespace MediaBrowser.Api.UserLibrary private readonly ILibrarySearchEngine _searchEngine; private readonly ILocalizationManager _localization; + private readonly IItemRepository _itemRepo; + /// /// Initializes a new instance of the class. /// @@ -211,13 +213,14 @@ namespace MediaBrowser.Api.UserLibrary /// The library manager. /// The search engine. /// The user data repository. - public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ILibrarySearchEngine searchEngine, IUserDataRepository userDataRepository, ILocalizationManager localization) + public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ILibrarySearchEngine searchEngine, IUserDataRepository userDataRepository, ILocalizationManager localization, IItemRepository itemRepo) { _userManager = userManager; _libraryManager = libraryManager; _searchEngine = searchEngine; _userDataRepository = userDataRepository; _localization = localization; + _itemRepo = itemRepo; } /// @@ -266,7 +269,7 @@ namespace MediaBrowser.Api.UserLibrary var fields = request.GetItemFields().ToList(); - var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository); + var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo); var returnItems = await Task.WhenAll(pagedItems.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user))).ConfigureAwait(false); @@ -663,18 +666,6 @@ namespace MediaBrowser.Api.UserLibrary return item.ScreenshotImagePaths != null && item.ScreenshotImagePaths.Count > 0; } - if (imageType == ImageType.Chapter) - { - var video = item as Video; - - if (video != null) - { - return video.Chapters != null && video.Chapters.Any(c => !string.IsNullOrEmpty(c.ImagePath)); - } - - return false; - } - return item.HasImage(imageType); } diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index 0b482aaf45..a4c60e2d98 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -63,8 +63,8 @@ namespace MediaBrowser.Api.UserLibrary public class MusicGenresService : BaseItemsByNameService { - public MusicGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + public MusicGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + : base(userManager, libraryManager, userDataRepository, itemRepo) { } @@ -92,7 +92,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository); if (request.UserId.HasValue) { diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 9d0aa88c9d..06aa3111da 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -80,13 +80,14 @@ namespace MediaBrowser.Api.UserLibrary public class PersonsService : BaseItemsByNameService { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The user manager. /// The library manager. /// The user data repository. - public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + /// The item repo. + public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + : base(userManager, libraryManager, userDataRepository, itemRepo) { } @@ -114,7 +115,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository); if (request.UserId.HasValue) { diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index 7b9539a409..687e237bdf 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -70,8 +70,8 @@ namespace MediaBrowser.Api.UserLibrary /// public class StudiosService : BaseItemsByNameService { - public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + : base(userManager, libraryManager, userDataRepository, itemRepo) { } @@ -99,7 +99,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository); if (request.UserId.HasValue) { diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 786eea5b31..197ba1f4a9 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -397,7 +397,7 @@ namespace MediaBrowser.Api.UserLibrary var movie = (Movie)item; - var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository); + var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo); var items = _itemRepo.RetrieveItems private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public YearsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + public YearsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + : base(userManager, libraryManager, userDataRepository, itemRepo) { } @@ -83,7 +83,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository, ItemRepository); if (request.UserId.HasValue) { diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index 3c74a42889..c977cc9f3d 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -60,7 +60,7 @@ namespace MediaBrowser.Api .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) .ToList(); - var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository); + var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo); var video = (Video)item; diff --git a/MediaBrowser.Controller/Drawing/ImageManager.cs b/MediaBrowser.Controller/Drawing/ImageManager.cs index 14994ac5ce..d6bc983c06 100644 --- a/MediaBrowser.Controller/Drawing/ImageManager.cs +++ b/MediaBrowser.Controller/Drawing/ImageManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -65,10 +66,7 @@ namespace MediaBrowser.Controller.Drawing /// private readonly ILogger _logger; - /// - /// The _kernel - /// - private readonly Kernel _kernel; + private readonly IItemRepository _itemRepo; /// /// The _locks @@ -78,13 +76,13 @@ namespace MediaBrowser.Controller.Drawing /// /// Initializes a new instance of the class. /// - /// The kernel. /// The logger. /// The app paths. - public ImageManager(Kernel kernel, ILogger logger, IServerApplicationPaths appPaths) + /// The item repo. + public ImageManager(ILogger logger, IServerApplicationPaths appPaths, IItemRepository itemRepo) { _logger = logger; - _kernel = kernel; + _itemRepo = itemRepo; ImageSizeCache = new FileSystemRepository(Path.Combine(appPaths.ImageCachePath, "image-sizes")); ResizedImageCache = new FileSystemRepository(Path.Combine(appPaths.ImageCachePath, "resized-images")); @@ -437,14 +435,7 @@ namespace MediaBrowser.Controller.Drawing if (imageType == ImageType.Chapter) { - var video = (Video)item; - - if (video.Chapters == null) - { - throw new InvalidOperationException(string.Format("Item {0} does not have any Chapters.", item.Name)); - } - - return video.Chapters[imageIndex].ImagePath; + return _itemRepo.GetChapter(item.Id, imageIndex).ImagePath; } return item.GetImage(imageType); diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs index 1e0e5286fa..11cf5b1529 100644 --- a/MediaBrowser.Controller/Dto/DtoBuilder.cs +++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs @@ -31,12 +31,14 @@ namespace MediaBrowser.Controller.Dto private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IUserDataRepository _userDataRepository; + private readonly IItemRepository _itemRepo; - public DtoBuilder(ILogger logger, ILibraryManager libraryManager, IUserDataRepository userDataRepository) + public DtoBuilder(ILogger logger, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) { _logger = logger; _libraryManager = libraryManager; _userDataRepository = userDataRepository; + _itemRepo = itemRepo; } /// @@ -443,9 +445,9 @@ namespace MediaBrowser.Controller.Dto dto.PartCount = video.AdditionalPartIds.Count + 1; - if (fields.Contains(ItemFields.Chapters) && video.Chapters != null) + if (fields.Contains(ItemFields.Chapters)) { - dto.Chapters = video.Chapters.Select(c => GetChapterInfoDto(c, item)).ToList(); + dto.Chapters = _itemRepo.GetChapters(video.Id).Select(c => GetChapterInfoDto(c, item)).ToList(); } } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index ee717a1918..ec7b712096 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -1,8 +1,7 @@ -using System.Collections; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -24,7 +23,6 @@ namespace MediaBrowser.Controller.Entities public Video() { MediaStreams = new List(); - Chapters = new List(); PlayableStreamFileNames = new List(); AdditionalPartIds = new List(); } @@ -53,12 +51,6 @@ namespace MediaBrowser.Controller.Entities /// The media streams. public List MediaStreams { get; set; } - /// - /// Gets or sets the chapters. - /// - /// The chapters. - public List Chapters { get; set; } - /// /// If the video is a folder-rip, this will hold the file list for the largest playlist /// diff --git a/MediaBrowser.Controller/Library/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/Library/IDisplayPreferencesManager.cs deleted file mode 100644 index f1d782b1d0..0000000000 --- a/MediaBrowser.Controller/Library/IDisplayPreferencesManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using MediaBrowser.Model.Entities; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Library -{ - /// - /// Interface IDisplayPreferencesManager - /// - public interface IDisplayPreferencesManager - { - /// - /// Gets the display preferences. - /// - /// The display preferences id. - /// DisplayPreferences. - Task GetDisplayPreferences(Guid displayPreferencesId); - - /// - /// Saves display preferences for an item - /// - /// The display preferences. - /// The cancellation token. - /// Task. - Task SaveDisplayPreferences(DisplayPreferences displayPreferences, CancellationToken cancellationToken); - } -} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 8765998f38..f49221ce85 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -102,7 +102,6 @@ - diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 4b992fd81f..81ab155486 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.IO; +using System.Collections.Generic; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; @@ -34,6 +36,7 @@ namespace MediaBrowser.Controller.MediaInfo private readonly IServerApplicationPaths _appPaths; private readonly IMediaEncoder _encoder; private readonly ILogger _logger; + private readonly IItemRepository _itemRepo; /// /// Initializes a new instance of the class. @@ -42,13 +45,15 @@ namespace MediaBrowser.Controller.MediaInfo /// The encoder. /// The library manager. /// The logger. + /// The item repo. /// zipClient - public FFMpegManager(IServerApplicationPaths appPaths, IMediaEncoder encoder, ILibraryManager libraryManager, ILogger logger) + public FFMpegManager(IServerApplicationPaths appPaths, IMediaEncoder encoder, ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) { _appPaths = appPaths; _encoder = encoder; _libraryManager = libraryManager; _logger = logger; + _itemRepo = itemRepo; VideoImageCache = new FileSystemRepository(VideoImagesDataPath); SubtitleCache = new FileSystemRepository(SubtitleCachePath); @@ -99,18 +104,14 @@ namespace MediaBrowser.Controller.MediaInfo /// Extracts the chapter images. /// /// The video. - /// The cancellation token. + /// The chapters. /// if set to true [extract images]. - /// if set to true [save item]. + /// if set to true [save chapters]. + /// The cancellation token. /// Task. /// - public async Task PopulateChapterImages(Video video, CancellationToken cancellationToken, bool extractImages, bool saveItem) + public async Task PopulateChapterImages(Video video, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken) { - if (video.Chapters == null) - { - throw new ArgumentNullException(); - } - // Can't extract images if there are no video streams if (video.MediaStreams == null || video.MediaStreams.All(m => m.Type != MediaStreamType.Video)) { @@ -122,7 +123,7 @@ namespace MediaBrowser.Controller.MediaInfo var runtimeTicks = video.RunTimeTicks ?? 0; - foreach (var chapter in video.Chapters) + foreach (var chapter in chapters) { if (chapter.StartPositionTicks >= runtimeTicks) { @@ -186,9 +187,9 @@ namespace MediaBrowser.Controller.MediaInfo } } - if (saveItem && changesMade) + if (saveChapters && changesMade) { - await _libraryManager.UpdateItem(video, CancellationToken.None).ConfigureAwait(false); + await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); } return success; diff --git a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs index 9774bb68e6..4d7345f489 100644 --- a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs +++ b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs @@ -24,6 +24,6 @@ namespace MediaBrowser.Controller.Persistence /// /// The display preferences id. /// Task{DisplayPreferences}. - Task GetDisplayPreferences(Guid displayPreferencesId); + DisplayPreferences GetDisplayPreferences(Guid displayPreferencesId); } } diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index d854d0e207..534e64a3f0 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -51,6 +51,30 @@ namespace MediaBrowser.Controller.Persistence /// The type. /// BaseItem. BaseItem RetrieveItem(Guid id, Type type); + + /// + /// Gets chapters for an item + /// + /// + /// + IEnumerable GetChapters(Guid id); + + /// + /// Gets a single chapter for an item + /// + /// + /// + /// + ChapterInfo GetChapter(Guid id, int index); + + /// + /// Saves the chapters. + /// + /// The id. + /// The chapters. + /// The cancellation token. + /// Task. + Task SaveChapters(Guid id, IEnumerable chapters, CancellationToken cancellationToken); } /// diff --git a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs index 9e3a468c6c..b757a5ff95 100644 --- a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.MediaInfo; @@ -42,53 +41,6 @@ namespace MediaBrowser.Providers.MediaInfo protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// - /// The item. - /// if set to true [force]. - /// The cancellation token. - /// Task{System.Boolean}. - public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - var myItem = (T)item; - - var isoMount = await MountIsoIfNeeded(myItem, cancellationToken).ConfigureAwait(false); - - try - { - OnPreFetch(myItem, isoMount); - - var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - - NormalizeFFProbeResult(result); - - cancellationToken.ThrowIfCancellationRequested(); - - Fetch(myItem, cancellationToken, result, isoMount); - - var video = myItem as Video; - - if (video != null) - { - await Kernel.Instance.FFMpegManager.PopulateChapterImages(video, cancellationToken, false, false).ConfigureAwait(false); - } - - SetLastRefreshed(item, DateTime.UtcNow); - } - finally - { - if (isoMount != null) - { - isoMount.Dispose(); - } - } - - return true; - } - /// /// Gets the media info. /// @@ -99,7 +51,7 @@ namespace MediaBrowser.Providers.MediaInfo /// inputPath /// or /// cache - private async Task GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) + protected async Task GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -141,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo /// Normalizes the FF probe result. /// /// The result. - private void NormalizeFFProbeResult(MediaInfoResult result) + protected void NormalizeFFProbeResult(MediaInfoResult result) { if (result.format != null && result.format.tags != null) { @@ -166,16 +118,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - /// - /// Subclasses must set item values using this - /// - /// The item. - /// The cancellation token. - /// The result. - /// The iso mount. - /// Task. - protected abstract void Fetch(T item, CancellationToken cancellationToken, MediaInfoResult result, IIsoMount isoMount); - /// /// Converts ffprobe stream info to our MediaStream class /// diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs index c182109012..8a91525079 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using System.Threading.Tasks; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -23,6 +24,27 @@ namespace MediaBrowser.Providers.MediaInfo { } + public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + var myItem = (Audio)item; + + OnPreFetch(myItem, null); + + var result = await GetMediaInfo(item, null, cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + NormalizeFFProbeResult(result); + + cancellationToken.ThrowIfCancellationRequested(); + + Fetch(myItem, cancellationToken, result); + + SetLastRefreshed(item, DateTime.UtcNow); + + return true; + } + /// /// Fetches the specified audio. /// @@ -31,7 +53,7 @@ namespace MediaBrowser.Providers.MediaInfo /// The data. /// The iso mount. /// Task. - protected override void Fetch(Audio audio, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount) + protected void Fetch(Audio audio, CancellationToken cancellationToken, MediaInfoResult data) { if (data.streams == null) { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs index 82a6a1d8e5..cfb33d7572 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs @@ -1,6 +1,7 @@ using System.Globalization; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -113,6 +114,39 @@ namespace MediaBrowser.Providers.MediaInfo base.OnPreFetch(item, mount); } + public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + var myItem = (Video)item; + + var isoMount = await MountIsoIfNeeded(myItem, cancellationToken).ConfigureAwait(false); + + try + { + OnPreFetch(myItem, isoMount); + + var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + NormalizeFFProbeResult(result); + + cancellationToken.ThrowIfCancellationRequested(); + + await Fetch(myItem, cancellationToken, result, isoMount).ConfigureAwait(false); + + SetLastRefreshed(item, DateTime.UtcNow); + } + finally + { + if (isoMount != null) + { + isoMount.Dispose(); + } + } + + return true; + } + /// /// Mounts the iso if needed. /// @@ -196,7 +230,7 @@ namespace MediaBrowser.Providers.MediaInfo /// The data. /// The iso mount. /// Task. - protected override void Fetch(Video video, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount) + protected async Task Fetch(Video video, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount) { if (data.format != null) { @@ -216,25 +250,24 @@ namespace MediaBrowser.Providers.MediaInfo .ToList(); } - if (data.Chapters != null) - { - video.Chapters = data.Chapters; - } - - if (video.Chapters == null || video.Chapters.Count == 0) - { - AddDummyChapters(video); - } - + var chapters = data.Chapters ?? new List(); + if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) { var inputPath = isoMount != null ? isoMount.MountedPath : video.Path; - FetchBdInfo(video, inputPath, cancellationToken); + FetchBdInfo(video, chapters, inputPath, cancellationToken); } AddExternalSubtitles(video); FetchWtvInfo(video, data); + + if (chapters.Count == 0) + { + AddDummyChapters(video, chapters); + } + + await Kernel.Instance.FFMpegManager.PopulateChapterImages(video, chapters, false, true, cancellationToken).ConfigureAwait(false); } /// @@ -380,7 +413,8 @@ namespace MediaBrowser.Providers.MediaInfo /// Adds the dummy chapters. /// /// The video. - private void AddDummyChapters(Video video) + /// The chapters. + private void AddDummyChapters(Video video, List chapters) { var runtime = video.RunTimeTicks ?? 0; @@ -392,8 +426,6 @@ namespace MediaBrowser.Providers.MediaInfo long currentChapterTicks = 0; var index = 1; - var chapters = new List(); - while (currentChapterTicks < runtime) { chapters.Add(new ChapterInfo @@ -405,17 +437,16 @@ namespace MediaBrowser.Providers.MediaInfo index++; currentChapterTicks += _dummyChapterDuration; } - - video.Chapters = chapters; } /// /// Fetches the bd info. /// /// The item. + /// The chapters. /// The input path. /// The cancellation token. - private void FetchBdInfo(BaseItem item, string inputPath, CancellationToken cancellationToken) + private void FetchBdInfo(BaseItem item, List chapters, string inputPath, CancellationToken cancellationToken) { var video = (Video)item; @@ -438,7 +469,7 @@ namespace MediaBrowser.Providers.MediaInfo } // Fill video properties from the BDInfo result - Fetch(video, result); + Fetch(video, result, chapters); videoStream = video.MediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); @@ -466,7 +497,8 @@ namespace MediaBrowser.Providers.MediaInfo /// /// The video. /// The stream. - private void Fetch(Video video, BlurayDiscInfo stream) + /// The chapters. + private void Fetch(Video video, BlurayDiscInfo stream, List chapters) { // Check all input for null/empty/zero @@ -481,11 +513,13 @@ namespace MediaBrowser.Providers.MediaInfo if (stream.Chapters != null) { - video.Chapters = stream.Chapters.Select(c => new ChapterInfo + chapters.Clear(); + + chapters.AddRange(stream.Chapters.Select(c => new ChapterInfo { StartPositionTicks = TimeSpan.FromSeconds(c).Ticks - }).ToList(); + })); } } diff --git a/MediaBrowser.Server.Implementations/Library/DisplayPreferencesManager.cs b/MediaBrowser.Server.Implementations/Library/DisplayPreferencesManager.cs deleted file mode 100644 index 57a9c9d785..0000000000 --- a/MediaBrowser.Server.Implementations/Library/DisplayPreferencesManager.cs +++ /dev/null @@ -1,99 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Concurrent; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// - /// Class DisplayPreferencesManager - /// - public class DisplayPreferencesManager : IDisplayPreferencesManager - { - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// The _display preferences - /// - private readonly ConcurrentDictionary> _displayPreferences = new ConcurrentDictionary>(); - - /// - /// Gets the active user repository - /// - /// The display preferences repository. - public IDisplayPreferencesRepository Repository { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public DisplayPreferencesManager(ILogger logger) - { - _logger = logger; - } - - /// - /// Gets the display preferences. - /// - /// The display preferences id. - /// DisplayPreferences. - public Task GetDisplayPreferences(Guid displayPreferencesId) - { - return _displayPreferences.GetOrAdd(displayPreferencesId, keyName => RetrieveDisplayPreferences(displayPreferencesId)); - } - - /// - /// Retrieves the display preferences. - /// - /// The display preferences id. - /// DisplayPreferences. - private async Task RetrieveDisplayPreferences(Guid displayPreferencesId) - { - var displayPreferences = await Repository.GetDisplayPreferences(displayPreferencesId).ConfigureAwait(false); - - return displayPreferences ?? new DisplayPreferences { Id = displayPreferencesId }; - } - - /// - /// Saves display preferences for an item - /// - /// The display preferences. - /// The cancellation token. - /// Task. - public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, CancellationToken cancellationToken) - { - if (displayPreferences == null) - { - throw new ArgumentNullException("displayPreferences"); - } - if (displayPreferences.Id == Guid.Empty) - { - throw new ArgumentNullException("displayPreferences.Id"); - } - - try - { - await Repository.SaveDisplayPreferences(displayPreferences, - cancellationToken).ConfigureAwait(false); - - var newValue = Task.FromResult(displayPreferences); - - // Once it succeeds, put it into the dictionary to make it available to everyone else - _displayPreferences.AddOrUpdate(displayPreferences.Id, newValue, delegate { return newValue; }); - } - catch (Exception ex) - { - _logger.ErrorException("Error saving display preferences", ex); - - throw; - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index fc7e1e6bad..34930b34e9 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -119,7 +119,6 @@ - @@ -139,8 +138,8 @@ + - diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs new file mode 100644 index 0000000000..dd6343a677 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs @@ -0,0 +1,326 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SQLite; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Persistence +{ + public class SqliteChapterRepository + { + private SQLiteConnection _connection; + + private readonly ILogger _logger; + + /// + /// The _app paths + /// + private readonly IApplicationPaths _appPaths; + + private SQLiteCommand _deleteChaptersCommand; + private SQLiteCommand _saveChapterCommand; + + /// + /// Initializes a new instance of the class. + /// + /// The app paths. + /// The json serializer. + /// The log manager. + /// + /// appPaths + /// or + /// jsonSerializer + /// + public SqliteChapterRepository(IApplicationPaths appPaths, ILogManager logManager) + { + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + + _appPaths = appPaths; + + _logger = logManager.GetLogger(GetType().Name); + } + + /// + /// Opens the connection to the database + /// + /// Task. + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "chapters.db"); + + _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); + + string[] queries = { + + "create table if not exists chapters (ItemId GUID, ChapterIndex INT, StartPositionTicks BIGINT, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))", + "create index if not exists idx_chapters on chapters(ItemId, ChapterIndex)", + + //pragmas + "pragma temp_store = memory" + }; + + _connection.RunQueries(queries, _logger); + + PrepareStatements(); + } + + /// + /// The _write lock + /// + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + + /// + /// Prepares the statements. + /// + private void PrepareStatements() + { + _deleteChaptersCommand = new SQLiteCommand + { + CommandText = "delete from chapters where ItemId=@ItemId" + }; + + _deleteChaptersCommand.Parameters.Add(new SQLiteParameter("@ItemId")); + + _saveChapterCommand = new SQLiteCommand + { + CommandText = "replace into chapters (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath)" + }; + + _saveChapterCommand.Parameters.Add(new SQLiteParameter("@ItemId")); + _saveChapterCommand.Parameters.Add(new SQLiteParameter("@ChapterIndex")); + _saveChapterCommand.Parameters.Add(new SQLiteParameter("@StartPositionTicks")); + _saveChapterCommand.Parameters.Add(new SQLiteParameter("@Name")); + _saveChapterCommand.Parameters.Add(new SQLiteParameter("@ImagePath")); + } + + /// + /// Gets chapters for an item + /// + /// The id. + /// IEnumerable{ChapterInfo}. + /// id + public IEnumerable GetChapters(Guid id) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select StartPositionTicks,Name,ImagePath from Chapters where ItemId = @ItemId order by ChapterIndex asc"; + + cmd.Parameters.Add("@ItemId", DbType.Guid).Value = id; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + var chapter = new ChapterInfo + { + StartPositionTicks = reader.GetInt64(0) + }; + + if (!reader.IsDBNull(1)) + { + chapter.Name = reader.GetString(1); + } + + if (!reader.IsDBNull(2)) + { + chapter.ImagePath = reader.GetString(2); + } + + yield return chapter; + } + } + } + } + + /// + /// Gets a single chapter for an item + /// + /// The id. + /// The index. + /// ChapterInfo. + /// id + public ChapterInfo GetChapter(Guid id, int index) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select StartPositionTicks,Name,ImagePath from Chapters where ItemId = @ItemId and ChapterIndex=@ChapterIndex"; + + cmd.Parameters.Add("@ItemId", DbType.Guid).Value = id; + cmd.Parameters.Add("@ChapterIndex", DbType.Int32).Value = index; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + return new ChapterInfo + { + StartPositionTicks = reader.GetInt64(0), + Name = reader.GetString(1), + ImagePath = reader.GetString(2) + }; + } + } + return null; + } + } + + /// + /// Saves the chapters. + /// + /// The id. + /// The chapters. + /// The cancellation token. + /// Task. + /// + /// id + /// or + /// chapters + /// or + /// cancellationToken + /// + public async Task SaveChapters(Guid id, IEnumerable chapters, CancellationToken cancellationToken) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + if (chapters == null) + { + throw new ArgumentNullException("chapters"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + SQLiteTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + // First delete chapters + _deleteChaptersCommand.Parameters[0].Value = id; + _deleteChaptersCommand.Transaction = transaction; + await _deleteChaptersCommand.ExecuteNonQueryAsync(cancellationToken); + + var index = 0; + + foreach (var chapter in chapters) + { + cancellationToken.ThrowIfCancellationRequested(); + + _saveChapterCommand.Parameters[0].Value = id; + _saveChapterCommand.Parameters[1].Value = index; + _saveChapterCommand.Parameters[2].Value = chapter.StartPositionTicks; + _saveChapterCommand.Parameters[3].Value = chapter.Name; + _saveChapterCommand.Parameters[4].Value = chapter.ImagePath; + + _saveChapterCommand.Transaction = transaction; + + await _saveChapterCommand.ExecuteNonQueryAsync(cancellationToken); + + index++; + } + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save chapters:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + try + { + lock (_disposeLock) + { + if (_connection != null) + { + if (_connection.IsOpen()) + { + _connection.Close(); + } + + _connection.Dispose(); + _connection = null; + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing database", ex); + } + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs index f4d341c347..cb965c3f91 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs @@ -15,13 +15,12 @@ namespace MediaBrowser.Server.Implementations.Persistence /// /// Class SQLiteDisplayPreferencesRepository /// - public class SqliteDisplayPreferencesRepository : SqliteRepository, IDisplayPreferencesRepository + public class SqliteDisplayPreferencesRepository : IDisplayPreferencesRepository { - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; + private SQLiteConnection _connection; + private readonly ILogger _logger; + /// /// Gets the name of the repository /// @@ -30,7 +29,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -58,7 +57,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// appPaths /// public SqliteDisplayPreferencesRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) { if (jsonSerializer == null) { @@ -71,6 +69,8 @@ namespace MediaBrowser.Server.Implementations.Persistence _jsonSerializer = jsonSerializer; _appPaths = appPaths; + + _logger = logManager.GetLogger(GetType().Name); } /// @@ -81,7 +81,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db"); - await ConnectToDb(dbFile).ConfigureAwait(false); + _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); string[] queries = { @@ -92,7 +92,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); } /// @@ -127,9 +127,9 @@ namespace MediaBrowser.Server.Implementations.Persistence try { - transaction = Connection.BeginTransaction(); + transaction = _connection.BeginTransaction(); - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)"; cmd.AddParam("@1", displayPreferences.Id); @@ -153,7 +153,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } catch (Exception e) { - Logger.ErrorException("Failed to save display preferences:", e); + _logger.ErrorException("Failed to save display preferences:", e); if (transaction != null) { @@ -179,24 +179,24 @@ namespace MediaBrowser.Server.Implementations.Persistence /// The display preferences id. /// Task{DisplayPreferences}. /// item - public async Task GetDisplayPreferences(Guid displayPreferencesId) + public DisplayPreferences GetDisplayPreferences(Guid displayPreferencesId) { if (displayPreferencesId == Guid.Empty) { throw new ArgumentNullException("displayPreferencesId"); } - var cmd = Connection.CreateCommand(); + var cmd = _connection.CreateCommand(); cmd.CommandText = "select data from displaypreferences where id = @id"; var idParam = cmd.Parameters.Add("@id", DbType.Guid); idParam.Value = displayPreferencesId; - using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow).ConfigureAwait(false)) + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) { if (reader.Read()) { - using (var stream = GetStream(reader, 0)) + using (var stream = reader.GetMemoryStream(0)) { return _jsonSerializer.DeserializeFromStream(stream); } @@ -205,5 +205,47 @@ namespace MediaBrowser.Server.Implementations.Persistence return null; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + try + { + lock (_disposeLock) + { + if (_connection != null) + { + if (_connection.IsOpen()) + { + _connection.Close(); + } + + _connection.Dispose(); + _connection = null; + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing database", ex); + } + } + } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs index 00dbbe513f..2b14e9b248 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs @@ -1,6 +1,9 @@ using System; using System.Data; using System.Data.SQLite; +using System.IO; +using System.Threading.Tasks; +using MediaBrowser.Model.Logging; namespace MediaBrowser.Server.Implementations.Persistence { @@ -57,5 +60,103 @@ namespace MediaBrowser.Server.Implementations.Persistence { return conn.State == ConnectionState.Open; } + + /// + /// Gets a stream from a DataReader at a given ordinal + /// + /// The reader. + /// The ordinal. + /// Stream. + /// reader + public static Stream GetMemoryStream(this IDataReader reader, int ordinal) + { + if (reader == null) + { + throw new ArgumentNullException("reader"); + } + + var memoryStream = new MemoryStream(); + var num = 0L; + var array = new byte[4096]; + long bytes; + do + { + bytes = reader.GetBytes(ordinal, num, array, 0, array.Length); + memoryStream.Write(array, 0, (int)bytes); + num += bytes; + } + while (bytes > 0L); + memoryStream.Position = 0; + return memoryStream; + } + + /// + /// Runs the queries. + /// + /// The connection. + /// The queries. + /// The logger. + /// true if XXXX, false otherwise + /// queries + public static void RunQueries(this IDbConnection connection, string[] queries, ILogger logger) + { + if (queries == null) + { + throw new ArgumentNullException("queries"); + } + + using (var tran = connection.BeginTransaction()) + { + try + { + using (var cmd = connection.CreateCommand()) + { + foreach (var query in queries) + { + cmd.Transaction = tran; + cmd.CommandText = query; + cmd.ExecuteNonQuery(); + } + } + + tran.Commit(); + } + catch (Exception e) + { + logger.ErrorException("Error running queries", e); + tran.Rollback(); + throw; + } + } + } + + /// + /// Connects to db. + /// + /// The db path. + /// Task{IDbConnection}. + /// dbPath + public static async Task ConnectToDb(string dbPath) + { + if (string.IsNullOrEmpty(dbPath)) + { + throw new ArgumentNullException("dbPath"); + } + + var connectionstr = new SQLiteConnectionStringBuilder + { + PageSize = 4096, + CacheSize = 4096, + SyncMode = SynchronizationModes.Off, + DataSource = dbPath, + JournalMode = SQLiteJournalModeEnum.Wal + }; + + var connection = new SQLiteConnection(connectionstr.ConnectionString); + + await connection.OpenAsync().ConfigureAwait(false); + + return connection; + } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index a9cd3d1eb4..b3251ddb94 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -18,13 +18,12 @@ namespace MediaBrowser.Server.Implementations.Persistence /// /// Class SQLiteItemRepository /// - public class SqliteItemRepository : SqliteRepository, IItemRepository + public class SqliteItemRepository : IItemRepository { - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; + private SQLiteConnection _connection; + private readonly ILogger _logger; + /// /// Gets the name of the repository /// @@ -33,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -55,6 +54,8 @@ namespace MediaBrowser.Server.Implementations.Persistence private readonly string _criticReviewsPath; + private SqliteChapterRepository _chapterRepository; + /// /// Initializes a new instance of the class. /// @@ -67,7 +68,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// jsonSerializer /// public SqliteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) { if (appPaths == null) { @@ -82,6 +82,10 @@ namespace MediaBrowser.Server.Implementations.Persistence _jsonSerializer = jsonSerializer; _criticReviewsPath = Path.Combine(_appPaths.DataPath, "critic-reviews"); + + _logger = logManager.GetLogger(GetType().Name); + + _chapterRepository = new SqliteChapterRepository(appPaths, logManager); } /// @@ -92,20 +96,22 @@ namespace MediaBrowser.Server.Implementations.Persistence { var dbFile = Path.Combine(_appPaths.DataPath, "library.db"); - await ConnectToDb(dbFile).ConfigureAwait(false); + _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); string[] queries = { "create table if not exists baseitems (guid GUID primary key, data BLOB)", "create index if not exists idx_baseitems on baseitems(guid)", - "create table if not exists schema_version (table_name primary key, version)", + //pragmas "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); PrepareStatements(); + + await _chapterRepository.Initialize().ConfigureAwait(false); } /// @@ -175,7 +181,7 @@ namespace MediaBrowser.Server.Implementations.Persistence try { - transaction = Connection.BeginTransaction(); + transaction = _connection.BeginTransaction(); foreach (var item in items) { @@ -202,7 +208,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } catch (Exception e) { - Logger.ErrorException("Failed to save items:", e); + _logger.ErrorException("Failed to save items:", e); if (transaction != null) { @@ -237,7 +243,7 @@ namespace MediaBrowser.Server.Implementations.Persistence throw new ArgumentNullException("id"); } - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "select data from baseitems where guid = @guid"; var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); @@ -247,7 +253,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { if (reader.Read()) { - using (var stream = GetStream(reader, 0)) + using (var stream = reader.GetMemoryStream(0)) { return _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem; } @@ -305,5 +311,95 @@ namespace MediaBrowser.Server.Implementations.Persistence _jsonSerializer.SerializeToFile(criticReviews.ToList(), path); }); } + + /// + /// Gets chapters for an item + /// + /// The id. + /// IEnumerable{ChapterInfo}. + /// id + public IEnumerable GetChapters(Guid id) + { + return _chapterRepository.GetChapters(id); + } + + /// + /// Gets a single chapter for an item + /// + /// The id. + /// The index. + /// ChapterInfo. + /// id + public ChapterInfo GetChapter(Guid id, int index) + { + return _chapterRepository.GetChapter(id, index); + } + + /// + /// Saves the chapters. + /// + /// The id. + /// The chapters. + /// The cancellation token. + /// Task. + /// + /// id + /// or + /// chapters + /// or + /// cancellationToken + /// + public Task SaveChapters(Guid id, IEnumerable chapters, CancellationToken cancellationToken) + { + return _chapterRepository.SaveChapters(id, chapters, cancellationToken); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + try + { + lock (_disposeLock) + { + if (_connection != null) + { + if (_connection.IsOpen()) + { + _connection.Close(); + } + + _connection.Dispose(); + _connection = null; + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing database", ex); + } + + if (_chapterRepository != null) + { + _chapterRepository.Dispose(); + _chapterRepository = null; + } + } + } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteRepository.cs deleted file mode 100644 index cfdc9b5fb6..0000000000 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteRepository.cs +++ /dev/null @@ -1,182 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.Data; -using System.Data.SQLite; -using System.IO; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Persistence -{ - /// - /// Class SqliteRepository - /// - public abstract class SqliteRepository : IDisposable - { - /// - /// The db file name - /// - protected string DbFileName; - /// - /// The connection - /// - protected SQLiteConnection Connection; - - /// - /// Gets the logger. - /// - /// The logger. - protected ILogger Logger { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The log manager. - /// logger - protected SqliteRepository(ILogManager logManager) - { - if (logManager == null) - { - throw new ArgumentNullException("logManager"); - } - - Logger = logManager.GetLogger(GetType().Name); - } - - /// - /// Connects to DB. - /// - /// The db path. - /// Task{System.Boolean}. - /// dbPath - protected Task ConnectToDb(string dbPath) - { - if (string.IsNullOrEmpty(dbPath)) - { - throw new ArgumentNullException("dbPath"); - } - - DbFileName = dbPath; - var connectionstr = new SQLiteConnectionStringBuilder - { - PageSize = 4096, - CacheSize = 40960, - SyncMode = SynchronizationModes.Off, - DataSource = dbPath, - JournalMode = SQLiteJournalModeEnum.Wal - }; - - Connection = new SQLiteConnection(connectionstr.ConnectionString); - - return Connection.OpenAsync(); - } - - /// - /// Runs the queries. - /// - /// The queries. - /// true if XXXX, false otherwise - /// queries - protected void RunQueries(string[] queries) - { - if (queries == null) - { - throw new ArgumentNullException("queries"); - } - - using (var tran = Connection.BeginTransaction()) - { - try - { - using (var cmd = Connection.CreateCommand()) - { - foreach (var query in queries) - { - cmd.Transaction = tran; - cmd.CommandText = query; - cmd.ExecuteNonQuery(); - } - } - - tran.Commit(); - } - catch (Exception e) - { - Logger.ErrorException("Error running queries", e); - tran.Rollback(); - throw; - } - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private readonly object _disposeLock = new object(); - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - try - { - lock (_disposeLock) - { - if (Connection != null) - { - if (Connection.IsOpen()) - { - Connection.Close(); - } - - Connection.Dispose(); - Connection = null; - } - } - } - catch (Exception ex) - { - Logger.ErrorException("Error disposing database", ex); - } - } - } - - /// - /// Gets a stream from a DataReader at a given ordinal - /// - /// The reader. - /// The ordinal. - /// Stream. - /// reader - protected static Stream GetStream(IDataReader reader, int ordinal) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - var memoryStream = new MemoryStream(); - var num = 0L; - var array = new byte[4096]; - long bytes; - do - { - bytes = reader.GetBytes(ordinal, num, array, 0, array.Length); - memoryStream.Write(array, 0, (int)bytes); - num += bytes; - } - while (bytes > 0L); - memoryStream.Position = 0; - return memoryStream; - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs index 05829e007b..1d127ae966 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs @@ -13,17 +13,16 @@ using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Persistence { - public class SqliteUserDataRepository : SqliteRepository, IUserDataRepository + public class SqliteUserDataRepository : IUserDataRepository { + private readonly ILogger _logger; + private readonly ConcurrentDictionary _userData = new ConcurrentDictionary(); private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; - + private SQLiteConnection _connection; + /// /// Gets the name of the repository /// @@ -32,7 +31,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -55,7 +54,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// appPaths /// public SqliteUserDataRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) { if (jsonSerializer == null) { @@ -68,6 +66,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _jsonSerializer = jsonSerializer; _appPaths = appPaths; + _logger = logManager.GetLogger(GetType().Name); } /// @@ -78,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { var dbFile = Path.Combine(_appPaths.DataPath, "userdata.db"); - await ConnectToDb(dbFile).ConfigureAwait(false); + _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); string[] queries = { @@ -89,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); } /// @@ -139,7 +138,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } catch (Exception ex) { - Logger.ErrorException("Error saving user data", ex); + _logger.ErrorException("Error saving user data", ex); throw; } @@ -178,9 +177,9 @@ namespace MediaBrowser.Server.Implementations.Persistence try { - transaction = Connection.BeginTransaction(); + transaction = _connection.BeginTransaction(); - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)"; cmd.AddParam("@1", key); @@ -205,7 +204,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } catch (Exception e) { - Logger.ErrorException("Failed to save user data:", e); + _logger.ErrorException("Failed to save user data:", e); if (transaction != null) { @@ -258,7 +257,7 @@ namespace MediaBrowser.Server.Implementations.Persistence /// Task{UserItemData}. private UserItemData RetrieveUserData(Guid userId, string key) { - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "select data from userdata where key = @key and userId=@userId"; @@ -272,7 +271,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { if (reader.Read()) { - using (var stream = GetStream(reader, 0)) + using (var stream = reader.GetMemoryStream(0)) { return _jsonSerializer.DeserializeFromStream(stream); } @@ -282,5 +281,47 @@ namespace MediaBrowser.Server.Implementations.Persistence return new UserItemData(); } } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + try + { + lock (_disposeLock) + { + if (_connection != null) + { + if (_connection.IsOpen()) + { + _connection.Close(); + } + + _connection.Dispose(); + _connection = null; + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing database", ex); + } + } + } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs index efd39529a9..09e34cf08e 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs @@ -16,15 +16,14 @@ namespace MediaBrowser.Server.Implementations.Persistence /// /// Class SQLiteUserRepository /// - public class SqliteUserRepository : SqliteRepository, IUserRepository + public class SqliteUserRepository : IUserRepository { - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; - + private readonly ILogger _logger; + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + private SQLiteConnection _connection; + /// /// Gets the name of the repository /// @@ -33,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -56,7 +55,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// The log manager. /// appPaths public SqliteUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) { if (appPaths == null) { @@ -69,6 +67,8 @@ namespace MediaBrowser.Server.Implementations.Persistence _appPaths = appPaths; _jsonSerializer = jsonSerializer; + + _logger = logManager.GetLogger(GetType().Name); } /// @@ -79,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { var dbFile = Path.Combine(_appPaths.DataPath, "users.db"); - await ConnectToDb(dbFile).ConfigureAwait(false); + _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); string[] queries = { @@ -90,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); } /// @@ -124,9 +124,9 @@ namespace MediaBrowser.Server.Implementations.Persistence try { - transaction = Connection.BeginTransaction(); + transaction = _connection.BeginTransaction(); - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "replace into users (guid, data) values (@1, @2)"; cmd.AddParam("@1", user.Id); @@ -150,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } catch (Exception e) { - Logger.ErrorException("Failed to save user:", e); + _logger.ErrorException("Failed to save user:", e); if (transaction != null) { @@ -176,7 +176,7 @@ namespace MediaBrowser.Server.Implementations.Persistence /// IEnumerable{User}. public IEnumerable RetrieveAllUsers() { - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "select data from users"; @@ -184,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { while (reader.Read()) { - using (var stream = GetStream(reader, 0)) + using (var stream = reader.GetMemoryStream(0)) { var user = _jsonSerializer.DeserializeFromStream(stream); yield return user; @@ -221,9 +221,9 @@ namespace MediaBrowser.Server.Implementations.Persistence try { - transaction = Connection.BeginTransaction(); + transaction = _connection.BeginTransaction(); - using (var cmd = Connection.CreateCommand()) + using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "delete from users where guid=@guid"; @@ -248,7 +248,7 @@ namespace MediaBrowser.Server.Implementations.Persistence } catch (Exception e) { - Logger.ErrorException("Failed to delete user:", e); + _logger.ErrorException("Failed to delete user:", e); if (transaction != null) { @@ -267,5 +267,47 @@ namespace MediaBrowser.Server.Implementations.Persistence _writeLock.Release(); } } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + try + { + lock (_disposeLock) + { + if (_connection != null) + { + if (_connection.IsOpen()) + { + _connection.Close(); + } + + _connection.Dispose(); + _connection = null; + } + } + } + catch (Exception ex) + { + _logger.ErrorException("Error disposing database", ex); + } + } + } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index 2d9d5abfe3..79ea89ac65 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; @@ -45,6 +46,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// The new item timer. private Timer NewItemTimer { get; set; } + private readonly IItemRepository _itemRepo; + /// /// Initializes a new instance of the class. /// @@ -52,12 +55,14 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// The log manager. /// The library manager. /// The json serializer. - public ChapterImagesTask(Kernel kernel, ILogManager logManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer) + /// The item repo. + public ChapterImagesTask(Kernel kernel, ILogManager logManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer, IItemRepository itemRepo) { _kernel = kernel; _logger = logManager.GetLogger(GetType().Name); _libraryManager = libraryManager; _jsonSerializer = jsonSerializer; + _itemRepo = itemRepo; libraryManager.ItemAdded += libraryManager_ItemAdded; libraryManager.ItemUpdated += libraryManager_ItemAdded; @@ -106,7 +111,9 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks { try { - await _kernel.FFMpegManager.PopulateChapterImages(item, CancellationToken.None, true, true); + var chapters = _itemRepo.GetChapters(item.Id).ToList(); + + await _kernel.FFMpegManager.PopulateChapterImages(item, chapters, true, true, CancellationToken.None); } catch (Exception ex) { @@ -137,7 +144,6 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks { var videos = _libraryManager.RootFolder.RecursiveChildren .OfType