diff options
| author | Luke <luke.pulverenti@gmail.com> | 2015-01-04 09:27:54 -0500 |
|---|---|---|
| committer | Luke <luke.pulverenti@gmail.com> | 2015-01-04 09:27:54 -0500 |
| commit | c5ff30f66e368efc2ca7dea7813fba6d9f6a657c (patch) | |
| tree | c5552b898f66b7d510e9257eb8bbeafd6a003676 /MediaBrowser.Api | |
| parent | 767590125b27c2498e3ad9544edbede30fb70f45 (diff) | |
| parent | 59b6bc28c332701d5e383fbf99170bdc740fb6cc (diff) | |
Merge pull request #965 from MediaBrowser/dev
3.0.5482.0
Diffstat (limited to 'MediaBrowser.Api')
37 files changed, 638 insertions, 267 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index d386373d4b..0eb92d5a7e 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -1,7 +1,10 @@ using MediaBrowser.Api.Playback; -using MediaBrowser.Controller; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; using System; @@ -33,7 +36,7 @@ namespace MediaBrowser.Api /// <summary> /// The application paths /// </summary> - private readonly IServerApplicationPaths _appPaths; + private readonly IServerConfigurationManager _config; private readonly ISessionManager _sessionManager; @@ -43,13 +46,13 @@ namespace MediaBrowser.Api /// Initializes a new instance of the <see cref="ApiEntryPoint" /> class. /// </summary> /// <param name="logger">The logger.</param> - /// <param name="appPaths">The application paths.</param> /// <param name="sessionManager">The session manager.</param> - public ApiEntryPoint(ILogger logger, IServerApplicationPaths appPaths, ISessionManager sessionManager) + /// <param name="config">The configuration.</param> + public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config) { Logger = logger; - _appPaths = appPaths; _sessionManager = sessionManager; + _config = config; Instance = this; } @@ -73,12 +76,19 @@ namespace MediaBrowser.Api } } + public EncodingOptions GetEncodingOptions() + { + return _config.GetConfiguration<EncodingOptions>("encoding"); + } + /// <summary> /// Deletes the encoded media cache. /// </summary> private void DeleteEncodedMediaCache() { - foreach (var file in Directory.EnumerateFiles(_appPaths.TranscodingTempPath, "*", SearchOption.AllDirectories) + var path = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower()); + + foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) .ToList()) { File.Delete(file); @@ -185,7 +195,9 @@ namespace MediaBrowser.Api CompletionPercentage = percentComplete, Width = state.OutputWidth, Height = state.OutputHeight, - AudioChannels = state.OutputAudioChannels + AudioChannels = state.OutputAudioChannels, + IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase), + IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) }); } } diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index 5fe606e16e..3eb0296fcb 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -123,7 +123,7 @@ namespace MediaBrowser.Api public void Post(AutoSetMetadataOptions request) { - _configurationManager.DisableMetadataService("Media Browser Legacy Xml"); + _configurationManager.DisableMetadataService("Media Browser Xml"); _configurationManager.SaveConfiguration(); } diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs index 9981d506ee..94d6e25b03 100644 --- a/MediaBrowser.Api/Dlna/DlnaServerService.cs +++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Dlna; using ServiceStack; using ServiceStack.Text.Controller; using ServiceStack.Web; @@ -77,14 +76,11 @@ namespace MediaBrowser.Api.Dlna private readonly IContentDirectory _contentDirectory; private readonly IConnectionManager _connectionManager; - private readonly IConfigurationManager _config; - - public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IConfigurationManager config) + public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager) { _dlnaManager = dlnaManager; _contentDirectory = contentDirectory; _connectionManager = connectionManager; - _config = config; } public object Get(GetDescriptionXml request) diff --git a/MediaBrowser.Api/IHasDtoOptions.cs b/MediaBrowser.Api/IHasDtoOptions.cs index f7fb57f014..7fe47c4a1e 100644 --- a/MediaBrowser.Api/IHasDtoOptions.cs +++ b/MediaBrowser.Api/IHasDtoOptions.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Model.Dto; +using MediaBrowser.Controller.Dto; using MediaBrowser.Model.Entities; using System; using System.Linq; @@ -28,17 +28,7 @@ namespace MediaBrowser.Api options.ImageTypeLimit = request.ImageTypeLimit.Value; } - if (string.IsNullOrWhiteSpace(request.EnableImageTypes)) - { - if (options.EnableImages) - { - // Get everything - options.ImageTypes = Enum.GetNames(typeof(ImageType)) - .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true)) - .ToList(); - } - } - else + if (!string.IsNullOrWhiteSpace(request.EnableImageTypes)) { options.ImageTypes = (request.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList(); } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 0e4ccf0b17..f2586b0438 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Images { @@ -668,26 +669,26 @@ namespace MediaBrowser.Api.Images { if (format == ImageFormat.Bmp) { - return Common.Net.MimeTypes.GetMimeType("i.bmp"); + return MimeTypes.GetMimeType("i.bmp"); } if (format == ImageFormat.Gif) { - return Common.Net.MimeTypes.GetMimeType("i.gif"); + return MimeTypes.GetMimeType("i.gif"); } if (format == ImageFormat.Jpg) { - return Common.Net.MimeTypes.GetMimeType("i.jpg"); + return MimeTypes.GetMimeType("i.jpg"); } if (format == ImageFormat.Png) { - return Common.Net.MimeTypes.GetMimeType("i.png"); + return MimeTypes.GetMimeType("i.png"); } if (format == ImageFormat.Webp) { - return Common.Net.MimeTypes.GetMimeType("i.webp"); + return MimeTypes.GetMimeType("i.webp"); } - return Common.Net.MimeTypes.GetMimeType(path); + return MimeTypes.GetMimeType(path); } /// <summary> diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 65c51befff..272dff3ecc 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -1,9 +1,14 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using ServiceStack; using System; using System.Collections.Generic; @@ -20,14 +25,165 @@ namespace MediaBrowser.Api public string ItemId { get; set; } } + [Route("/Items/{ItemId}/MetadataEditor", "GET", Summary = "Gets metadata editor info for an item")] + public class GetMetadataEditorInfo : IReturn<MetadataEditorInfo> + { + [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string ItemId { get; set; } + } + + [Route("/Items/{ItemId}/ContentType", "POST", Summary = "Updates an item's content type")] + public class UpdateItemContentType : IReturnVoid + { + [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string ItemId { get; set; } + + [ApiMember(Name = "ContentType", Description = "The content type of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string ContentType { get; set; } + } + [Authenticated] public class ItemUpdateService : BaseApiService { private readonly ILibraryManager _libraryManager; + private readonly IProviderManager _providerManager; + private readonly ILocalizationManager _localizationManager; + private readonly IServerConfigurationManager _config; - public ItemUpdateService(ILibraryManager libraryManager) + public ItemUpdateService(ILibraryManager libraryManager, IProviderManager providerManager, ILocalizationManager localizationManager, IServerConfigurationManager config) { _libraryManager = libraryManager; + _providerManager = providerManager; + _localizationManager = localizationManager; + _config = config; + } + + public object Get(GetMetadataEditorInfo request) + { + var item = _libraryManager.GetItemById(request.ItemId); + + var info = new MetadataEditorInfo + { + ParentalRatingOptions = _localizationManager.GetParentalRatings().ToList(), + ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToList(), + Countries = _localizationManager.GetCountries().ToList(), + Cultures = _localizationManager.GetCultures().ToList() + }; + + var locationType = item.LocationType; + if (locationType == LocationType.FileSystem || + locationType == LocationType.Offline) + { + if (!(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName)) + { + var collectionType = _libraryManager.GetInheritedContentType(item); + if (string.IsNullOrWhiteSpace(collectionType)) + { + info.ContentTypeOptions = GetContentTypeOptions(true); + info.ContentType = _libraryManager.GetContentType(item); + } + } + } + + return ToOptimizedResult(info); + } + + public void Post(UpdateItemContentType request) + { + var item = _libraryManager.GetItemById(request.ItemId); + var path = item.ContainingFolderPath; + + var types = _config.Configuration.ContentTypes + .Where(i => !string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (!string.IsNullOrWhiteSpace(request.ContentType)) + { + types.Add(new NameValuePair + { + Name = path, + Value = request.ContentType + }); + } + + _config.Configuration.ContentTypes = types.ToArray(); + _config.SaveConfiguration(); + } + + private List<NameValuePair> GetContentTypeOptions(bool isForItem) + { + var list = new List<NameValuePair>(); + + if (isForItem) + { + list.Add(new NameValuePair + { + Name = "FolderTypeInherit", + Value = "" + }); + } + + list.Add(new NameValuePair + { + Name = "FolderTypeMovies", + Value = "movies" + }); + list.Add(new NameValuePair + { + Name = "FolderTypeMusic", + Value = "music" + }); + list.Add(new NameValuePair + { + Name = "FolderTypeTvShows", + Value = "tvshows" + }); + + if (!isForItem) + { + list.Add(new NameValuePair + { + Name = "FolderTypeBooks", + Value = "books" + }); + list.Add(new NameValuePair + { + Name = "FolderTypeGames", + Value = "games" + }); + } + + list.Add(new NameValuePair + { + Name = "FolderTypeHomeVideos", + Value = "homevideos" + }); + list.Add(new NameValuePair + { + Name = "FolderTypeMusicVideos", + Value = "musicvideos" + }); + list.Add(new NameValuePair + { + Name = "FolderTypePhotos", + Value = "photos" + }); + + if (!isForItem) + { + list.Add(new NameValuePair + { + Name = "FolderTypeMixed", + Value = "" + }); + } + + foreach (var val in list) + { + val.Name = _localizationManager.GetLocalizedString(val.Name); + } + + return list; } public void Post(UpdateItem request) diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 5cb007f8fe..5e1619672f 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -272,16 +272,13 @@ namespace MediaBrowser.Api.Library items = items.Where(i => i.IsHidden == val).ToList(); } - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); + var dtoOptions = new DtoOptions(); var result = new ItemsResult { TotalRecordCount = items.Count, - Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields)).ToArray() + Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions)).ToArray() }; return ToOptimizedResult(result); @@ -347,10 +344,7 @@ namespace MediaBrowser.Api.Library var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null; - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); + var dtoOptions = new DtoOptions(); BaseItem parent = item.Parent; @@ -361,7 +355,7 @@ namespace MediaBrowser.Api.Library parent = TranslateParentItem(parent, user); } - baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, fields, user)); + baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user)); parent = parent.Parent; } @@ -473,20 +467,20 @@ namespace MediaBrowser.Api.Library var auth = _authContext.GetAuthorizationInfo(Request); var user = _userManager.GetUserById(auth.UserId); - if (item is Playlist) + if (item is Playlist || item is BoxSet) { // For now this is allowed if user can see the playlist } else if (item is ILiveTvRecording) { - if (!user.Configuration.EnableLiveTvManagement) + if (!user.Policy.EnableLiveTvManagement) { throw new UnauthorizedAccessException(); } } else { - if (!user.Configuration.EnableContentDeletion) + if (!user.Policy.EnableContentDeletion) { throw new UnauthorizedAccessException(); } @@ -583,11 +577,6 @@ namespace MediaBrowser.Api.Library item = item.Parent; } - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); - var themeSongIds = GetThemeSongIds(item); if (themeSongIds.Count == 0 && request.InheritFromParent) @@ -607,10 +596,12 @@ namespace MediaBrowser.Api.Library } } } - + + var dtoOptions = new DtoOptions(); + var dtos = themeSongIds.Select(_libraryManager.GetItemById) .OrderBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item)); + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); var items = dtos.ToArray(); @@ -651,11 +642,6 @@ namespace MediaBrowser.Api.Library item = item.Parent; } - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); - var themeVideoIds = GetThemeVideoIds(item); if (themeVideoIds.Count == 0 && request.InheritFromParent) @@ -681,9 +667,11 @@ namespace MediaBrowser.Api.Library } } + var dtoOptions = new DtoOptions(); + var dtos = themeVideoIds.Select(_libraryManager.GetItemById) .OrderBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item)); + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); var items = dtos.ToArray(); @@ -754,10 +742,7 @@ namespace MediaBrowser.Api.Library : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(id); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); + var dtoOptions = new DtoOptions(); var dtos = GetSoundtrackSongIds(item, inheritFromParent) .Select(_libraryManager.GetItemById) @@ -765,7 +750,7 @@ namespace MediaBrowser.Api.Library .SelectMany(i => i.RecursiveChildren) .OfType<Audio>() .OrderBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item)); + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); var items = dtos.ToArray(); diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 3afe72866d..f3dcf57e03 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -319,7 +319,7 @@ namespace MediaBrowser.Api.LiveTv throw new UnauthorizedAccessException("Anonymous live tv management is not allowed."); } - if (!user.Configuration.EnableLiveTvManagement) + if (!user.Policy.EnableLiveTvManagement) { throw new UnauthorizedAccessException("The current user does not have permission to manage live tv."); } diff --git a/MediaBrowser.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs index 346c085f8c..97c6cd87da 100644 --- a/MediaBrowser.Api/Movies/CollectionService.cs +++ b/MediaBrowser.Api/Movies/CollectionService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Collections; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using ServiceStack; using System; @@ -70,7 +71,9 @@ namespace MediaBrowser.Api.Movies }).ConfigureAwait(false); - var dto = _dtoService.GetBaseItemDto(item, new List<ItemFields>()); + var dtoOptions = new DtoOptions(); + + var dto = _dtoService.GetBaseItemDto(item, dtoOptions); return ToOptimizedResult(new CollectionCreationResult { diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index fc03ab466e..ba3c15a90b 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -157,7 +157,11 @@ namespace MediaBrowser.Api.Movies .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase) .ToList(); - var result = GetRecommendationCategories(user, listEligibleForCategories, listEligibleForSuggestion, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList()); + var dtoOptions = new DtoOptions(); + + dtoOptions.Fields = request.GetItemFields().ToList(); + + var result = GetRecommendationCategories(user, listEligibleForCategories, listEligibleForSuggestion, request.CategoryLimit, request.ItemLimit, dtoOptions); return ToOptimizedResult(result); } @@ -232,7 +236,7 @@ namespace MediaBrowser.Api.Movies return result; } - private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, List<BaseItem> allMoviesForCategories, List<BaseItem> allMovies, int categoryLimit, int itemLimit, List<ItemFields> fields) + private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, List<BaseItem> allMoviesForCategories, List<BaseItem> allMovies, int categoryLimit, int itemLimit, DtoOptions dtoOptions) { var categories = new List<RecommendationDto>(); @@ -282,11 +286,11 @@ namespace MediaBrowser.Api.Movies .OrderBy(i => Guid.NewGuid()) .ToList(); - var similarToRecentlyPlayed = GetSimilarTo(user, allMovies, recentlyPlayedMovies.Take(7).OrderBy(i => Guid.NewGuid()), itemLimit, fields, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator(); - var similarToLiked = GetSimilarTo(user, allMovies, likedMovies, itemLimit, fields, RecommendationType.SimilarToLikedItem).GetEnumerator(); + var similarToRecentlyPlayed = GetSimilarTo(user, allMovies, recentlyPlayedMovies.Take(7).OrderBy(i => Guid.NewGuid()), itemLimit, dtoOptions, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator(); + var similarToLiked = GetSimilarTo(user, allMovies, likedMovies, itemLimit, dtoOptions, RecommendationType.SimilarToLikedItem).GetEnumerator(); - var hasDirectorFromRecentlyPlayed = GetWithDirector(user, allMovies, recentDirectors, itemLimit, fields, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator(); - var hasActorFromRecentlyPlayed = GetWithActor(user, allMovies, recentActors, itemLimit, fields, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator(); + var hasDirectorFromRecentlyPlayed = GetWithDirector(user, allMovies, recentDirectors, itemLimit, dtoOptions, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator(); + var hasActorFromRecentlyPlayed = GetWithActor(user, allMovies, recentActors, itemLimit, dtoOptions, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator(); var categoryTypes = new List<IEnumerator<RecommendationDto>> { @@ -329,7 +333,7 @@ namespace MediaBrowser.Api.Movies return categories.OrderBy(i => i.RecommendationType).ThenBy(i => Guid.NewGuid()); } - private IEnumerable<RecommendationDto> GetWithDirector(User user, List<BaseItem> allMovies, IEnumerable<string> directors, int itemLimit, List<ItemFields> fields, RecommendationType type) + private IEnumerable<RecommendationDto> GetWithDirector(User user, List<BaseItem> allMovies, IEnumerable<string> directors, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { var userId = user.Id; @@ -347,13 +351,13 @@ namespace MediaBrowser.Api.Movies BaselineItemName = director, CategoryId = director.GetMD5().ToString("N"), RecommendationType = type, - Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray() + Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() }; } } } - private IEnumerable<RecommendationDto> GetWithActor(User user, List<BaseItem> allMovies, IEnumerable<string> names, int itemLimit, List<ItemFields> fields, RecommendationType type) + private IEnumerable<RecommendationDto> GetWithActor(User user, List<BaseItem> allMovies, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { var userId = user.Id; @@ -371,13 +375,13 @@ namespace MediaBrowser.Api.Movies BaselineItemName = name, CategoryId = name.GetMD5().ToString("N"), RecommendationType = type, - Items = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray() + Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() }; } } } - private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> allMovies, IEnumerable<BaseItem> baselineItems, int itemLimit, List<ItemFields> fields, RecommendationType type) + private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> allMovies, IEnumerable<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { var userId = user.Id; @@ -395,7 +399,7 @@ namespace MediaBrowser.Api.Movies BaselineItemName = item.Name, CategoryId = item.Id.ToString("N"), RecommendationType = type, - Items = similar.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray() + Items = similar.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() }; } } diff --git a/MediaBrowser.Api/NotificationsService.cs b/MediaBrowser.Api/NotificationsService.cs index 69f1f34891..5103d1b5cd 100644 --- a/MediaBrowser.Api/NotificationsService.cs +++ b/MediaBrowser.Api/NotificationsService.cs @@ -135,7 +135,7 @@ namespace MediaBrowser.Api Level = request.Level, Name = request.Name, Url = request.Url, - UserIds = _userManager.Users.Where(i => i.Configuration.IsAdministrator).Select(i => i.Id.ToString("N")).ToList() + UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList() }; await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 12ccfb6b1d..8662e64b4f 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -118,8 +119,8 @@ namespace MediaBrowser.Api.Playback /// <returns>System.String.</returns> private string GetOutputFilePath(StreamState state) { - var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath; - + var folder = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower()); + var outputFileExtension = GetOutputFileExtension(state); var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false); @@ -202,6 +203,10 @@ namespace MediaBrowser.Api.Playback { args += " -map -0:s"; } + else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) + { + args += " -map 1:0 -sn"; + } return args; } @@ -245,7 +250,7 @@ namespace MediaBrowser.Api.Playback protected EncodingQuality GetQualitySetting() { - var quality = ServerConfigurationManager.Configuration.MediaEncodingQuality; + var quality = ApiEntryPoint.Instance.GetEncodingOptions().EncodingQuality; if (quality == EncodingQuality.Auto) { @@ -273,7 +278,7 @@ namespace MediaBrowser.Api.Playback // Recommended per docs return Math.Max(Environment.ProcessorCount - 1, 2); } - + // Use more when this is true. -re will keep cpu usage under control if (state.ReadInputAtNativeFramerate) { @@ -302,6 +307,21 @@ namespace MediaBrowser.Api.Playback } } + protected string H264Encoder + { + get + { + var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder; + + if (!string.IsNullOrWhiteSpace(lib)) + { + return lib; + } + + return "libx264"; + } + } + /// <summary> /// Gets the video bitrate to specify on the command line /// </summary> @@ -318,7 +338,7 @@ namespace MediaBrowser.Api.Playback var qualitySetting = GetQualitySetting(); - if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(videoCodec, H264Encoder, StringComparison.OrdinalIgnoreCase)) { switch (qualitySetting) { @@ -442,7 +462,7 @@ namespace MediaBrowser.Api.Playback { if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) { - volParam = ",volume=" + ServerConfigurationManager.Configuration.DownMixAudioBoost.ToString(UsCulture); + volParam = ",volume=" + ApiEntryPoint.Instance.GetEncodingOptions().DownMixAudioBoost.ToString(UsCulture); } } @@ -651,9 +671,18 @@ namespace MediaBrowser.Api.Playback videoSizeParam = string.Format(",scale={0}:{1}", state.VideoStream.Width.Value.ToString(UsCulture), state.VideoStream.Height.Value.ToString(UsCulture)); } - return string.Format(" -filter_complex \"[0:{0}]format=yuva444p{3},lut=u=128:v=128:y=gammaval(.3)[sub] ; [0:{1}] [sub] overlay{2}\"", - state.SubtitleStream.Index, - state.VideoStream.Index, + var mapPrefix = state.SubtitleStream.IsExternal ? + 1 : + 0; + + var subtitleStreamIndex = state.SubtitleStream.IsExternal + ? 0 + : state.SubtitleStream.Index; + + return string.Format(" -filter_complex \"[{0}:{1}]format=yuva444p{4},lut=u=128:v=128:y=gammaval(.3)[sub] ; [0:{2}] [sub] overlay{3}\"", + mapPrefix.ToString(UsCulture), + subtitleStreamIndex.ToString(UsCulture), + state.VideoStream.Index.ToString(UsCulture), outputSizeParam, videoSizeParam); } @@ -700,7 +729,8 @@ namespace MediaBrowser.Api.Playback return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value); } - return request.MaxAudioChannels.Value; + // If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels + return Math.Min(request.MaxAudioChannels.Value, 5); } return request.AudioChannels; @@ -761,7 +791,7 @@ namespace MediaBrowser.Api.Playback { if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) { - return "libx264"; + return H264Encoder; } if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase)) { @@ -798,6 +828,21 @@ namespace MediaBrowser.Api.Playback /// <returns>System.String.</returns> protected string GetInputArgument(string transcodingJobId, StreamState state) { + var arg = "-i " + GetInputPathArgument(transcodingJobId, state); + + if (state.SubtitleStream != null) + { + if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) + { + arg += " -i \"" + state.SubtitleStream.Path + "\""; + } + } + + return arg; + } + + private string GetInputPathArgument(string transcodingJobId, StreamState state) + { if (state.InputProtocol == MediaProtocol.File && state.RunTimeTicks.HasValue && state.VideoType == VideoType.VideoFile && @@ -868,7 +913,7 @@ namespace MediaBrowser.Api.Playback state.InputProtocol = streamInfo.Protocol; await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); - + AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl); checkCodecs = true; } @@ -898,8 +943,8 @@ namespace MediaBrowser.Api.Playback /// <param name="cancellationTokenSource">The cancellation token source.</param> /// <param name="workingDirectory">The working directory.</param> /// <returns>Task.</returns> - protected async Task<TranscodingJob> StartFfMpeg(StreamState state, - string outputPath, + protected async Task<TranscodingJob> StartFfMpeg(StreamState state, + string outputPath, CancellationTokenSource cancellationTokenSource, string workingDirectory = null) { @@ -910,7 +955,7 @@ namespace MediaBrowser.Api.Playback var transcodingId = Guid.NewGuid().ToString("N"); var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true); - if (ServerConfigurationManager.Configuration.EnableDebugEncodingLogging) + if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging) { commandLineArgs = "-loglevel debug " + commandLineArgs; } @@ -1088,7 +1133,7 @@ namespace MediaBrowser.Api.Playback if (scale.HasValue) { long val; - + if (long.TryParse(size, NumberStyles.Any, UsCulture, out val)) { bytesTranscoded = val * scale.Value; @@ -1562,9 +1607,6 @@ namespace MediaBrowser.Api.Playback mediaStreams = new List<MediaStream>(); state.DeInterlace = true; - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; @@ -1630,7 +1672,7 @@ namespace MediaBrowser.Api.Playback if (string.IsNullOrEmpty(container)) { - container = request.Static ? + container = request.Static ? state.InputContainer : (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.'); } @@ -1696,9 +1738,16 @@ namespace MediaBrowser.Api.Playback state.InputFileSize = mediaSource.Size; state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + if (state.ReadInputAtNativeFramerate) + { + state.OutputAudioSync = "1000"; + state.InputVideoSync = "-1"; + state.InputAudioSync = "1"; + } + AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl); } - + private void AttachMediaStreamInfo(StreamState state, List<MediaStream> mediaStreams, VideoStreamRequest videoRequest, diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index c963636fdf..8a33a88f28 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.Net; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -6,7 +7,6 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using System; using System.Collections.Generic; @@ -14,6 +14,7 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; namespace MediaBrowser.Api.Playback.Hls { @@ -119,11 +120,7 @@ namespace MediaBrowser.Api.Playback.Hls if (isLive) { - //var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); - - //file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file); - - return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite); + return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } var audioBitrate = state.OutputAudioBitrate ?? 0; @@ -144,6 +141,22 @@ namespace MediaBrowser.Api.Playback.Hls return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } + private string GetLivePlaylistText(string path, int segmentLength) + { + using (var stream = FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (var reader = new StreamReader(stream)) + { + var text = reader.ReadToEnd(); + + var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture) + Environment.NewLine + "#EXT-X-ALLOW-CACHE:NO"; + + // ffmpeg pads the reported length by a full second + return text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase); + } + } + } + private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate) { var builder = new StringBuilder(); @@ -227,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls "hls/" + Path.GetFileNameWithoutExtension(outputPath)); } - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", + var args = string.Format("{0} {1} {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", itsOffset, inputModifier, GetInputArgument(transcodingJobId, state), diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index fe3bd12fb1..86866bdf5e 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -20,6 +20,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Playback.Hls { @@ -387,7 +388,7 @@ namespace MediaBrowser.Api.Playback.Hls playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate); } - return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); + return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } private string GetMasterPlaylistFileText(StreamState state, int totalBitrate) @@ -603,7 +604,7 @@ namespace MediaBrowser.Api.Playback.Hls var playlistText = builder.ToString(); - return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); + return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } protected override string GetAudioArguments(StreamState state) @@ -640,10 +641,19 @@ namespace MediaBrowser.Api.Playback.Hls { var codec = state.OutputVideoCodec; + var args = "-codec:v:0 " + codec; + + if (state.EnableMpegtsM2TsMode) + { + args += " -mpegts_m2ts_mode 1"; + } + // See if we can save come cpu cycles by avoiding encoding - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return state.VideoStream != null && IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? + args + " -bsf:v h264_mp4toannexb" : + args; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", @@ -651,7 +661,7 @@ namespace MediaBrowser.Api.Playback.Hls var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg; + args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; // Add resolution params, if specified if (!hasGraphicalSubs) @@ -677,7 +687,7 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + var args = string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(transcodingJobId, state), threads, diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 2263a2b371..14045b3a56 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller; +using MediaBrowser.Model.Dlna; using ServiceStack; using System; using System.IO; @@ -65,7 +66,7 @@ namespace MediaBrowser.Api.Playback.Hls { var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); - file = Path.Combine(_appPaths.TranscodingTempPath, file); + file = Path.Combine(_appPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file); return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); } @@ -84,7 +85,7 @@ namespace MediaBrowser.Api.Playback.Hls { var file = request.SegmentId + Path.GetExtension(Request.PathInfo); - file = Path.Combine(_appPaths.TranscodingTempPath, file); + file = Path.Combine(_appPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file); return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); } diff --git a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs index 260a4c467e..87e2eedcf5 100644 --- a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs @@ -18,6 +18,7 @@ using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; +using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Playback.Hls { @@ -97,7 +98,7 @@ namespace MediaBrowser.Api.Playback.Hls playlistText = GetManifestText(state); } - return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.mpd"), new Dictionary<string, string>()); + return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.mpd"), new Dictionary<string, string>()); } private string GetManifestText(StreamState state) @@ -583,10 +584,19 @@ namespace MediaBrowser.Api.Playback.Hls { var codec = state.OutputVideoCodec; + var args = "-codec:v:0 " + codec; + + if (state.EnableMpegtsM2TsMode) + { + args += " -mpegts_m2ts_mode 1"; + } + // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? + args + " -bsf:v h264_mp4toannexb" : + args; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", @@ -594,7 +604,7 @@ namespace MediaBrowser.Api.Playback.Hls var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg; + args+= " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; args += " -r 24 -g 24"; @@ -627,7 +637,7 @@ namespace MediaBrowser.Api.Playback.Hls var segmentFilename = Path.GetFileNameWithoutExtension(outputPath) + "%03d" + GetSegmentFileExtension(state); - var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -copyts {5} -f ssegment -segment_time {6} -segment_list_size {8} -segment_list \"{9}\" {10}", + var args = string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts {5} -f ssegment -segment_time {6} -segment_list_size {8} -segment_list \"{9}\" {10}", inputModifier, GetInputArgument(transcodingJobId, state), threads, diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 06fa4065c2..d786b51b3a 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -5,12 +5,11 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.IO; using ServiceStack; using System; using System.IO; -using System.Linq; -using System.Threading.Tasks; namespace MediaBrowser.Api.Playback.Hls { @@ -72,7 +71,7 @@ namespace MediaBrowser.Api.Playback.Hls { var file = request.SegmentId + Path.GetExtension(Request.PathInfo); - file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file); + file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file); return ResultFactory.GetStaticFileResult(Request, file); } @@ -136,18 +135,27 @@ namespace MediaBrowser.Api.Playback.Hls { var codec = state.OutputVideoCodec; + var args = "-codec:v:0 " + codec; + + if (state.EnableMpegtsM2TsMode) + { + args += " -mpegts_m2ts_mode 1"; + } + // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return state.VideoStream != null && IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? + args + " -bsf:v h264_mp4toannexb" : + args; } - + var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", state.SegmentLength.ToString(UsCulture)); var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg; + args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; // Add resolution params, if specified if (!hasGraphicalSubs) diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index ae592c428a..725526ecdc 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -82,7 +82,7 @@ namespace MediaBrowser.Api.Playback.Progressive var inputModifier = GetInputModifier(state); - return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"", + return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"", inputModifier, GetInputArgument(transcodingJobId, state), threads, diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index a64866d68a..5ef72a4952 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback.Progressive var inputModifier = GetInputModifier(state); - return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"", + return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"", inputModifier, GetInputArgument(transcodingJobId, state), keyFrame, @@ -124,7 +124,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// <returns>System.String.</returns> private string GetVideoArguments(StreamState state, string codec) { - var args = "-vcodec " + codec; + var args = "-codec:v:0 " + codec; if (state.EnableMpegtsM2TsMode) { @@ -134,7 +134,9 @@ namespace MediaBrowser.Api.Playback.Progressive // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf:v h264_mp4toannexb" : args; + return state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) ? + args + " -bsf:v h264_mp4toannexb" : + args; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", @@ -182,13 +184,13 @@ namespace MediaBrowser.Api.Playback.Progressive // Get the output codec name var codec = state.OutputAudioCodec; + var args = "-codec:a:0 " + codec; + if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return "-acodec copy"; + return args; } - var args = "-acodec " + codec; - // Add the number of audio channels var channels = state.OutputAudioChannels; diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d26259a3ae..40e765f1ae 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading; +using MediaBrowser.Model.Net; namespace MediaBrowser.Api.Playback { diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index f34c53b168..cb8f91ab6e 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index e6b525e534..25130776ee 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index d2881893ba..59b8e85c22 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; @@ -299,6 +300,7 @@ namespace MediaBrowser.Api.Session private readonly IUserManager _userManager; private readonly IAuthorizationContext _authContext; private readonly IAuthenticationRepository _authRepo; + private readonly IDeviceManager _deviceManager; /// <summary> /// Initializes a new instance of the <see cref="SessionsService" /> class. @@ -307,12 +309,13 @@ namespace MediaBrowser.Api.Session /// <param name="userManager">The user manager.</param> /// <param name="authContext">The authentication context.</param> /// <param name="authRepo">The authentication repo.</param> - public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo) + public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo, IDeviceManager deviceManager) { _sessionManager = sessionManager; _userManager = userManager; _authContext = authContext; _authRepo = authRepo; + _deviceManager = deviceManager; } public void Delete(RevokeKey request) @@ -373,15 +376,30 @@ namespace MediaBrowser.Api.Session var user = _userManager.GetUserById(request.ControllableByUserId.Value); - if (!user.Configuration.EnableRemoteControlOfOtherUsers) + if (!user.Policy.EnableRemoteControlOfOtherUsers) { result = result.Where(i => i.ContainsUser(request.ControllableByUserId.Value)); } - if (!user.Configuration.EnableSharedDeviceControl) + if (!user.Policy.EnableSharedDeviceControl) { result = result.Where(i => !i.UserId.HasValue); } + + result = result.Where(i => + { + var deviceId = i.DeviceId; + + if (!string.IsNullOrWhiteSpace(deviceId)) + { + if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), deviceId)) + { + return false; + } + } + + return true; + }); } return ToOptimizedResult(result.Select(_sessionManager.GetSessionInfoDto).ToList()); diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index a2a93d50e3..32e6ba076d 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Subtitles { @@ -175,7 +176,7 @@ namespace MediaBrowser.Api.Subtitles builder.AppendLine("#EXT-X-ENDLIST"); - return ResultFactory.GetResult(builder.ToString(), Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); + return ResultFactory.GetResult(builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } public object Get(GetSubtitle request) @@ -199,7 +200,7 @@ namespace MediaBrowser.Api.Subtitles var stream = GetSubtitles(request).Result; - return ResultFactory.GetResult(stream, Common.Net.MimeTypes.GetMimeType("file." + request.Format)); + return ResultFactory.GetResult(stream, MimeTypes.GetMimeType("file." + request.Format)); } private async Task<Stream> GetSubtitles(GetSubtitle request) @@ -240,7 +241,7 @@ namespace MediaBrowser.Api.Subtitles { var result = _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).Result; - return ResultFactory.GetResult(result.Stream, Common.Net.MimeTypes.GetMimeType("file." + result.Format)); + return ResultFactory.GetResult(result.Stream, MimeTypes.GetMimeType("file." + result.Format)); } public void Post(DownloadRemoteSubtitles request) diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs index cefb0e46e8..a7467c12f1 100644 --- a/MediaBrowser.Api/Sync/SyncService.cs +++ b/MediaBrowser.Api/Sync/SyncService.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Sync; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; +using MediaBrowser.Model.Users; using ServiceStack; using System; using System.Collections.Generic; @@ -27,6 +28,11 @@ namespace MediaBrowser.Api.Sync public string Id { get; set; } } + [Route("/Sync/Jobs/{Id}", "POST", Summary = "Updates a sync job.")] + public class UpdateSyncJob : SyncJob, IReturnVoid + { + } + [Route("/Sync/JobItems", "GET", Summary = "Gets sync job items.")] public class GetSyncJobItems : SyncJobItemQuery, IReturn<QueryResult<SyncJobItem>> { @@ -55,8 +61,14 @@ namespace MediaBrowser.Api.Sync [ApiMember(Name = "UserId", Description = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] public string UserId { get; set; } - [ApiMember(Name = "ItemIds", Description = "ItemIds", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "ItemIds", Description = "ItemIds", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string ItemIds { get; set; } + + [ApiMember(Name = "ParentId", Description = "ParentId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ParentId { get; set; } + + [ApiMember(Name = "Category", Description = "Category", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public SyncCategory? Category { get; set; } } [Route("/Sync/JobItems/{Id}/Transferred", "POST", Summary = "Reports that a sync job item has successfully been transferred.")] @@ -73,6 +85,23 @@ namespace MediaBrowser.Api.Sync public string Id { get; set; } } + [Route("/Sync/OfflineActions", "POST", Summary = "Reports an action that occurred while offline.")] + public class ReportOfflineActions : List<UserAction>, IReturnVoid + { + } + + [Route("/Sync/Items/Ready", "GET", Summary = "Gets ready to download sync items.")] + public class GetReadySyncItems : IReturn<List<SyncedItem>> + { + [ApiMember(Name = "TargetId", Description = "TargetId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string TargetId { get; set; } + } + + [Route("/Sync/Data", "POST", Summary = "Syncs data between device and server")] + public class SyncData : SyncDataRequest, IReturn<SyncDataResponse> + { + } + [Authenticated] public class SyncService : BaseApiService { @@ -94,9 +123,9 @@ namespace MediaBrowser.Api.Sync return ToOptimizedResult(result); } - public object Get(GetSyncJobs request) + public async Task<object> Get(GetSyncJobs request) { - var result = _syncManager.GetJobs(request); + var result = await _syncManager.GetJobs(request).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -155,21 +184,64 @@ namespace MediaBrowser.Api.Sync result.Targets = _syncManager.GetSyncTargets(request.UserId) .ToList(); - var dtos = request.ItemIds.Split(',') - .Select(_libraryManager.GetItemById) - .Where(i => i != null) - .Select(i => _dtoService.GetBaseItemDto(i, new DtoOptions + if (request.Category.HasValue) + { + result.Options = SyncHelper.GetSyncOptions(request.Category.Value); + } + else + { + var dtoOptions = new DtoOptions { Fields = new List<ItemFields> { ItemFields.SyncInfo } - })) - .ToList(); + }; - result.Options = SyncHelper.GetSyncOptions(dtos); + var dtos = request.ItemIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(_libraryManager.GetItemById) + .Where(i => i != null) + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions)) + .ToList(); + + result.Options = SyncHelper.GetSyncOptions(dtos); + } return ToOptimizedResult(result); } + + public void Post(ReportOfflineActions request) + { + var task = PostAsync(request); + + Task.WaitAll(task); + } + + public async Task PostAsync(ReportOfflineActions request) + { + foreach (var action in request) + { + await _syncManager.ReportOfflineAction(action).ConfigureAwait(false); + } + } + + public object Get(GetReadySyncItems request) + { + return ToOptimizedResult(_syncManager.GetReadySyncItems(request.TargetId)); + } + + public async Task<object> Post(SyncData request) + { + var response = await _syncManager.SyncData(request).ConfigureAwait(false); + + return ToOptimizedResult(response); + } + + public void Post(UpdateSyncJob request) + { + var task = _syncManager.UpdateJob(request); + + Task.WaitAll(task); + } } } diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index 4629b2a8ce..a951cd3d68 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -1,5 +1,5 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Activity; +using MediaBrowser.Controller.Activity; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; diff --git a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs index c20cef3b38..49a3e3291e 100644 --- a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs +++ b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; using MediaBrowser.Model.System; using System.Threading.Tasks; diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 2299b2b1aa..1284232388 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -83,17 +83,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetArtist(request.Name, LibraryManager); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs index adc21c362a..2f1c73acef 100644 --- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs @@ -69,17 +69,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetGameGenre(request.Name, LibraryManager); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 7b6b18c9ca..db0b0fe612 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -74,17 +74,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetGenre(request.Name, LibraryManager); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index 1088ada023..f8575aa7c7 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -69,17 +69,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetMusicGenre(request.Name, LibraryManager); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index dd80801eea..33ce6cd803 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -86,17 +86,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetPerson(request.Name, LibraryManager); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index 2024d779b7..272134b70f 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -73,17 +73,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = GetStudio(request.Name, LibraryManager); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 040cad436e..45a330a501 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -407,9 +407,6 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); - var query = new UserViewQuery { UserId = request.UserId @@ -423,7 +420,9 @@ namespace MediaBrowser.Api.UserLibrary var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false); - var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + var dtoOptions = new DtoOptions(); + + var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) .ToArray(); var result = new QueryResult<BaseItemDto> @@ -443,14 +442,16 @@ namespace MediaBrowser.Api.UserLibrary user.RootFolder : _libraryManager.GetItemById(request.Id); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); - var series = item as Series; // Get them from the child tree if (series != null) { + var dtoOptions = new DtoOptions(); + + // Avoid implicitly captured closure + var currentUser = user; + var dtos = series .GetRecursiveChildren() .Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) @@ -468,7 +469,7 @@ namespace MediaBrowser.Api.UserLibrary return DateTime.MinValue; }) .ThenBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)); + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, currentUser)); return dtos.ToList(); } @@ -478,10 +479,12 @@ namespace MediaBrowser.Api.UserLibrary // Get them from the db if (movie != null) { + var dtoOptions = new DtoOptions(); + var dtos = movie.SpecialFeatureIds .Select(_libraryManager.GetItemById) .OrderBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item)); + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); return dtos.ToList(); } @@ -507,9 +510,6 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); - var trailerIds = new List<Guid>(); var hasTrailers = item as IHasTrailers; @@ -518,10 +518,12 @@ namespace MediaBrowser.Api.UserLibrary trailerIds = hasTrailers.GetTrailerIds(); } + var dtoOptions = new DtoOptions(); + var dtos = trailerIds .Select(_libraryManager.GetItemById) .OrderBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item)); + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); return dtos.ToList(); } @@ -537,10 +539,9 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); + var dtoOptions = new DtoOptions(); - var result = _dtoService.GetBaseItemDto(item, fields, user); + var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); return ToOptimizedSerializedResultUsingCache(result); } @@ -556,10 +557,9 @@ namespace MediaBrowser.Api.UserLibrary var item = user.RootFolder; - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); + var dtoOptions = new DtoOptions(); - var result = _dtoService.GetBaseItemDto(item, fields, user); + var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); return ToOptimizedSerializedResultUsingCache(result); } @@ -577,12 +577,9 @@ namespace MediaBrowser.Api.UserLibrary var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); + var dtoOptions = new DtoOptions(); - var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) .ToArray(); var result = new ItemsResult diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index 2b300f9003..b1b0aeb636 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -73,17 +73,16 @@ namespace MediaBrowser.Api.UserLibrary { var item = LibraryManager.GetYear(request.Year); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + var dtoOptions = new DtoOptions(); if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - return DtoService.GetBaseItemDto(item, fields.ToList(), user); + return DtoService.GetBaseItemDto(item, dtoOptions, user); } - return DtoService.GetBaseItemDto(item, fields.ToList()); + return DtoService.GetBaseItemDto(item, dtoOptions); } /// <summary> diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 4b720c775c..51a7584b8d 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -1,10 +1,12 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Connect; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Users; @@ -51,7 +53,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid Id { get; set; } + public string Id { get; set; } } /// <summary> @@ -66,7 +68,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public Guid Id { get; set; } + public string Id { get; set; } } /// <summary> @@ -80,7 +82,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public Guid Id { get; set; } + public string Id { get; set; } /// <summary> /// Gets or sets the password. @@ -125,7 +127,7 @@ namespace MediaBrowser.Api /// Gets or sets the id. /// </summary> /// <value>The id.</value> - public Guid Id { get; set; } + public string Id { get; set; } /// <summary> /// Gets or sets the password. @@ -156,6 +158,28 @@ namespace MediaBrowser.Api } /// <summary> + /// Class UpdateUser + /// </summary> + [Route("/Users/{Id}/Policy", "POST", Summary = "Updates a user policy")] + [Authenticated(Roles = "admin")] + public class UpdateUserPolicy : UserPolicy, IReturnVoid + { + [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + } + + /// <summary> + /// Class UpdateUser + /// </summary> + [Route("/Users/{Id}/Configuration", "POST", Summary = "Updates a user configuration")] + [Authenticated] + public class UpdateUserConfiguration : UserConfiguration, IReturnVoid + { + [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + } + + /// <summary> /// Class CreateUser /// </summary> [Route("/Users/New", "POST", Summary = "Creates a user")] @@ -193,22 +217,18 @@ namespace MediaBrowser.Api private readonly ISessionManager _sessionMananger; private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; + private readonly IDeviceManager _deviceManager; public IAuthorizationContext AuthorizationContext { get; set; } - /// <summary> - /// Initializes a new instance of the <see cref="UserService" /> class. - /// </summary> - /// <param name="userManager">The user manager.</param> - /// <param name="dtoService">The dto service.</param> - /// <param name="sessionMananger">The session mananger.</param> - public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager) + public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager) { _userManager = userManager; _dtoService = dtoService; _sessionMananger = sessionMananger; _config = config; _networkManager = networkManager; + _deviceManager = deviceManager; } public object Get(GetPublicUsers request) @@ -222,18 +242,12 @@ namespace MediaBrowser.Api }); } - // TODO: Uncomment once clients can handle an empty user list (and below) - //if (Request.IsLocal || IsInLocalNetwork(Request.RemoteIp)) + return Get(new GetUsers { - return Get(new GetUsers - { - IsHidden = false, - IsDisabled = false - }); - } + IsHidden = false, + IsDisabled = false - //// Return empty when external - //return ToOptimizedResult(new List<UserDto>()); + }, true); } /// <summary> @@ -243,24 +257,38 @@ namespace MediaBrowser.Api /// <returns>System.Object.</returns> public object Get(GetUsers request) { + return Get(request, false); + } + + private object Get(GetUsers request, bool filterByDevice) + { var users = _userManager.Users; if (request.IsDisabled.HasValue) { - users = users.Where(i => i.Configuration.IsDisabled == request.IsDisabled.Value); + users = users.Where(i => i.Policy.IsDisabled == request.IsDisabled.Value); } if (request.IsHidden.HasValue) { - users = users.Where(i => i.Configuration.IsHidden == request.IsHidden.Value); + users = users.Where(i => i.Policy.IsHidden == request.IsHidden.Value); } if (request.IsGuest.HasValue) { - users = users.Where(i => (i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest) == request.IsGuest.Value); } + if (filterByDevice) + { + var deviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId; + + if (!string.IsNullOrWhiteSpace(deviceId)) + { + users = users.Where(i => _deviceManager.CanAccessDevice(i.Id.ToString("N"), deviceId)); + } + } + var result = users .OrderBy(u => u.Name) .Select(i => _userManager.GetUserDto(i, Request.RemoteIp)) @@ -428,39 +456,13 @@ namespace MediaBrowser.Api var user = _userManager.GetUserById(id); - // If removing admin access - if (!dtoUser.Configuration.IsAdministrator && user.Configuration.IsAdministrator) - { - if (_userManager.Users.Count(i => i.Configuration.IsAdministrator) == 1) - { - throw new ArgumentException("There must be at least one user in the system with administrative access."); - } - } - - // If disabling - if (dtoUser.Configuration.IsDisabled && user.Configuration.IsAdministrator) - { - throw new ArgumentException("Administrators cannot be disabled."); - } - - // If disabling - if (dtoUser.Configuration.IsDisabled && !user.Configuration.IsDisabled) - { - if (_userManager.Users.Count(i => !i.Configuration.IsDisabled) == 1) - { - throw new ArgumentException("There must be at least one enabled user in the system."); - } - - await _sessionMananger.RevokeUserTokens(user.Id.ToString("N")).ConfigureAwait(false); - } - var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ? _userManager.UpdateUser(user) : _userManager.RenameUser(user, dtoUser.Name); await task.ConfigureAwait(false); - user.UpdateConfiguration(dtoUser.Configuration); + await _userManager.UpdateConfiguration(dtoUser.Id, dtoUser.Configuration); } /// <summary> @@ -495,5 +497,51 @@ namespace MediaBrowser.Api { return _userManager.RedeemPasswordResetPin(request.Pin); } + + public void Post(UpdateUserConfiguration request) + { + var task = _userManager.UpdateConfiguration(request.Id, request); + + Task.WaitAll(task); + } + + public void Post(UpdateUserPolicy request) + { + var task = UpdateUserPolicy(request); + Task.WaitAll(task); + } + + private async Task UpdateUserPolicy(UpdateUserPolicy request) + { + var user = _userManager.GetUserById(request.Id); + + // If removing admin access + if (!request.IsAdministrator && user.Policy.IsAdministrator) + { + if (_userManager.Users.Count(i => i.Policy.IsAdministrator) == 1) + { + throw new ArgumentException("There must be at least one user in the system with administrative access."); + } + } + + // If disabling + if (request.IsDisabled && user.Policy.IsAdministrator) + { + throw new ArgumentException("Administrators cannot be disabled."); + } + + // If disabling + if (request.IsDisabled && !user.Policy.IsDisabled) + { + if (_userManager.Users.Count(i => !i.Policy.IsDisabled) == 1) + { + throw new ArgumentException("There must be at least one enabled user in the system."); + } + + await _sessionMananger.RevokeUserTokens(user.Id.ToString("N")).ConfigureAwait(false); + } + + await _userManager.UpdateUserPolicy(request.Id, request).ConfigureAwait(false); + } } } diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index fbf9cec2e7..28db46b986 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using ServiceStack; using System; @@ -79,15 +80,12 @@ namespace MediaBrowser.Api : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToList(); + var dtoOptions = new DtoOptions(); var video = (Video)item; var items = video.GetAdditionalParts() - .Select(i => _dtoService.GetBaseItemDto(i, fields, user, video)) + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, video)) .ToArray(); var result = new ItemsResult @@ -114,11 +112,11 @@ namespace MediaBrowser.Api { link.PrimaryVersionId = null; - await link.UpdateToRepository(ItemUpdateType.MetadataDownload, CancellationToken.None).ConfigureAwait(false); + await link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } video.LinkedAlternateVersions.Clear(); - await video.UpdateToRepository(ItemUpdateType.MetadataDownload, CancellationToken.None).ConfigureAwait(false); + await video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } public void Post(MergeVersions request) @@ -186,7 +184,7 @@ namespace MediaBrowser.Api { item.PrimaryVersionId = primaryVersion.Id; - await item.UpdateToRepository(ItemUpdateType.MetadataDownload, CancellationToken.None).ConfigureAwait(false); + await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); primaryVersion.LinkedAlternateVersions.Add(new LinkedChild { @@ -195,7 +193,7 @@ namespace MediaBrowser.Api }); } - await primaryVersion.UpdateToRepository(ItemUpdateType.MetadataDownload, CancellationToken.None).ConfigureAwait(false); + await primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } } } |
