diff options
47 files changed, 967 insertions, 599 deletions
diff --git a/MediaBrowser.Api/AlbumsService.cs b/MediaBrowser.Api/AlbumsService.cs index 7ffe8b600..17eec73d3 100644 --- a/MediaBrowser.Api/AlbumsService.cs +++ b/MediaBrowser.Api/AlbumsService.cs @@ -29,12 +29,14 @@ namespace MediaBrowser.Api /// The _library manager /// </summary> 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; } /// <summary> @@ -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 634e79de8..fea34ec19 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 /// <summary> /// The _display preferences manager /// </summary> - private readonly IDisplayPreferencesManager _displayPreferencesManager; + private readonly IDisplayPreferencesRepository _displayPreferencesManager; /// <summary> /// The _json serializer /// </summary> @@ -54,7 +54,7 @@ namespace MediaBrowser.Api /// </summary> /// <param name="jsonSerializer">The json serializer.</param> /// <param name="displayPreferencesManager">The display preferences manager.</param> - public DisplayPreferencesService(IJsonSerializer jsonSerializer, IDisplayPreferencesManager displayPreferencesManager) + public DisplayPreferencesService(IJsonSerializer jsonSerializer, IDisplayPreferencesRepository displayPreferencesManager) { _jsonSerializer = jsonSerializer; _displayPreferencesManager = displayPreferencesManager; @@ -66,9 +66,9 @@ namespace MediaBrowser.Api /// <param name="request">The request.</param> public object Get(GetDisplayPreferences request) { - var task = _displayPreferencesManager.GetDisplayPreferences(request.Id); + var result = _displayPreferencesManager.GetDisplayPreferences(request.Id); - return ToOptimizedResult(task.Result); + return ToOptimizedResult(result); } /// <summary> diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 7dc19a937..49c24fe51 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -33,17 +33,21 @@ namespace MediaBrowser.Api /// </summary> private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// <summary> - /// Initializes a new instance of the <see cref="GamesService"/> class. + /// Initializes a new instance of the <see cref="GamesService" /> class. /// </summary> /// <param name="userManager">The user manager.</param> /// <param name="userDataRepository">The user data repository.</param> /// <param name="libraryManager">The library manager.</param> - public GamesService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager) + /// <param name="itemRepo">The item repo.</param> + public GamesService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; + _itemRepo = itemRepo; } /// <summary> @@ -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 56a1e1e17..673593d82 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; + /// <summary> /// Initializes a new instance of the <see cref="ImageService" /> class. /// </summary> @@ -298,12 +301,13 @@ namespace MediaBrowser.Api.Images /// <param name="libraryManager">The library manager.</param> /// <param name="appPaths">The app paths.</param> /// <param name="providerManager">The provider manager.</param> - 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; } /// <summary> @@ -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 f1338c44b..ef4602b8f 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<Audio>(item.ThemeSongIds) .OrderBy(i => i.SortName) @@ -468,7 +468,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<Video>(item.ThemeVideoIds) diff --git a/MediaBrowser.Api/MoviesService.cs b/MediaBrowser.Api/MoviesService.cs index 9cd7c5b76..fe9f4cef1 100644 --- a/MediaBrowser.Api/MoviesService.cs +++ b/MediaBrowser.Api/MoviesService.cs @@ -41,17 +41,20 @@ namespace MediaBrowser.Api /// </summary> private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// <summary> /// Initializes a new instance of the <see cref="MoviesService"/> class. /// </summary> /// <param name="userManager">The user manager.</param> /// <param name="userDataRepository">The user data repository.</param> /// <param name="libraryManager">The library manager.</param> - 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; } /// <summary> @@ -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 b6a7c7204..1c4c3d70e 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 /// <param name="libraryManager">The library manager.</param> /// <param name="isoManager">The iso manager.</param> /// <param name="mediaEncoder">The media encoder.</param> - 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 961c8770c..c4f258d8a 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 /// </summary> 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; } /// <summary> @@ -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 80ea77d8e..742fba1c3 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 /// <param name="libraryManager">The library manager.</param> /// <param name="isoManager">The iso manager.</param> /// <param name="mediaEncoder">The media encoder.</param> - 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 c96fc504f..06bdfe49b 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -71,6 +71,7 @@ namespace MediaBrowser.Api /// Gets the similar items. /// </summary> /// <param name="userManager">The user manager.</param> + /// <param name="itemRepository">The item repository.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="userDataRepository">The user data repository.</param> /// <param name="logger">The logger.</param> @@ -78,7 +79,7 @@ namespace MediaBrowser.Api /// <param name="includeInSearch">The include in search.</param> /// <param name="getSimilarityScore">The get similarity score.</param> /// <returns>ItemsResult.</returns> - internal static ItemsResult GetSimilarItems(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, ILogger logger, BaseGetSimilarItems request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore) + internal static ItemsResult GetSimilarItems(IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataRepository userDataRepository, ILogger logger, BaseGetSimilarItems request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> 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 bc6313de0..777aced07 100644 --- a/MediaBrowser.Api/TrailersService.cs +++ b/MediaBrowser.Api/TrailersService.cs @@ -34,17 +34,20 @@ namespace MediaBrowser.Api /// </summary> private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// <summary> /// Initializes a new instance of the <see cref="TrailersService"/> class. /// </summary> /// <param name="userManager">The user manager.</param> /// <param name="userDataRepository">The user data repository.</param> /// <param name="libraryManager">The library manager.</param> - 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; } /// <summary> @@ -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 09bdc0547..3f3259171 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -90,17 +90,20 @@ namespace MediaBrowser.Api /// </summary> private readonly ILibraryManager _libraryManager; + private readonly IItemRepository _itemRepo; + /// <summary> /// Initializes a new instance of the <see cref="TvShowsService" /> class. /// </summary> /// <param name="userManager">The user manager.</param> /// <param name="userDataRepository">The user data repository.</param> /// <param name="libraryManager">The library manager.</param> - 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; } /// <summary> @@ -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 /// <returns>Task.</returns> private Task<BaseItemDto[]> GetItemDtos(IEnumerable<BaseItem> pagedItems, User user, List<ItemFields> 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 fc981d0de..7a027a052 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<Artist> { /// <summary> - /// Initializes a new instance of the <see cref="ArtistsService"/> class. + /// Initializes a new instance of the <see cref="ArtistsService" /> class. /// </summary> /// <param name="userManager">The user manager.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="userDataRepository">The user data repository.</param> - public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + /// <param name="itemRepo">The item repo.</param> + 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 65ec74bcf..b93d339ce 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -29,6 +29,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> protected readonly ILibraryManager LibraryManager; protected readonly IUserDataRepository UserDataRepository; + protected readonly IItemRepository ItemRepository; /// <summary> /// Initializes a new instance of the <see cref="BaseItemsByNameService{TItemType}" /> class. @@ -36,11 +37,12 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="userManager">The user manager.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="userDataRepository">The user data repository.</param> - 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; } /// <summary> @@ -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 d44394c4f..7c49501ab 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -69,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> public class GenresService : BaseItemsByNameService<Genre> { - 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 a4ed0396e..b96b94823 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; + /// <summary> /// Initializes a new instance of the <see cref="ItemsService" /> class. /// </summary> @@ -211,13 +213,14 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="libraryManager">The library manager.</param> /// <param name="searchEngine">The search engine.</param> /// <param name="userDataRepository">The user data repository.</param> - 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; } /// <summary> @@ -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 0b482aaf4..a4c60e2d9 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<MusicGenre> { - 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 9d0aa88c9..06aa3111d 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<Person> { /// <summary> - /// Initializes a new instance of the <see cref="PersonsService"/> class. + /// Initializes a new instance of the <see cref="PersonsService" /> class. /// </summary> /// <param name="userManager">The user manager.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="userDataRepository">The user data repository.</param> - public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) - : base(userManager, libraryManager, userDataRepository) + /// <param name="itemRepo">The item repo.</param> + 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 7b9539a40..687e237bd 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -70,8 +70,8 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> public class StudiosService : BaseItemsByNameService<Studio> { - 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 786eea5b3..197ba1f4a 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<Video>(movie.SpecialFeatureIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)).Select(t => t.Result).ToList(); @@ -418,7 +418,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).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<Trailer>(item.LocalTrailerIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)).Select(t => t.Result).ToList(); @@ -439,7 +439,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).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 result = dtoBuilder.GetBaseItemDto(item, fields, user).Result; @@ -460,7 +460,7 @@ namespace MediaBrowser.Api.UserLibrary // Get everything var fields = Enum.GetNames(typeof(ItemFields)).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 result = dtoBuilder.GetBaseItemDto(item, fields, user).Result; diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index 16697e400..bfd493240 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -54,8 +54,8 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> 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 3c74a4288..c977cc9f3 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 14994ac5c..d6bc983c0 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 /// </summary> private readonly ILogger _logger; - /// <summary> - /// The _kernel - /// </summary> - private readonly Kernel _kernel; + private readonly IItemRepository _itemRepo; /// <summary> /// The _locks @@ -78,13 +76,13 @@ namespace MediaBrowser.Controller.Drawing /// <summary> /// Initializes a new instance of the <see cref="ImageManager" /> class. /// </summary> - /// <param name="kernel">The kernel.</param> /// <param name="logger">The logger.</param> /// <param name="appPaths">The app paths.</param> - public ImageManager(Kernel kernel, ILogger logger, IServerApplicationPaths appPaths) + /// <param name="itemRepo">The item repo.</param> + 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 1e0e5286f..11cf5b152 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; } /// <summary> @@ -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 ee717a191..ec7b71209 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<MediaStream>(); - Chapters = new List<ChapterInfo>(); PlayableStreamFileNames = new List<string>(); AdditionalPartIds = new List<Guid>(); } @@ -54,12 +52,6 @@ namespace MediaBrowser.Controller.Entities public List<MediaStream> MediaStreams { get; set; } /// <summary> - /// Gets or sets the chapters. - /// </summary> - /// <value>The chapters.</value> - public List<ChapterInfo> Chapters { get; set; } - - /// <summary> /// If the video is a folder-rip, this will hold the file list for the largest playlist /// </summary> public List<string> PlayableStreamFileNames { get; set; } diff --git a/MediaBrowser.Controller/Library/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/Library/IDisplayPreferencesManager.cs deleted file mode 100644 index f1d782b1d..000000000 --- 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 -{ - /// <summary> - /// Interface IDisplayPreferencesManager - /// </summary> - public interface IDisplayPreferencesManager - { - /// <summary> - /// Gets the display preferences. - /// </summary> - /// <param name="displayPreferencesId">The display preferences id.</param> - /// <returns>DisplayPreferences.</returns> - Task<DisplayPreferences> GetDisplayPreferences(Guid displayPreferencesId); - - /// <summary> - /// Saves display preferences for an item - /// </summary> - /// <param name="displayPreferences">The display preferences.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SaveDisplayPreferences(DisplayPreferences displayPreferences, CancellationToken cancellationToken); - } -} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 8765998f3..f49221ce8 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -102,7 +102,6 @@ <Compile Include="Entities\Movies\BoxSet.cs" /> <Compile Include="Entities\Movies\Movie.cs" /> <Compile Include="Entities\Person.cs" /> - <Compile Include="Library\IDisplayPreferencesManager.cs" /> <Compile Include="Library\ILibrarySearchEngine.cs" /> <Compile Include="Library\ItemChangeEventArgs.cs" /> <Compile Include="Library\PlaybackProgressEventArgs.cs" /> diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 4b992fd81..81ab15548 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; /// <summary> /// Initializes a new instance of the <see cref="FFMpegManager" /> class. @@ -42,13 +45,15 @@ namespace MediaBrowser.Controller.MediaInfo /// <param name="encoder">The encoder.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="logger">The logger.</param> + /// <param name="itemRepo">The item repo.</param> /// <exception cref="System.ArgumentNullException">zipClient</exception> - 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. /// </summary> /// <param name="video">The video.</param> - /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="chapters">The chapters.</param> /// <param name="extractImages">if set to <c>true</c> [extract images].</param> - /// <param name="saveItem">if set to <c>true</c> [save item].</param> + /// <param name="saveChapters">if set to <c>true</c> [save chapters].</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException"></exception> - public async Task<bool> PopulateChapterImages(Video video, CancellationToken cancellationToken, bool extractImages, bool saveItem) + public async Task<bool> PopulateChapterImages(Video video, List<ChapterInfo> 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 9774bb68e..4d7345f48 100644 --- a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs +++ b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs @@ -24,6 +24,6 @@ namespace MediaBrowser.Controller.Persistence /// </summary> /// <param name="displayPreferencesId">The display preferences id.</param> /// <returns>Task{DisplayPreferences}.</returns> - Task<DisplayPreferences> GetDisplayPreferences(Guid displayPreferencesId); + DisplayPreferences GetDisplayPreferences(Guid displayPreferencesId); } } diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index d854d0e20..534e64a3f 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -51,6 +51,30 @@ namespace MediaBrowser.Controller.Persistence /// <param name="type">The type.</param> /// <returns>BaseItem.</returns> BaseItem RetrieveItem(Guid id, Type type); + + /// <summary> + /// Gets chapters for an item + /// </summary> + /// <param name="id"></param> + /// <returns></returns> + IEnumerable<ChapterInfo> GetChapters(Guid id); + + /// <summary> + /// Gets a single chapter for an item + /// </summary> + /// <param name="id"></param> + /// <param name="index"></param> + /// <returns></returns> + ChapterInfo GetChapter(Guid id, int index); + + /// <summary> + /// Saves the chapters. + /// </summary> + /// <param name="id">The id.</param> + /// <param name="chapters">The chapters.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken); } /// <summary> diff --git a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs index 9e3a468c6..b757a5ff9 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; @@ -43,53 +42,6 @@ namespace MediaBrowser.Providers.MediaInfo protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> 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; - } - - /// <summary> /// Gets the media info. /// </summary> /// <param name="item">The item.</param> @@ -99,7 +51,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <exception cref="System.ArgumentNullException">inputPath /// or /// cache</exception> - private async Task<MediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) + protected async Task<MediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -141,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo /// Normalizes the FF probe result. /// </summary> /// <param name="result">The result.</param> - private void NormalizeFFProbeResult(MediaInfoResult result) + protected void NormalizeFFProbeResult(MediaInfoResult result) { if (result.format != null && result.format.tags != null) { @@ -167,16 +119,6 @@ namespace MediaBrowser.Providers.MediaInfo } /// <summary> - /// Subclasses must set item values using this - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="result">The result.</param> - /// <param name="isoMount">The iso mount.</param> - /// <returns>Task.</returns> - protected abstract void Fetch(T item, CancellationToken cancellationToken, MediaInfoResult result, IIsoMount isoMount); - - /// <summary> /// Converts ffprobe stream info to our MediaStream class /// </summary> /// <param name="streamInfo">The stream info.</param> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs index c18210901..8a9152507 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<bool> 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; + } + /// <summary> /// Fetches the specified audio. /// </summary> @@ -31,7 +53,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="data">The data.</param> /// <param name="isoMount">The iso mount.</param> /// <returns>Task.</returns> - 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 82a6a1d8e..cfb33d757 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<bool> 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; + } + /// <summary> /// Mounts the iso if needed. /// </summary> @@ -196,7 +230,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="data">The data.</param> /// <param name="isoMount">The iso mount.</param> /// <returns>Task.</returns> - 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<ChapterInfo>(); + 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); } /// <summary> @@ -380,7 +413,8 @@ namespace MediaBrowser.Providers.MediaInfo /// Adds the dummy chapters. /// </summary> /// <param name="video">The video.</param> - private void AddDummyChapters(Video video) + /// <param name="chapters">The chapters.</param> + private void AddDummyChapters(Video video, List<ChapterInfo> chapters) { var runtime = video.RunTimeTicks ?? 0; @@ -392,8 +426,6 @@ namespace MediaBrowser.Providers.MediaInfo long currentChapterTicks = 0; var index = 1; - var chapters = new List<ChapterInfo>(); - while (currentChapterTicks < runtime) { chapters.Add(new ChapterInfo @@ -405,17 +437,16 @@ namespace MediaBrowser.Providers.MediaInfo index++; currentChapterTicks += _dummyChapterDuration; } - - video.Chapters = chapters; } /// <summary> /// Fetches the bd info. /// </summary> /// <param name="item">The item.</param> + /// <param name="chapters">The chapters.</param> /// <param name="inputPath">The input path.</param> /// <param name="cancellationToken">The cancellation token.</param> - private void FetchBdInfo(BaseItem item, string inputPath, CancellationToken cancellationToken) + private void FetchBdInfo(BaseItem item, List<ChapterInfo> 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 /// </summary> /// <param name="video">The video.</param> /// <param name="stream">The stream.</param> - private void Fetch(Video video, BlurayDiscInfo stream) + /// <param name="chapters">The chapters.</param> + private void Fetch(Video video, BlurayDiscInfo stream, List<ChapterInfo> 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 57a9c9d78..000000000 --- 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 -{ - /// <summary> - /// Class DisplayPreferencesManager - /// </summary> - public class DisplayPreferencesManager : IDisplayPreferencesManager - { - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - /// <summary> - /// The _display preferences - /// </summary> - private readonly ConcurrentDictionary<Guid, Task<DisplayPreferences>> _displayPreferences = new ConcurrentDictionary<Guid, Task<DisplayPreferences>>(); - - /// <summary> - /// Gets the active user repository - /// </summary> - /// <value>The display preferences repository.</value> - public IDisplayPreferencesRepository Repository { get; set; } - - /// <summary> - /// Initializes a new instance of the <see cref="DisplayPreferencesManager"/> class. - /// </summary> - /// <param name="logger">The logger.</param> - public DisplayPreferencesManager(ILogger logger) - { - _logger = logger; - } - - /// <summary> - /// Gets the display preferences. - /// </summary> - /// <param name="displayPreferencesId">The display preferences id.</param> - /// <returns>DisplayPreferences.</returns> - public Task<DisplayPreferences> GetDisplayPreferences(Guid displayPreferencesId) - { - return _displayPreferences.GetOrAdd(displayPreferencesId, keyName => RetrieveDisplayPreferences(displayPreferencesId)); - } - - /// <summary> - /// Retrieves the display preferences. - /// </summary> - /// <param name="displayPreferencesId">The display preferences id.</param> - /// <returns>DisplayPreferences.</returns> - private async Task<DisplayPreferences> RetrieveDisplayPreferences(Guid displayPreferencesId) - { - var displayPreferences = await Repository.GetDisplayPreferences(displayPreferencesId).ConfigureAwait(false); - - return displayPreferences ?? new DisplayPreferences { Id = displayPreferencesId }; - } - - /// <summary> - /// Saves display preferences for an item - /// </summary> - /// <param name="displayPreferences">The display preferences.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - 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 fc7e1e6ba..34930b34e 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -119,7 +119,6 @@ <Compile Include="HttpServer\SwaggerService.cs" /> <Compile Include="IO\DirectoryWatchers.cs" /> <Compile Include="Library\CoreResolutionIgnoreRule.cs" /> - <Compile Include="Library\DisplayPreferencesManager.cs" /> <Compile Include="Library\LibraryManager.cs" /> <Compile Include="Library\LuceneSearchEngine.cs" /> <Compile Include="Library\ResolverHelper.cs" /> @@ -139,8 +138,8 @@ <Compile Include="Library\UserManager.cs" /> <Compile Include="Localization\LocalizationManager.cs" /> <Compile Include="MediaEncoder\MediaEncoder.cs" /> + <Compile Include="Persistence\SqliteChapterRepository.cs" /> <Compile Include="Persistence\SqliteExtensions.cs" /> - <Compile Include="Persistence\SqliteRepository.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Providers\ProviderManager.cs" /> <Compile Include="ScheduledTasks\ArtistValidationTask.cs" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs new file mode 100644 index 000000000..dd6343a67 --- /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; + + /// <summary> + /// The _app paths + /// </summary> + private readonly IApplicationPaths _appPaths; + + private SQLiteCommand _deleteChaptersCommand; + private SQLiteCommand _saveChapterCommand; + + /// <summary> + /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. + /// </summary> + /// <param name="appPaths">The app paths.</param> + /// <param name="jsonSerializer">The json serializer.</param> + /// <param name="logManager">The log manager.</param> + /// <exception cref="System.ArgumentNullException"> + /// appPaths + /// or + /// jsonSerializer + /// </exception> + public SqliteChapterRepository(IApplicationPaths appPaths, ILogManager logManager) + { + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + + _appPaths = appPaths; + + _logger = logManager.GetLogger(GetType().Name); + } + + /// <summary> + /// Opens the connection to the database + /// </summary> + /// <returns>Task.</returns> + 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(); + } + + /// <summary> + /// The _write lock + /// </summary> + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + + /// <summary> + /// Prepares the statements. + /// </summary> + 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")); + } + + /// <summary> + /// Gets chapters for an item + /// </summary> + /// <param name="id">The id.</param> + /// <returns>IEnumerable{ChapterInfo}.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + public IEnumerable<ChapterInfo> 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; + } + } + } + } + + /// <summary> + /// Gets a single chapter for an item + /// </summary> + /// <param name="id">The id.</param> + /// <param name="index">The index.</param> + /// <returns>ChapterInfo.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + 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; + } + } + + /// <summary> + /// Saves the chapters. + /// </summary> + /// <param name="id">The id.</param> + /// <param name="chapters">The chapters.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException"> + /// id + /// or + /// chapters + /// or + /// cancellationToken + /// </exception> + public async Task SaveChapters(Guid id, IEnumerable<ChapterInfo> 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(); + } + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + 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 f4d341c34..cb965c3f9 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs @@ -15,13 +15,12 @@ namespace MediaBrowser.Server.Implementations.Persistence /// <summary> /// Class SQLiteDisplayPreferencesRepository /// </summary> - public class SqliteDisplayPreferencesRepository : SqliteRepository, IDisplayPreferencesRepository + public class SqliteDisplayPreferencesRepository : IDisplayPreferencesRepository { - /// <summary> - /// The repository name - /// </summary> - public const string RepositoryName = "SQLite"; + private SQLiteConnection _connection; + private readonly ILogger _logger; + /// <summary> /// Gets the name of the repository /// </summary> @@ -30,7 +29,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -58,7 +57,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// appPaths /// </exception> 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); } /// <summary> @@ -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); } /// <summary> @@ -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 /// <param name="displayPreferencesId">The display preferences id.</param> /// <returns>Task{DisplayPreferences}.</returns> /// <exception cref="System.ArgumentNullException">item</exception> - public async Task<DisplayPreferences> 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<DisplayPreferences>(stream); } @@ -205,5 +205,47 @@ namespace MediaBrowser.Server.Implementations.Persistence return null; } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + 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 00dbbe513..2b14e9b24 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; } + + /// <summary> + /// Gets a stream from a DataReader at a given ordinal + /// </summary> + /// <param name="reader">The reader.</param> + /// <param name="ordinal">The ordinal.</param> + /// <returns>Stream.</returns> + /// <exception cref="System.ArgumentNullException">reader</exception> + 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; + } + + /// <summary> + /// Runs the queries. + /// </summary> + /// <param name="connection">The connection.</param> + /// <param name="queries">The queries.</param> + /// <param name="logger">The logger.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + /// <exception cref="System.ArgumentNullException">queries</exception> + 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; + } + } + } + + /// <summary> + /// Connects to db. + /// </summary> + /// <param name="dbPath">The db path.</param> + /// <returns>Task{IDbConnection}.</returns> + /// <exception cref="System.ArgumentNullException">dbPath</exception> + public static async Task<SQLiteConnection> 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 a9cd3d1eb..b3251ddb9 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -18,13 +18,12 @@ namespace MediaBrowser.Server.Implementations.Persistence /// <summary> /// Class SQLiteItemRepository /// </summary> - public class SqliteItemRepository : SqliteRepository, IItemRepository + public class SqliteItemRepository : IItemRepository { - /// <summary> - /// The repository name - /// </summary> - public const string RepositoryName = "SQLite"; + private SQLiteConnection _connection; + private readonly ILogger _logger; + /// <summary> /// Gets the name of the repository /// </summary> @@ -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; + /// <summary> /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. /// </summary> @@ -67,7 +68,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// jsonSerializer /// </exception> 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); } /// <summary> @@ -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); } /// <summary> @@ -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); }); } + + /// <summary> + /// Gets chapters for an item + /// </summary> + /// <param name="id">The id.</param> + /// <returns>IEnumerable{ChapterInfo}.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + public IEnumerable<ChapterInfo> GetChapters(Guid id) + { + return _chapterRepository.GetChapters(id); + } + + /// <summary> + /// Gets a single chapter for an item + /// </summary> + /// <param name="id">The id.</param> + /// <param name="index">The index.</param> + /// <returns>ChapterInfo.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + public ChapterInfo GetChapter(Guid id, int index) + { + return _chapterRepository.GetChapter(id, index); + } + + /// <summary> + /// Saves the chapters. + /// </summary> + /// <param name="id">The id.</param> + /// <param name="chapters">The chapters.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException"> + /// id + /// or + /// chapters + /// or + /// cancellationToken + /// </exception> + public Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken) + { + return _chapterRepository.SaveChapters(id, chapters, cancellationToken); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + 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 cfdc9b5fb..000000000 --- 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 -{ - /// <summary> - /// Class SqliteRepository - /// </summary> - public abstract class SqliteRepository : IDisposable - { - /// <summary> - /// The db file name - /// </summary> - protected string DbFileName; - /// <summary> - /// The connection - /// </summary> - protected SQLiteConnection Connection; - - /// <summary> - /// Gets the logger. - /// </summary> - /// <value>The logger.</value> - protected ILogger Logger { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="SqliteRepository" /> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <exception cref="System.ArgumentNullException">logger</exception> - protected SqliteRepository(ILogManager logManager) - { - if (logManager == null) - { - throw new ArgumentNullException("logManager"); - } - - Logger = logManager.GetLogger(GetType().Name); - } - - /// <summary> - /// Connects to DB. - /// </summary> - /// <param name="dbPath">The db path.</param> - /// <returns>Task{System.Boolean}.</returns> - /// <exception cref="System.ArgumentNullException">dbPath</exception> - 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(); - } - - /// <summary> - /// Runs the queries. - /// </summary> - /// <param name="queries">The queries.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - /// <exception cref="System.ArgumentNullException">queries</exception> - 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; - } - } - } - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private readonly object _disposeLock = new object(); - - /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - 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); - } - } - } - - /// <summary> - /// Gets a stream from a DataReader at a given ordinal - /// </summary> - /// <param name="reader">The reader.</param> - /// <param name="ordinal">The ordinal.</param> - /// <returns>Stream.</returns> - /// <exception cref="System.ArgumentNullException">reader</exception> - 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 05829e007..1d127ae96 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<string, UserItemData> _userData = new ConcurrentDictionary<string, UserItemData>(); private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - /// <summary> - /// The repository name - /// </summary> - public const string RepositoryName = "SQLite"; - + private SQLiteConnection _connection; + /// <summary> /// Gets the name of the repository /// </summary> @@ -32,7 +31,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -55,7 +54,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// appPaths /// </exception> 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); } /// <summary> @@ -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); } /// <summary> @@ -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 /// <returns>Task{UserItemData}.</returns> 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<UserItemData>(stream); } @@ -282,5 +281,47 @@ namespace MediaBrowser.Server.Implementations.Persistence return new UserItemData(); } } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + 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 efd39529a..09e34cf08 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs @@ -16,15 +16,14 @@ namespace MediaBrowser.Server.Implementations.Persistence /// <summary> /// Class SQLiteUserRepository /// </summary> - public class SqliteUserRepository : SqliteRepository, IUserRepository + public class SqliteUserRepository : IUserRepository { - /// <summary> - /// The repository name - /// </summary> - public const string RepositoryName = "SQLite"; - + private readonly ILogger _logger; + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + private SQLiteConnection _connection; + /// <summary> /// Gets the name of the repository /// </summary> @@ -33,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Persistence { get { - return RepositoryName; + return "SQLite"; } } @@ -56,7 +55,6 @@ namespace MediaBrowser.Server.Implementations.Persistence /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">appPaths</exception> 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); } /// <summary> @@ -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); } /// <summary> @@ -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 /// <returns>IEnumerable{User}.</returns> public IEnumerable<User> 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<User>(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(); } } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private readonly object _disposeLock = new object(); + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + 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 2d9d5abfe..79ea89ac6 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 /// <value>The new item timer.</value> private Timer NewItemTimer { get; set; } + private readonly IItemRepository _itemRepo; + /// <summary> /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. /// </summary> @@ -52,12 +55,14 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// <param name="logManager">The log manager.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="jsonSerializer">The json serializer.</param> - public ChapterImagesTask(Kernel kernel, ILogManager logManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer) + /// <param name="itemRepo">The item repo.</param> + 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<Video>() - .Where(v => v.Chapters != null && v.Chapters.Count != 0) .ToList(); var numComplete = 0; @@ -163,7 +169,9 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var extract = !previouslyFailedImages.Contains(key, StringComparer.OrdinalIgnoreCase); - var success = await _kernel.FFMpegManager.PopulateChapterImages(video, cancellationToken, extract, true); + var chapters = _itemRepo.GetChapters(video.Id).ToList(); + + var success = await _kernel.FFMpegManager.PopulateChapterImages(video, chapters, extract, true, cancellationToken); if (!success) { diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs index 260081416..5ed253763 100644 --- a/MediaBrowser.ServerApplication/App.xaml.cs +++ b/MediaBrowser.ServerApplication/App.xaml.cs @@ -175,7 +175,7 @@ namespace MediaBrowser.ServerApplication var task = CompositionRoot.RunStartupTasks(); - new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager, CompositionRoot.UserManager, CompositionRoot.LibraryManager, CompositionRoot.JsonSerializer, CompositionRoot.DisplayPreferencesManager).Show(); + new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager, CompositionRoot.UserManager, CompositionRoot.LibraryManager, CompositionRoot.JsonSerializer, CompositionRoot.DisplayPreferencesRepository).Show(); await task.ConfigureAwait(false); } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 24093a181..0df0d36f9 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -161,12 +161,6 @@ namespace MediaBrowser.ServerApplication private IHttpServer HttpServer { get; set; } /// <summary> - /// Gets or sets the display preferences manager. - /// </summary> - /// <value>The display preferences manager.</value> - internal IDisplayPreferencesManager DisplayPreferencesManager { get; set; } - - /// <summary> /// Gets or sets the media encoder. /// </summary> /// <value>The media encoder.</value> @@ -180,7 +174,7 @@ namespace MediaBrowser.ServerApplication /// <value>The user data repository.</value> private IUserDataRepository UserDataRepository { get; set; } private IUserRepository UserRepository { get; set; } - private IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; } + internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; } private IItemRepository ItemRepository { get; set; } /// <summary> @@ -271,9 +265,6 @@ namespace MediaBrowser.ServerApplication ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager); RegisterSingleInstance(ProviderManager); - DisplayPreferencesManager = new DisplayPreferencesManager(LogManager.GetLogger("DisplayPreferencesManager")); - RegisterSingleInstance(DisplayPreferencesManager); - RegisterSingleInstance<ILibrarySearchEngine>(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager)); MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ZipClient, ApplicationPaths, JsonSerializer); @@ -306,10 +297,10 @@ namespace MediaBrowser.ServerApplication /// </summary> private void SetKernelProperties() { - ServerKernel.ImageManager = new ImageManager(ServerKernel, LogManager.GetLogger("ImageManager"), - ApplicationPaths); + ServerKernel.ImageManager = new ImageManager(LogManager.GetLogger("ImageManager"), + ApplicationPaths, ItemRepository); Parallel.Invoke( - () => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, LibraryManager, Logger), + () => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, LibraryManager, Logger, ItemRepository), () => ServerKernel.WeatherProviders = GetExports<IWeatherProvider>(), () => ServerKernel.ImageManager.ImageEnhancers = GetExports<IImageEnhancer>().OrderBy(e => e.Priority).ToArray(), () => LocalizedStrings.StringFiles = GetExports<LocalizedStringData>(), @@ -324,8 +315,6 @@ namespace MediaBrowser.ServerApplication private async Task ConfigureDisplayPreferencesRepositories() { await DisplayPreferencesRepository.Initialize().ConfigureAwait(false); - - ((DisplayPreferencesManager)DisplayPreferencesManager).Repository = DisplayPreferencesRepository; } /// <summary> diff --git a/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs b/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs index f7b3b27c6..577ac93bb 100644 --- a/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs +++ b/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -34,7 +35,7 @@ namespace MediaBrowser.ServerApplication private readonly IJsonSerializer _jsonSerializer; private readonly ILibraryManager _libraryManager; - private readonly IDisplayPreferencesManager _displayPreferencesManager; + private readonly IDisplayPreferencesRepository _displayPreferencesManager; /// <summary> /// The current user @@ -49,7 +50,7 @@ namespace MediaBrowser.ServerApplication /// <param name="userManager">The user manager.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="displayPreferencesManager">The display preferences manager.</param> - public LibraryExplorer(IJsonSerializer jsonSerializer, ILogger logger, IApplicationHost appHost, IUserManager userManager, ILibraryManager libraryManager, IDisplayPreferencesManager displayPreferencesManager) + public LibraryExplorer(IJsonSerializer jsonSerializer, ILogger logger, IApplicationHost appHost, IUserManager userManager, ILibraryManager libraryManager, IDisplayPreferencesRepository displayPreferencesManager) { _logger = logger; _jsonSerializer = jsonSerializer; @@ -98,7 +99,7 @@ namespace MediaBrowser.ServerApplication var currentFolder = folder; Task.Factory.StartNew(() => { - var prefs = ddlProfile.SelectedItem != null ? _displayPreferencesManager.GetDisplayPreferences(currentFolder.GetDisplayPreferencesId((ddlProfile.SelectedItem as User).Id)).Result ?? new DisplayPreferences { SortBy = ItemSortBy.SortName } : new DisplayPreferences { SortBy = ItemSortBy.SortName }; + var prefs = ddlProfile.SelectedItem != null ? _displayPreferencesManager.GetDisplayPreferences(currentFolder.GetDisplayPreferencesId((ddlProfile.SelectedItem as User).Id)) ?? new DisplayPreferences { SortBy = ItemSortBy.SortName } : new DisplayPreferences { SortBy = ItemSortBy.SortName }; var node = new TreeViewItem { Tag = currentFolder }; var subChildren = currentFolder.GetChildren(CurrentUser, prefs.IndexBy); @@ -151,7 +152,7 @@ namespace MediaBrowser.ServerApplication var subFolder = item as Folder; if (subFolder != null) { - var prefs = _displayPreferencesManager.GetDisplayPreferences(subFolder.GetDisplayPreferencesId(user.Id)).Result; + var prefs = _displayPreferencesManager.GetDisplayPreferences(subFolder.GetDisplayPreferencesId(user.Id)); AddChildren(node, OrderBy(subFolder.GetChildren(user), user, prefs.SortBy), user); node.Header = item.Name + " (" + node.Items.Count + ")"; @@ -199,9 +200,7 @@ namespace MediaBrowser.ServerApplication ItemSortBy.Runtime }; - var prefs = - await - _displayPreferencesManager.GetDisplayPreferences(folder.GetDisplayPreferencesId((ddlProfile.SelectedItem as User).Id)); + var prefs = _displayPreferencesManager.GetDisplayPreferences(folder.GetDisplayPreferencesId((ddlProfile.SelectedItem as User).Id)); ddlIndexBy.SelectedItem = prefs != null ? prefs.IndexBy ?? LocalizedStrings.Instance.GetString("NoneDispPref") @@ -360,7 +359,7 @@ namespace MediaBrowser.ServerApplication var folder = treeItem != null ? treeItem.Tag as Folder : null; - var prefs = folder != null ? _displayPreferencesManager.GetDisplayPreferences(folder.GetDisplayPreferencesId(CurrentUser.Id)).Result : new DisplayPreferences { SortBy = ItemSortBy.SortName }; + var prefs = folder != null ? _displayPreferencesManager.GetDisplayPreferences(folder.GetDisplayPreferencesId(CurrentUser.Id)) : new DisplayPreferences { SortBy = ItemSortBy.SortName }; if (folder != null && prefs.IndexBy != ddlIndexBy.SelectedItem as string) { //grab UI context so we can update within the below task @@ -401,7 +400,7 @@ namespace MediaBrowser.ServerApplication var folder = treeItem != null ? treeItem.Tag as Folder : null; - var prefs = folder != null ? _displayPreferencesManager.GetDisplayPreferences(folder.GetDisplayPreferencesId(CurrentUser.Id)).Result : new DisplayPreferences(); + var prefs = folder != null ? _displayPreferencesManager.GetDisplayPreferences(folder.GetDisplayPreferencesId(CurrentUser.Id)) : new DisplayPreferences(); if (folder != null && prefs.SortBy != ddlSortBy.SelectedItem as string) { //grab UI context so we can update within the below task diff --git a/MediaBrowser.ServerApplication/MainWindow.xaml.cs b/MediaBrowser.ServerApplication/MainWindow.xaml.cs index 0131933c8..974bb6f48 100644 --- a/MediaBrowser.ServerApplication/MainWindow.xaml.cs +++ b/MediaBrowser.ServerApplication/MainWindow.xaml.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MediaBrowser.ServerApplication.Logging; @@ -44,7 +45,7 @@ namespace MediaBrowser.ServerApplication private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly IJsonSerializer _jsonSerializer; - private readonly IDisplayPreferencesManager _displayPreferencesManager; + private readonly IDisplayPreferencesRepository _displayPreferencesManager; /// <summary> /// Initializes a new instance of the <see cref="MainWindow" /> class. @@ -57,7 +58,7 @@ namespace MediaBrowser.ServerApplication /// <param name="jsonSerializer">The json serializer.</param> /// <param name="displayPreferencesManager">The display preferences manager.</param> /// <exception cref="System.ArgumentNullException">logger</exception> - public MainWindow(ILogManager logManager, IServerApplicationHost appHost, IServerConfigurationManager configurationManager, IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer, IDisplayPreferencesManager displayPreferencesManager) + public MainWindow(ILogManager logManager, IServerApplicationHost appHost, IServerConfigurationManager configurationManager, IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer, IDisplayPreferencesRepository displayPreferencesManager) { if (logManager == null) { |
