diff options
| author | Eric Reed <ebr@mediabrowser3.com> | 2013-06-18 15:28:18 -0400 |
|---|---|---|
| committer | Eric Reed <ebr@mediabrowser3.com> | 2013-06-18 15:28:18 -0400 |
| commit | cc728d87c2507d5392686df6e18c7ad2ba5c45bd (patch) | |
| tree | 1eee3719a4cd8abd78f422900620e62042161766 | |
| parent | 90155278f8b4465a4b5eaf140c5e6e4905cc8dcf (diff) | |
| parent | 5d8ed2c16fdeaec1344964778e98cadfaa9571b4 (diff) | |
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
89 files changed, 1823 insertions, 1491 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 c2ccf4dcd..ef4602b8f 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -429,9 +429,9 @@ 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.GetItems(item.ThemeSongIds) + var items = _itemRepo.RetrieveItems<Audio>(item.ThemeSongIds) .OrderBy(i => i.SortName) .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)) .Select(t => t.Result) @@ -468,10 +468,10 @@ 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.GetItems(item.ThemeVideoIds) + _itemRepo.RetrieveItems<Video>(item.ThemeVideoIds) .OrderBy(i => i.SortName) .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)) .Select(t => t.Result) diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 82c632c11..171f2316c 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -39,17 +39,17 @@ <Reference Include="MoreLinq"> <HintPath>..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Common, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Common, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Common.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Common.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Interfaces, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Interfaces, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Interfaces.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Text, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Text, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Text.3.9.45\lib\net35\ServiceStack.Text.dll</HintPath> + <HintPath>..\packages\ServiceStack.Text.3.9.54\lib\net35\ServiceStack.Text.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> 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/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 1c1b569d7..f035aebd9 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback /// </summary> /// <param name="state">The state.</param> /// <returns>System.String.</returns> - protected string GetOutputFilePath(StreamState state) + protected virtual string GetOutputFilePath(StreamState state) { var folder = ApplicationPaths.EncodedMediaCachePath; diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 985288e6f..749b090e0 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -6,7 +7,6 @@ using MediaBrowser.Controller.Library; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; namespace MediaBrowser.Api.Playback.Hls @@ -19,7 +19,16 @@ namespace MediaBrowser.Api.Playback.Hls /// <summary> /// The segment file prefix /// </summary> - public const string SegmentFilePrefix = "segment-"; + public const string SegmentFilePrefix = "hls-"; + + protected override string GetOutputFilePath(StreamState state) + { + var folder = ApplicationPaths.EncodedMediaCachePath; + + var outputFileExtension = GetOutputFileExtension(state); + + return Path.Combine(folder, SegmentFilePrefix + GetCommandLineArguments("dummy\\dummy", state, false).GetMD5() + (outputFileExtension ?? string.Empty).ToLower()); + } /// <summary> /// Initializes a new instance of the <see cref="BaseStreamingService" /> class. @@ -29,7 +38,7 @@ namespace MediaBrowser.Api.Playback.Hls /// <param name="libraryManager">The library manager.</param> /// <param name="isoManager">The iso manager.</param> /// <param name="mediaEncoder">The media encoder.</param> - protected BaseHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder) + protected BaseHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder) : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder) { } @@ -72,7 +81,7 @@ namespace MediaBrowser.Api.Playback.Hls protected object ProcessRequest(StreamRequest request) { var state = GetState(request); - + return ProcessRequestAsync(state).Result; } @@ -139,23 +148,14 @@ namespace MediaBrowser.Api.Playback.Hls await Task.Delay(25).ConfigureAwait(false); } - // The segement paths within the playlist are phsyical, so strip that out to make it relative - fileText = fileText.Replace(Path.GetDirectoryName(playlist) + Path.DirectorySeparatorChar, string.Empty); - fileText = fileText.Replace(SegmentFilePrefix, "segments/").Replace(".ts", "/stream.ts").Replace(".aac", "/stream.aac").Replace(".mp3", "/stream.mp3"); // It's considered live while still encoding (EVENT). Once the encoding has finished, it's video on demand (VOD). var playlistType = fileText.IndexOf("#EXT-X-ENDLIST", StringComparison.OrdinalIgnoreCase) == -1 ? "EVENT" : "VOD"; - const string allowCacheAttributeName = "#EXT-X-ALLOW-CACHE"; - - // fix this to make the media stream validator happy - // https://ffmpeg.org/trac/ffmpeg/ticket/2228 - fileText = fileText.Replace("#EXT-X-ALLOWCACHE", allowCacheAttributeName); - // Add event type at the top - fileText = fileText.Replace(allowCacheAttributeName, "#EXT-X-PLAYLIST-TYPE:" + playlistType + Environment.NewLine + allowCacheAttributeName); - + //fileText = fileText.Replace(allowCacheAttributeName, "#EXT-X-PLAYLIST-TYPE:" + playlistType + Environment.NewLine + allowCacheAttributeName); + return fileText; } @@ -187,14 +187,9 @@ namespace MediaBrowser.Api.Playback.Hls /// <returns>System.String.</returns> protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions) { - var segmentOutputPath = Path.GetDirectoryName(outputPath); - var segmentOutputName = SegmentFilePrefix + Path.GetFileNameWithoutExtension(outputPath); - - segmentOutputPath = Path.Combine(segmentOutputPath, segmentOutputName + "%03d." + GetSegmentFileExtension(state).TrimStart('.')); - var probeSize = GetProbeSizeArgument(state.Item); - return string.Format("{0} {1} {2} -i {3}{4} -threads 0 {5} {6} {7} -f ssegment -segment_list_flags +live -segment_time 10 -segment_list \"{8}\" \"{9}\"", + return string.Format("{0} {1} {2} -i {3}{4} -threads 0 {5} {6} {7} -hls_time 10 -start_number 0 -hls_list_size 1440 \"{8}\"", probeSize, GetUserAgentParam(state.Item), GetFastSeekCommandLineParameter(state.Request), @@ -203,8 +198,7 @@ namespace MediaBrowser.Api.Playback.Hls GetMapArgs(state), GetVideoArguments(state, performSubtitleConversions), GetAudioArguments(state), - outputPath, - segmentOutputPath + outputPath ).Trim(); } } 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 04a18e40e..3f3259171 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -70,7 +70,7 @@ namespace MediaBrowser.Api public class GetSimilarShows : BaseGetSimilarItems { } - + /// <summary> /// Class TvShowsService /// </summary> @@ -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> @@ -110,9 +113,10 @@ namespace MediaBrowser.Api /// <returns>System.Object.</returns> public object Get(GetSimilarShows request) { - var result = SimilarItemsHelper.GetSimilarItems(_userManager, - _libraryManager, - _userDataRepository, + var result = SimilarItemsHelper.GetSimilarItems(_userManager, + _itemRepo, + _libraryManager, + _userDataRepository, Logger, request, item => item is Series, SimilarItemsHelper.GetSimiliarityScore); @@ -141,20 +145,19 @@ namespace MediaBrowser.Api { var user = _userManager.GetUserById(request.UserId); - var tasks = user.RootFolder + var itemsArray = user.RootFolder .GetRecursiveChildren(user) .OfType<Series>() .AsParallel() - .Select(i => GetNextUp(i, user)); - - var itemsArray = await Task.WhenAll(tasks).ConfigureAwait(false); + .Select(i => GetNextUp(i, user)) + .ToArray(); itemsArray = itemsArray .Where(i => i.Item1 != null) .OrderByDescending(i => { var seriesUserData = - _userDataRepository.GetUserData(user.Id, i.Item1.Series.GetUserDataKey()).Result; + _userDataRepository.GetUserData(user.Id, i.Item1.Series.GetUserDataKey()); if (seriesUserData.IsFavorite) { @@ -190,7 +193,7 @@ namespace MediaBrowser.Api /// <param name="series">The series.</param> /// <param name="user">The user.</param> /// <returns>Task{Episode}.</returns> - private async Task<Tuple<Episode,DateTime>> GetNextUp(Series series, User user) + private Tuple<Episode, DateTime> GetNextUp(Series series, User user) { var allEpisodes = series.GetRecursiveChildren(user) .OfType<Episode>() @@ -205,7 +208,7 @@ namespace MediaBrowser.Api // Go back starting with the most recent episodes foreach (var episode in allEpisodes) { - var userData = await _userDataRepository.GetUserData(user.Id, episode.GetUserDataKey()).ConfigureAwait(false); + var userData = _userDataRepository.GetUserData(user.Id, episode.GetUserDataKey()); if (userData.Played) { @@ -240,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 26b0aa192..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)) { @@ -337,7 +339,7 @@ namespace MediaBrowser.Api.UserLibrary public string Name; public BaseItem Item; - private Task<UserItemData> _userData; + private UserItemData _userData; public List<BaseItem> Items { @@ -353,12 +355,7 @@ namespace MediaBrowser.Api.UserLibrary { var item = await GetItem().ConfigureAwait(false); - if (_userData == null) - { - _userData = repo.GetUserData(userId, item.GetUserDataKey()); - } - - return await _userData.ConfigureAwait(false); + return _userData ?? (_userData = repo.GetUserData(userId, item.GetUserDataKey())); } public IbnStub(string name, Func<IEnumerable<BaseItem>> childItems, Func<string,Task<T>> item) 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/ItemByNameUserDataService.cs b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs index 42b76e29d..eaa65dc2d 100644 --- a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs @@ -240,9 +240,9 @@ namespace MediaBrowser.Api.UserLibrary } var key = item.GetUserDataKey(); - + // Get the user data for this item - var data = await UserDataRepository.GetUserData(userId, key).ConfigureAwait(false); + var data = UserDataRepository.GetUserData(userId, key); // Set favorite status data.IsFavorite = isFavorite; @@ -288,9 +288,9 @@ namespace MediaBrowser.Api.UserLibrary } var key = item.GetUserDataKey(); - + // Get the user data for this item - var data = await UserDataRepository.GetUserData(userId, key).ConfigureAwait(false); + var data = UserDataRepository.GetUserData(userId, key); data.Likes = likes; diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index a06ac68b7..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); @@ -335,7 +338,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.Likes: return items.Where(item => { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()).Result; + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; }); @@ -343,7 +346,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.Dislikes: return items.Where(item => { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()).Result; + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; }); @@ -351,7 +354,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsFavorite: return items.Where(item => { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()).Result; + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); return userdata != null && userdata.IsFavorite; }); @@ -362,7 +365,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsResumable: return items.Where(item => { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()).Result; + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); return userdata != null && userdata.PlaybackPositionTicks > 0; }); @@ -370,7 +373,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsPlayed: return items.Where(item => { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()).Result; + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); return userdata != null && userdata.Played; }); @@ -378,7 +381,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsUnplayed: return items.Where(item => { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()).Result; + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); return userdata == null || !userdata.Played; }); @@ -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 8c1f3b500..197ba1f4a 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -397,9 +397,9 @@ 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.GetItems(movie.SpecialFeatureIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)).Select(t => t.Result).ToList(); + var items = _itemRepo.RetrieveItems<Video>(movie.SpecialFeatureIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)).Select(t => t.Result).ToList(); return ToOptimizedResult(items); } @@ -418,9 +418,9 @@ 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.GetItems(item.LocalTrailerIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)).Select(t => t.Result).ToList(); + var items = _itemRepo.RetrieveItems<Trailer>(item.LocalTrailerIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)).Select(t => t.Result).ToList(); return ToOptimizedResult(items); } @@ -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; @@ -496,7 +496,7 @@ namespace MediaBrowser.Api.UserLibrary // Get the user data for this item var key = item.GetUserDataKey(); - var data = _userDataRepository.GetUserData(user.Id, key).Result; + var data = _userDataRepository.GetUserData(user.Id, key); // Set favorite status data.IsFavorite = true; @@ -519,7 +519,7 @@ namespace MediaBrowser.Api.UserLibrary var key = item.GetUserDataKey(); // Get the user data for this item - var data = _userDataRepository.GetUserData(user.Id, key).Result; + var data = _userDataRepository.GetUserData(user.Id, key); // Set favorite status data.IsFavorite = false; @@ -542,7 +542,7 @@ namespace MediaBrowser.Api.UserLibrary var key = item.GetUserDataKey(); // Get the user data for this item - var data = _userDataRepository.GetUserData(user.Id, key).Result; + var data = _userDataRepository.GetUserData(user.Id, key); data.Rating = null; @@ -564,7 +564,7 @@ namespace MediaBrowser.Api.UserLibrary var key = item.GetUserDataKey(); // Get the user data for this item - var data = _userDataRepository.GetUserData(user.Id, key).Result; + var data = _userDataRepository.GetUserData(user.Id, key); data.Likes = request.Likes; 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 d2b58dc96..c977cc9f3 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -60,11 +60,11 @@ 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; - var items = _itemRepo.GetItems(video.AdditionalPartIds) + var items = _itemRepo.RetrieveItems<Video>(video.AdditionalPartIds) .OrderBy(i => i.SortName) .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)) .Select(t => t.Result) diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config index ac6dcee9b..a28a9a4cb 100644 --- a/MediaBrowser.Api/packages.config +++ b/MediaBrowser.Api/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="morelinq" version="1.0.15631-beta" targetFramework="net45" /> - <package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" /> - <package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" /> + <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" /> + <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index d13b04081..d804f6f1c 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -39,9 +39,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\NLog.2.0.1.2\lib\net45\NLog.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Text, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Text, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Text.3.9.45\lib\net35\ServiceStack.Text.dll</HintPath> + <HintPath>..\packages\ServiceStack.Text.3.9.54\lib\net35\ServiceStack.Text.dll</HintPath> </Reference> <Reference Include="SimpleInjector, Version=2.2.3.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index de0fe4fd9..b09c580d7 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="NLog" version="2.0.1.2" targetFramework="net45" /> - <package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" /> + <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> <package id="SimpleInjector" version="2.2.3" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 3213ccffe..69439c573 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -37,17 +37,17 @@ </ApplicationIcon> </PropertyGroup> <ItemGroup> - <Reference Include="ServiceStack.Common, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Common, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Common.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Common.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Interfaces, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Interfaces, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Interfaces.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Text, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Text, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Text.3.9.45\lib\net35\ServiceStack.Text.dll</HintPath> + <HintPath>..\packages\ServiceStack.Text.3.9.54\lib\net35\ServiceStack.Text.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> diff --git a/MediaBrowser.Common/packages.config b/MediaBrowser.Common/packages.config index 38ef0d1c0..df679017f 100644 --- a/MediaBrowser.Common/packages.config +++ b/MediaBrowser.Common/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" /> - <package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" /> + <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" /> + <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> </packages>
\ No newline at end of file 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 7fd188acb..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> @@ -73,11 +75,6 @@ namespace MediaBrowser.Controller.Dto tasks.Add(AttachPeople(dto, item)); } - if (user != null) - { - tasks.Add(AttachUserSpecificInfo(dto, item, user, fields)); - } - if (fields.Contains(ItemFields.PrimaryImageAspectRatio)) { try @@ -91,6 +88,11 @@ namespace MediaBrowser.Controller.Dto } } + if (user != null) + { + AttachUserSpecificInfo(dto, item, user, fields); + } + AttachBasicFields(dto, item, fields); // Make sure all the tasks we kicked off have completed. @@ -109,7 +111,7 @@ namespace MediaBrowser.Controller.Dto /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <param name="fields">The fields.</param> - private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, List<ItemFields> fields) + private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, List<ItemFields> fields) { if (item.IsFolder && fields.Contains(ItemFields.DisplayPreferencesId)) { @@ -127,13 +129,13 @@ namespace MediaBrowser.Controller.Dto // Skip sorting since all we want is a count dto.ChildCount = folder.GetChildren(user).Count(); - await SetSpecialCounts(folder, user, dto, _userDataRepository).ConfigureAwait(false); + SetSpecialCounts(folder, user, dto, _userDataRepository); } } if (addUserData) { - var userData = await _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()).ConfigureAwait(false); + var userData = _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()); dto.UserData = GetUserItemDataDto(userData); @@ -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(); } } @@ -529,7 +531,7 @@ namespace MediaBrowser.Controller.Dto /// <param name="dto">The dto.</param> /// <param name="userDataRepository">The user data repository.</param> /// <returns>Task.</returns> - private static async Task SetSpecialCounts(Folder folder, User user, BaseItemDto dto, IUserDataRepository userDataRepository) + private static void SetSpecialCounts(Folder folder, User user, BaseItemDto dto, IUserDataRepository userDataRepository) { var rcentlyAddedItemCount = 0; var recursiveItemCount = 0; @@ -540,7 +542,7 @@ namespace MediaBrowser.Controller.Dto // Loop through each recursive child foreach (var child in folder.GetRecursiveChildren(user).Where(i => !i.IsFolder).ToList()) { - var userdata = await userDataRepository.GetUserData(user.Id, child.GetUserDataKey()).ConfigureAwait(false); + var userdata = userDataRepository.GetUserData(user.Id, child.GetUserDataKey()); recursiveItemCount++; @@ -767,7 +769,7 @@ namespace MediaBrowser.Controller.Dto { if (data == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException("data"); } return new UserItemDataDto diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6ae465aa7..bd2f5ef71 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -273,7 +273,7 @@ namespace MediaBrowser.Controller.Entities { return Guid.Empty; } - + try { if (!ResolveArgs.IsDirectory) @@ -681,11 +681,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{Video}.</returns> private IEnumerable<Trailer> LoadLocalTrailers() { - if (LocationType != LocationType.FileSystem) - { - return new List<Trailer>(); - } - ItemResolveArgs resolveArgs; try @@ -737,7 +732,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Trailer>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(video.Id) as Trailer; + var dbItem = LibraryManager.RetrieveItem(video.Id, typeof(Trailer)) as Trailer; if (dbItem != null) { @@ -756,11 +751,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{Audio.Audio}.</returns> private IEnumerable<Audio.Audio> LoadThemeSongs() { - if (LocationType != LocationType.FileSystem) - { - return new List<Audio.Audio>(); - } - ItemResolveArgs resolveArgs; try @@ -797,13 +787,13 @@ namespace MediaBrowser.Controller.Entities // Support plex/xbmc convention files.AddRange(resolveArgs.FileSystemChildren - .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.FullName), ThemeSongFilename, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsAudioFile(i.FullName)) + .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsAudioFile(i.Name)) ); return LibraryManager.ResolvePaths<Audio.Audio>(files, null).Select(audio => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(audio.Id) as Audio.Audio; + var dbItem = LibraryManager.RetrieveItem(audio.Id, typeof(Audio.Audio)) as Audio.Audio; if (dbItem != null) { @@ -821,11 +811,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{Video}.</returns> private IEnumerable<Video> LoadThemeVideos() { - if (LocationType != LocationType.FileSystem) - { - return new List<Video>(); - } - ItemResolveArgs resolveArgs; try @@ -866,7 +851,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Video>(files, null).Select(item => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(item.Id) as Video; + var dbItem = LibraryManager.RetrieveItem(item.Id, typeof(Video)) as Video; if (dbItem != null) { @@ -896,13 +881,20 @@ namespace MediaBrowser.Controller.Entities cancellationToken.ThrowIfCancellationRequested(); - var themeSongsChanged = await RefreshThemeSongs(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var themeSongsChanged = false; - var themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var themeVideosChanged = false; - var localTrailersChanged = await RefreshLocalTrailers(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var localTrailersChanged = false; - cancellationToken.ThrowIfCancellationRequested(); + if (LocationType == LocationType.FileSystem && Parent != null) + { + themeSongsChanged = await RefreshThemeSongs(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + + themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + + localTrailersChanged = await RefreshLocalTrailers(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + } cancellationToken.ThrowIfCancellationRequested(); @@ -1096,8 +1088,7 @@ namespace MediaBrowser.Controller.Entities parent = parent.Parent; } - //not found - load from repo - return LibraryManager.RetrieveItem(id); + return null; } /// <summary> @@ -1315,7 +1306,7 @@ namespace MediaBrowser.Controller.Entities var key = GetUserDataKey(); - var data = await userManager.GetUserData(user.Id, key).ConfigureAwait(false); + var data = userManager.GetUserData(user.Id, key); if (wasPlayed) { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index ce36366b4..de965221b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Reflection; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using System; @@ -21,6 +22,15 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Folder : BaseItem { + private static TypeMapper _typeMapper = new TypeMapper(); + + public Folder() + { + ChildDefinitions = new ConcurrentDictionary<Guid, string>(); + } + + public ConcurrentDictionary<Guid, string> ChildDefinitions { get; set; } + /// <summary> /// Gets a value indicating whether this instance is folder. /// </summary> @@ -108,16 +118,14 @@ namespace MediaBrowser.Controller.Entities item.DateModified = DateTime.Now; } - if (!_children.TryAdd(item.Id, item)) + if (!_children.TryAdd(item.Id, item) || !ChildDefinitions.TryAdd(item.Id, item.GetType().FullName)) { throw new InvalidOperationException("Unable to add " + item.Name); } - var newChildren = Children.ToList(); - await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); - await LibraryManager.SaveChildren(Id, newChildren, cancellationToken).ConfigureAwait(false); + await LibraryManager.UpdateItem(this, cancellationToken).ConfigureAwait(false); } /// <summary> @@ -145,19 +153,18 @@ namespace MediaBrowser.Controller.Entities public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) { BaseItem removed; + string removedType; - if (!_children.TryRemove(item.Id, out removed)) + if (!_children.TryRemove(item.Id, out removed) || !ChildDefinitions.TryRemove(item.Id, out removedType)) { throw new InvalidOperationException("Unable to remove " + item.Name); } item.Parent = null; - var newChildren = Children.ToList(); - LibraryManager.ReportItemRemoved(item); - return LibraryManager.SaveChildren(Id, newChildren, cancellationToken); + return LibraryManager.UpdateItem(this, cancellationToken); } #region Indexing @@ -652,7 +659,7 @@ namespace MediaBrowser.Controller.Entities var options = new ParallelOptions { - MaxDegreeOfParallelism = 50 + MaxDegreeOfParallelism = 20 }; Parallel.ForEach(nonCachedChildren, options, child => @@ -702,6 +709,9 @@ namespace MediaBrowser.Controller.Entities } else { + string removedType; + ChildDefinitions.TryRemove(item.Id, out removedType); + LibraryManager.ReportItemRemoved(item); } } @@ -716,11 +726,13 @@ namespace MediaBrowser.Controller.Entities } else { + ChildDefinitions.TryAdd(item.Id, item.GetType().FullName); + Logger.Debug("** " + item.Name + " Added to library."); } } - await LibraryManager.SaveChildren(Id, newChildren, CancellationToken.None).ConfigureAwait(false); + await LibraryManager.UpdateItem(this, CancellationToken.None).ConfigureAwait(false); //force the indexes to rebuild next time IndexCache.Clear(); @@ -848,9 +860,38 @@ namespace MediaBrowser.Controller.Entities /// Get our children from the repo - stubbed for now /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> - protected virtual IEnumerable<BaseItem> GetCachedChildren() + protected IEnumerable<BaseItem> GetCachedChildren() + { + var items = ChildDefinitions.ToList().Select(RetrieveChild).Where(i => i != null).ToList(); + + foreach (var item in items) + { + item.Parent = this; + } + + return items; + } + + /// <summary> + /// Retrieves the child. + /// </summary> + /// <param name="child">The child.</param> + /// <returns>BaseItem.</returns> + private BaseItem RetrieveChild(KeyValuePair<Guid,string> child) { - return LibraryManager.RetrieveChildren(this).Select(i => i is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(i) : i); + var type = child.Value; + + var itemType = _typeMapper.GetType(type); + + if (itemType == null) + { + Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); + return null; + } + + var item = LibraryManager.RetrieveItem(child.Key, itemType); + + return item is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(item) : item; } /// <summary> diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 307fe1954..6e649fd65 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -68,7 +68,14 @@ namespace MediaBrowser.Controller.Entities.Movies // Kick off a task to refresh the main item var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); - var specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + var specialFeaturesChanged = false; + + // Must have a parent to have special features + // In other words, it must be part of the Parent/Child tree + if (LocationType == LocationType.FileSystem && Parent != null) + { + specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); + } return specialFeaturesChanged || result; } @@ -95,11 +102,6 @@ namespace MediaBrowser.Controller.Entities.Movies /// <returns>IEnumerable{Video}.</returns> private IEnumerable<Video> LoadSpecialFeatures() { - if (LocationType != LocationType.FileSystem) - { - return new List<Video>(); - } - FileSystemInfo folder; try @@ -133,7 +135,7 @@ namespace MediaBrowser.Controller.Entities.Movies return LibraryManager.ResolvePaths<Video>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(video.Id) as Video; + var dbItem = LibraryManager.RetrieveItem(video.Id, typeof(Video)) as Video; if (dbItem != null) { diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index ad4cb2d09..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; } @@ -139,7 +131,10 @@ namespace MediaBrowser.Controller.Entities var additionalPartsChanged = false; - if (IsMultiPart && LocationType == LocationType.FileSystem) + // Must have a parent to have additional parts + // In other words, it must be part of the Parent/Child tree + // The additional parts won't have additional parts themselves + if (IsMultiPart && LocationType == LocationType.FileSystem && Parent != null) { try { @@ -164,11 +159,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>Task{System.Boolean}.</returns> private async Task<bool> RefreshAdditionalParts(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true) { - if (!IsMultiPart || LocationType != LocationType.FileSystem) - { - return false; - } - var newItems = LoadAdditionalParts().ToList(); var newItemIds = newItems.Select(i => i.Id).ToList(); @@ -214,7 +204,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths<Video>(files, null).Select(video => { // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.RetrieveItem(video.Id) as Video; + var dbItem = LibraryManager.RetrieveItem(video.Id, typeof(Video)) as Video; if (dbItem != null) { 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/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 7e84350b3..6d6fab3be 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -216,24 +216,9 @@ namespace MediaBrowser.Controller.Library /// Retrieves the item. /// </summary> /// <param name="id">The id.</param> - /// <returns>Task{BaseItem}.</returns> - BaseItem RetrieveItem(Guid id); - - /// <summary> - /// Saves the children. - /// </summary> - /// <param name="id">The id.</param> - /// <param name="children">The children.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken); - - /// <summary> - /// Retrieves the children. - /// </summary> - /// <param name="parent">The parent.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> RetrieveChildren(Folder parent); + /// <param name="type">The type.</param> + /// <returns>BaseItem.</returns> + BaseItem RetrieveItem(Guid id, Type type); /// <summary> /// Validates the artists. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ba9e9f5bd..f49221ce8 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -80,6 +80,7 @@ <Compile Include="Library\ILibraryPrescanTask.cs" /> <Compile Include="Library\IMetadataSaver.cs" /> <Compile Include="Localization\ILocalizationManager.cs" /> + <Compile Include="Reflection\TypeMapper.cs" /> <Compile Include="Session\ISessionManager.cs" /> <Compile Include="Drawing\ImageExtensions.cs" /> <Compile Include="Drawing\ImageHeader.cs" /> @@ -101,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 bf3bc626a..534e64a3f 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -1,9 +1,10 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Persistence { @@ -21,36 +22,6 @@ namespace MediaBrowser.Controller.Persistence Task SaveItem(BaseItem item, CancellationToken cancellationToken); /// <summary> - /// Gets an item - /// </summary> - /// <param name="id">The id.</param> - /// <returns>BaseItem.</returns> - BaseItem GetItem(Guid id); - - /// <summary> - /// Gets children of a given Folder - /// </summary> - /// <param name="parent">The parent.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> RetrieveChildren(Folder parent); - - /// <summary> - /// Retrieves the items. - /// </summary> - /// <param name="ids">The ids.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> GetItems(IEnumerable<Guid> ids); - - /// <summary> - /// Saves children of a given Folder - /// </summary> - /// <param name="parentId">The parent id.</param> - /// <param name="children">The children.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SaveChildren(Guid parentId, IEnumerable<BaseItem> children, CancellationToken cancellationToken); - - /// <summary> /// Gets the critic reviews. /// </summary> /// <param name="itemId">The item id.</param> @@ -72,5 +43,70 @@ namespace MediaBrowser.Controller.Persistence /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> Task SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken); + + /// <summary> + /// Retrieves the item. + /// </summary> + /// <param name="id">The id.</param> + /// <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> + /// Class ItemRepositoryExtensions + /// </summary> + public static class ItemRepositoryExtensions + { + /// <summary> + /// Retrieves the item. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="repository">The repository.</param> + /// <param name="id">The id.</param> + /// <returns>``0.</returns> + public static T RetrieveItem<T>(this IItemRepository repository, Guid id) + where T : BaseItem, new() + { + return repository.RetrieveItem(id, typeof(T)) as T; + } + + /// <summary> + /// Retrieves the item. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="repository">The repository.</param> + /// <param name="idList">The id list.</param> + /// <returns>IEnumerable{``0}.</returns> + public static IEnumerable<T> RetrieveItems<T>(this IItemRepository repository, IEnumerable<Guid> idList) + where T : BaseItem, new() + { + return idList.Select(repository.RetrieveItem<T>).Where(i => i != null); + } } } + diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 1b4efc58b..ad111f4ed 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -27,6 +27,6 @@ namespace MediaBrowser.Controller.Persistence /// <param name="userId">The user id.</param> /// <param name="key">The key.</param> /// <returns>Task{UserItemData}.</returns> - Task<UserItemData> GetUserData(Guid userId, string key); + UserItemData GetUserData(Guid userId, string key); } } diff --git a/MediaBrowser.Server.Implementations/Reflection/TypeMapper.cs b/MediaBrowser.Controller/Reflection/TypeMapper.cs index 5f411a002..d968a3b42 100644 --- a/MediaBrowser.Server.Implementations/Reflection/TypeMapper.cs +++ b/MediaBrowser.Controller/Reflection/TypeMapper.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.Linq; -namespace MediaBrowser.Server.Implementations.Reflection +namespace MediaBrowser.Controller.Reflection { /// <summary> /// Class TypeMapper diff --git a/MediaBrowser.Model/Entities/IHasProviderIds.cs b/MediaBrowser.Model/Entities/IHasProviderIds.cs index 2ddf8ffad..1c54455da 100644 --- a/MediaBrowser.Model/Entities/IHasProviderIds.cs +++ b/MediaBrowser.Model/Entities/IHasProviderIds.cs @@ -39,6 +39,11 @@ namespace MediaBrowser.Model.Entities /// <returns>System.String.</returns> public static string GetProviderId(this IHasProviderIds instance, string name) { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + if (instance.ProviderIds == null) { return null; @@ -57,6 +62,11 @@ namespace MediaBrowser.Model.Entities /// <param name="value">The value.</param> public static void SetProviderId(this IHasProviderIds instance, string name, string value) { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + // If it's null remove the key from the dictionary if (string.IsNullOrEmpty(value)) { diff --git a/MediaBrowser.Providers/Extensions/XDocumentExtensions.cs b/MediaBrowser.Providers/Extensions/XDocumentExtensions.cs new file mode 100644 index 000000000..9df920955 --- /dev/null +++ b/MediaBrowser.Providers/Extensions/XDocumentExtensions.cs @@ -0,0 +1,18 @@ +using System.Xml; +using System.Xml.Linq; + +namespace MediaBrowser.Providers.Extensions +{ + public static class XDocumentExtensions + { + public static XmlDocument ToXmlDocument(this XElement xDocument) + { + var xmlDocument = new XmlDocument(); + using (var xmlReader = xDocument.CreateReader()) + { + xmlDocument.Load(xmlReader); + } + return xmlDocument; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 29a85dd9c..74397a458 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -45,6 +45,7 @@ <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> + <Compile Include="Extensions\XDocumentExtensions.cs" /> <Compile Include="Extensions\XmlExtensions.cs" /> <Compile Include="FanartBaseProvider.cs" /> <Compile Include="FolderProviderFromXml.cs" /> @@ -78,6 +79,7 @@ <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Savers\MovieXmlSaver.cs" /> <Compile Include="TV\EpisodeImageFromMediaLocationProvider.cs" /> + <Compile Include="TV\EpisodeIndexNumberProvider.cs" /> <Compile Include="TV\EpisodeProviderFromXml.cs" /> <Compile Include="TV\EpisodeXmlParser.cs" /> <Compile Include="TV\FanArtSeasonProvider.cs" /> diff --git a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs index 99d1cdbc1..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; @@ -41,53 +40,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. @@ -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(); @@ -134,14 +86,14 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="mount">The mount.</param> protected virtual void OnPreFetch(T item, IIsoMount mount) { - + } /// <summary> /// 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> @@ -187,12 +129,16 @@ namespace MediaBrowser.Providers.MediaInfo var stream = new MediaStream { Codec = streamInfo.codec_name, - Language = GetDictionaryValue(streamInfo.tags, "language"), Profile = streamInfo.profile, Level = streamInfo.level, Index = streamInfo.index }; + if (streamInfo.tags != null) + { + stream.Language = GetDictionaryValue(streamInfo.tags, "language"); + } + if (streamInfo.codec_type.Equals("audio", StringComparison.OrdinalIgnoreCase)) { stream.Type = MediaStreamType.Audio; 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.Providers/Movies/MovieProviderFromXml.cs b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs index b7d3e6e57..a427f1bac 100644 --- a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs +++ b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs @@ -1,12 +1,12 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Logging; namespace MediaBrowser.Providers.Movies { @@ -15,7 +15,8 @@ namespace MediaBrowser.Providers.Movies /// </summary> public class MovieProviderFromXml : BaseMetadataProvider { - public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) + public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) { } @@ -78,25 +79,49 @@ namespace MediaBrowser.Providers.Movies private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - + var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "movie.xml")); if (metadataFile != null) { var path = metadataFile.FullName; - var boxset = item as BoxSet; await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); try { - if (boxset != null) + var movie = item as Movie; + + if (movie != null) { - new BaseItemXmlParser<BoxSet>(Logger).Fetch(boxset, path, cancellationToken); + new BaseItemXmlParser<Movie>(Logger).Fetch(movie, path, cancellationToken); } else { - new BaseItemXmlParser<Movie>(Logger).Fetch((Movie)item, path, cancellationToken); + var boxset = item as BoxSet; + + if (boxset != null) + { + new BaseItemXmlParser<BoxSet>(Logger).Fetch(boxset, path, cancellationToken); + } + else + { + var musicVideo = item as MusicVideo; + + if (musicVideo != null) + { + new BaseItemXmlParser<MusicVideo>(Logger).Fetch(musicVideo, path, cancellationToken); + } + else + { + var trailer = item as Trailer; + + if (trailer != null) + { + new BaseItemXmlParser<Trailer>(Logger).Fetch(trailer, path, cancellationToken); + } + } + } } } finally diff --git a/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs b/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs new file mode 100644 index 000000000..aa59ff7cf --- /dev/null +++ b/MediaBrowser.Providers/TV/EpisodeIndexNumberProvider.cs @@ -0,0 +1,67 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.TV +{ + /// <summary> + /// Making this a provider because of how slow it is + /// It only ever needs to run once + /// </summary> + public class EpisodeIndexNumberProvider : BaseMetadataProvider + { + /// <summary> + /// Initializes a new instance of the <see cref="BaseMetadataProvider" /> class. + /// </summary> + /// <param name="logManager">The log manager.</param> + /// <param name="configurationManager">The configuration manager.</param> + public EpisodeIndexNumberProvider(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) + { + } + + /// <summary> + /// Supportses the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + public override bool Supports(BaseItem item) + { + return item is Episode; + } + + /// <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 Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + var episode = (Episode)item; + + episode.IndexNumber = TVUtils.GetEpisodeNumberFromFile(item.Path, item.Parent is Season); + episode.IndexNumberEnd = TVUtils.GetEndingEpisodeNumberFromFile(item.Path); + + SetLastRefreshed(item, DateTime.UtcNow); + + return TrueTaskResult; + } + + /// <summary> + /// Gets the priority. + /// </summary> + /// <value>The priority.</value> + public override MetadataProviderPriority Priority + { + get { return MetadataProviderPriority.First; } + } + } +} diff --git a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs index 0ddc84c65..e67dd254b 100644 --- a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Common.Extensions; +using System.Collections.Generic; +using System.Xml.Linq; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -258,7 +260,30 @@ namespace MediaBrowser.Providers.TV { return status; } + IEnumerable<XmlDocument> extraEpisodesNode = new XmlDocument[]{}; + if (episode.IndexNumberEnd.HasValue) + { + var seriesXDocument = XDocument.Load(new XmlNodeReader(seriesXml)); + if (usingAbsoluteData) + { + extraEpisodesNode = + seriesXDocument.Descendants("Episode") + .Where( + x => + int.Parse(x.Element("absolute_number").Value) > episode.IndexNumber && + int.Parse(x.Element("absolute_number").Value) <= episode.IndexNumberEnd.Value).OrderBy(x => x.Element("absolute_number").Value).Select(x => x.ToXmlDocument()); + } + else + { + var all = + seriesXDocument.Descendants("Episode").Where(x => int.Parse(x.Element("SeasonNumber").Value) == seasonNumber.Value); + + var xElements = all.Where(x => int.Parse(x.Element("EpisodeNumber").Value) > episode.IndexNumber && int.Parse(x.Element("EpisodeNumber").Value) <= episode.IndexNumberEnd.Value); + extraEpisodesNode = xElements.OrderBy(x => x.Element("EpisodeNumber").Value).Select(x => x.ToXmlDocument()); + } + + } var doc = new XmlDocument(); doc.LoadXml(episodeNode.OuterXml); @@ -281,7 +306,8 @@ namespace MediaBrowser.Providers.TV } if (!episode.LockedFields.Contains(MetadataFields.Overview)) { - episode.Overview = doc.SafeGetString("//Overview"); + var extraOverview = extraEpisodesNode.Aggregate("", (current, xmlDocument) => current + ("\r\n\r\n" + xmlDocument.SafeGetString("//Overview"))); + episode.Overview = doc.SafeGetString("//Overview") + extraOverview; } if (usingAbsoluteData) episode.IndexNumber = doc.SafeGetInt32("//absolute_number", -1); @@ -289,7 +315,8 @@ namespace MediaBrowser.Providers.TV episode.IndexNumber = doc.SafeGetInt32("//EpisodeNumber"); if (!episode.LockedFields.Contains(MetadataFields.Name)) { - episode.Name = doc.SafeGetString("//EpisodeName"); + var extraNames = extraEpisodesNode.Aggregate("", (current, xmlDocument) => current + (", " + xmlDocument.SafeGetString("//EpisodeName"))); + episode.Name = doc.SafeGetString("//EpisodeName") + extraNames; } episode.CommunityRating = doc.SafeGetSingle("//Rating", -1, 10); var firstAired = doc.SafeGetString("//FirstAired"); @@ -314,7 +341,18 @@ namespace MediaBrowser.Providers.TV episode.AddPerson(person); } } - + foreach (var xmlDocument in extraEpisodesNode) + { + var extraActors = xmlDocument.SafeGetString("//GuestStars"); + if (extraActors == null) continue; + // Sometimes tvdb actors have leading spaces + foreach (var person in extraActors.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) + .Where(i => !string.IsNullOrWhiteSpace(i)) + .Select(str => new PersonInfo { Type = PersonType.GuestStar, Name = str.Trim() }).Where(person => !episode.People.Any(x=>x.Type == person.Type && x.Name == person.Name))) + { + episode.AddPerson(person); + } + } var directors = doc.SafeGetString("//Director"); if (directors != null) 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/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f4d0f9c50..e174b9a23 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -564,7 +564,7 @@ namespace MediaBrowser.Server.Implementations.Library Directory.CreateDirectory(rootFolderPath); } - var rootFolder = RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); + var rootFolder = RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder)), typeof(AggregateFolder)) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); // Add in the plug-in folders foreach (var child in PluginFolderCreators) @@ -589,7 +589,8 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>UserRootFolder.</returns> public UserRootFolder GetUserRootFolder(string userRootPath) { - return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ?? (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath))); + return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder)), typeof(UserRootFolder)) as UserRootFolder ?? + (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath))); } /// <summary> @@ -779,9 +780,11 @@ namespace MediaBrowser.Server.Implementations.Library cancellationToken.ThrowIfCancellationRequested(); - var id = path.GetMBId(typeof(T)); + var type = typeof(T); - var item = RetrieveItem(id) as T; + var id = path.GetMBId(type); + + var item = RetrieveItem(id, type) as T; if (item == null) { item = new T @@ -816,7 +819,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>Task.</returns> public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) { - const int maxTasks = 10; + const int maxTasks = 15; var tasks = new List<Task>(); @@ -1166,7 +1169,7 @@ namespace MediaBrowser.Server.Implementations.Library return item; } - return ItemRepository.GetItem(id); + return null; } /// <summary> @@ -1340,39 +1343,11 @@ namespace MediaBrowser.Server.Implementations.Library /// Retrieves the item. /// </summary> /// <param name="id">The id.</param> - /// <returns>Task{BaseItem}.</returns> - public BaseItem RetrieveItem(Guid id) - { - return ItemRepository.GetItem(id); - } - - /// <summary> - /// Saves the children. - /// </summary> - /// <param name="id">The id.</param> - /// <param name="children">The children.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken) - { - return ItemRepository.SaveChildren(id, children, cancellationToken); - } - - /// <summary> - /// Retrieves the children. - /// </summary> - /// <param name="parent">The parent.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - public IEnumerable<BaseItem> RetrieveChildren(Folder parent) + /// <param name="type">The type.</param> + /// <returns>BaseItem.</returns> + public BaseItem RetrieveItem(Guid id, Type type) { - var children = ItemRepository.RetrieveChildren(parent).ToList(); - - foreach (var child in children) - { - child.Parent = parent; - } - - return children; + return ItemRepository.RetrieveItem(id, type); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 3969acac7..84d57b972 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -51,9 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV if (episode != null) { - episode.IndexNumber = TVUtils.GetEpisodeNumberFromFile(args.Path, season != null); - episode.IndexNumberEnd = TVUtils.GetEndingEpisodeNumberFromFile(args.Path); - if (season != null) { episode.ParentIndexNumber = season.IndexNumber; diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index a006161d4..34930b34e 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -48,21 +48,21 @@ <Reference Include="MoreLinq"> <HintPath>..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll</HintPath> </Reference> - <Reference Include="ServiceStack, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.3.9.46\lib\net35\ServiceStack.dll</HintPath> + <HintPath>..\packages\ServiceStack.3.9.54\lib\net35\ServiceStack.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Api.Swagger, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Api.Swagger, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Api.Swagger.3.9.46\lib\net35\ServiceStack.Api.Swagger.dll</HintPath> + <HintPath>..\packages\ServiceStack.Api.Swagger.3.9.54\lib\net35\ServiceStack.Api.Swagger.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Common, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Common, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Common.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Common.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Interfaces, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Interfaces, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Interfaces.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath> </Reference> <Reference Include="ServiceStack.OrmLite.SqlServer, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> @@ -72,22 +72,20 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll</HintPath> </Reference> - <Reference Include="ServiceStack.ServiceInterface, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.ServiceInterface, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.3.9.46\lib\net35\ServiceStack.ServiceInterface.dll</HintPath> + <HintPath>..\packages\ServiceStack.3.9.54\lib\net35\ServiceStack.ServiceInterface.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Text, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Text, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Text.3.9.45\lib\net35\ServiceStack.Text.dll</HintPath> + <HintPath>..\packages\ServiceStack.Text.3.9.54\lib\net35\ServiceStack.Text.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> - <Reference Include="System.Data.SQLite, Version=1.0.86.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> + <Reference Include="System.Data.SQLite"> <HintPath>..\packages\System.Data.SQLite.x86.1.0.86.0\lib\net45\System.Data.SQLite.dll</HintPath> </Reference> - <Reference Include="System.Data.SQLite.Linq, Version=1.0.86.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> + <Reference Include="System.Data.SQLite.Linq"> <HintPath>..\packages\System.Data.SQLite.x86.1.0.86.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath> </Reference> <Reference Include="System.Reactive.Core"> @@ -99,6 +97,7 @@ <Reference Include="System.Reactive.Linq"> <HintPath>..\packages\Rx-Linq.2.1.30214.0\lib\Net45\System.Reactive.Linq.dll</HintPath> </Reference> + <Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Web" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> @@ -120,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" /> @@ -140,9 +138,10 @@ <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="Properties\AssemblyInfo.cs" /> <Compile Include="Providers\ProviderManager.cs" /> - <Compile Include="Reflection\TypeMapper.cs" /> <Compile Include="ScheduledTasks\ArtistValidationTask.cs" /> <Compile Include="ScheduledTasks\PeopleValidationTask.cs" /> <Compile Include="ScheduledTasks\ChapterImagesTask.cs" /> @@ -171,12 +170,10 @@ <Compile Include="Sorting\RevenueComparer.cs" /> <Compile Include="Sorting\RuntimeComparer.cs" /> <Compile Include="Sorting\SortNameComparer.cs" /> - <Compile Include="Sqlite\SQLiteDisplayPreferencesRepository.cs" /> - <Compile Include="Sqlite\SQLiteExtensions.cs" /> - <Compile Include="Sqlite\SQLiteItemRepository.cs" /> - <Compile Include="Sqlite\SQLiteRepository.cs" /> - <Compile Include="Sqlite\SQLiteUserDataRepository.cs" /> - <Compile Include="Sqlite\SQLiteUserRepository.cs" /> + <Compile Include="Persistence\SqliteDisplayPreferencesRepository.cs" /> + <Compile Include="Persistence\SqliteItemRepository.cs" /> + <Compile Include="Persistence\SqliteUserDataRepository.cs" /> + <Compile Include="Persistence\SqliteUserRepository.cs" /> <Compile Include="Udp\UdpMessageReceivedEventArgs.cs" /> <Compile Include="Udp\UdpServer.cs" /> <Compile Include="Updates\InstallationManager.cs" /> @@ -218,6 +215,15 @@ <EmbeddedResource Include="Localization\Ratings\kz.txt" /> <EmbeddedResource Include="Localization\Ratings\nz.txt" /> <EmbeddedResource Include="Localization\Ratings\ru.txt" /> + <EmbeddedResource Include="MediaEncoder\readme.txt" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="MediaEncoder\fonts\ARIALUNI.TTF" /> + <EmbeddedResource Include="MediaEncoder\fonts\fonts.conf" /> + <EmbeddedResource Include="MediaEncoder\ffmpeg20130614.zip" /> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> <Content Include="swagger-ui\css\hightlight.default.css"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> @@ -272,15 +278,7 @@ <Content Include="swagger-ui\swagger-ui.min.js"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <EmbeddedResource Include="MediaEncoder\readme.txt" /> - </ItemGroup> - <ItemGroup> - <EmbeddedResource Include="MediaEncoder\fonts\ARIALUNI.TTF" /> - <EmbeddedResource Include="MediaEncoder\fonts\fonts.conf" /> - <EmbeddedResource Include="MediaEncoder\ffmpeg20130614.zip" /> - <None Include="packages.config" /> </ItemGroup> - <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(SolutionDir)\.nuget\nuget.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 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/Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs index 60c3c1fe0..cb965c3f9 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs @@ -10,18 +10,17 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -namespace MediaBrowser.Server.Implementations.Sqlite +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.Sqlite { get { - return RepositoryName; + return "SQLite"; } } @@ -45,9 +44,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite private readonly IApplicationPaths _appPaths; private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - + /// <summary> - /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. + /// Initializes a new instance of the <see cref="SqliteDisplayPreferencesRepository" /> class. /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="jsonSerializer">The json serializer.</param> @@ -57,8 +56,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// or /// appPaths /// </exception> - public SQLiteDisplayPreferencesRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) + public SqliteDisplayPreferencesRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) { if (jsonSerializer == null) { @@ -71,6 +69,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite _jsonSerializer = jsonSerializer; _appPaths = appPaths; + + _logger = logManager.GetLogger(GetType().Name); } /// <summary> @@ -81,7 +81,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite { 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.Sqlite "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); } /// <summary> @@ -127,9 +127,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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.Sqlite } 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.Sqlite /// <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.Sqlite 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/Sqlite/SQLiteRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs index 6cd816e51..2b14e9b24 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs @@ -1,94 +1,115 @@ -using MediaBrowser.Model.Logging; -using System; +using System; using System.Data; -using System.Data.Common; using System.Data.SQLite; using System.IO; using System.Threading.Tasks; +using MediaBrowser.Model.Logging; -namespace MediaBrowser.Server.Implementations.Sqlite +namespace MediaBrowser.Server.Implementations.Persistence { /// <summary> - /// Class SqliteRepository + /// Class SQLiteExtensions /// </summary> - public abstract class SqliteRepository : IDisposable + static class SqliteExtensions { /// <summary> - /// The db file name - /// </summary> - protected string DbFileName; - /// <summary> - /// The connection + /// Adds the param. /// </summary> - protected SQLiteConnection Connection; + /// <param name="cmd">The CMD.</param> + /// <param name="param">The param.</param> + /// <returns>SQLiteParameter.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param) + { + if (string.IsNullOrEmpty(param)) + { + throw new ArgumentNullException(); + } - /// <summary> - /// Gets the logger. - /// </summary> - /// <value>The logger.</value> - protected ILogger Logger { get; private set; } + var sqliteParam = new SQLiteParameter(param); + cmd.Parameters.Add(sqliteParam); + return sqliteParam; + } /// <summary> - /// Initializes a new instance of the <see cref="SqliteRepository" /> class. + /// Adds the param. /// </summary> - /// <param name="logManager">The log manager.</param> - /// <exception cref="System.ArgumentNullException">logger</exception> - protected SqliteRepository(ILogManager logManager) + /// <param name="cmd">The CMD.</param> + /// <param name="param">The param.</param> + /// <param name="data">The data.</param> + /// <returns>SQLiteParameter.</returns> + /// <exception cref="System.ArgumentNullException"></exception> + public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param, object data) { - if (logManager == null) + if (string.IsNullOrEmpty(param)) { - throw new ArgumentNullException("logManager"); + throw new ArgumentNullException(); } - Logger = logManager.GetLogger(GetType().Name); + var sqliteParam = AddParam(cmd, param); + sqliteParam.Value = data; + return sqliteParam; } /// <summary> - /// Connects to DB. + /// Determines whether the specified conn is open. /// </summary> - /// <param name="dbPath">The db path.</param> - /// <returns>Task{System.Boolean}.</returns> - /// <exception cref="System.ArgumentNullException">dbPath</exception> - protected Task ConnectToDb(string dbPath) + /// <param name="conn">The conn.</param> + /// <returns><c>true</c> if the specified conn is open; otherwise, <c>false</c>.</returns> + public static bool IsOpen(this SQLiteConnection conn) { - if (string.IsNullOrEmpty(dbPath)) + 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("dbPath"); + throw new ArgumentNullException("reader"); } - DbFileName = dbPath; - var connectionstr = new SQLiteConnectionStringBuilder + var memoryStream = new MemoryStream(); + var num = 0L; + var array = new byte[4096]; + long bytes; + do { - PageSize = 4096, - CacheSize = 40960, - SyncMode = SynchronizationModes.Off, - DataSource = dbPath, - JournalMode = SQLiteJournalModeEnum.Wal - }; - - Connection = new SQLiteConnection(connectionstr.ConnectionString); - - return Connection.OpenAsync(); + 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> - protected void RunQueries(string[] queries) + public static void RunQueries(this IDbConnection connection, string[] queries, ILogger logger) { if (queries == null) { throw new ArgumentNullException("queries"); } - using (var tran = Connection.BeginTransaction()) + using (var tran = connection.BeginTransaction()) { try { - using (var cmd = Connection.CreateCommand()) + using (var cmd = connection.CreateCommand()) { foreach (var query in queries) { @@ -102,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite } catch (Exception e) { - Logger.ErrorException("Error running queries", e); + logger.ErrorException("Error running queries", e); tran.Rollback(); throw; } @@ -110,74 +131,32 @@ namespace MediaBrowser.Server.Implementations.Sqlite } /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Connects to db. /// </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) + /// <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 (dispose) + if (string.IsNullOrEmpty(dbPath)) { - try - { - lock (_disposeLock) - { - if (Connection != null) - { - if (Connection.IsOpen()) - { - Connection.Close(); - } - - Connection.Dispose(); - Connection = null; - } - } - } - catch (Exception ex) - { - Logger.ErrorException("Error disposing database", ex); - } + throw new ArgumentNullException("dbPath"); } - } - /// <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) + var connectionstr = new SQLiteConnectionStringBuilder { - throw new ArgumentNullException("reader"); - } + PageSize = 4096, + CacheSize = 4096, + SyncMode = SynchronizationModes.Off, + DataSource = dbPath, + JournalMode = SQLiteJournalModeEnum.Wal + }; - 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; + 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 new file mode 100644 index 000000000..b3251ddb9 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -0,0 +1,405 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +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.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Persistence +{ + /// <summary> + /// Class SQLiteItemRepository + /// </summary> + public class SqliteItemRepository : IItemRepository + { + private SQLiteConnection _connection; + + private readonly ILogger _logger; + + /// <summary> + /// Gets the name of the repository + /// </summary> + /// <value>The name.</value> + public string Name + { + get + { + return "SQLite"; + } + } + + /// <summary> + /// Gets the json serializer. + /// </summary> + /// <value>The json serializer.</value> + private readonly IJsonSerializer _jsonSerializer; + + /// <summary> + /// The _app paths + /// </summary> + private readonly IApplicationPaths _appPaths; + + /// <summary> + /// The _save item command + /// </summary> + private SQLiteCommand _saveItemCommand; + + private readonly string _criticReviewsPath; + + private SqliteChapterRepository _chapterRepository; + + /// <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 SqliteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) + { + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + _appPaths = appPaths; + _jsonSerializer = jsonSerializer; + + _criticReviewsPath = Path.Combine(_appPaths.DataPath, "critic-reviews"); + + _logger = logManager.GetLogger(GetType().Name); + + _chapterRepository = new SqliteChapterRepository(appPaths, logManager); + } + + /// <summary> + /// Opens the connection to the database + /// </summary> + /// <returns>Task.</returns> + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "library.db"); + + _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)", + + //pragmas + "pragma temp_store = memory" + }; + + _connection.RunQueries(queries, _logger); + + PrepareStatements(); + + await _chapterRepository.Initialize().ConfigureAwait(false); + } + + /// <summary> + /// The _write lock + /// </summary> + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + + /// <summary> + /// Prepares the statements. + /// </summary> + private void PrepareStatements() + { + _saveItemCommand = new SQLiteCommand + { + CommandText = "replace into baseitems (guid, data) values (@1, @2)" + }; + + _saveItemCommand.Parameters.Add(new SQLiteParameter("@1")); + _saveItemCommand.Parameters.Add(new SQLiteParameter("@2")); + } + + /// <summary> + /// Save a standard item in the repo + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">item</exception> + public Task SaveItem(BaseItem item, CancellationToken cancellationToken) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + return SaveItems(new[] { item }, cancellationToken); + } + + /// <summary> + /// Saves the items. + /// </summary> + /// <param name="items">The items.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException"> + /// items + /// or + /// cancellationToken + /// </exception> + public async Task SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken) + { + if (items == null) + { + throw new ArgumentNullException("items"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + SQLiteTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + foreach (var item in items) + { + cancellationToken.ThrowIfCancellationRequested(); + + _saveItemCommand.Parameters[0].Value = item.Id; + _saveItemCommand.Parameters[1].Value = _jsonSerializer.SerializeToBytes(item); + + _saveItemCommand.Transaction = transaction; + + await _saveItemCommand.ExecuteNonQueryAsync(cancellationToken); + } + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save items:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + + /// <summary> + /// Internal retrieve from items or users table + /// </summary> + /// <param name="id">The id.</param> + /// <param name="type">The type.</param> + /// <returns>BaseItem.</returns> + /// <exception cref="System.ArgumentNullException">id</exception> + /// <exception cref="System.ArgumentException"></exception> + public BaseItem RetrieveItem(Guid id, Type type) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select data from baseitems where guid = @guid"; + var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); + guidParam.Value = id; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + using (var stream = reader.GetMemoryStream(0)) + { + return _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem; + } + } + } + return null; + } + } + + /// <summary> + /// Gets the critic reviews. + /// </summary> + /// <param name="itemId">The item id.</param> + /// <returns>Task{IEnumerable{ItemReview}}.</returns> + public Task<IEnumerable<ItemReview>> GetCriticReviews(Guid itemId) + { + return Task.Run<IEnumerable<ItemReview>>(() => + { + + try + { + var path = Path.Combine(_criticReviewsPath, itemId + ".json"); + + return _jsonSerializer.DeserializeFromFile<List<ItemReview>>(path); + } + catch (DirectoryNotFoundException) + { + return new List<ItemReview>(); + } + catch (FileNotFoundException) + { + return new List<ItemReview>(); + } + + }); + } + + /// <summary> + /// Saves the critic reviews. + /// </summary> + /// <param name="itemId">The item id.</param> + /// <param name="criticReviews">The critic reviews.</param> + /// <returns>Task.</returns> + public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews) + { + return Task.Run(() => + { + if (!Directory.Exists(_criticReviewsPath)) + { + Directory.CreateDirectory(_criticReviewsPath); + } + + var path = Path.Combine(_criticReviewsPath, itemId + ".json"); + + _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/Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs index d378809ff..1d127ae96 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs @@ -1,5 +1,4 @@ -using System.Data.SQLite; -using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Logging; @@ -7,26 +6,23 @@ using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; using System.Data; +using System.Data.SQLite; using System.IO; using System.Threading; using System.Threading.Tasks; -namespace MediaBrowser.Server.Implementations.Sqlite +namespace MediaBrowser.Server.Implementations.Persistence { - /// <summary> - /// Class SQLiteUserDataRepository - /// </summary> - public class SQLiteUserDataRepository : SqliteRepository, IUserDataRepository + public class SqliteUserDataRepository : IUserDataRepository { - private readonly ConcurrentDictionary<string, Task<UserItemData>> _userData = new ConcurrentDictionary<string, Task<UserItemData>>(); + 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> @@ -35,7 +31,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite { get { - return RepositoryName; + return "SQLite"; } } @@ -47,7 +43,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite private readonly IApplicationPaths _appPaths; /// <summary> - /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. + /// Initializes a new instance of the <see cref="SqliteUserDataRepository"/> class. /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="jsonSerializer">The json serializer.</param> @@ -57,8 +53,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// or /// appPaths /// </exception> - public SQLiteUserDataRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) + public SqliteUserDataRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) { if (jsonSerializer == null) { @@ -71,6 +66,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite _jsonSerializer = jsonSerializer; _appPaths = appPaths; + _logger = logManager.GetLogger(GetType().Name); } /// <summary> @@ -81,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite { var dbFile = Path.Combine(_appPaths.DataPath, "userdata.db"); - await ConnectToDb(dbFile).ConfigureAwait(false); + _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); string[] queries = { @@ -92,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); } /// <summary> @@ -135,14 +131,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite { await PersistUserData(userId, key, userData, cancellationToken).ConfigureAwait(false); - var newValue = Task.FromResult(userData); + var newValue = userData; // Once it succeeds, put it into the dictionary to make it available to everyone else _userData.AddOrUpdate(GetInternalKey(userId, key), newValue, delegate { return newValue; }); } catch (Exception ex) { - Logger.ErrorException("Error saving user data", ex); + _logger.ErrorException("Error saving user data", ex); throw; } @@ -181,9 +177,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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); @@ -208,7 +204,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite } catch (Exception e) { - Logger.ErrorException("Failed to save user data:", e); + _logger.ErrorException("Failed to save user data:", e); if (transaction != null) { @@ -239,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// or /// key /// </exception> - public Task<UserItemData> GetUserData(Guid userId, string key) + public UserItemData GetUserData(Guid userId, string key) { if (userId == Guid.Empty) { @@ -259,9 +255,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// <param name="userId">The user id.</param> /// <param name="key">The key.</param> /// <returns>Task{UserItemData}.</returns> - private async Task<UserItemData> RetrieveUserData(Guid userId, string key) + 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"; @@ -271,11 +267,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite var userIdParam = cmd.Parameters.Add("@userId", DbType.Guid); userIdParam.Value = userId; - 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<UserItemData>(stream); } @@ -285,5 +281,47 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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/Sqlite/SQLiteUserRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs index 335841549..09e34cf08 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs @@ -11,19 +11,18 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -namespace MediaBrowser.Server.Implementations.Sqlite +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 @@ -33,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite { get { - return RepositoryName; + return "SQLite"; } } @@ -49,14 +48,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite private readonly IApplicationPaths _appPaths; /// <summary> - /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class. + /// Initializes a new instance of the <see cref="SqliteUserRepository" /> 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</exception> - public SQLiteUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) + public SqliteUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) { if (appPaths == null) { @@ -69,6 +67,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite _appPaths = appPaths; _jsonSerializer = jsonSerializer; + + _logger = logManager.GetLogger(GetType().Name); } /// <summary> @@ -79,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite { 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.Sqlite "pragma temp_store = memory" }; - RunQueries(queries); + _connection.RunQueries(queries, _logger); } /// <summary> @@ -124,9 +124,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite 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.Sqlite } 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.Sqlite /// <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.Sqlite { 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.Sqlite 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.Sqlite } 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.Sqlite _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/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 08398e22e..beab6200f 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -106,6 +106,11 @@ namespace MediaBrowser.Server.Implementations.Providers /// <returns>Task{System.Boolean}.</returns> public async Task<bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true) { + if (item == null) + { + throw new ArgumentNullException("item"); + } + // Allow providers of the same priority to execute in parallel MetadataProviderPriority? currentPriority = null; var currentTasks = new List<Task<bool>>(); 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.Server.Implementations/ScheduledTasks/VideoImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs index 12f98cef3..d63494c1e 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs @@ -210,9 +210,9 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks { var allItems = sourceItems.ToList(); - var localTrailers = allItems.SelectMany(i => _itemRepo.GetItems(i.LocalTrailerIds).Cast<Video>()); + var localTrailers = allItems.SelectMany(i => _itemRepo.RetrieveItems<Trailer>(i.LocalTrailerIds)); - var themeVideos = allItems.SelectMany(i => _itemRepo.GetItems(i.ThemeVideoIds).Cast<Video>()); + var themeVideos = allItems.SelectMany(i => _itemRepo.RetrieveItems<Video>(i.ThemeVideoIds)); var videos = allItems.OfType<Video>().ToList(); @@ -222,8 +222,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks items.AddRange(themeVideos); - items.AddRange(videos.SelectMany(i => _itemRepo.GetItems(i.AdditionalPartIds).Cast<Video>()).ToList()); - items.AddRange(videos.OfType<Movie>().SelectMany(i => _itemRepo.GetItems(i.SpecialFeatureIds).Cast<Video>()).ToList()); + items.AddRange(videos.SelectMany(i => _itemRepo.RetrieveItems<Video>(i.AdditionalPartIds)).ToList()); + items.AddRange(videos.OfType<Movie>().SelectMany(i => _itemRepo.RetrieveItems<Video>(i.SpecialFeatureIds)).ToList()); return items.Where(i => { diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 29ce2698c..dda9658d4 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -215,7 +215,7 @@ namespace MediaBrowser.Server.Implementations.Session var key = item.GetUserDataKey(); - var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false); + var data = _userDataRepository.GetUserData(user.Id, key); data.PlayCount++; data.LastPlayedDate = DateTime.UtcNow; @@ -226,7 +226,7 @@ namespace MediaBrowser.Server.Implementations.Session } await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false); - + // Nothing to save here // Fire events to inform plugins EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs @@ -266,7 +266,7 @@ namespace MediaBrowser.Server.Implementations.Session if (positionTicks.HasValue) { - var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false); + var data = _userDataRepository.GetUserData(user.Id, key); UpdatePlayState(item, data, positionTicks.Value); await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false); @@ -307,7 +307,7 @@ namespace MediaBrowser.Server.Implementations.Session var key = item.GetUserDataKey(); - var data = await _userDataRepository.GetUserData(user.Id, key).ConfigureAwait(false); + var data = _userDataRepository.GetUserData(user.Id, key); if (positionTicks.HasValue) { diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs index c634c760e..2abd4d0f2 100644 --- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Server.Implementations.Sorting /// <returns>DateTime.</returns> private DateTime GetDate(BaseItem x) { - var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()).Result; + var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()); if (userdata != null && userdata.LastPlayedDate.HasValue) { diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs index a7cbd2149..d4c22e6e0 100644 --- a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Sorting /// <returns>DateTime.</returns> private int GetValue(BaseItem x) { - var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()).Result; + var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()); return userdata == null ? 0 : userdata.PlayCount; } diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs deleted file mode 100644 index 6aed8a352..000000000 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Data; -using System.Data.SQLite; - -namespace MediaBrowser.Server.Implementations.Sqlite -{ - /// <summary> - /// Class SQLiteExtensions - /// </summary> - static class SQLiteExtensions - { - /// <summary> - /// Adds the param. - /// </summary> - /// <param name="cmd">The CMD.</param> - /// <param name="param">The param.</param> - /// <returns>SQLiteParameter.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param) - { - if (string.IsNullOrEmpty(param)) - { - throw new ArgumentNullException(); - } - - var sqliteParam = new SQLiteParameter(param); - cmd.Parameters.Add(sqliteParam); - return sqliteParam; - } - - /// <summary> - /// Adds the param. - /// </summary> - /// <param name="cmd">The CMD.</param> - /// <param name="param">The param.</param> - /// <param name="data">The data.</param> - /// <returns>SQLiteParameter.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param, object data) - { - if (string.IsNullOrEmpty(param)) - { - throw new ArgumentNullException(); - } - - var sqliteParam = AddParam(cmd, param); - sqliteParam.Value = data; - return sqliteParam; - } - - /// <summary> - /// Determines whether the specified conn is open. - /// </summary> - /// <param name="conn">The conn.</param> - /// <returns><c>true</c> if the specified conn is open; otherwise, <c>false</c>.</returns> - public static bool IsOpen(this SQLiteConnection conn) - { - return conn.State == ConnectionState.Open; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs deleted file mode 100644 index bead1360b..000000000 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs +++ /dev/null @@ -1,532 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Server.Implementations.Reflection; -using System; -using System.Collections.Generic; -using System.Data; -using System.Data.SQLite; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Sqlite -{ - /// <summary> - /// Class SQLiteItemRepository - /// </summary> - public class SQLiteItemRepository : SqliteRepository, IItemRepository - { - /// <summary> - /// The _type mapper - /// </summary> - private readonly TypeMapper _typeMapper = new TypeMapper(); - - /// <summary> - /// The repository name - /// </summary> - public const string RepositoryName = "SQLite"; - - /// <summary> - /// Gets the name of the repository - /// </summary> - /// <value>The name.</value> - public string Name - { - get - { - return RepositoryName; - } - } - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - private readonly IJsonSerializer _jsonSerializer; - - /// <summary> - /// The _app paths - /// </summary> - private readonly IApplicationPaths _appPaths; - - /// <summary> - /// The _save item command - /// </summary> - private SQLiteCommand _saveItemCommand; - /// <summary> - /// The _delete children command - /// </summary> - private SQLiteCommand _deleteChildrenCommand; - /// <summary> - /// The _save children command - /// </summary> - private SQLiteCommand _saveChildrenCommand; - - /// <summary> - /// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> 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</exception> - public SQLiteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) - : base(logManager) - { - if (appPaths == null) - { - throw new ArgumentNullException("appPaths"); - } - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - - _appPaths = appPaths; - _jsonSerializer = jsonSerializer; - } - - /// <summary> - /// Opens the connection to the database - /// </summary> - /// <returns>Task.</returns> - public async Task Initialize() - { - var dbFile = Path.Combine(_appPaths.DataPath, "library.db"); - - await ConnectToDb(dbFile).ConfigureAwait(false); - - string[] queries = { - - "create table if not exists items (guid GUID primary key, obj_type, data BLOB)", - "create index if not exists idx_items on items(guid)", - "create table if not exists children (guid GUID, child GUID)", - "create unique index if not exists idx_children on children(guid, child)", - "create table if not exists schema_version (table_name primary key, version)", - //triggers - TriggerSql, - //pragmas - "pragma temp_store = memory" - }; - - RunQueries(queries); - - PrepareStatements(); - } - - //cascade delete triggers - /// <summary> - /// The trigger SQL - /// </summary> - protected string TriggerSql = - @"CREATE TRIGGER if not exists delete_item - AFTER DELETE - ON items - FOR EACH ROW - BEGIN - DELETE FROM children WHERE children.guid = old.child; - DELETE FROM children WHERE children.child = old.child; - END"; - - /// <summary> - /// The _write lock - /// </summary> - private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - - /// <summary> - /// Prepares the statements. - /// </summary> - private void PrepareStatements() - { - _saveItemCommand = new SQLiteCommand - { - CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)" - }; - - _saveItemCommand.Parameters.Add(new SQLiteParameter("@1")); - _saveItemCommand.Parameters.Add(new SQLiteParameter("@2")); - _saveItemCommand.Parameters.Add(new SQLiteParameter("@3")); - - _deleteChildrenCommand = new SQLiteCommand - { - CommandText = "delete from children where guid = @guid" - }; - _deleteChildrenCommand.Parameters.Add(new SQLiteParameter("@guid")); - - _saveChildrenCommand = new SQLiteCommand - { - CommandText = "replace into children (guid, child) values (@guid, @child)" - }; - _saveChildrenCommand.Parameters.Add(new SQLiteParameter("@guid")); - _saveChildrenCommand.Parameters.Add(new SQLiteParameter("@child")); - } - - /// <summary> - /// Save a standard item in the repo - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - public Task SaveItem(BaseItem item, CancellationToken cancellationToken) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - return SaveItems(new[] { item }, cancellationToken); - } - - /// <summary> - /// Saves the items. - /// </summary> - /// <param name="items">The items.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException"> - /// items - /// or - /// cancellationToken - /// </exception> - public async Task SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken) - { - if (items == null) - { - throw new ArgumentNullException("items"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - SQLiteTransaction transaction = null; - - try - { - transaction = Connection.BeginTransaction(); - - foreach (var item in items) - { - cancellationToken.ThrowIfCancellationRequested(); - - _saveItemCommand.Parameters[0].Value = item.Id; - _saveItemCommand.Parameters[1].Value = item.GetType().FullName; - _saveItemCommand.Parameters[2].Value = _jsonSerializer.SerializeToBytes(item); - - _saveItemCommand.Transaction = transaction; - - await _saveItemCommand.ExecuteNonQueryAsync(cancellationToken); - } - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - Logger.ErrorException("Failed to save items:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - - _writeLock.Release(); - } - } - - /// <summary> - /// Retrieve a standard item from the repo - /// </summary> - /// <param name="id">The id.</param> - /// <returns>BaseItem.</returns> - /// <exception cref="System.ArgumentNullException">id</exception> - /// <exception cref="System.ArgumentException"></exception> - public BaseItem GetItem(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - return RetrieveItemInternal(id); - } - - /// <summary> - /// Retrieves the items. - /// </summary> - /// <param name="ids">The ids.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - /// <exception cref="System.ArgumentNullException">ids</exception> - public IEnumerable<BaseItem> GetItems(IEnumerable<Guid> ids) - { - if (ids == null) - { - throw new ArgumentNullException("ids"); - } - - return ids.Select(RetrieveItemInternal); - } - - /// <summary> - /// Internal retrieve from items or users table - /// </summary> - /// <param name="id">The id.</param> - /// <returns>BaseItem.</returns> - /// <exception cref="System.ArgumentNullException">id</exception> - /// <exception cref="System.ArgumentException"></exception> - protected BaseItem RetrieveItemInternal(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - using (var cmd = Connection.CreateCommand()) - { - cmd.CommandText = "select obj_type,data from items where guid = @guid"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = id; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) - { - if (reader.Read()) - { - var type = reader.GetString(0); - using (var stream = GetStream(reader, 1)) - { - var itemType = _typeMapper.GetType(type); - - if (itemType == null) - { - Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); - return null; - } - - var item = _jsonSerializer.DeserializeFromStream(stream, itemType); - return item as BaseItem; - } - } - } - return null; - } - } - - /// <summary> - /// Retrieve all the children of the given folder - /// </summary> - /// <param name="parent">The parent.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public IEnumerable<BaseItem> RetrieveChildren(Folder parent) - { - if (parent == null) - { - throw new ArgumentNullException(); - } - - using (var cmd = Connection.CreateCommand()) - { - cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = parent.Id; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - var type = reader.GetString(0); - - using (var stream = GetStream(reader, 1)) - { - var itemType = _typeMapper.GetType(type); - if (itemType == null) - { - Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); - continue; - } - var item = _jsonSerializer.DeserializeFromStream(stream, itemType) as BaseItem; - if (item != null) - { - item.Parent = parent; - yield return item; - } - } - } - } - } - } - - /// <summary> - /// Save references to all the children for the given folder - /// (Doesn't actually save the child entities) - /// </summary> - /// <param name="id">The id.</param> - /// <param name="children">The children.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException">id</exception> - public async Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - if (children == null) - { - throw new ArgumentNullException("children"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - SQLiteTransaction transaction = null; - - try - { - transaction = Connection.BeginTransaction(); - - // Delete exising children - _deleteChildrenCommand.Parameters[0].Value = id; - _deleteChildrenCommand.Transaction = transaction; - await _deleteChildrenCommand.ExecuteNonQueryAsync(cancellationToken); - - // Save new children - foreach (var child in children) - { - _saveChildrenCommand.Transaction = transaction; - - _saveChildrenCommand.Parameters[0].Value = id; - _saveChildrenCommand.Parameters[1].Value = child.Id; - - await _saveChildrenCommand.ExecuteNonQueryAsync(cancellationToken); - } - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - Logger.ErrorException("Failed to save children:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - - _writeLock.Release(); - } - } - - /// <summary> - /// Gets the critic reviews path. - /// </summary> - /// <param name="create">if set to <c>true</c> [create].</param> - /// <returns>System.String.</returns> - private string GetCriticReviewsPath(bool create) - { - var path = Path.Combine(_appPaths.DataPath, "critic-reviews"); - - if (create && !Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - return path; - } - - /// <summary> - /// Gets the critic reviews. - /// </summary> - /// <param name="itemId">The item id.</param> - /// <returns>Task{IEnumerable{ItemReview}}.</returns> - public Task<IEnumerable<ItemReview>> GetCriticReviews(Guid itemId) - { - return Task.Run<IEnumerable<ItemReview>>(() => - { - - try - { - var path = Path.Combine(GetCriticReviewsPath(false), itemId + ".json"); - - return _jsonSerializer.DeserializeFromFile<List<ItemReview>>(path); - } - catch (DirectoryNotFoundException) - { - return new List<ItemReview>(); - } - catch (FileNotFoundException) - { - return new List<ItemReview>(); - } - - }); - } - - /// <summary> - /// Saves the critic reviews. - /// </summary> - /// <param name="itemId">The item id.</param> - /// <param name="criticReviews">The critic reviews.</param> - /// <returns>Task.</returns> - public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews) - { - return Task.Run(() => - { - var path = Path.Combine(GetCriticReviewsPath(true), itemId + ".json"); - - _jsonSerializer.SerializeToFile(criticReviews.ToList(), path); - }); - } - } -} diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 12b6ef650..b3294492f 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -7,12 +7,12 @@ <package id="Rx-Core" version="2.1.30214.0" targetFramework="net45" /> <package id="Rx-Interfaces" version="2.1.30214.0" targetFramework="net45" /> <package id="Rx-Linq" version="2.1.30214.0" targetFramework="net45" /> - <package id="ServiceStack" version="3.9.46" targetFramework="net45" /> - <package id="ServiceStack.Api.Swagger" version="3.9.46" targetFramework="net45" /> - <package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" /> + <package id="ServiceStack" version="3.9.54" targetFramework="net45" /> + <package id="ServiceStack.Api.Swagger" version="3.9.54" targetFramework="net45" /> + <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" /> <package id="ServiceStack.OrmLite.SqlServer" version="3.9.43" targetFramework="net45" /> <package id="ServiceStack.Redis" version="3.9.43" targetFramework="net45" /> - <package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" /> + <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> <package id="SharpZipLib" version="0.86.0" targetFramework="net45" /> <package id="System.Data.SQLite.x86" version="1.0.86.0" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/swagger-ui/index.html b/MediaBrowser.Server.Implementations/swagger-ui/index.html index f854eeb26..11a938add 100644 --- a/MediaBrowser.Server.Implementations/swagger-ui/index.html +++ b/MediaBrowser.Server.Implementations/swagger-ui/index.html @@ -19,7 +19,7 @@ <script type="text/javascript"> $(function () { window.swaggerUi = new SwaggerUi({ - discoveryUrl: "../resources", + discoveryUrl:"../resources", apiKey:"special-key", dom_id:"swagger-ui-container", supportHeaderParams: false, diff --git a/MediaBrowser.Server.Implementations/swagger-ui/lib/swagger.js b/MediaBrowser.Server.Implementations/swagger-ui/lib/swagger.js index b1f3abfff..c6f0cd379 100644 --- a/MediaBrowser.Server.Implementations/swagger-ui/lib/swagger.js +++ b/MediaBrowser.Server.Implementations/swagger-ui/lib/swagger.js @@ -324,7 +324,7 @@ return _results; }; - SwaggerModel.prototype.getMockSignature = function(prefix, modelsToIgnore) { + SwaggerModel.prototype.getMockSignature = function(modelsToIgnore) { var classClose, classOpen, prop, propertiesStr, returnVal, strong, strongClose, stronger, _i, _j, _len, _len1, _ref, _ref1; propertiesStr = []; _ref = this.properties; @@ -332,15 +332,12 @@ prop = _ref[_i]; propertiesStr.push(prop.toString()); } - strong = '<span style="font-weight: bold; color: #000; font-size: 1.0em">'; - stronger = '<span style="font-weight: bold; color: #000; font-size: 1.1em">'; + strong = '<span class="strong">'; + stronger = '<span class="stronger">'; strongClose = '</span>'; - classOpen = strong + 'class ' + this.name + '(' + strongClose; - classClose = strong + ')' + strongClose; - returnVal = classOpen + '<span>' + propertiesStr.join('</span>, <span>') + '</span>' + classClose; - if (prefix != null) { - returnVal = stronger + prefix + strongClose + '<br/>' + returnVal; - } + classOpen = strong + this.name + ' {' + strongClose; + classClose = strong + '}' + strongClose; + returnVal = classOpen + '<div>' + propertiesStr.join(',</div><div>') + '</div>' + classClose; if (!modelsToIgnore) { modelsToIgnore = []; } @@ -349,19 +346,21 @@ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { prop = _ref1[_j]; if ((prop.refModel != null) && (modelsToIgnore.indexOf(prop.refModel)) === -1) { - returnVal = returnVal + ('<br>' + prop.refModel.getMockSignature(void 0, modelsToIgnore)); + returnVal = returnVal + ('<br>' + prop.refModel.getMockSignature(modelsToIgnore)); } } return returnVal; }; - SwaggerModel.prototype.createJSONSample = function(modelToIgnore) { + SwaggerModel.prototype.createJSONSample = function(modelsToIgnore) { var prop, result, _i, _len, _ref; result = {}; + modelsToIgnore = modelsToIgnore || []; + modelsToIgnore.push(this.name); _ref = this.properties; for (_i = 0, _len = _ref.length; _i < _len; _i++) { prop = _ref[_i]; - result[prop.name] = prop.getSampleValue(modelToIgnore); + result[prop.name] = prop.getSampleValue(modelsToIgnore); } return result; }; @@ -375,8 +374,9 @@ function SwaggerModelProperty(name, obj) { this.name = name; this.dataType = obj.type; - this.isArray = this.dataType.toLowerCase() === 'array'; + this.isCollection = this.dataType && (this.dataType.toLowerCase() === 'array' || this.dataType.toLowerCase() === 'list' || this.dataType.toLowerCase() === 'set'); this.descr = obj.description; + this.required = obj.required; if (obj.items != null) { if (obj.items.type != null) { this.refDataType = obj.items.type; @@ -395,18 +395,18 @@ } } - SwaggerModelProperty.prototype.getSampleValue = function(modelToIgnore) { + SwaggerModelProperty.prototype.getSampleValue = function(modelsToIgnore) { var result; - if ((this.refModel != null) && (!(this.refModel === modelToIgnore))) { - result = this.refModel.createJSONSample(this.refModel); + if ((this.refModel != null) && (modelsToIgnore.indexOf(this.refModel.name) === -1)) { + result = this.refModel.createJSONSample(modelsToIgnore); } else { - if (this.isArray) { + if (this.isCollection) { result = this.refDataType; } else { result = this.dataType; } } - if (this.isArray) { + if (this.isCollection) { return [result]; } else { return result; @@ -414,13 +414,18 @@ }; SwaggerModelProperty.prototype.toString = function() { - var str; - str = this.name + ': ' + this.dataTypeWithRef; + var req, str; + req = this.required ? 'propReq' : 'propOpt'; + str = '<span class="propName ' + req + '">' + this.name + '</span> (<span class="propType">' + this.dataTypeWithRef + '</span>'; + if (!this.required) { + str += ', <span class="propOptKey">optional</span>'; + } + str += ')'; if (this.values != null) { - str += " = ['" + this.values.join("' or '") + "']"; + str += " = <span class='propVals'>['" + this.values.join("' or '") + "']</span>"; } if (this.descr != null) { - str += ' {' + this.descr + '}'; + str += ': <span class="propDesc">' + this.descr + '</span>'; } return str; }; @@ -525,9 +530,9 @@ return dataType; } else { if (listType != null) { - return models[listType].getMockSignature(dataType); + return models[listType].getMockSignature(); } else { - return models[dataType].getMockSignature(dataType); + return models[dataType].getMockSignature(); } } }; @@ -762,4 +767,6 @@ window.SwaggerRequest = SwaggerRequest; + window.SwaggerModelProperty = SwaggerModelProperty; + }).call(this); diff --git a/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.js b/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.js index 93cd0bea4..7ac8ed7b5 100644 --- a/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.js +++ b/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.js @@ -300,7 +300,7 @@ function program3(depth0,data) { function program5(depth0,data) { - return "\n <h4>Parameters</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th style=\"width: 100px; max-width: 100px\" >Parameter</th>\n <th style=\"width: 310px; max-width: 310px\">Value</th>\n <th style=\"width: 200px; max-width: 200px\">Description</th>\n <th style=\"width: 320px; max-width: 330px\">Data Type</th>\n </tr>\n </thead>\n <tbody class=\"operation-params\">\n\n </tbody>\n </table>\n ";} + return "\n <h4>Parameters</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th style=\"width: 100px; max-width: 100px\">Parameter</th>\n <th style=\"width: 310px; max-width: 310px\">Value</th>\n <th style=\"width: 200px; max-width: 200px\">Description</th>\n <th style=\"width: 100px; max-width: 100px\">Parameter Type</th>\n <th style=\"width: 220px; max-width: 230px\">Data Type</th>\n </tr>\n </thead>\n <tbody class=\"operation-params\">\n\n </tbody>\n </table>\n ";} function program7(depth0,data) { @@ -629,6 +629,12 @@ function program12(depth0,data) { if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "description", { hash: {} }); } if(stack1 || stack1 === 0) { buffer += stack1; } + buffer += "</td>\n<td>"; + foundHelper = helpers.paramType; + stack1 = foundHelper || depth0.paramType; + if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } + else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "paramType", { hash: {} }); } + if(stack1 || stack1 === 0) { buffer += stack1; } buffer += "</td>\n<td>\n <span class=\"model-signature\"></span>\n</td>\n\n"; return buffer;}); })(); @@ -753,6 +759,12 @@ function program11(depth0,data) { if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "description", { hash: {} }); } if(stack1 || stack1 === 0) { buffer += stack1; } + buffer += "</td>\n<td>"; + foundHelper = helpers.paramType; + stack1 = foundHelper || depth0.paramType; + if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } + else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "paramType", { hash: {} }); } + if(stack1 || stack1 === 0) { buffer += stack1; } buffer += "</td>\n<td><span class=\"model-signature\"></span></td>\n"; return buffer;}); })(); @@ -831,6 +843,12 @@ function program6(depth0,data) { if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "description", { hash: {} }); } if(stack1 || stack1 === 0) { buffer += stack1; } + buffer += "</td>\n<td>"; + foundHelper = helpers.paramType; + stack1 = foundHelper || depth0.paramType; + if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } + else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "paramType", { hash: {} }); } + if(stack1 || stack1 === 0) { buffer += stack1; } buffer += "</td>\n<td><span class=\"model-signature\"></span></td>\n"; return buffer;}); })(); @@ -909,6 +927,12 @@ function program6(depth0,data) { if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "description", { hash: {} }); } if(stack1 || stack1 === 0) { buffer += stack1; } + buffer += "</td>\n<td>"; + foundHelper = helpers.paramType; + stack1 = foundHelper || depth0.paramType; + if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } + else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "paramType", { hash: {} }); } + if(stack1 || stack1 === 0) { buffer += stack1; } buffer += "</td>\n<td><span class=\"model-signature\"></span></td>\n"; return buffer;}); })(); @@ -1076,7 +1100,13 @@ function program15(depth0,data) { if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "description", { hash: {} }); } if(stack1 || stack1 === 0) { buffer += stack1; } - buffer += "</strong>\n</td>\n<td><span class=\"model-signature\"></span></td>\n"; + buffer += "</strong>\n</td>\n<td>"; + foundHelper = helpers.paramType; + stack1 = foundHelper || depth0.paramType; + if(typeof stack1 === functionType) { stack1 = stack1.call(depth0, { hash: {} }); } + else if(stack1=== undef) { stack1 = helperMissing.call(depth0, "paramType", { hash: {} }); } + if(stack1 || stack1 === 0) { buffer += stack1; } + buffer += "</td>\n<td><span class=\"model-signature\"></span></td>\n"; return buffer;}); })(); @@ -1590,7 +1620,7 @@ templates['status_code'] = template(function (Handlebars,depth0,helpers,partials _ref2 = this.model.parameters; for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { param = _ref2[_k]; - if ((param.paramType === 'body' || 'form') && param.name !== 'file' && (map[param.name] != null)) { + if ((param.paramType === 'body' || 'form') && param.name !== 'file' && param.name !== 'File' && (map[param.name] != null)) { bodyParam.append(param.name, map[param.name]); } } @@ -1646,7 +1676,7 @@ templates['status_code'] = template(function (Handlebars,depth0,helpers,partials obj.contentType = paramContentTypeField; } log('content type = ' + obj.contentType); - if (!obj.data || (obj.type === 'GET' || obj.type === 'DELETE')) { + if (!(obj.data || (obj.type === 'GET' || obj.type === 'DELETE')) && obj.contentType === !"application/x-www-form-urlencoded") { obj.contentType = false; } log('content type is now = ' + obj.contentType); diff --git a/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.min.js b/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.min.js index 03a862a1a..4947965ee 100644 --- a/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.min.js +++ b/MediaBrowser.Server.Implementations/swagger-ui/swagger-ui.min.js @@ -1 +1 @@ -$(function(){$.fn.vAlign=function(){return this.each(function(c){var a=$(this).height();var d=$(this).parent().height();var b=(d-a)/2;$(this).css("margin-top",b)})};$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(b){var d=$(this).closest("form").innerWidth();var c=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10);var a=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",d-c-a)})};$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent();$("ul.downplayed li div.content p").vAlign();$("form.sandbox").submit(function(){var a=true;$(this).find("input.required").each(function(){$(this).removeClass("error");if($(this).val()==""){$(this).addClass("error");$(this).wiggle();a=false}});return a})});function clippyCopiedCallback(b){$("#api_key_copied").fadeIn().delay(1000).fadeOut()}function log(){if(window.console){console.log.apply(console,arguments)}}if(Function.prototype.bind&&console&&typeof console.log=="object"){["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(a){console[a]=this.bind(console[a],console)},Function.prototype.call)}var Docs={shebang:function(){var b=$.param.fragment().split("/");b.shift();switch(b.length){case 1:var d="resource_"+b[0];Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});break;case 2:Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});var c=b.join("_");var a=c+"_content";Docs.expandOperation($("#"+a));$("#"+c).slideto({highlight:false});break}},toggleEndpointListForResource:function(b){var a=$("li#resource_"+Docs.escapeResourceName(b)+" ul.endpoints");if(a.is(":visible")){Docs.collapseEndpointListForResource(b)}else{Docs.expandEndpointListForResource(b)}},expandEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);if(b==""){$(".resource ul.endpoints").slideDown();return}$("li#resource_"+b).addClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideDown()},collapseEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);$("li#resource_"+b).removeClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideUp()},expandOperationsForResource:function(a){Docs.expandEndpointListForResource(a);if(a==""){$(".resource ul.endpoints li.operation div.content").slideDown();return}$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(a){Docs.expandEndpointListForResource(a);$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(a){return a.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(a){a.slideDown()},collapseOperation:function(a){a.slideUp()}};(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.content_type=b(function(g,p,f,n,m){f=f||g.helpers;var l="",c,s,k,j,q=this,h="function",o=f.helperMissing,i=void 0;function e(x,w){var t="",v,u;t+="\n ";k=f.produces;v=k||x.produces;u=f.each;j=q.program(2,d,w);j.hash={};j.fn=j;j.inverse=q.noop;v=u.call(x,v,j);if(v||v===0){t+=v}t+="\n";return t}function d(w,v){var t="",u;t+='\n <option value="';u=w;if(typeof u===h){u=u.call(w,{hash:{}})}else{if(u===i){u=o.call(w,"this",{hash:{}})}}if(u||u===0){t+=u}t+='">';u=w;if(typeof u===h){u=u.call(w,{hash:{}})}else{if(u===i){u=o.call(w,"this",{hash:{}})}}if(u||u===0){t+=u}t+="</option>\n ";return t}function r(u,t){return'\n <option value="application/json">application/json</option>\n'}l+='<label for="contentType"></label>\n<select name="contentType">\n';k=f.produces;c=k||p.produces;s=f["if"];j=q.program(1,e,m);j.hash={};j.fn=j;j.inverse=q.program(4,r,m);c=s.call(p,c,j);if(c||c===0){l+=c}l+="\n</select>\n";return l})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.main=b(function(f,p,e,n,m){e=e||f.helpers;var k="",c,r,j,i,q=this,g="function",o=e.helperMissing,h=void 0,l=this.escapeExpression;function d(v,u){var s="",t;s+='\n , <span style="font-variant: small-caps">api version</span>: ';j=e.apiVersion;t=j||v.apiVersion;if(typeof t===g){t=t.call(v,{hash:{}})}else{if(t===h){t=o.call(v,"apiVersion",{hash:{}})}}s+=l(t)+"\n ";return s}k+="\n<div class='container' id='resources_container'>\n <ul id='resources'>\n </ul>\n\n <div class=\"footer\">\n <br>\n <br>\n <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: ";j=e.basePath;c=j||p.basePath;if(typeof c===g){c=c.call(p,{hash:{}})}else{if(c===h){c=o.call(p,"basePath",{hash:{}})}}k+=l(c)+"\n ";j=e.apiVersion;c=j||p.apiVersion;r=e["if"];i=q.program(1,d,m);i.hash={};i.fn=i;i.inverse=q.noop;c=r.call(p,c,i);if(c||c===0){k+=c}k+="]</h4>\n </div>\n</div>\n";return k})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.operation=b(function(h,u,s,m,w){s=s||h.helpers;var t="",j,g,i,q,p=this,e="function",r=s.helperMissing,c=void 0,d=this.escapeExpression;function o(A,z){var x="",y;x+="\n <h4>Implementation Notes</h4>\n <p>";i=s.notes;y=i||A.notes;if(typeof y===e){y=y.call(A,{hash:{}})}else{if(y===c){y=r.call(A,"notes",{hash:{}})}}if(y||y===0){x+=y}x+="</p>\n ";return x}function n(y,x){return'\n <h4>Response Class</h4>\n <p><span class="model-signature" /></p>\n <br/>\n <div class="content-type" />\n '}function l(y,x){return'\n <h4>Parameters</h4>\n <table class=\'fullwidth\'>\n <thead>\n <tr>\n <th style="width: 100px; max-width: 100px" >Parameter</th>\n <th style="width: 310px; max-width: 310px">Value</th>\n <th style="width: 200px; max-width: 200px">Description</th>\n <th style="width: 320px; max-width: 330px">Data Type</th>\n </tr>\n </thead>\n <tbody class="operation-params">\n\n </tbody>\n </table>\n '}function k(y,x){return"\n <div style='margin:0;padding:0;display:inline'></div>\n <h4>Error Status Codes</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th>HTTP Status Code</th>\n <th>Reason</th>\n </tr>\n </thead>\n <tbody class=\"operation-status\">\n \n </tbody>\n </table>\n "}function f(y,x){return"\n "}function v(y,x){return"\n <div class='sandbox_header'>\n <input class='submit' name='commit' type='button' value='Try it out!' />\n <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n <img alt='Throbber' class='response_throbber' src='images/throbber.gif' style='display:none' />\n </div>\n "}t+="\n <ul class='operations' >\n <li class='";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+" operation' id='";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"_";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+"'>\n <div class='heading'>\n <h3>\n <span class='http_method'>\n <a href='#!/";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"/";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+'\' class="toggleOperation">';i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"</a>\n </span>\n <span class='path'>\n <a href='#!/";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"/";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+'\' class="toggleOperation">';i=s.path;j=i||u.path;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"path",{hash:{}})}}t+=d(j)+"</a>\n </span>\n </h3>\n <ul class='options'>\n <li>\n <a href='#!/";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"/";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+'\' class="toggleOperation">';i=s.summary;j=i||u.summary;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"summary",{hash:{}})}}if(j||j===0){t+=j}t+="</a>\n </li>\n </ul>\n </div>\n <div class='content' id='";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"_";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+"_content' style='display:none'>\n ";i=s.notes;j=i||u.notes;g=s["if"];q=p.program(1,o,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n ";i=s.responseClass;j=i||u.responseClass;g=s["if"];q=p.program(3,n,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n <form accept-charset='UTF-8' class='sandbox'>\n <div style='margin:0;padding:0;display:inline'></div>\n ";i=s.parameters;j=i||u.parameters;g=s["if"];q=p.program(5,l,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n ";i=s.errorResponses;j=i||u.errorResponses;g=s["if"];q=p.program(7,k,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n ";i=s.isReadOnly;j=i||u.isReadOnly;g=s["if"];q=p.program(9,f,w);q.hash={};q.fn=q;q.inverse=p.program(11,v,w);j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n </form>\n <div class='response' style='display:none'>\n <h4>Request URL</h4>\n <div class='block request_url'></div>\n <h4>Response Body</h4>\n <div class='block response_body'></div>\n <h4>Response Code</h4>\n <div class='block response_code'></div>\n <h4>Response Headers</h4>\n <div class='block response_headers'></div>\n </div>\n </div>\n </li>\n </ul>\n";return t})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param=b(function(h,v,t,m,y){t=t||h.helpers;var u="",j,g,i,r,q=this,e="function",s=t.helperMissing,c=void 0,d=this.escapeExpression;function p(D,C){var z="",B,A;z+="\n ";i=t.isFile;B=i||D.isFile;A=t["if"];r=q.program(2,o,C);r.hash={};r.fn=r;r.inverse=q.program(4,n,C);B=A.call(D,B,r);if(B||B===0){z+=B}z+="\n ";return z}function o(C,B){var z="",A;z+='\n <input type="file" name=\'';i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"'/>\n ";return z}function n(D,C){var z="",B,A;z+="\n ";i=t.defaultValue;B=i||D.defaultValue;A=t["if"];r=q.program(5,l,C);r.hash={};r.fn=r;r.inverse=q.program(7,k,C);B=A.call(D,B,r);if(B||B===0){z+=B}z+="\n ";return z}function l(C,B){var z="",A;z+="\n <textarea class='body-textarea' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"'>";i=t.defaultValue;A=i||C.defaultValue;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"defaultValue",{hash:{}})}}z+=d(A)+"</textarea>\n ";return z}function k(C,B){var z="",A;z+="\n <textarea class='body-textarea' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+'\'></textarea>\n <br />\n <div class="content-type" />\n ';return z}function f(D,C){var z="",B,A;z+="\n ";i=t.defaultValue;B=i||D.defaultValue;A=t["if"];r=q.program(10,x,C);r.hash={};r.fn=r;r.inverse=q.program(12,w,C);B=A.call(D,B,r);if(B||B===0){z+=B}z+="\n ";return z}function x(C,B){var z="",A;z+="\n <input class='parameter' minlength='0' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"' placeholder='' type='text' value='";i=t.defaultValue;A=i||C.defaultValue;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"defaultValue",{hash:{}})}}z+=d(A)+"'/>\n ";return z}function w(C,B){var z="",A;z+="\n <input class='parameter' minlength='0' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"' placeholder='' type='text' value=''/>\n ";return z}u+="<td class='code'>";i=t.name;j=i||v.name;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"name",{hash:{}})}}u+=d(j)+"</td>\n<td>\n\n ";i=t.isBody;j=i||v.isBody;g=t["if"];r=q.program(1,p,y);r.hash={};r.fn=r;r.inverse=q.program(9,f,y);j=g.call(v,j,r);if(j||j===0){u+=j}u+="\n\n</td>\n<td>";i=t.description;j=i||v.description;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"description",{hash:{}})}}if(j||j===0){u+=j}u+='</td>\n<td>\n <span class="model-signature"></span>\n</td>\n\n';return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_list=b(function(h,v,t,m,x){t=t||h.helpers;var u="",k,g,j,r,q=this,e="function",s=t.helperMissing,c=void 0,d=this.escapeExpression;function p(z,y){return"\n "}function o(C,B){var y="",A,z;y+="\n ";j=t.defaultValue;A=j||C.defaultValue;z=t["if"];r=q.program(4,n,B);r.hash={};r.fn=r;r.inverse=q.program(6,l,B);A=z.call(C,A,r);if(A||A===0){y+=A}y+="\n ";return y}function n(z,y){return"\n "}function l(z,y){return"\n <option selected=\"\" value=''></option>\n "}function i(C,B){var y="",A,z;y+="\n ";j=t.isDefault;A=j||C.isDefault;z=t["if"];r=q.program(9,f,B);r.hash={};r.fn=r;r.inverse=q.program(11,w,B);A=z.call(C,A,r);if(A||A===0){y+=A}y+="\n ";return y}function f(B,A){var y="",z;y+="\n <option value='";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+"'>";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+" (default)</option>\n ";return y}function w(B,A){var y="",z;y+="\n <option value='";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+"'>";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+"</option>\n ";return y}u+="<td class='code'>";j=t.name;k=j||v.name;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"name",{hash:{}})}}u+=d(k)+"</td>\n<td>\n <select class='parameter' name='";j=t.name;k=j||v.name;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"name",{hash:{}})}}u+=d(k)+"'>\n ";j=t.required;k=j||v.required;g=t["if"];r=q.program(1,p,x);r.hash={};r.fn=r;r.inverse=q.program(3,o,x);k=g.call(v,k,r);if(k||k===0){u+=k}u+="\n ";j=t.allowableValues;k=j||v.allowableValues;k=(k===null||k===undefined||k===false?k:k.descriptiveValues);g=t.each;r=q.program(8,i,x);r.hash={};r.fn=r;r.inverse=q.noop;k=g.call(v,k,r);if(k||k===0){u+=k}u+="\n </select>\n</td>\n<td>";j=t.description;k=j||v.description;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"description",{hash:{}})}}if(k||k===0){u+=k}u+='</td>\n<td><span class="model-signature"></span></td>\n';return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly=b(function(g,t,r,k,u){r=r||g.helpers;var s="",i,f,h,p,o=this,e="function",q=r.helperMissing,c=void 0,d=this.escapeExpression;function n(y,x){var v="",w;v+="\n <textarea class='body-textarea' readonly='readonly' name='";h=r.name;w=h||y.name;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"name",{hash:{}})}}v+=d(w)+"'>";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"</textarea>\n ";return v}function m(z,y){var v="",x,w;v+="\n ";h=r.defaultValue;x=h||z.defaultValue;w=r["if"];p=o.program(4,l,y);p.hash={};p.fn=p;p.inverse=o.program(6,j,y);x=w.call(z,x,p);if(x||x===0){v+=x}v+="\n ";return v}function l(y,x){var v="",w;v+="\n ";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"\n ";return v}function j(w,v){return"\n (empty)\n "}s+="<td class='code'>";h=r.name;i=h||t.name;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"name",{hash:{}})}}s+=d(i)+"</td>\n<td>\n ";h=r.isBody;i=h||t.isBody;f=r["if"];p=o.program(1,n,u);p.hash={};p.fn=p;p.inverse=o.program(3,m,u);i=f.call(t,i,p);if(i||i===0){s+=i}s+="\n</td>\n<td>";h=r.description;i=h||t.description;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"description",{hash:{}})}}if(i||i===0){s+=i}s+='</td>\n<td><span class="model-signature"></span></td>\n';return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly_required=b(function(g,t,r,k,u){r=r||g.helpers;var s="",i,f,h,p,o=this,e="function",q=r.helperMissing,c=void 0,d=this.escapeExpression;function n(y,x){var v="",w;v+="\n <textarea class='body-textarea' readonly='readonly' placeholder='(required)' name='";h=r.name;w=h||y.name;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"name",{hash:{}})}}v+=d(w)+"'>";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"</textarea>\n ";return v}function m(z,y){var v="",x,w;v+="\n ";h=r.defaultValue;x=h||z.defaultValue;w=r["if"];p=o.program(4,l,y);p.hash={};p.fn=p;p.inverse=o.program(6,j,y);x=w.call(z,x,p);if(x||x===0){v+=x}v+="\n ";return v}function l(y,x){var v="",w;v+="\n ";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"\n ";return v}function j(w,v){return"\n (empty)\n "}s+="<td class='code required'>";h=r.name;i=h||t.name;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"name",{hash:{}})}}s+=d(i)+"</td>\n<td>\n ";h=r.isBody;i=h||t.isBody;f=r["if"];p=o.program(1,n,u);p.hash={};p.fn=p;p.inverse=o.program(3,m,u);i=f.call(t,i,p);if(i||i===0){s+=i}s+="\n</td>\n<td>";h=r.description;i=h||t.description;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"description",{hash:{}})}}if(i||i===0){s+=i}s+='</td>\n<td><span class="model-signature"></span></td>\n';return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_required=b(function(h,v,t,m,A){t=t||h.helpers;var u="",j,g,i,r,q=this,e="function",s=t.helperMissing,c=void 0,d=this.escapeExpression;function p(F,E){var B="",D,C;B+="\n ";i=t.isFile;D=i||F.isFile;C=t["if"];r=q.program(2,o,E);r.hash={};r.fn=r;r.inverse=q.program(4,n,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function o(E,D){var B="",C;B+='\n <input type="file" name=\'';i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"'/>\n ";return B}function n(F,E){var B="",D,C;B+="\n ";i=t.defaultValue;D=i||F.defaultValue;C=t["if"];r=q.program(5,l,E);r.hash={};r.fn=r;r.inverse=q.program(7,k,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function l(E,D){var B="",C;B+="\n <textarea class='body-textarea' placeholder='(required)' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"'>";i=t.defaultValue;C=i||E.defaultValue;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"defaultValue",{hash:{}})}}B+=d(C)+"</textarea>\n ";return B}function k(E,D){var B="",C;B+="\n <textarea class='body-textarea' placeholder='(required)' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+'\'></textarea>\n <br />\n <div class="content-type" />\n ';return B}function f(F,E){var B="",D,C;B+="\n ";i=t.isFile;D=i||F.isFile;C=t["if"];r=q.program(10,z,E);r.hash={};r.fn=r;r.inverse=q.program(12,y,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function z(E,D){var B="",C;B+="\n <input class='parameter' class='required' type='file' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"'/>\n ";return B}function y(F,E){var B="",D,C;B+="\n ";i=t.defaultValue;D=i||F.defaultValue;C=t["if"];r=q.program(13,x,E);r.hash={};r.fn=r;r.inverse=q.program(15,w,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function x(E,D){var B="",C;B+="\n <input class='parameter required' minlength='1' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"' placeholder='(required)' type='text' value='";i=t.defaultValue;C=i||E.defaultValue;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"defaultValue",{hash:{}})}}B+=d(C)+"'/>\n ";return B}function w(E,D){var B="",C;B+="\n <input class='parameter required' minlength='1' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"' placeholder='(required)' type='text' value=''/>\n ";return B}u+="<td class='code required'>";i=t.name;j=i||v.name;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"name",{hash:{}})}}u+=d(j)+"</td>\n<td>\n ";i=t.isBody;j=i||v.isBody;g=t["if"];r=q.program(1,p,A);r.hash={};r.fn=r;r.inverse=q.program(9,f,A);j=g.call(v,j,r);if(j||j===0){u+=j}u+="\n</td>\n<td>\n <strong>";i=t.description;j=i||v.description;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"description",{hash:{}})}}if(j||j===0){u+=j}u+='</strong>\n</td>\n<td><span class="model-signature"></span></td>\n';return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.resource=b(function(e,n,d,l,k){d=d||e.helpers;var i="",c,h,o=this,f="function",m=d.helperMissing,g=void 0,j=this.escapeExpression;i+="<div class='heading'>\n <h2>\n <a href='#!/";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"' onclick=\"Docs.toggleEndpointListForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"');\">/";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"</a>\n </h2>\n <ul class='options'>\n <li>\n <a href='#!/";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"' id='endpointListTogger_";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"'\n onclick=\"Docs.toggleEndpointListForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"');\">Show/Hide</a>\n </li>\n <li>\n <a href='#' onclick=\"Docs.collapseOperationsForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"'); return false;\">\n List Operations\n </a>\n </li>\n <li>\n <a href='#' onclick=\"Docs.expandOperationsForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"'); return false;\">\n Expand Operations\n </a>\n </li>\n <li>\n <a href='";h=d.url;c=h||n.url;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"url",{hash:{}})}}i+=j(c)+"'>Raw</a>\n </li>\n </ul>\n</div>\n<ul class='endpoints' id='";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"_endpoint_list' style='display:none'>\n\n</ul>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.signature=b(function(e,n,d,l,k){d=d||e.helpers;var i="",c,h,o=this,f="function",m=d.helperMissing,g=void 0,j=this.escapeExpression;i+='<div>\n<ul class="signature-nav">\n <li><a class="description-link" href="#">Model</a></li>\n <li><a class="snippet-link" href="#">Model Schema</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n <div class="description">\n ';h=d.signature;c=h||n.signature;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"signature",{hash:{}})}}if(c||c===0){i+=c}i+='\n </div>\n\n <div class="snippet">\n <pre><code>';h=d.sampleJSON;c=h||n.sampleJSON;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"sampleJSON",{hash:{}})}}i+=j(c)+'</code></pre>\n <small class="notice"></small>\n </div>\n</div>\n\n';return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.status_code=b(function(e,n,d,l,k){d=d||e.helpers;var i="",c,h,o=this,f="function",m=d.helperMissing,g=void 0,j=this.escapeExpression;i+="<td width='15%' class='code'>";h=d.code;c=h||n.code;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"code",{hash:{}})}}i+=j(c)+"</td>\n<td>";h=d.reason;c=h||n.reason;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"reason",{hash:{}})}}if(c||c===0){i+=c}i+="</td>\n\n";return i})})();(function(){var f,b,h,c,e,j,k,i,a,g={}.hasOwnProperty,d=function(o,m){for(var l in m){if(g.call(m,l)){o[l]=m[l]}}function n(){this.constructor=o}n.prototype=m.prototype;o.prototype=new n();o.__super__=m.prototype;return o};a=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.dom_id="swagger_ui";l.prototype.options=null;l.prototype.api=null;l.prototype.headerView=null;l.prototype.mainView=null;l.prototype.initialize=function(n){var o=this;if(n==null){n={}}if(n.dom_id!=null){this.dom_id=n.dom_id;delete n.dom_id}if(!($("#"+this.dom_id)!=null)){$("body").append('<div id="'+this.dom_id+'"></div>')}this.options=n;this.options.success=function(){return o.render()};this.options.progress=function(p){return o.showMessage(p)};this.options.failure=function(p){return o.onLoadFailure(p)};this.headerView=new b({el:$("#header")});return this.headerView.on("update-swagger-ui",function(p){return o.updateSwaggerUi(p)})};l.prototype.updateSwaggerUi=function(n){this.options.discoveryUrl=n.discoveryUrl;this.options.apiKey=n.apiKey;return this.load()};l.prototype.load=function(){var n;if((n=this.mainView)!=null){n.clear()}this.headerView.update(this.options.discoveryUrl,this.options.apiKey);return this.api=new SwaggerApi(this.options)};l.prototype.render=function(){var n=this;this.showMessage("Finished Loading Resource Information. Rendering Swagger UI...");this.mainView=new h({model:this.api,el:$("#"+this.dom_id)}).render();this.showMessage();switch(this.options.docExpansion){case"full":Docs.expandOperationsForResource("");break;case"list":Docs.collapseOperationsForResource("")}if(this.options.onComplete){this.options.onComplete(this.api,this)}return setTimeout(function(){return Docs.shebang()},400)};l.prototype.showMessage=function(n){if(n==null){n=""}$("#message-bar").removeClass("message-fail");$("#message-bar").addClass("message-success");return $("#message-bar").html(n)};l.prototype.onLoadFailure=function(n){var o;if(n==null){n=""}$("#message-bar").removeClass("message-success");$("#message-bar").addClass("message-fail");o=$("#message-bar").html(n);if(this.options.onFailure!=null){this.options.onFailure(n)}return o};return l})(Backbone.Router);window.SwaggerUi=a;b=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.events={"click #show-pet-store-icon":"showPetStore","click #show-wordnik-dev-icon":"showWordnikDev","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"};l.prototype.initialize=function(){};l.prototype.showPetStore=function(n){return this.trigger("update-swagger-ui",{discoveryUrl:"http://petstore.swagger.wordnik.com/api/api-docs.json",apiKey:"special-key"})};l.prototype.showWordnikDev=function(n){return this.trigger("update-swagger-ui",{discoveryUrl:"http://api.wordnik.com/v4/resources.json",apiKey:""})};l.prototype.showCustomOnKeyup=function(n){if(n.keyCode===13){return this.showCustom()}};l.prototype.showCustom=function(n){if(n!=null){n.preventDefault()}return this.trigger("update-swagger-ui",{discoveryUrl:$("#input_baseUrl").val(),apiKey:$("#input_apiKey").val()})};l.prototype.update=function(o,p,n){if(n==null){n=false}$("#input_baseUrl").val(o);$("#input_apiKey").val(p);if(n){return this.trigger("update-swagger-ui",{discoveryUrl:o,apiKey:p})}};return l})(Backbone.View);h=(function(l){d(m,l);function m(){return m.__super__.constructor.apply(this,arguments)}m.prototype.initialize=function(){};m.prototype.render=function(){var q,p,n,o;$(this.el).html(Handlebars.templates.main(this.model));o=this.model.apisArray;for(p=0,n=o.length;p<n;p++){q=o[p];this.addResource(q)}return this};m.prototype.addResource=function(o){var n;n=new j({model:o,tagName:"li",id:"resource_"+o.name,className:"resource"});return $("#resources").append(n.render().el)};m.prototype.clear=function(){return $(this.el).html("")};return m})(Backbone.View);j=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.initialize=function(){};l.prototype.render=function(){var o,q,n,p;$(this.el).html(Handlebars.templates.resource(this.model));this.number=0;p=this.model.operationsArray;for(q=0,n=p.length;q<n;q++){o=p[q];this.addOperation(o)}return this};l.prototype.addOperation=function(n){var o;n.number=this.number;o=new c({model:n,tagName:"li",className:"endpoint"});$(".endpoints",$(this.el)).append(o.render().el);return this.number++};return l})(Backbone.View);c=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.events={"submit .sandbox":"submitOperation","click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent"};l.prototype.initialize=function(){};l.prototype.render=function(){var n,x,y,p,u,z,v,s,r,w,o,t,q;y=jQuery.inArray(this.model.httpMethod,this.model.supportedSubmitMethods())>=0;if(!y){this.model.isReadOnly=true}$(this.el).html(Handlebars.templates.operation(this.model));if(this.model.responseClassSignature&&this.model.responseClassSignature!=="string"){z={sampleJSON:this.model.responseSampleJSON,isParam:false,signature:this.model.responseClassSignature};u=new k({model:z,tagName:"div"});$(".model-signature",$(this.el)).append(u.render().el)}else{$(".model-signature",$(this.el)).html(this.model.responseClass)}n={isParam:false};if(this.model.supportedContentTypes){n.produces=this.model.supportedContentTypes}if(this.model.produces){n.produces=this.model.produces}x=new f({model:n});$(".content-type",$(this.el)).append(x.render().el);t=this.model.parameters;for(s=0,w=t.length;s<w;s++){p=t[s];this.addParameter(p)}q=this.model.errorResponses;for(r=0,o=q.length;r<o;r++){v=q[r];this.addStatusCode(v)}return this};l.prototype.addParameter=function(o){var n;n=new e({model:o,tagName:"tr",readOnly:this.model.isReadOnly});return $(".operation-params",$(this.el)).append(n.render().el)};l.prototype.addStatusCode=function(o){var n;n=new i({model:o,tagName:"tr"});return $(".operation-status",$(this.el)).append(n.render().el)};l.prototype.submitOperation=function(K){var F,L,E,v,q,A,P,J,Q,H,D,B,G,s,x,u,r,p,O,S,R,N,M,n,C,z,y,w,t,I=this;if(K!=null){K.preventDefault()}v=$(".sandbox",$(this.el));E=true;v.find("input.required").each(function(){var o=this;$(this).removeClass("error");if(jQuery.trim($(this).val())===""){$(this).addClass("error");$(this).wiggle({callback:function(){return $(o).focus()}});return E=false}});if(E){Q={};C=v.serializeArray();for(x=0,O=C.length;x<O;x++){H=C[x];if((H.value!=null)&&jQuery.trim(H.value).length>0){Q[H.name]=H.value}}P=v.children().find('input[type~="file"]').size()!==0;J=false;L="application/json";if(this.model.consumes&&this.model.consumes.length>0){L=this.model.consumes[0]}else{z=this.model.parameters;for(u=0,S=z.length;u<S;u++){H=z[u];if(H.paramType==="form"){J=true;L=false}}if(P){L=false}else{if(this.model.httpMethod.toLowerCase()==="post"&&J===false){L="application/json"}}}if(P){F=new FormData();y=this.model.parameters;for(r=0,R=y.length;r<R;r++){B=y[r];if((B.paramType==="body"||"form")&&B.name!=="file"&&(Q[B.name]!=null)){F.append(B.name,Q[B.name])}}$.each(v.children().find('input[type~="file"]'),function(o,T){return F.append($(T).attr("name"),T.files[0])});console.log(F)}else{if(J){F=new FormData();w=this.model.parameters;for(p=0,N=w.length;p<N;p++){B=w[p];if(Q[B.name]!=null){F.append(B.name,Q[B.name])}}}else{F=null;t=this.model.parameters;for(n=0,M=t.length;n<M;n++){B=t[n];if(B.paramType==="body"){F=Q[B.name]}}}}log("bodyParam = "+F);q=null;A=this.model.supportHeaderParams()?(q=this.model.getHeaderParams(Q),this.model.urlify(Q,false)):this.model.urlify(Q,true);log("submitting "+A);$(".request_url",$(this.el)).html("<pre>"+A+"</pre>");$(".response_throbber",$(this.el)).show();D={type:this.model.httpMethod,url:A,headers:q,data:F,contentType:L,dataType:"json",processData:false,error:function(T,U,o){return I.showErrorStatus(T,U,o)},success:function(o){return I.showResponse(o)},complete:function(o){return I.showCompleteStatus(o)}};G=$("td select[name=contentType]",$(this.el)).val();if(G){D.contentType=G}log("content type = "+D.contentType);if(!D.data||(D.type==="GET"||D.type==="DELETE")){D.contentType=false}log("content type is now = "+D.contentType);s=$(".content > .content-type > div > select[name=contentType]",$(this.el)).val();if(s){D.headers=D.headers!=null?D.headers:{};D.headers.accept=s}jQuery.ajax(D);return false}};l.prototype.hideResponse=function(n){if(n!=null){n.preventDefault()}$(".response",$(this.el)).slideUp();return $(".response_hider",$(this.el)).fadeOut()};l.prototype.showResponse=function(n){var o;o=JSON.stringify(n,null,"\t").replace(/\n/g,"<br>");return $(".response_body",$(this.el)).html(escape(o))};l.prototype.showErrorStatus=function(n){return this.showStatus(n)};l.prototype.showCompleteStatus=function(n){return this.showStatus(n)};l.prototype.formatXml=function(u){var q,t,o,v,A,w,p,n,y,z,s,r,x;n=/(>)(<)(\/*)/g;z=/[ ]*(.*)[ ]+\n/g;q=/(<.+>)(.+\n)/g;u=u.replace(n,"$1\n$2$3").replace(z,"$1\n").replace(q,"$1\n$2");p=0;t="";A=u.split("\n");o=0;v="other";y={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0};s=function(G){var C,B,E,I,F,D,H;D={single:Boolean(G.match(/<.+\/>/)),closing:Boolean(G.match(/<\/.+>/)),opening:Boolean(G.match(/<[^!?].*>/))};F=((function(){var J;J=[];for(E in D){H=D[E];if(H){J.push(E)}}return J})())[0];F=F===void 0?"other":F;C=v+"->"+F;v=F;I="";o+=y[C];I=((function(){var K,L,J;J=[];for(B=K=0,L=o;0<=L?K<L:K>L;B=0<=L?++K:--K){J.push(" ")}return J})()).join("");if(C==="opening->closing"){return t=t.substr(0,t.length-1)+G+"\n"}else{return t+=I+G+"\n"}};for(r=0,x=A.length;r<x;r++){w=A[r];s(w)}return t};l.prototype.showStatus=function(q){var p,r,o;try{p=$("<code />").text(JSON.stringify(JSON.parse(q.responseText),null,2));r=$('<pre class="json" />').append(p)}catch(n){p=$("<code />").text(this.formatXml(q.responseText));r=$('<pre class="xml" />').append(p)}o=r;$(".response_code",$(this.el)).html("<pre>"+q.status+"</pre>");$(".response_body",$(this.el)).html(o);$(".response_headers",$(this.el)).html("<pre>"+q.getAllResponseHeaders()+"</pre>");$(".response",$(this.el)).slideDown();$(".response_hider",$(this.el)).show();$(".response_throbber",$(this.el)).hide();return hljs.highlightBlock($(".response_body",$(this.el))[0])};l.prototype.toggleOperationContent=function(){var n;n=$("#"+Docs.escapeResourceName(this.model.resourceName)+"_"+this.model.nickname+"_"+this.model.httpMethod+"_"+this.model.number+"_content");if(n.is(":visible")){return Docs.collapseOperation(n)}else{return Docs.expandOperation(n)}};return l})(Backbone.View);i=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.initialize=function(){};l.prototype.render=function(){var n;n=this.template();$(this.el).html(n(this.model));return this};l.prototype.template=function(){return Handlebars.templates.status_code};return l})(Backbone.View);e=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.initialize=function(){};l.prototype.render=function(){var q,o,n,r,p;if(this.model.paramType==="body"){this.model.isBody=true}if(this.model.dataType==="file"){this.model.isFile=true}p=this.template();$(this.el).html(p(this.model));n={sampleJSON:this.model.sampleJSON,isParam:true,signature:this.model.signature};if(this.model.sampleJSON){r=new k({model:n,tagName:"div"});$(".model-signature",$(this.el)).append(r.render().el)}else{$(".model-signature",$(this.el)).html(this.model.signature)}q={isParam:false};if(this.model.supportedContentTypes){q.produces=this.model.supportedContentTypes}if(this.model.produces){q.produces=this.model.produces}o=new f({model:q});$(".content-type",$(this.el)).append(o.render().el);return this};l.prototype.template=function(){if(this.model.isList){return Handlebars.templates.param_list}else{if(this.options.readOnly){if(this.model.required){return Handlebars.templates.param_readonly_required}else{return Handlebars.templates.param_readonly}}else{if(this.model.required){return Handlebars.templates.param_required}else{return Handlebars.templates.param}}}};return l})(Backbone.View);k=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.events={"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet":"snippetToTextArea"};l.prototype.initialize=function(){};l.prototype.render=function(){var n;n=this.template();$(this.el).html(n(this.model));this.switchToDescription();this.isParam=this.model.isParam;if(this.isParam){$(".notice",$(this.el)).text("Click to set as parameter value")}return this};l.prototype.template=function(){return Handlebars.templates.signature};l.prototype.switchToDescription=function(n){if(n!=null){n.preventDefault()}$(".snippet",$(this.el)).hide();$(".description",$(this.el)).show();$(".description-link",$(this.el)).addClass("selected");return $(".snippet-link",$(this.el)).removeClass("selected")};l.prototype.switchToSnippet=function(n){if(n!=null){n.preventDefault()}$(".description",$(this.el)).hide();$(".snippet",$(this.el)).show();$(".snippet-link",$(this.el)).addClass("selected");return $(".description-link",$(this.el)).removeClass("selected")};l.prototype.snippetToTextArea=function(n){var o;if(this.isParam){if(n!=null){n.preventDefault()}o=$("textarea",$(this.el.parentNode.parentNode.parentNode));if($.trim(o.val())===""){return o.val(this.model.sampleJSON)}}};return l})(Backbone.View);f=(function(l){d(m,l);function m(){return m.__super__.constructor.apply(this,arguments)}m.prototype.initialize=function(){};m.prototype.render=function(){var n;n=this.template();$(this.el).html(n(this.model));this.isParam=this.model.isParam;if(this.isParam){$("label[for=contentType]",$(this.el)).text("Parameter content type:")}else{$("label[for=contentType]",$(this.el)).text("Response Content Type")}return this};m.prototype.template=function(){return Handlebars.templates.content_type};return m})(Backbone.View)}).call(this);
\ No newline at end of file +$(function(){$.fn.vAlign=function(){return this.each(function(c){var a=$(this).height();var d=$(this).parent().height();var b=(d-a)/2;$(this).css("margin-top",b)})};$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(b){var d=$(this).closest("form").innerWidth();var c=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10);var a=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",d-c-a)})};$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent();$("ul.downplayed li div.content p").vAlign();$("form.sandbox").submit(function(){var a=true;$(this).find("input.required").each(function(){$(this).removeClass("error");if($(this).val()==""){$(this).addClass("error");$(this).wiggle();a=false}});return a})});function clippyCopiedCallback(b){$("#api_key_copied").fadeIn().delay(1000).fadeOut()}function log(){if(window.console){console.log.apply(console,arguments)}}if(Function.prototype.bind&&console&&typeof console.log=="object"){["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(a){console[a]=this.bind(console[a],console)},Function.prototype.call)}var Docs={shebang:function(){var b=$.param.fragment().split("/");b.shift();switch(b.length){case 1:var d="resource_"+b[0];Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});break;case 2:Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});var c=b.join("_");var a=c+"_content";Docs.expandOperation($("#"+a));$("#"+c).slideto({highlight:false});break}},toggleEndpointListForResource:function(b){var a=$("li#resource_"+Docs.escapeResourceName(b)+" ul.endpoints");if(a.is(":visible")){Docs.collapseEndpointListForResource(b)}else{Docs.expandEndpointListForResource(b)}},expandEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);if(b==""){$(".resource ul.endpoints").slideDown();return}$("li#resource_"+b).addClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideDown()},collapseEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);$("li#resource_"+b).removeClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideUp()},expandOperationsForResource:function(a){Docs.expandEndpointListForResource(a);if(a==""){$(".resource ul.endpoints li.operation div.content").slideDown();return}$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(a){Docs.expandEndpointListForResource(a);$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(a){return a.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(a){a.slideDown()},collapseOperation:function(a){a.slideUp()}};(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.content_type=b(function(g,p,f,n,m){f=f||g.helpers;var l="",c,s,k,j,q=this,h="function",o=f.helperMissing,i=void 0;function e(x,w){var t="",v,u;t+="\n ";k=f.produces;v=k||x.produces;u=f.each;j=q.program(2,d,w);j.hash={};j.fn=j;j.inverse=q.noop;v=u.call(x,v,j);if(v||v===0){t+=v}t+="\n";return t}function d(w,v){var t="",u;t+='\n <option value="';u=w;if(typeof u===h){u=u.call(w,{hash:{}})}else{if(u===i){u=o.call(w,"this",{hash:{}})}}if(u||u===0){t+=u}t+='">';u=w;if(typeof u===h){u=u.call(w,{hash:{}})}else{if(u===i){u=o.call(w,"this",{hash:{}})}}if(u||u===0){t+=u}t+="</option>\n ";return t}function r(u,t){return'\n <option value="application/json">application/json</option>\n'}l+='<label for="contentType"></label>\n<select name="contentType">\n';k=f.produces;c=k||p.produces;s=f["if"];j=q.program(1,e,m);j.hash={};j.fn=j;j.inverse=q.program(4,r,m);c=s.call(p,c,j);if(c||c===0){l+=c}l+="\n</select>\n";return l})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.main=b(function(f,p,e,n,m){e=e||f.helpers;var k="",c,r,j,i,q=this,g="function",o=e.helperMissing,h=void 0,l=this.escapeExpression;function d(v,u){var s="",t;s+='\n , <span style="font-variant: small-caps">api version</span>: ';j=e.apiVersion;t=j||v.apiVersion;if(typeof t===g){t=t.call(v,{hash:{}})}else{if(t===h){t=o.call(v,"apiVersion",{hash:{}})}}s+=l(t)+"\n ";return s}k+="\n<div class='container' id='resources_container'>\n <ul id='resources'>\n </ul>\n\n <div class=\"footer\">\n <br>\n <br>\n <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: ";j=e.basePath;c=j||p.basePath;if(typeof c===g){c=c.call(p,{hash:{}})}else{if(c===h){c=o.call(p,"basePath",{hash:{}})}}k+=l(c)+"\n ";j=e.apiVersion;c=j||p.apiVersion;r=e["if"];i=q.program(1,d,m);i.hash={};i.fn=i;i.inverse=q.noop;c=r.call(p,c,i);if(c||c===0){k+=c}k+="]</h4>\n </div>\n</div>\n";return k})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.operation=b(function(h,u,s,m,w){s=s||h.helpers;var t="",j,g,i,q,p=this,e="function",r=s.helperMissing,c=void 0,d=this.escapeExpression;function o(A,z){var x="",y;x+="\n <h4>Implementation Notes</h4>\n <p>";i=s.notes;y=i||A.notes;if(typeof y===e){y=y.call(A,{hash:{}})}else{if(y===c){y=r.call(A,"notes",{hash:{}})}}if(y||y===0){x+=y}x+="</p>\n ";return x}function n(y,x){return'\n <h4>Response Class</h4>\n <p><span class="model-signature" /></p>\n <br/>\n <div class="content-type" />\n '}function l(y,x){return'\n <h4>Parameters</h4>\n <table class=\'fullwidth\'>\n <thead>\n <tr>\n <th style="width: 100px; max-width: 100px">Parameter</th>\n <th style="width: 310px; max-width: 310px">Value</th>\n <th style="width: 200px; max-width: 200px">Description</th>\n <th style="width: 100px; max-width: 100px">Parameter Type</th>\n <th style="width: 220px; max-width: 230px">Data Type</th>\n </tr>\n </thead>\n <tbody class="operation-params">\n\n </tbody>\n </table>\n '}function k(y,x){return"\n <div style='margin:0;padding:0;display:inline'></div>\n <h4>Error Status Codes</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th>HTTP Status Code</th>\n <th>Reason</th>\n </tr>\n </thead>\n <tbody class=\"operation-status\">\n \n </tbody>\n </table>\n "}function f(y,x){return"\n "}function v(y,x){return"\n <div class='sandbox_header'>\n <input class='submit' name='commit' type='button' value='Try it out!' />\n <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n <img alt='Throbber' class='response_throbber' src='images/throbber.gif' style='display:none' />\n </div>\n "}t+="\n <ul class='operations' >\n <li class='";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+" operation' id='";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"_";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+"'>\n <div class='heading'>\n <h3>\n <span class='http_method'>\n <a href='#!/";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"/";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+'\' class="toggleOperation">';i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"</a>\n </span>\n <span class='path'>\n <a href='#!/";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"/";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+'\' class="toggleOperation">';i=s.path;j=i||u.path;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"path",{hash:{}})}}t+=d(j)+"</a>\n </span>\n </h3>\n <ul class='options'>\n <li>\n <a href='#!/";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"/";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+'\' class="toggleOperation">';i=s.summary;j=i||u.summary;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"summary",{hash:{}})}}if(j||j===0){t+=j}t+="</a>\n </li>\n </ul>\n </div>\n <div class='content' id='";i=s.resourceName;j=i||u.resourceName;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"resourceName",{hash:{}})}}t+=d(j)+"_";i=s.nickname;j=i||u.nickname;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"nickname",{hash:{}})}}t+=d(j)+"_";i=s.httpMethod;j=i||u.httpMethod;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"httpMethod",{hash:{}})}}t+=d(j)+"_";i=s.number;j=i||u.number;if(typeof j===e){j=j.call(u,{hash:{}})}else{if(j===c){j=r.call(u,"number",{hash:{}})}}t+=d(j)+"_content' style='display:none'>\n ";i=s.notes;j=i||u.notes;g=s["if"];q=p.program(1,o,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n ";i=s.responseClass;j=i||u.responseClass;g=s["if"];q=p.program(3,n,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n <form accept-charset='UTF-8' class='sandbox'>\n <div style='margin:0;padding:0;display:inline'></div>\n ";i=s.parameters;j=i||u.parameters;g=s["if"];q=p.program(5,l,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n ";i=s.errorResponses;j=i||u.errorResponses;g=s["if"];q=p.program(7,k,w);q.hash={};q.fn=q;q.inverse=p.noop;j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n ";i=s.isReadOnly;j=i||u.isReadOnly;g=s["if"];q=p.program(9,f,w);q.hash={};q.fn=q;q.inverse=p.program(11,v,w);j=g.call(u,j,q);if(j||j===0){t+=j}t+="\n </form>\n <div class='response' style='display:none'>\n <h4>Request URL</h4>\n <div class='block request_url'></div>\n <h4>Response Body</h4>\n <div class='block response_body'></div>\n <h4>Response Code</h4>\n <div class='block response_code'></div>\n <h4>Response Headers</h4>\n <div class='block response_headers'></div>\n </div>\n </div>\n </li>\n </ul>\n";return t})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param=b(function(h,v,t,m,y){t=t||h.helpers;var u="",j,g,i,r,q=this,e="function",s=t.helperMissing,c=void 0,d=this.escapeExpression;function p(D,C){var z="",B,A;z+="\n ";i=t.isFile;B=i||D.isFile;A=t["if"];r=q.program(2,o,C);r.hash={};r.fn=r;r.inverse=q.program(4,n,C);B=A.call(D,B,r);if(B||B===0){z+=B}z+="\n ";return z}function o(C,B){var z="",A;z+='\n <input type="file" name=\'';i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"'/>\n ";return z}function n(D,C){var z="",B,A;z+="\n ";i=t.defaultValue;B=i||D.defaultValue;A=t["if"];r=q.program(5,l,C);r.hash={};r.fn=r;r.inverse=q.program(7,k,C);B=A.call(D,B,r);if(B||B===0){z+=B}z+="\n ";return z}function l(C,B){var z="",A;z+="\n <textarea class='body-textarea' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"'>";i=t.defaultValue;A=i||C.defaultValue;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"defaultValue",{hash:{}})}}z+=d(A)+"</textarea>\n ";return z}function k(C,B){var z="",A;z+="\n <textarea class='body-textarea' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+'\'></textarea>\n <br />\n <div class="content-type" />\n ';return z}function f(D,C){var z="",B,A;z+="\n ";i=t.defaultValue;B=i||D.defaultValue;A=t["if"];r=q.program(10,x,C);r.hash={};r.fn=r;r.inverse=q.program(12,w,C);B=A.call(D,B,r);if(B||B===0){z+=B}z+="\n ";return z}function x(C,B){var z="",A;z+="\n <input class='parameter' minlength='0' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"' placeholder='' type='text' value='";i=t.defaultValue;A=i||C.defaultValue;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"defaultValue",{hash:{}})}}z+=d(A)+"'/>\n ";return z}function w(C,B){var z="",A;z+="\n <input class='parameter' minlength='0' name='";i=t.name;A=i||C.name;if(typeof A===e){A=A.call(C,{hash:{}})}else{if(A===c){A=s.call(C,"name",{hash:{}})}}z+=d(A)+"' placeholder='' type='text' value=''/>\n ";return z}u+="<td class='code'>";i=t.name;j=i||v.name;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"name",{hash:{}})}}u+=d(j)+"</td>\n<td>\n\n ";i=t.isBody;j=i||v.isBody;g=t["if"];r=q.program(1,p,y);r.hash={};r.fn=r;r.inverse=q.program(9,f,y);j=g.call(v,j,r);if(j||j===0){u+=j}u+="\n\n</td>\n<td>";i=t.description;j=i||v.description;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"description",{hash:{}})}}if(j||j===0){u+=j}u+="</td>\n<td>";i=t.paramType;j=i||v.paramType;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"paramType",{hash:{}})}}if(j||j===0){u+=j}u+='</td>\n<td>\n <span class="model-signature"></span>\n</td>\n\n';return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_list=b(function(h,v,t,m,x){t=t||h.helpers;var u="",k,g,j,r,q=this,e="function",s=t.helperMissing,c=void 0,d=this.escapeExpression;function p(z,y){return"\n "}function o(C,B){var y="",A,z;y+="\n ";j=t.defaultValue;A=j||C.defaultValue;z=t["if"];r=q.program(4,n,B);r.hash={};r.fn=r;r.inverse=q.program(6,l,B);A=z.call(C,A,r);if(A||A===0){y+=A}y+="\n ";return y}function n(z,y){return"\n "}function l(z,y){return"\n <option selected=\"\" value=''></option>\n "}function i(C,B){var y="",A,z;y+="\n ";j=t.isDefault;A=j||C.isDefault;z=t["if"];r=q.program(9,f,B);r.hash={};r.fn=r;r.inverse=q.program(11,w,B);A=z.call(C,A,r);if(A||A===0){y+=A}y+="\n ";return y}function f(B,A){var y="",z;y+="\n <option value='";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+"'>";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+" (default)</option>\n ";return y}function w(B,A){var y="",z;y+="\n <option value='";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+"'>";j=t.value;z=j||B.value;if(typeof z===e){z=z.call(B,{hash:{}})}else{if(z===c){z=s.call(B,"value",{hash:{}})}}y+=d(z)+"</option>\n ";return y}u+="<td class='code'>";j=t.name;k=j||v.name;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"name",{hash:{}})}}u+=d(k)+"</td>\n<td>\n <select class='parameter' name='";j=t.name;k=j||v.name;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"name",{hash:{}})}}u+=d(k)+"'>\n ";j=t.required;k=j||v.required;g=t["if"];r=q.program(1,p,x);r.hash={};r.fn=r;r.inverse=q.program(3,o,x);k=g.call(v,k,r);if(k||k===0){u+=k}u+="\n ";j=t.allowableValues;k=j||v.allowableValues;k=(k===null||k===undefined||k===false?k:k.descriptiveValues);g=t.each;r=q.program(8,i,x);r.hash={};r.fn=r;r.inverse=q.noop;k=g.call(v,k,r);if(k||k===0){u+=k}u+="\n </select>\n</td>\n<td>";j=t.description;k=j||v.description;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"description",{hash:{}})}}if(k||k===0){u+=k}u+="</td>\n<td>";j=t.paramType;k=j||v.paramType;if(typeof k===e){k=k.call(v,{hash:{}})}else{if(k===c){k=s.call(v,"paramType",{hash:{}})}}if(k||k===0){u+=k}u+='</td>\n<td><span class="model-signature"></span></td>\n';return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly=b(function(g,t,r,k,u){r=r||g.helpers;var s="",i,f,h,p,o=this,e="function",q=r.helperMissing,c=void 0,d=this.escapeExpression;function n(y,x){var v="",w;v+="\n <textarea class='body-textarea' readonly='readonly' name='";h=r.name;w=h||y.name;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"name",{hash:{}})}}v+=d(w)+"'>";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"</textarea>\n ";return v}function m(z,y){var v="",x,w;v+="\n ";h=r.defaultValue;x=h||z.defaultValue;w=r["if"];p=o.program(4,l,y);p.hash={};p.fn=p;p.inverse=o.program(6,j,y);x=w.call(z,x,p);if(x||x===0){v+=x}v+="\n ";return v}function l(y,x){var v="",w;v+="\n ";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"\n ";return v}function j(w,v){return"\n (empty)\n "}s+="<td class='code'>";h=r.name;i=h||t.name;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"name",{hash:{}})}}s+=d(i)+"</td>\n<td>\n ";h=r.isBody;i=h||t.isBody;f=r["if"];p=o.program(1,n,u);p.hash={};p.fn=p;p.inverse=o.program(3,m,u);i=f.call(t,i,p);if(i||i===0){s+=i}s+="\n</td>\n<td>";h=r.description;i=h||t.description;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"description",{hash:{}})}}if(i||i===0){s+=i}s+="</td>\n<td>";h=r.paramType;i=h||t.paramType;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"paramType",{hash:{}})}}if(i||i===0){s+=i}s+='</td>\n<td><span class="model-signature"></span></td>\n';return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly_required=b(function(g,t,r,k,u){r=r||g.helpers;var s="",i,f,h,p,o=this,e="function",q=r.helperMissing,c=void 0,d=this.escapeExpression;function n(y,x){var v="",w;v+="\n <textarea class='body-textarea' readonly='readonly' placeholder='(required)' name='";h=r.name;w=h||y.name;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"name",{hash:{}})}}v+=d(w)+"'>";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"</textarea>\n ";return v}function m(z,y){var v="",x,w;v+="\n ";h=r.defaultValue;x=h||z.defaultValue;w=r["if"];p=o.program(4,l,y);p.hash={};p.fn=p;p.inverse=o.program(6,j,y);x=w.call(z,x,p);if(x||x===0){v+=x}v+="\n ";return v}function l(y,x){var v="",w;v+="\n ";h=r.defaultValue;w=h||y.defaultValue;if(typeof w===e){w=w.call(y,{hash:{}})}else{if(w===c){w=q.call(y,"defaultValue",{hash:{}})}}v+=d(w)+"\n ";return v}function j(w,v){return"\n (empty)\n "}s+="<td class='code required'>";h=r.name;i=h||t.name;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"name",{hash:{}})}}s+=d(i)+"</td>\n<td>\n ";h=r.isBody;i=h||t.isBody;f=r["if"];p=o.program(1,n,u);p.hash={};p.fn=p;p.inverse=o.program(3,m,u);i=f.call(t,i,p);if(i||i===0){s+=i}s+="\n</td>\n<td>";h=r.description;i=h||t.description;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"description",{hash:{}})}}if(i||i===0){s+=i}s+="</td>\n<td>";h=r.paramType;i=h||t.paramType;if(typeof i===e){i=i.call(t,{hash:{}})}else{if(i===c){i=q.call(t,"paramType",{hash:{}})}}if(i||i===0){s+=i}s+='</td>\n<td><span class="model-signature"></span></td>\n';return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_required=b(function(h,v,t,m,A){t=t||h.helpers;var u="",j,g,i,r,q=this,e="function",s=t.helperMissing,c=void 0,d=this.escapeExpression;function p(F,E){var B="",D,C;B+="\n ";i=t.isFile;D=i||F.isFile;C=t["if"];r=q.program(2,o,E);r.hash={};r.fn=r;r.inverse=q.program(4,n,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function o(E,D){var B="",C;B+='\n <input type="file" name=\'';i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"'/>\n ";return B}function n(F,E){var B="",D,C;B+="\n ";i=t.defaultValue;D=i||F.defaultValue;C=t["if"];r=q.program(5,l,E);r.hash={};r.fn=r;r.inverse=q.program(7,k,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function l(E,D){var B="",C;B+="\n <textarea class='body-textarea' placeholder='(required)' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"'>";i=t.defaultValue;C=i||E.defaultValue;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"defaultValue",{hash:{}})}}B+=d(C)+"</textarea>\n ";return B}function k(E,D){var B="",C;B+="\n <textarea class='body-textarea' placeholder='(required)' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+'\'></textarea>\n <br />\n <div class="content-type" />\n ';return B}function f(F,E){var B="",D,C;B+="\n ";i=t.isFile;D=i||F.isFile;C=t["if"];r=q.program(10,z,E);r.hash={};r.fn=r;r.inverse=q.program(12,y,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function z(E,D){var B="",C;B+="\n <input class='parameter' class='required' type='file' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"'/>\n ";return B}function y(F,E){var B="",D,C;B+="\n ";i=t.defaultValue;D=i||F.defaultValue;C=t["if"];r=q.program(13,x,E);r.hash={};r.fn=r;r.inverse=q.program(15,w,E);D=C.call(F,D,r);if(D||D===0){B+=D}B+="\n ";return B}function x(E,D){var B="",C;B+="\n <input class='parameter required' minlength='1' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"' placeholder='(required)' type='text' value='";i=t.defaultValue;C=i||E.defaultValue;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"defaultValue",{hash:{}})}}B+=d(C)+"'/>\n ";return B}function w(E,D){var B="",C;B+="\n <input class='parameter required' minlength='1' name='";i=t.name;C=i||E.name;if(typeof C===e){C=C.call(E,{hash:{}})}else{if(C===c){C=s.call(E,"name",{hash:{}})}}B+=d(C)+"' placeholder='(required)' type='text' value=''/>\n ";return B}u+="<td class='code required'>";i=t.name;j=i||v.name;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"name",{hash:{}})}}u+=d(j)+"</td>\n<td>\n ";i=t.isBody;j=i||v.isBody;g=t["if"];r=q.program(1,p,A);r.hash={};r.fn=r;r.inverse=q.program(9,f,A);j=g.call(v,j,r);if(j||j===0){u+=j}u+="\n</td>\n<td>\n <strong>";i=t.description;j=i||v.description;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"description",{hash:{}})}}if(j||j===0){u+=j}u+="</strong>\n</td>\n<td>";i=t.paramType;j=i||v.paramType;if(typeof j===e){j=j.call(v,{hash:{}})}else{if(j===c){j=s.call(v,"paramType",{hash:{}})}}if(j||j===0){u+=j}u+='</td>\n<td><span class="model-signature"></span></td>\n';return u})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.resource=b(function(e,n,d,l,k){d=d||e.helpers;var i="",c,h,o=this,f="function",m=d.helperMissing,g=void 0,j=this.escapeExpression;i+="<div class='heading'>\n <h2>\n <a href='#!/";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"' onclick=\"Docs.toggleEndpointListForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"');\">/";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"</a>\n </h2>\n <ul class='options'>\n <li>\n <a href='#!/";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"' id='endpointListTogger_";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"'\n onclick=\"Docs.toggleEndpointListForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"');\">Show/Hide</a>\n </li>\n <li>\n <a href='#' onclick=\"Docs.collapseOperationsForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"'); return false;\">\n List Operations\n </a>\n </li>\n <li>\n <a href='#' onclick=\"Docs.expandOperationsForResource('";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"'); return false;\">\n Expand Operations\n </a>\n </li>\n <li>\n <a href='";h=d.url;c=h||n.url;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"url",{hash:{}})}}i+=j(c)+"'>Raw</a>\n </li>\n </ul>\n</div>\n<ul class='endpoints' id='";h=d.name;c=h||n.name;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"name",{hash:{}})}}i+=j(c)+"_endpoint_list' style='display:none'>\n\n</ul>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.signature=b(function(e,n,d,l,k){d=d||e.helpers;var i="",c,h,o=this,f="function",m=d.helperMissing,g=void 0,j=this.escapeExpression;i+='<div>\n<ul class="signature-nav">\n <li><a class="description-link" href="#">Model</a></li>\n <li><a class="snippet-link" href="#">Model Schema</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n <div class="description">\n ';h=d.signature;c=h||n.signature;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"signature",{hash:{}})}}if(c||c===0){i+=c}i+='\n </div>\n\n <div class="snippet">\n <pre><code>';h=d.sampleJSON;c=h||n.sampleJSON;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"sampleJSON",{hash:{}})}}i+=j(c)+'</code></pre>\n <small class="notice"></small>\n </div>\n</div>\n\n';return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.status_code=b(function(e,n,d,l,k){d=d||e.helpers;var i="",c,h,o=this,f="function",m=d.helperMissing,g=void 0,j=this.escapeExpression;i+="<td width='15%' class='code'>";h=d.code;c=h||n.code;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"code",{hash:{}})}}i+=j(c)+"</td>\n<td>";h=d.reason;c=h||n.reason;if(typeof c===f){c=c.call(n,{hash:{}})}else{if(c===g){c=m.call(n,"reason",{hash:{}})}}if(c||c===0){i+=c}i+="</td>\n\n";return i})})();(function(){var f,b,h,c,e,j,k,i,a,g={}.hasOwnProperty,d=function(o,m){for(var l in m){if(g.call(m,l)){o[l]=m[l]}}function n(){this.constructor=o}n.prototype=m.prototype;o.prototype=new n();o.__super__=m.prototype;return o};a=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.dom_id="swagger_ui";l.prototype.options=null;l.prototype.api=null;l.prototype.headerView=null;l.prototype.mainView=null;l.prototype.initialize=function(n){var o=this;if(n==null){n={}}if(n.dom_id!=null){this.dom_id=n.dom_id;delete n.dom_id}if(!($("#"+this.dom_id)!=null)){$("body").append('<div id="'+this.dom_id+'"></div>')}this.options=n;this.options.success=function(){return o.render()};this.options.progress=function(p){return o.showMessage(p)};this.options.failure=function(p){return o.onLoadFailure(p)};this.headerView=new b({el:$("#header")});return this.headerView.on("update-swagger-ui",function(p){return o.updateSwaggerUi(p)})};l.prototype.updateSwaggerUi=function(n){this.options.discoveryUrl=n.discoveryUrl;this.options.apiKey=n.apiKey;return this.load()};l.prototype.load=function(){var n;if((n=this.mainView)!=null){n.clear()}this.headerView.update(this.options.discoveryUrl,this.options.apiKey);return this.api=new SwaggerApi(this.options)};l.prototype.render=function(){var n=this;this.showMessage("Finished Loading Resource Information. Rendering Swagger UI...");this.mainView=new h({model:this.api,el:$("#"+this.dom_id)}).render();this.showMessage();switch(this.options.docExpansion){case"full":Docs.expandOperationsForResource("");break;case"list":Docs.collapseOperationsForResource("")}if(this.options.onComplete){this.options.onComplete(this.api,this)}return setTimeout(function(){return Docs.shebang()},400)};l.prototype.showMessage=function(n){if(n==null){n=""}$("#message-bar").removeClass("message-fail");$("#message-bar").addClass("message-success");return $("#message-bar").html(n)};l.prototype.onLoadFailure=function(n){var o;if(n==null){n=""}$("#message-bar").removeClass("message-success");$("#message-bar").addClass("message-fail");o=$("#message-bar").html(n);if(this.options.onFailure!=null){this.options.onFailure(n)}return o};return l})(Backbone.Router);window.SwaggerUi=a;b=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.events={"click #show-pet-store-icon":"showPetStore","click #show-wordnik-dev-icon":"showWordnikDev","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"};l.prototype.initialize=function(){};l.prototype.showPetStore=function(n){return this.trigger("update-swagger-ui",{discoveryUrl:"http://petstore.swagger.wordnik.com/api/api-docs.json",apiKey:"special-key"})};l.prototype.showWordnikDev=function(n){return this.trigger("update-swagger-ui",{discoveryUrl:"http://api.wordnik.com/v4/resources.json",apiKey:""})};l.prototype.showCustomOnKeyup=function(n){if(n.keyCode===13){return this.showCustom()}};l.prototype.showCustom=function(n){if(n!=null){n.preventDefault()}return this.trigger("update-swagger-ui",{discoveryUrl:$("#input_baseUrl").val(),apiKey:$("#input_apiKey").val()})};l.prototype.update=function(o,p,n){if(n==null){n=false}$("#input_baseUrl").val(o);$("#input_apiKey").val(p);if(n){return this.trigger("update-swagger-ui",{discoveryUrl:o,apiKey:p})}};return l})(Backbone.View);h=(function(l){d(m,l);function m(){return m.__super__.constructor.apply(this,arguments)}m.prototype.initialize=function(){};m.prototype.render=function(){var q,p,n,o;$(this.el).html(Handlebars.templates.main(this.model));o=this.model.apisArray;for(p=0,n=o.length;p<n;p++){q=o[p];this.addResource(q)}return this};m.prototype.addResource=function(o){var n;n=new j({model:o,tagName:"li",id:"resource_"+o.name,className:"resource"});return $("#resources").append(n.render().el)};m.prototype.clear=function(){return $(this.el).html("")};return m})(Backbone.View);j=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.initialize=function(){};l.prototype.render=function(){var o,q,n,p;$(this.el).html(Handlebars.templates.resource(this.model));this.number=0;p=this.model.operationsArray;for(q=0,n=p.length;q<n;q++){o=p[q];this.addOperation(o)}return this};l.prototype.addOperation=function(n){var o;n.number=this.number;o=new c({model:n,tagName:"li",className:"endpoint"});$(".endpoints",$(this.el)).append(o.render().el);return this.number++};return l})(Backbone.View);c=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.events={"submit .sandbox":"submitOperation","click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent"};l.prototype.initialize=function(){};l.prototype.render=function(){var n,x,y,p,u,z,v,s,r,w,o,t,q;y=jQuery.inArray(this.model.httpMethod,this.model.supportedSubmitMethods())>=0;if(!y){this.model.isReadOnly=true}$(this.el).html(Handlebars.templates.operation(this.model));if(this.model.responseClassSignature&&this.model.responseClassSignature!=="string"){z={sampleJSON:this.model.responseSampleJSON,isParam:false,signature:this.model.responseClassSignature};u=new k({model:z,tagName:"div"});$(".model-signature",$(this.el)).append(u.render().el)}else{$(".model-signature",$(this.el)).html(this.model.responseClass)}n={isParam:false};if(this.model.supportedContentTypes){n.produces=this.model.supportedContentTypes}if(this.model.produces){n.produces=this.model.produces}x=new f({model:n});$(".content-type",$(this.el)).append(x.render().el);t=this.model.parameters;for(s=0,w=t.length;s<w;s++){p=t[s];this.addParameter(p)}q=this.model.errorResponses;for(r=0,o=q.length;r<o;r++){v=q[r];this.addStatusCode(v)}return this};l.prototype.addParameter=function(o){var n;n=new e({model:o,tagName:"tr",readOnly:this.model.isReadOnly});return $(".operation-params",$(this.el)).append(n.render().el)};l.prototype.addStatusCode=function(o){var n;n=new i({model:o,tagName:"tr"});return $(".operation-status",$(this.el)).append(n.render().el)};l.prototype.submitOperation=function(K){var F,L,E,v,q,A,P,J,Q,H,D,B,G,s,x,u,r,p,O,S,R,N,M,n,C,z,y,w,t,I=this;if(K!=null){K.preventDefault()}v=$(".sandbox",$(this.el));E=true;v.find("input.required").each(function(){var o=this;$(this).removeClass("error");if(jQuery.trim($(this).val())===""){$(this).addClass("error");$(this).wiggle({callback:function(){return $(o).focus()}});return E=false}});if(E){Q={};C=v.serializeArray();for(x=0,O=C.length;x<O;x++){H=C[x];if((H.value!=null)&&jQuery.trim(H.value).length>0){Q[H.name]=H.value}}P=v.children().find('input[type~="file"]').size()!==0;J=false;L="application/json";if(this.model.consumes&&this.model.consumes.length>0){L=this.model.consumes[0]}else{z=this.model.parameters;for(u=0,S=z.length;u<S;u++){H=z[u];if(H.paramType==="form"){J=true;L=false}}if(P){L=false}else{if(this.model.httpMethod.toLowerCase()==="post"&&J===false){L="application/json"}}}if(P){F=new FormData();y=this.model.parameters;for(r=0,R=y.length;r<R;r++){B=y[r];if((B.paramType==="body"||"form")&&B.name!=="file"&&B.name!=="File"&&(Q[B.name]!=null)){F.append(B.name,Q[B.name])}}$.each(v.children().find('input[type~="file"]'),function(o,T){return F.append($(T).attr("name"),T.files[0])});console.log(F)}else{if(J){F=new FormData();w=this.model.parameters;for(p=0,N=w.length;p<N;p++){B=w[p];if(Q[B.name]!=null){F.append(B.name,Q[B.name])}}}else{F=null;t=this.model.parameters;for(n=0,M=t.length;n<M;n++){B=t[n];if(B.paramType==="body"){F=Q[B.name]}}}}log("bodyParam = "+F);q=null;A=this.model.supportHeaderParams()?(q=this.model.getHeaderParams(Q),this.model.urlify(Q,false)):this.model.urlify(Q,true);log("submitting "+A);$(".request_url",$(this.el)).html("<pre>"+A+"</pre>");$(".response_throbber",$(this.el)).show();D={type:this.model.httpMethod,url:A,headers:q,data:F,contentType:L,dataType:"json",processData:false,error:function(T,U,o){return I.showErrorStatus(T,U,o)},success:function(o){return I.showResponse(o)},complete:function(o){return I.showCompleteStatus(o)}};G=$("td select[name=contentType]",$(this.el)).val();if(G){D.contentType=G}log("content type = "+D.contentType);if(!(D.data||(D.type==="GET"||D.type==="DELETE"))&&D.contentType===!"application/x-www-form-urlencoded"){D.contentType=false}log("content type is now = "+D.contentType);s=$(".content > .content-type > div > select[name=contentType]",$(this.el)).val();if(s){D.headers=D.headers!=null?D.headers:{};D.headers.accept=s}jQuery.ajax(D);return false}};l.prototype.hideResponse=function(n){if(n!=null){n.preventDefault()}$(".response",$(this.el)).slideUp();return $(".response_hider",$(this.el)).fadeOut()};l.prototype.showResponse=function(n){var o;o=JSON.stringify(n,null,"\t").replace(/\n/g,"<br>");return $(".response_body",$(this.el)).html(escape(o))};l.prototype.showErrorStatus=function(n){return this.showStatus(n)};l.prototype.showCompleteStatus=function(n){return this.showStatus(n)};l.prototype.formatXml=function(u){var q,t,o,v,A,w,p,n,y,z,s,r,x;n=/(>)(<)(\/*)/g;z=/[ ]*(.*)[ ]+\n/g;q=/(<.+>)(.+\n)/g;u=u.replace(n,"$1\n$2$3").replace(z,"$1\n").replace(q,"$1\n$2");p=0;t="";A=u.split("\n");o=0;v="other";y={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0};s=function(G){var C,B,E,I,F,D,H;D={single:Boolean(G.match(/<.+\/>/)),closing:Boolean(G.match(/<\/.+>/)),opening:Boolean(G.match(/<[^!?].*>/))};F=((function(){var J;J=[];for(E in D){H=D[E];if(H){J.push(E)}}return J})())[0];F=F===void 0?"other":F;C=v+"->"+F;v=F;I="";o+=y[C];I=((function(){var K,L,J;J=[];for(B=K=0,L=o;0<=L?K<L:K>L;B=0<=L?++K:--K){J.push(" ")}return J})()).join("");if(C==="opening->closing"){return t=t.substr(0,t.length-1)+G+"\n"}else{return t+=I+G+"\n"}};for(r=0,x=A.length;r<x;r++){w=A[r];s(w)}return t};l.prototype.showStatus=function(q){var p,r,o;try{p=$("<code />").text(JSON.stringify(JSON.parse(q.responseText),null,2));r=$('<pre class="json" />').append(p)}catch(n){p=$("<code />").text(this.formatXml(q.responseText));r=$('<pre class="xml" />').append(p)}o=r;$(".response_code",$(this.el)).html("<pre>"+q.status+"</pre>");$(".response_body",$(this.el)).html(o);$(".response_headers",$(this.el)).html("<pre>"+q.getAllResponseHeaders()+"</pre>");$(".response",$(this.el)).slideDown();$(".response_hider",$(this.el)).show();$(".response_throbber",$(this.el)).hide();return hljs.highlightBlock($(".response_body",$(this.el))[0])};l.prototype.toggleOperationContent=function(){var n;n=$("#"+Docs.escapeResourceName(this.model.resourceName)+"_"+this.model.nickname+"_"+this.model.httpMethod+"_"+this.model.number+"_content");if(n.is(":visible")){return Docs.collapseOperation(n)}else{return Docs.expandOperation(n)}};return l})(Backbone.View);i=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.initialize=function(){};l.prototype.render=function(){var n;n=this.template();$(this.el).html(n(this.model));return this};l.prototype.template=function(){return Handlebars.templates.status_code};return l})(Backbone.View);e=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.initialize=function(){};l.prototype.render=function(){var q,o,n,r,p;if(this.model.paramType==="body"){this.model.isBody=true}if(this.model.dataType==="file"){this.model.isFile=true}p=this.template();$(this.el).html(p(this.model));n={sampleJSON:this.model.sampleJSON,isParam:true,signature:this.model.signature};if(this.model.sampleJSON){r=new k({model:n,tagName:"div"});$(".model-signature",$(this.el)).append(r.render().el)}else{$(".model-signature",$(this.el)).html(this.model.signature)}q={isParam:false};if(this.model.supportedContentTypes){q.produces=this.model.supportedContentTypes}if(this.model.produces){q.produces=this.model.produces}o=new f({model:q});$(".content-type",$(this.el)).append(o.render().el);return this};l.prototype.template=function(){if(this.model.isList){return Handlebars.templates.param_list}else{if(this.options.readOnly){if(this.model.required){return Handlebars.templates.param_readonly_required}else{return Handlebars.templates.param_readonly}}else{if(this.model.required){return Handlebars.templates.param_required}else{return Handlebars.templates.param}}}};return l})(Backbone.View);k=(function(m){d(l,m);function l(){return l.__super__.constructor.apply(this,arguments)}l.prototype.events={"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet":"snippetToTextArea"};l.prototype.initialize=function(){};l.prototype.render=function(){var n;n=this.template();$(this.el).html(n(this.model));this.switchToDescription();this.isParam=this.model.isParam;if(this.isParam){$(".notice",$(this.el)).text("Click to set as parameter value")}return this};l.prototype.template=function(){return Handlebars.templates.signature};l.prototype.switchToDescription=function(n){if(n!=null){n.preventDefault()}$(".snippet",$(this.el)).hide();$(".description",$(this.el)).show();$(".description-link",$(this.el)).addClass("selected");return $(".snippet-link",$(this.el)).removeClass("selected")};l.prototype.switchToSnippet=function(n){if(n!=null){n.preventDefault()}$(".description",$(this.el)).hide();$(".snippet",$(this.el)).show();$(".snippet-link",$(this.el)).addClass("selected");return $(".description-link",$(this.el)).removeClass("selected")};l.prototype.snippetToTextArea=function(n){var o;if(this.isParam){if(n!=null){n.preventDefault()}o=$("textarea",$(this.el.parentNode.parentNode.parentNode));if($.trim(o.val())===""){return o.val(this.model.sampleJSON)}}};return l})(Backbone.View);f=(function(l){d(m,l);function m(){return m.__super__.constructor.apply(this,arguments)}m.prototype.initialize=function(){};m.prototype.render=function(){var n;n=this.template();$(this.el).html(n(this.model));this.isParam=this.model.isParam;if(this.isParam){$("label[for=contentType]",$(this.el)).text("Parameter content type:")}else{$("label[for=contentType]",$(this.el)).text("Response Content Type")}return this};m.prototype.template=function(){return Handlebars.templates.content_type};return m})(Backbone.View)}).call(this);
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/App.config b/MediaBrowser.ServerApplication/App.config index e461d8c9e..a5cbacb61 100644 --- a/MediaBrowser.ServerApplication/App.config +++ b/MediaBrowser.ServerApplication/App.config @@ -34,10 +34,6 @@ <bindingRedirect oldVersion="0.0.0.0-2.0.20823.0" newVersion="2.0.20823.0" /> </dependentAssembly> <dependentAssembly> - <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.0.86.0" newVersion="1.0.86.0" /> - </dependentAssembly> - <dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-1.5.11.0" newVersion="1.5.11.0" /> </dependentAssembly> 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 583053fa4..0df0d36f9 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -38,10 +38,10 @@ using MediaBrowser.Server.Implementations.IO; using MediaBrowser.Server.Implementations.Library; using MediaBrowser.Server.Implementations.Localization; using MediaBrowser.Server.Implementations.MediaEncoder; +using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Implementations.Providers; using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; -using MediaBrowser.Server.Implementations.Sqlite; using MediaBrowser.Server.Implementations.Updates; using MediaBrowser.Server.Implementations.WebSocket; using MediaBrowser.ServerApplication.Implementations; @@ -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> @@ -244,16 +238,16 @@ namespace MediaBrowser.ServerApplication ZipClient = new DotNetZipClient(); RegisterSingleInstance(ZipClient); - UserDataRepository = new SQLiteUserDataRepository(ApplicationPaths, JsonSerializer, LogManager); + UserDataRepository = new SqliteUserDataRepository(ApplicationPaths, JsonSerializer, LogManager); RegisterSingleInstance(UserDataRepository); - UserRepository = new SQLiteUserRepository(ApplicationPaths, JsonSerializer, LogManager); + UserRepository = new SqliteUserRepository(ApplicationPaths, JsonSerializer, LogManager); RegisterSingleInstance(UserRepository); - DisplayPreferencesRepository = new SQLiteDisplayPreferencesRepository(ApplicationPaths, JsonSerializer, LogManager); + DisplayPreferencesRepository = new SqliteDisplayPreferencesRepository(ApplicationPaths, JsonSerializer, LogManager); RegisterSingleInstance(DisplayPreferencesRepository); - ItemRepository = new SQLiteItemRepository(ApplicationPaths, JsonSerializer, LogManager); + ItemRepository = new SqliteItemRepository(ApplicationPaths, JsonSerializer, LogManager); RegisterSingleInstance(ItemRepository); UserManager = new UserManager(Logger, ServerConfigurationManager); @@ -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) { diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 375c8f0fd..8b419abab 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -130,9 +130,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\MediaBrowser.Common.3.0.123\lib\net45\MediaBrowser.Common.dll</HintPath> </Reference> - <Reference Include="MediaBrowser.IsoMounter, Version=1.0.4915.20167, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="MediaBrowser.IsoMounter, Version=1.0.4917.10402, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.52\lib\net45\MediaBrowser.IsoMounter.dll</HintPath> + <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.53\lib\net45\MediaBrowser.IsoMounter.dll</HintPath> </Reference> <Reference Include="MediaBrowser.Model, Version=3.0.4912.27515, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> @@ -154,19 +154,19 @@ </Reference> <Reference Include="pfmclrapi, Version=0.0.0.0, Culture=neutral, processorArchitecture=x86"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.52\lib\net45\pfmclrapi.dll</HintPath> + <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.53\lib\net45\pfmclrapi.dll</HintPath> </Reference> - <Reference Include="ServiceStack, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.3.9.46\lib\net35\ServiceStack.dll</HintPath> + <HintPath>..\packages\ServiceStack.3.9.54\lib\net35\ServiceStack.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Common, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Common, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Common.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Common.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Interfaces, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Interfaces, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Interfaces.dll</HintPath> + <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath> </Reference> <Reference Include="ServiceStack.OrmLite.SqlServer"> <HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.44\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath> @@ -174,13 +174,13 @@ <Reference Include="ServiceStack.Redis"> <HintPath>..\packages\ServiceStack.Redis.3.9.44\lib\net35\ServiceStack.Redis.dll</HintPath> </Reference> - <Reference Include="ServiceStack.ServiceInterface, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.ServiceInterface, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.3.9.46\lib\net35\ServiceStack.ServiceInterface.dll</HintPath> + <HintPath>..\packages\ServiceStack.3.9.54\lib\net35\ServiceStack.ServiceInterface.dll</HintPath> </Reference> - <Reference Include="ServiceStack.Text, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="ServiceStack.Text, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ServiceStack.Text.3.9.45\lib\net35\ServiceStack.Text.dll</HintPath> + <HintPath>..\packages\ServiceStack.Text.3.9.54\lib\net35\ServiceStack.Text.dll</HintPath> </Reference> <Reference Include="SimpleInjector, Version=2.2.3.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index c6f0671c1..15fd50aeb 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -4,16 +4,16 @@ <package id="Hardcodet.Wpf.TaskbarNotification" version="1.0.4.0" targetFramework="net45" /> <package id="MahApps.Metro" version="0.11.0.17-ALPHA" targetFramework="net45" /> <package id="MediaBrowser.Common" version="3.0.123" targetFramework="net45" /> - <package id="MediaBrowser.IsoMounting" version="3.0.52" targetFramework="net45" /> + <package id="MediaBrowser.IsoMounting" version="3.0.53" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.0.19" targetFramework="net45" /> <package id="Microsoft.Bcl.Async" version="1.0.16" targetFramework="net45" /> <package id="Microsoft.Bcl.Build" version="1.0.7" targetFramework="net45" /> <package id="morelinq" version="1.0.15631-beta" targetFramework="net45" /> <package id="NLog" version="2.0.1.2" targetFramework="net45" /> - <package id="ServiceStack" version="3.9.46" targetFramework="net45" /> - <package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" /> + <package id="ServiceStack" version="3.9.54" targetFramework="net45" /> + <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" /> <package id="ServiceStack.OrmLite.SqlServer" version="3.9.44" targetFramework="net45" /> <package id="ServiceStack.Redis" version="3.9.44" targetFramework="net45" /> - <package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" /> + <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> <package id="SimpleInjector" version="2.2.3" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 809c93a0b..36c8923bd 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -35,17 +35,17 @@ <RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
- <Reference Include="ServiceStack.Common, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL">
+ <Reference Include="ServiceStack.Common, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Common.dll</HintPath>
+ <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
- <Reference Include="ServiceStack.Interfaces, Version=3.9.46.0, Culture=neutral, processorArchitecture=MSIL">
+ <Reference Include="ServiceStack.Interfaces, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\ServiceStack.Common.3.9.46\lib\net35\ServiceStack.Interfaces.dll</HintPath>
+ <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
- <Reference Include="ServiceStack.Text, Version=3.9.45.0, Culture=neutral, processorArchitecture=MSIL">
+ <Reference Include="ServiceStack.Text, Version=3.9.54.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\ServiceStack.Text.3.9.45\lib\net35\ServiceStack.Text.dll</HintPath>
+ <HintPath>..\packages\ServiceStack.Text.3.9.54\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index f5f290f27..1d3d4e10f 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="MediaBrowser.ApiClient.Javascript" version="3.0.124" targetFramework="net45" /> - <package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" /> - <package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" /> + <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" /> + <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index aaa4ce6f8..50221ef1c 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.124</version> + <version>3.0.125</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.124" /> + <dependency id="MediaBrowser.Common" version="3.0.125" /> <dependency id="NLog" version="2.0.1.2" /> <dependency id="ServiceStack.Text" version="3.9.45" /> <dependency id="SimpleInjector" version="2.2.3" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 3e8859441..225d65bd6 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.124</version> + <version>3.0.125</version> <title>MediaBrowser.Common</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 6540d99cd..1da7b5dce 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.124</version> + <version>3.0.125</version> <title>Media Browser.Server.Core</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Media Browser Server.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.124" /> + <dependency id="MediaBrowser.Common" version="3.0.125" /> </dependencies> </metadata> <files> |
