diff options
Diffstat (limited to 'MediaBrowser.Api')
26 files changed, 244 insertions, 237 deletions
diff --git a/MediaBrowser.Api/AppThemeService.cs b/MediaBrowser.Api/AppThemeService.cs deleted file mode 100644 index 87084e415..000000000 --- a/MediaBrowser.Api/AppThemeService.cs +++ /dev/null @@ -1,100 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Themes; -using MediaBrowser.Model.Themes; -using ServiceStack; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Api -{ - [Route("/Themes", "GET", Summary = "Gets a list of available themes for an app")] - public class GetAppThemes : IReturn<List<AppThemeInfo>> - { - [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string App { get; set; } - } - - [Route("/Themes/Info", "GET", Summary = "Gets an app theme")] - public class GetAppTheme : IReturn<AppTheme> - { - [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string App { get; set; } - - [ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Name { get; set; } - } - - [Route("/Themes/Images", "GET", Summary = "Gets an app theme")] - public class GetAppThemeImage - { - [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string App { get; set; } - - [ApiMember(Name = "Theme", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Theme { get; set; } - - [ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Name { get; set; } - - [ApiMember(Name = "CacheTag", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string CacheTag { get; set; } - } - - [Route("/Themes", "POST", Summary = "Saves a theme")] - public class SaveTheme : AppTheme, IReturnVoid - { - } - - [Authenticated] - public class AppThemeService : BaseApiService - { - private readonly IAppThemeManager _themeManager; - private readonly IFileSystem _fileSystem; - - public AppThemeService(IAppThemeManager themeManager, IFileSystem fileSystem) - { - _themeManager = themeManager; - _fileSystem = fileSystem; - } - - public object Get(GetAppThemes request) - { - var result = _themeManager.GetThemes(request.App).ToList(); - - return ToOptimizedResult(result); - } - - public object Get(GetAppTheme request) - { - var result = _themeManager.GetTheme(request.App, request.Name); - - return ToOptimizedResult(result); - } - - public void Post(SaveTheme request) - { - _themeManager.SaveTheme(request); - } - - public object Get(GetAppThemeImage request) - { - var info = _themeManager.GetImageImageInfo(request.App, request.Theme, request.Name); - - var cacheGuid = new Guid(info.CacheTag); - - TimeSpan? cacheDuration = null; - - if (!string.IsNullOrEmpty(request.CacheTag) && cacheGuid == new Guid(request.CacheTag)) - { - cacheDuration = TimeSpan.FromDays(365); - } - - var contentType = MimeTypes.GetMimeType(info.Path); - - return ResultFactory.GetCachedResult(Request, cacheGuid, null, cacheDuration, () => _fileSystem.GetFileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read), contentType); - } - } -} diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index ab0a4a4b2..759eea353 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Devices; +using System; +using System.Linq; +using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Devices; using MediaBrowser.Model.Querying; @@ -128,15 +130,32 @@ namespace MediaBrowser.Api.Devices var id = Request.QueryString["Id"]; var name = Request.QueryString["Name"]; - var task = _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo + if (Request.ContentType.IndexOf("multi", StringComparison.OrdinalIgnoreCase) == -1) { - MimeType = Request.ContentType, - Album = album, - Name = name, - Id = id - }); - - Task.WaitAll(task); + var task = _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo + { + MimeType = Request.ContentType, + Album = album, + Name = name, + Id = id + }); + + Task.WaitAll(task); + } + else + { + var file = Request.Files.First(); + + var task = _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo + { + MimeType = file.ContentType, + Album = album, + Name = name, + Id = id + }); + + Task.WaitAll(task); + } } } } diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs index 4f5e2ab25..4e7b1a7d5 100644 --- a/MediaBrowser.Api/Dlna/DlnaServerService.cs +++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs @@ -108,7 +108,6 @@ namespace MediaBrowser.Api.Dlna private readonly IConnectionManager _connectionManager; private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar; - // TODO: Add utf-8 private const string XMLContentType = "text/xml; charset=UTF-8"; public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar) diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index 457b4709b..11a0eec23 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; @@ -96,13 +97,14 @@ namespace MediaBrowser.Api /// The _network manager /// </summary> private readonly INetworkManager _networkManager; + private IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="EnvironmentService" /> class. /// </summary> /// <param name="networkManager">The network manager.</param> /// <exception cref="System.ArgumentNullException">networkManager</exception> - public EnvironmentService(INetworkManager networkManager) + public EnvironmentService(INetworkManager networkManager, IFileSystem fileSystem) { if (networkManager == null) { @@ -110,6 +112,7 @@ namespace MediaBrowser.Api } _networkManager = networkManager; + _fileSystem = fileSystem; } /// <summary> @@ -135,7 +138,15 @@ namespace MediaBrowser.Api return ToOptimizedSerializedResultUsingCache(GetNetworkShares(path).OrderBy(i => i.Path).ToList()); } - return ToOptimizedSerializedResultUsingCache(GetFileSystemEntries(request).OrderBy(i => i.Path).ToList()); + try + { + return ToOptimizedSerializedResultUsingCache(GetFileSystemEntries(request).OrderBy(i => i.Path).ToList()); + } + catch (UnauthorizedAccessException) + { + // Don't throw the original UnauthorizedAccessException because it will cause a 401 response + throw new ApplicationException("Access to the path " + request.Path + " is denied."); + } } public object Get(GetNetworkShares request) @@ -222,8 +233,7 @@ namespace MediaBrowser.Api private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request) { // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks) - var entries = new DirectoryInfo(request.Path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly) - .Concat<FileSystemInfo>(new DirectoryInfo(request.Path).EnumerateFiles("*", SearchOption.TopDirectoryOnly)).Where(i => + var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i => { if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden)) { diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index b4d5a9949..a6e1af516 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -130,8 +130,7 @@ namespace MediaBrowser.Api.Images { try { - return new DirectoryInfo(path) - .GetFiles("*", SearchOption.AllDirectories) + return _fileSystem.GetFiles(path) .Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal)) .Select(i => new ImageByNameInfo { @@ -184,7 +183,7 @@ namespace MediaBrowser.Api.Images var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList(); - var path = paths.FirstOrDefault(File.Exists) ?? paths.FirstOrDefault(); + var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault(); return ToStaticFileResult(path); } @@ -198,11 +197,11 @@ namespace MediaBrowser.Api.Images { var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme); - if (Directory.Exists(themeFolder)) + if (_fileSystem.DirectoryExists(themeFolder)) { var path = BaseItem.SupportedImageExtensions .Select(i => Path.Combine(themeFolder, request.Name + i)) - .FirstOrDefault(File.Exists); + .FirstOrDefault(_fileSystem.FileExists); if (!string.IsNullOrEmpty(path)) { @@ -212,14 +211,14 @@ namespace MediaBrowser.Api.Images var allFolder = Path.Combine(_appPaths.RatingsPath, "all"); - if (Directory.Exists(allFolder)) + if (_fileSystem.DirectoryExists(allFolder)) { // Avoid implicitly captured closure var currentRequest = request; var path = BaseItem.SupportedImageExtensions .Select(i => Path.Combine(allFolder, currentRequest.Name + i)) - .FirstOrDefault(File.Exists); + .FirstOrDefault(_fileSystem.FileExists); if (!string.IsNullOrEmpty(path)) { @@ -239,10 +238,10 @@ namespace MediaBrowser.Api.Images { var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme); - if (Directory.Exists(themeFolder)) + if (_fileSystem.DirectoryExists(themeFolder)) { var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i)) - .FirstOrDefault(File.Exists); + .FirstOrDefault(_fileSystem.FileExists); if (!string.IsNullOrEmpty(path)) { @@ -252,13 +251,13 @@ namespace MediaBrowser.Api.Images var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all"); - if (Directory.Exists(allFolder)) + if (_fileSystem.DirectoryExists(allFolder)) { // Avoid implicitly captured closure var currentRequest = request; var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i)) - .FirstOrDefault(File.Exists); + .FirstOrDefault(_fileSystem.FileExists); if (!string.IsNullOrEmpty(path)) { diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 6d2579ea9..c67ef96c9 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -237,7 +237,7 @@ namespace MediaBrowser.Api.Images contentPath = await reader.ReadToEndAsync().ConfigureAwait(false); } - if (File.Exists(contentPath)) + if (_fileSystem.FileExists(contentPath)) { return ToStaticFileResult(contentPath); } @@ -281,7 +281,7 @@ namespace MediaBrowser.Api.Images var fullCachePath = GetFullCachePath(urlHash + "." + ext); - Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath)); using (var stream = result.Content) { using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) @@ -290,7 +290,7 @@ namespace MediaBrowser.Api.Images } } - Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); using (var writer = new StreamWriter(pointerCachePath)) { await writer.WriteAsync(fullCachePath).ConfigureAwait(false); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index a0ba6e61f..9f7f57155 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -200,7 +200,7 @@ namespace MediaBrowser.Api //} item.ProviderIds = request.ProviderIds; - var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions + var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem) { MetadataRefreshMode = MetadataRefreshMode.FullRefresh, ImageRefreshMode = ImageRefreshMode.FullRefresh, @@ -230,7 +230,7 @@ namespace MediaBrowser.Api contentPath = await reader.ReadToEndAsync().ConfigureAwait(false); } - if (File.Exists(contentPath)) + if (_fileSystem.FileExists(contentPath)) { return ToStaticFileResult(contentPath); } @@ -271,7 +271,7 @@ namespace MediaBrowser.Api var fullCachePath = GetFullCachePath(urlHash + "." + ext); - Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath)); using (var stream = result.Content) { using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) @@ -280,7 +280,7 @@ namespace MediaBrowser.Api } } - Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); using (var writer = new StreamWriter(pointerCachePath)) { await writer.WriteAsync(fullCachePath).ConfigureAwait(false); diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index f56fd3282..1e74b3692 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using ServiceStack; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.Api { @@ -37,11 +38,13 @@ namespace MediaBrowser.Api { private readonly ILibraryManager _libraryManager; private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; - public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager) + public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager, IFileSystem fileSystem) { _libraryManager = libraryManager; _providerManager = providerManager; + _fileSystem = fileSystem; } /// <summary> @@ -66,7 +69,7 @@ namespace MediaBrowser.Api private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request) { - return new MetadataRefreshOptions(new DirectoryService()) + return new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { MetadataRefreshMode = request.MetadataRefreshMode, ImageRefreshMode = request.ImageRefreshMode, diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs index 0ee28d6fe..5e40ac635 100644 --- a/MediaBrowser.Api/Library/LibraryHelpers.cs +++ b/MediaBrowser.Api/Library/LibraryHelpers.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Library var rootFolderPath = appPaths.DefaultUserViewsPath; var path = Path.Combine(rootFolderPath, virtualFolderName); - if (!Directory.Exists(path)) + if (!fileSystem.DirectoryExists(path)) { throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName)); } @@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Library /// <exception cref="System.ArgumentException">The path is not valid.</exception> public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, IServerApplicationPaths appPaths) { - if (!Directory.Exists(path)) + if (!fileSystem.DirectoryExists(path)) { throw new DirectoryNotFoundException("The path does not exist."); } @@ -69,7 +69,7 @@ namespace MediaBrowser.Api.Library var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - while (File.Exists(lnk)) + while (fileSystem.FileExists(lnk)) { shortcutFilename += "1"; lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 49dd121ba..eb1cd6084 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -27,6 +27,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Api.Library { @@ -282,12 +283,13 @@ namespace MediaBrowser.Api.Library private readonly IChannelManager _channelManager; private readonly ITVSeriesManager _tvManager; private readonly ILibraryMonitor _libraryMonitor; + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="LibraryService" /> class. /// </summary> public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor) + IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem) { _itemRepo = itemRepo; _libraryManager = libraryManager; @@ -301,6 +303,7 @@ namespace MediaBrowser.Api.Library _channelManager = channelManager; _tvManager = tvManager; _libraryMonitor = libraryMonitor; + _fileSystem = fileSystem; } public object Get(GetSimilarItems request) @@ -557,7 +560,7 @@ namespace MediaBrowser.Api.Library { throw new ArgumentException("This command cannot be used for remote or virtual items."); } - if (Directory.Exists(item.Path)) + if (_fileSystem.DirectoryExists(item.Path)) { throw new ArgumentException("This command cannot be used for directories."); } diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index f5fe921ce..c8731637c 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -195,7 +195,7 @@ namespace MediaBrowser.Api.Library var virtualFolderPath = Path.Combine(rootFolderPath, name); - if (Directory.Exists(virtualFolderPath)) + if (_fileSystem.DirectoryExists(virtualFolderPath)) { throw new ArgumentException("There is already a media collection with the name " + name + "."); } @@ -204,13 +204,16 @@ namespace MediaBrowser.Api.Library try { - Directory.CreateDirectory(virtualFolderPath); + _fileSystem.CreateDirectory(virtualFolderPath); if (!string.IsNullOrEmpty(request.CollectionType)) { var path = Path.Combine(virtualFolderPath, request.CollectionType + ".collection"); - File.Create(path); + using (File.Create(path)) + { + + } } } finally @@ -256,12 +259,12 @@ namespace MediaBrowser.Api.Library var currentPath = Path.Combine(rootFolderPath, request.Name); var newPath = Path.Combine(rootFolderPath, request.NewName); - if (!Directory.Exists(currentPath)) + if (!_fileSystem.DirectoryExists(currentPath)) { throw new DirectoryNotFoundException("The media collection does not exist"); } - if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(newPath)) + if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath)) { throw new ArgumentException("There is already a media collection with the name " + newPath + "."); } @@ -276,11 +279,11 @@ namespace MediaBrowser.Api.Library //Create an unique name var temporaryName = Guid.NewGuid().ToString(); var temporaryPath = Path.Combine(rootFolderPath, temporaryName); - Directory.Move(currentPath, temporaryPath); + _fileSystem.MoveDirectory(currentPath, temporaryPath); currentPath = temporaryPath; } - Directory.Move(currentPath, newPath); + _fileSystem.MoveDirectory(currentPath, newPath); } finally { @@ -319,7 +322,7 @@ namespace MediaBrowser.Api.Library var path = Path.Combine(rootFolderPath, request.Name); - if (!Directory.Exists(path)) + if (!_fileSystem.DirectoryExists(path)) { throw new DirectoryNotFoundException("The media folder does not exist"); } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 295cc78e9..901dd11e2 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -11,10 +11,10 @@ <AssemblyName>MediaBrowser.Api</AssemblyName> <FileAlignment>512</FileAlignment> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> - <ProductVersion>10.0.0</ProductVersion> - <SchemaVersion>2.0</SchemaVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <RestorePackages>true</RestorePackages> + <ReleaseVersion> + </ReleaseVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -25,7 +25,6 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <PlatformTarget>AnyCPU</PlatformTarget> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>none</DebugType> @@ -34,7 +33,6 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' "> <DebugType>none</DebugType> @@ -43,16 +41,11 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup> <RunPostBuildEvent>Always</RunPostBuildEvent> </PropertyGroup> <ItemGroup> - <Reference Include="MoreLinq, Version=1.1.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath> - </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="Microsoft.CSharp" /> @@ -64,12 +57,14 @@ <Reference Include="ServiceStack.Text"> <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath> </Reference> + <Reference Include="MoreLinq"> + <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> - <Compile Include="AppThemeService.cs" /> <Compile Include="BrandingService.cs" /> <Compile Include="ChannelService.cs" /> <Compile Include="ConnectService.cs" /> diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 531d67eed..86f06213c 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -290,13 +290,6 @@ namespace MediaBrowser.Api.Playback { get { - var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder; - - if (!string.IsNullOrWhiteSpace(lib)) - { - return lib; - } - return "libx264"; } } @@ -810,13 +803,53 @@ namespace MediaBrowser.Api.Playback } /// <summary> + /// Gets the name of the output video codec + /// </summary> + /// <param name="state">The state.</param> + /// <returns>System.String.</returns> + protected string GetVideoDecoder(StreamState state) + { + if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareVideoDecoder, "qsv", StringComparison.OrdinalIgnoreCase)) + { + if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) + { + switch (state.MediaSource.VideoStream.Codec.ToLower()) + { + case "avc": + case "h264": + if (MediaEncoder.SupportsDecoder("h264_qsv")) + { + return "-c:v h264_qsv "; + } + break; + case "mpeg2video": + if (MediaEncoder.SupportsDecoder("mpeg2_qsv")) + { + return "-c:v mpeg2_qsv "; + } + break; + case "vc1": + if (MediaEncoder.SupportsDecoder("vc1_qsv")) + { + return "-c:v vc1_qsv "; + } + break; + } + } + } + + // leave blank so ffmpeg will decide + return string.Empty; + } + + /// <summary> /// Gets the input argument. /// </summary> /// <param name="state">The state.</param> /// <returns>System.String.</returns> protected string GetInputArgument(StreamState state) { - var arg = "-i " + GetInputPathArgument(state); + var arg = string.Format("{1}-i {0}", GetInputPathArgument(state), GetVideoDecoder(state)); if (state.SubtitleStream != null) { @@ -826,7 +859,7 @@ namespace MediaBrowser.Api.Playback } } - return arg; + return arg.Trim(); } private string GetInputPathArgument(StreamState state) @@ -889,7 +922,7 @@ namespace MediaBrowser.Api.Playback CancellationTokenSource cancellationTokenSource, string workingDirectory = null) { - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false); @@ -942,7 +975,7 @@ namespace MediaBrowser.Api.Playback Logger.Info(commandLineLogMessage); var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt"); - Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); + FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); @@ -972,7 +1005,7 @@ namespace MediaBrowser.Api.Playback StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream); // Wait for the file to exist before proceeeding - while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited) + while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited) { await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); } diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs index c201ffd58..cbee821d2 100644 --- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs @@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Playback.Dash var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture)); state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath)); - Directory.CreateDirectory(workingDirectory); + FileSystem.CreateDirectory(workingDirectory); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false); await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false); } @@ -328,8 +328,7 @@ namespace MediaBrowser.Api.Playback.Dash try { - return new DirectoryInfo(folder) - .EnumerateFiles("*", SearchOption.AllDirectories) + return fileSystem.GetFiles(folder) .Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase)) .OrderByDescending(fileSystem.GetLastWriteTimeUtc) .Take(count) @@ -348,20 +347,20 @@ namespace MediaBrowser.Api.Playback.Dash if (requestedIndex == -1) { var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension); - return File.Exists(path) ? path : null; + return FileSystem.FileExists(path) ? path : null; } try { - foreach (var subfolder in new DirectoryInfo(folder).EnumerateDirectories().ToList()) + foreach (var subfolder in FileSystem.GetDirectoryPaths(folder).ToList()) { - var subfolderName = Path.GetFileNameWithoutExtension(subfolder.FullName); + var subfolderName = Path.GetFileNameWithoutExtension(subfolder); int startNumber; if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber)) { var segmentIndex = requestedIndex - startNumber + 1; var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension); - if (File.Exists(path)) + if (FileSystem.FileExists(path)) { return path; } diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 5d377366a..036397b4f 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -90,12 +90,12 @@ namespace MediaBrowser.Api.Playback.Hls TranscodingJob job = null; var playlist = state.OutputFilePath; - if (!File.Exists(playlist)) + if (!FileSystem.FileExists(playlist)) { await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { - if (!File.Exists(playlist)) + if (!FileSystem.FileExists(playlist)) { // If the playlist doesn't already exist, startup ffmpeg try @@ -312,31 +312,6 @@ namespace MediaBrowser.Api.Playback.Hls return 0; } - protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) - { - if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0) - { - Logger.Debug("Cannot stream copy video due to missing keyframe info"); - return false; - } - - var previousSegment = 0; - foreach (var frame in videoStream.KeyFrames) - { - var length = frame - previousSegment; - - // Don't allow really long segments because this could result in long download times - if (length > 10000) - { - Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length); - return false; - } - previousSegment = frame; - } - - return base.CanStreamCopyVideo(request, videoStream); - } - protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs) { return false; diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index cbea1ca0c..9359c65f2 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -165,7 +165,7 @@ namespace MediaBrowser.Api.Playback.Hls TranscodingJob job = null; - if (File.Exists(segmentPath)) + if (FileSystem.FileExists(segmentPath)) { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); @@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Playback.Hls await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { - if (File.Exists(segmentPath)) + if (FileSystem.FileExists(segmentPath)) { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); @@ -285,20 +285,23 @@ namespace MediaBrowser.Api.Playback.Hls private double[] GetSegmentLengths(StreamState state) { var result = new List<double>(); - var encoder = GetVideoEncoder(state); - - if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase)) + if (state.VideoRequest != null) { - var videoStream = state.VideoStream; - if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0) + var encoder = GetVideoEncoder(state); + + if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase)) { - foreach (var frame in videoStream.KeyFrames) + var videoStream = state.VideoStream; + if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0) { - var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds; - seconds -= result.Sum(); - result.Add(seconds); + foreach (var frame in videoStream.KeyFrames) + { + var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds; + seconds -= result.Sum(); + result.Add(seconds); + } + return result.ToArray(); } - return result.ToArray(); } } @@ -383,8 +386,7 @@ namespace MediaBrowser.Api.Playback.Hls try { - return new DirectoryInfo(folder) - .EnumerateFiles("*", SearchOption.TopDirectoryOnly) + return fileSystem.GetFiles(folder) .Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase) && Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase)) .OrderByDescending(fileSystem.GetLastWriteTimeUtc) .FirstOrDefault(); @@ -429,7 +431,7 @@ namespace MediaBrowser.Api.Playback.Hls CancellationToken cancellationToken) { // If all transcoding has completed, just return immediately - if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath)) + if (transcodingJob != null && transcodingJob.HasExited && FileSystem.FileExists(segmentPath)) { return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob); } @@ -449,7 +451,7 @@ namespace MediaBrowser.Api.Playback.Hls // If it appears in the playlist, it's done if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1) { - if (File.Exists(segmentPath)) + if (FileSystem.FileExists(segmentPath)) { return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob); } @@ -960,5 +962,30 @@ namespace MediaBrowser.Api.Playback.Hls { return isOutputVideo ? ".ts" : ".ts"; } + + protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) + { + if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0) + { + Logger.Debug("Cannot stream copy video due to missing keyframe info"); + return false; + } + + var previousSegment = 0; + foreach (var frame in videoStream.KeyFrames) + { + var length = frame - previousSegment; + + // Don't allow really long segments because this could result in long download times + if (length > 10000) + { + Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length); + return false; + } + previousSegment = frame; + } + + return base.CanStreamCopyVideo(request, videoStream); + } } } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 0b7b50134..2ab2d11f6 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Playback Size = 102400; } } - + [Authenticated] public class MediaInfoService : BaseApiService { @@ -289,7 +289,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { options.MaxBitrate = GetMaxBitrate(maxBitrate); - + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : @@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsTranscoding) { options.MaxBitrate = GetMaxBitrate(maxBitrate); - + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : @@ -336,9 +336,15 @@ namespace MediaBrowser.Api.Playback var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit; - if (remoteClientMaxBitrate > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp)) + if (remoteClientMaxBitrate > 0) { - maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); + var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp); + + Logger.Info("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork); + if (!isInLocalNetwork) + { + maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); + } } return maxBitrate; diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 910ac18e7..aa0cda133 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Progressive } var outputPath = state.OutputFilePath; - var outputPathExists = File.Exists(outputPath); + var outputPathExists = FileSystem.FileExists(outputPath); var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive); @@ -325,7 +325,7 @@ namespace MediaBrowser.Api.Playback.Progressive { TranscodingJob job; - if (!File.Exists(outputPath)) + if (!FileSystem.FileExists(outputPath)) { job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false); } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 84ae26248..1dfb43387 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -17,6 +17,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// <summary> /// Class GetVideoStream /// </summary> + [Route("/Videos/{Id}/stream.mpegts", "GET")] [Route("/Videos/{Id}/stream.ts", "GET")] [Route("/Videos/{Id}/stream.webm", "GET")] [Route("/Videos/{Id}/stream.asf", "GET")] diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 34dc5ea12..fdbe5835e 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -74,7 +74,7 @@ namespace MediaBrowser.Api.Playback { get { - return ReadInputAtNativeFramerate ? 1000 : 0; + return 0; } } diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs index c9ee6337f..fb694d6e1 100644 --- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs +++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs @@ -213,7 +213,7 @@ namespace MediaBrowser.Api.Reports }; foreach (var item in t) { - var ps = items.Where(x => x.People != null && x.SupportsPeople).SelectMany(x => x.People) + var ps = items.SelectMany(x => _libraryManager.GetPeople(x)) .Where(n => n.Type == item.ToString()) .GroupBy(x => x.Name) .OrderByDescending(x => x.Count()) diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index e9ac45fa2..962334cdc 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -69,10 +69,10 @@ namespace MediaBrowser.Api _config.Configuration.MergeMetadataAndImagesByName = true; _config.Configuration.EnableStandaloneMetadata = true; _config.Configuration.EnableLibraryMetadataSubFolder = true; - _config.Configuration.EnableUserSpecificUserViews = true; _config.Configuration.EnableCustomPathSubFolders = true; _config.Configuration.DisableXmlSavers = true; _config.Configuration.DisableStartupScan = true; + _config.Configuration.EnableUserViews = true; _config.SaveConfiguration(); } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index c9280b6f6..0bfe7354d 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -16,6 +16,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; +using MediaBrowser.Common.IO; namespace MediaBrowser.Api.Subtitles { @@ -127,14 +128,16 @@ namespace MediaBrowser.Api.Subtitles private readonly ISubtitleEncoder _subtitleEncoder; private readonly IMediaSourceManager _mediaSourceManager; private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; - public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager) + public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem) { _libraryManager = libraryManager; _subtitleManager = subtitleManager; _subtitleEncoder = subtitleEncoder; _mediaSourceManager = mediaSourceManager; _providerManager = providerManager; + _fileSystem = fileSystem; } public async Task<object> Get(GetSubtitlePlaylist request) @@ -259,7 +262,7 @@ namespace MediaBrowser.Api.Subtitles await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None) .ConfigureAwait(false); - _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions()); + _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(_fileSystem)); } catch (Exception ex) { diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index aa1226901..00935dd1e 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -122,8 +122,7 @@ namespace MediaBrowser.Api.System try { - files = new DirectoryInfo(_appPaths.LogDirectoryPath) - .EnumerateFiles("*", SearchOption.AllDirectories) + files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase)) .ToList(); } @@ -149,8 +148,7 @@ namespace MediaBrowser.Api.System public object Get(GetLogFile request) { - var file = new DirectoryInfo(_appPaths.LogDirectoryPath) - .EnumerateFiles("*", SearchOption.AllDirectories) + var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase)); return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite); diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index a49ab8556..9d7c38d6f 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -39,6 +39,17 @@ namespace MediaBrowser.Api.UserLibrary public string UserId { get; set; } } + [Route("/Users/{UserId}/GroupingOptions", "GET")] + public class GetGroupingOptions : IReturn<List<SpecialViewOption>> + { + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string UserId { get; set; } + } + public class UserViewsService : BaseApiService { private readonly IUserManager _userManager; @@ -105,6 +116,29 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(list); } + public async Task<object> Get(GetGroupingOptions request) + { + var user = _userManager.GetUserById(request.UserId); + + var views = user.RootFolder + .GetChildren(user, true) + .OfType<Folder>() + .Where(i => !UserView.IsExcludedFromGrouping(i)) + .ToList(); + + var list = views + .Select(i => new SpecialViewOption + { + Name = i.Name, + Id = i.Id.ToString("N") + + }) + .OrderBy(i => i.Name) + .ToList(); + + return ToOptimizedResult(list); + } + private bool IsEligibleForSpecialView(ICollectionFolder view) { var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos }; diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config index 6df166204..0cdb26bd5 100644 --- a/MediaBrowser.Api/packages.config +++ b/MediaBrowser.Api/packages.config @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="morelinq" version="1.1.0" targetFramework="net45" /> + <package id="morelinq" version="1.1.1" targetFramework="net45" /> </packages>
\ No newline at end of file |
