diff options
Diffstat (limited to 'MediaBrowser.Api')
| -rw-r--r-- | MediaBrowser.Api/BaseApiService.cs | 12 | ||||
| -rw-r--r-- | MediaBrowser.Api/ConfigurationService.cs | 24 | ||||
| -rw-r--r-- | MediaBrowser.Api/Images/ImageService.cs | 63 | ||||
| -rw-r--r-- | MediaBrowser.Api/Images/ImageWriter.cs | 96 | ||||
| -rw-r--r-- | MediaBrowser.Api/ItemLookupService.cs | 19 | ||||
| -rw-r--r-- | MediaBrowser.Api/ItemUpdateService.cs | 11 | ||||
| -rw-r--r-- | MediaBrowser.Api/Library/LibraryService.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Api/MediaBrowser.Api.csproj | 3 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/BaseStreamingService.cs | 27 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/EndlessStreamCopy.cs | 32 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 21 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs | 7 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs | 16 | ||||
| -rw-r--r-- | MediaBrowser.Api/Sync/SyncService.cs | 130 | ||||
| -rw-r--r-- | MediaBrowser.Api/UserService.cs | 92 |
16 files changed, 350 insertions, 210 deletions
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 09eb1ea41..727ee6fbc 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -125,7 +125,7 @@ namespace MediaBrowser.Api return ResultFactory.GetStaticFileResult(Request, path); } - private readonly char[] _dashReplaceChars = new[] { '?', '/' }; + private readonly char[] _dashReplaceChars = { '?', '/' }; private const char SlugChar = '-'; protected MusicArtist GetArtist(string name, ILibraryManager libraryManager) @@ -168,6 +168,11 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); + if (user == null) + { + throw new ArgumentException("User not found"); + } + return folder.GetRecursiveChildren(user); } @@ -177,6 +182,11 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); + if (user == null) + { + throw new ArgumentException("User not found"); + } + return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); } diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index c83028bb2..29848cfc0 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -122,42 +122,44 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } + const string XbmcMetadata = "Xbmc Nfo"; + const string MediaBrowserMetadata = "Media Browser Xml"; + public void Post(AutoSetMetadataOptions request) { var service = AutoDetectMetadataService(); Logger.Info("Setting preferred metadata format to " + service); - _configurationManager.SetPreferredMetadataService(service); + var serviceToDisable = string.Equals(service, XbmcMetadata) ? + MediaBrowserMetadata : + XbmcMetadata; + + _configurationManager.DisableMetadataService(serviceToDisable); _configurationManager.SaveConfiguration(); } private string AutoDetectMetadataService() { - const string xbmc = "Xbmc Nfo"; - const string mb = "Media Browser Xml"; - var paths = _libraryManager.GetDefaultVirtualFolders() .SelectMany(i => i.Locations) .Distinct(StringComparer.OrdinalIgnoreCase) .Select(i => new DirectoryInfo(i)) .ToList(); - if (paths.Select(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories)) - .SelectMany(i => i) + if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories)) .Any()) { - return xbmc; + return XbmcMetadata; } - if (paths.Select(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories)) - .SelectMany(i => i) + if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories)) .Any(i => string.Equals(i.Name, "series.xml", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "movie.xml", StringComparison.OrdinalIgnoreCase))) { - return mb; + return MediaBrowserMetadata; } - return xbmc; + return XbmcMetadata; } /// <summary> diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index deaefe019..30db91da8 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -535,9 +535,6 @@ namespace MediaBrowser.Api.Images throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type)); } - // See if we can avoid a file system lookup by looking for the file in ResolveArgs - var originalFileImageDateModified = imageInfo.DateModified; - var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.ImageEnhancers.Where(i => { try @@ -564,25 +561,59 @@ namespace MediaBrowser.Api.Images cacheDuration = TimeSpan.FromDays(365); } - // Avoid implicitly captured closure - var currentItem = item; - var currentRequest = request; - var responseHeaders = new Dictionary<string, string> { {"transferMode.dlna.org", "Interactive"}, {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"} }; - return ToCachedResult(cacheGuid, originalFileImageDateModified, cacheDuration, () => new ImageWriter + return GetImageResult(item, + request, + imageInfo, + supportedImageEnhancers, + contentType, + cacheDuration, + responseHeaders) + .Result; + } + + private async Task<object> GetImageResult(IHasImages item, + ImageRequest request, + ItemImageInfo image, + List<IImageEnhancer> enhancers, + string contentType, + TimeSpan? cacheDuration, + IDictionary<string,string> headers) + { + var cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art; + + if (request.CropWhitespace.HasValue) { - Item = currentItem, - Request = currentRequest, - Enhancers = supportedImageEnhancers, - Image = imageInfo, - ImageProcessor = _imageProcessor + cropwhitespace = request.CropWhitespace.Value; + } - }, contentType, responseHeaders); + var options = new ImageProcessingOptions + { + CropWhiteSpace = cropwhitespace, + Enhancers = enhancers, + Height = request.Height, + ImageIndex = request.Index ?? 0, + Image = image, + Item = item, + MaxHeight = request.MaxHeight, + MaxWidth = request.MaxWidth, + Quality = request.Quality, + Width = request.Width, + OutputFormat = request.Format, + AddPlayedIndicator = request.AddPlayedIndicator, + PercentPlayed = request.PercentPlayed, + UnplayedCount = request.UnplayedCount, + BackgroundColor = request.BackgroundColor + }; + + var file = await _imageProcessor.ProcessImage(options).ConfigureAwait(false); + + return ResultFactory.GetStaticFileResult(Request, file, contentType, cacheDuration, FileShare.Read, headers); } private string GetMimeType(ImageOutputFormat format, string path) @@ -603,6 +634,10 @@ namespace MediaBrowser.Api.Images { return Common.Net.MimeTypes.GetMimeType("i.png"); } + if (format == ImageOutputFormat.Webp) + { + return Common.Net.MimeTypes.GetMimeType("i.webp"); + } return Common.Net.MimeTypes.GetMimeType(path); } diff --git a/MediaBrowser.Api/Images/ImageWriter.cs b/MediaBrowser.Api/Images/ImageWriter.cs deleted file mode 100644 index b6638d9df..000000000 --- a/MediaBrowser.Api/Images/ImageWriter.cs +++ /dev/null @@ -1,96 +0,0 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using ServiceStack.Web; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace MediaBrowser.Api.Images -{ - /// <summary> - /// Class ImageWriter - /// </summary> - public class ImageWriter : IStreamWriter, IHasOptions - { - public List<IImageEnhancer> Enhancers; - - /// <summary> - /// Gets or sets the request. - /// </summary> - /// <value>The request.</value> - public ImageRequest Request { get; set; } - /// <summary> - /// Gets or sets the item. - /// </summary> - /// <value>The item.</value> - public IHasImages Item { get; set; } - /// <summary> - /// The original image date modified - /// </summary> - public ItemImageInfo Image; - - public IImageProcessor ImageProcessor { get; set; } - - /// <summary> - /// The _options - /// </summary> - private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); - /// <summary> - /// Gets the options. - /// </summary> - /// <value>The options.</value> - public IDictionary<string, string> Options - { - get { return _options; } - } - - /// <summary> - /// Writes to. - /// </summary> - /// <param name="responseStream">The response stream.</param> - public void WriteTo(Stream responseStream) - { - var task = WriteToAsync(responseStream); - - Task.WaitAll(task); - } - - /// <summary> - /// Writes to async. - /// </summary> - /// <param name="responseStream">The response stream.</param> - /// <returns>Task.</returns> - private Task WriteToAsync(Stream responseStream) - { - var cropwhitespace = Request.Type == ImageType.Logo || Request.Type == ImageType.Art; - - if (Request.CropWhitespace.HasValue) - { - cropwhitespace = Request.CropWhitespace.Value; - } - - var options = new ImageProcessingOptions - { - CropWhiteSpace = cropwhitespace, - Enhancers = Enhancers, - Height = Request.Height, - ImageIndex = Request.Index ?? 0, - Image = Image, - Item = Item, - MaxHeight = Request.MaxHeight, - MaxWidth = Request.MaxWidth, - Quality = Request.Quality, - Width = Request.Width, - OutputFormat = Request.Format, - AddPlayedIndicator = Request.AddPlayedIndicator, - PercentPlayed = Request.PercentPlayed, - UnplayedCount = Request.UnplayedCount, - BackgroundColor = Request.BackgroundColor - }; - - return ImageProcessor.ProcessImage(options, responseStream); - } - } -} diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index a08f28533..2ce7997a1 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -224,16 +224,23 @@ namespace MediaBrowser.Api } } - var task = item.RefreshMetadata(new MetadataRefreshOptions + var service = new ItemRefreshService(_libraryManager) { + Logger = Logger, + Request = Request, + ResultFactory = ResultFactory, + SessionContext = SessionContext + }; + + service.Post(new RefreshItem + { + Id = request.Id, MetadataRefreshMode = MetadataRefreshMode.FullRefresh, ImageRefreshMode = ImageRefreshMode.FullRefresh, ReplaceAllMetadata = true, - ReplaceAllImages = true - - }, CancellationToken.None); - - Task.WaitAll(task); + ReplaceAllImages = true, + Recursive = true + }); } /// <summary> diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index db6c6ce53..1fa1a5509 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -65,6 +65,11 @@ namespace MediaBrowser.Api } } + private DateTime NormalizeDateTime(DateTime val) + { + return DateTime.SpecifyKind(val, DateTimeKind.Utc); + } + private void UpdateItem(BaseItemDto request, BaseItem item) { item.Name = request.Name; @@ -140,11 +145,11 @@ namespace MediaBrowser.Api if (request.DateCreated.HasValue) { - item.DateCreated = request.DateCreated.Value.ToUniversalTime(); + item.DateCreated = NormalizeDateTime(request.DateCreated.Value); } - item.EndDate = request.EndDate.HasValue ? request.EndDate.Value.ToUniversalTime() : (DateTime?)null; - item.PremiereDate = request.PremiereDate.HasValue ? request.PremiereDate.Value.ToUniversalTime() : (DateTime?)null; + item.EndDate = request.EndDate.HasValue ? NormalizeDateTime(request.EndDate.Value) : (DateTime?)null; + item.PremiereDate = request.PremiereDate.HasValue ? NormalizeDateTime(request.PremiereDate.Value) : (DateTime?)null; item.ProductionYear = request.ProductionYear; item.OfficialRating = request.OfficialRating; item.CustomRating = request.CustomRating; diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 10aa23126..f6588714e 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -220,7 +220,7 @@ namespace MediaBrowser.Api.Library [Api(Description = "Reports that new episodes of a series have been added by an external source")] public class PostUpdatedSeries : IReturnVoid { - [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] public string TvdbId { get; set; } } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 6a1e45e25..f3857ce30 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -85,7 +85,6 @@ <Compile Include="Images\ImageByNameService.cs" /> <Compile Include="Images\ImageRequest.cs" /> <Compile Include="Images\ImageService.cs" /> - <Compile Include="Images\ImageWriter.cs" /> <Compile Include="Music\InstantMixService.cs" /> <Compile Include="ItemLookupService.cs" /> <Compile Include="ItemRefreshService.cs" /> @@ -102,7 +101,6 @@ <Compile Include="PackageReviewService.cs" /> <Compile Include="PackageService.cs" /> <Compile Include="Playback\BifService.cs" /> - <Compile Include="Playback\EndlessStreamCopy.cs" /> <Compile Include="Playback\Hls\BaseHlsService.cs" /> <Compile Include="Playback\Hls\DynamicHlsService.cs" /> <Compile Include="Playback\Hls\HlsSegmentService.cs" /> @@ -123,6 +121,7 @@ <Compile Include="SearchService.cs" /> <Compile Include="SessionsService.cs" /> <Compile Include="SimilarItemsHelper.cs" /> + <Compile Include="Sync\SyncService.cs" /> <Compile Include="SystemService.cs" /> <Compile Include="Movies\TrailersService.cs" /> <Compile Include="TvShowsService.cs" /> diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5d772527c..5cae4c3f7 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Api.Playback.Hls; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -608,7 +607,7 @@ namespace MediaBrowser.Api.Playback } } - // TODO: Perhaps also use original_size=1920x800 + // TODO: Perhaps also use original_size=1920x800 ?? return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB", subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"), charsetParam, @@ -818,7 +817,7 @@ namespace MediaBrowser.Api.Playback state.MediaPath = streamInfo.Path; state.InputProtocol = MediaProtocol.File; - await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); + await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } else if (!string.IsNullOrEmpty(streamInfo.Url)) { @@ -839,7 +838,7 @@ namespace MediaBrowser.Api.Playback state.MediaPath = streamInfo.Path; state.InputProtocol = MediaProtocol.File; - await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); + await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } else if (!string.IsNullOrEmpty(streamInfo.Url)) { @@ -944,19 +943,6 @@ namespace MediaBrowser.Api.Playback { await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); } - - // Allow a small amount of time to buffer a little - // But not with HLS because it already has it's own wait - if (state.IsInputVideo && TranscodingJobType != TranscodingJobType.Hls) - { - await Task.Delay(500, cancellationTokenSource.Token).ConfigureAwait(false); - } - - // This is arbitrary, but add a little buffer time when internet streaming - if (state.InputProtocol != MediaProtocol.File) - { - await Task.Delay(2500, cancellationTokenSource.Token).ConfigureAwait(false); - } } private async void StartStreamingLog(StreamState state, Stream source, Stream target) @@ -1459,11 +1445,6 @@ namespace MediaBrowser.Api.Playback state.RunTimeTicks = recording.RunTimeTicks; - if (recording.RecordingInfo.Status == RecordingStatus.InProgress) - { - await Task.Delay(1000, cancellationToken).ConfigureAwait(false); - } - state.OutputAudioSync = "1000"; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; diff --git a/MediaBrowser.Api/Playback/EndlessStreamCopy.cs b/MediaBrowser.Api/Playback/EndlessStreamCopy.cs deleted file mode 100644 index 40586261f..000000000 --- a/MediaBrowser.Api/Playback/EndlessStreamCopy.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Api.Playback -{ - public class EndlessStreamCopy - { - public async Task CopyStream(Stream source, Stream target, CancellationToken cancellationToken) - { - long position = 0; - - while (!cancellationToken.IsCancellationRequested) - { - await source.CopyToAsync(target, 81920, cancellationToken).ConfigureAwait(false); - - var fsPosition = source.Position; - - var bytesRead = fsPosition - position; - - //Logger.Debug("Streamed {0} bytes from file {1}", bytesRead, path); - - if (bytesRead == 0) - { - await Task.Delay(100, cancellationToken).ConfigureAwait(false); - } - - position = fsPosition; - } - } - } -} diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 8eff75533..317c4455a 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -104,7 +104,10 @@ namespace MediaBrowser.Api.Playback.Hls } else { - await ApiEntryPoint.Instance.KillTranscodingJobs(state.Request.DeviceId, TranscodingJobType.Hls, p => true, false).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(state.Request.DeviceId)) + { + await ApiEntryPoint.Instance.KillTranscodingJobs(state.Request.DeviceId, TranscodingJobType.Hls, p => true, false).ConfigureAwait(false); + } // If the playlist doesn't already exist, startup ffmpeg try diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 6657bf6de..a098d26da 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -342,7 +342,7 @@ namespace MediaBrowser.Api.Playback.Hls AppendPlaylist(builder, playlistUrl, totalBitrate); - if (state.VideoRequest.VideoBitRate.HasValue) + if (EnableAdaptiveBitrateStreaming(state)) { var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value; @@ -360,6 +360,17 @@ namespace MediaBrowser.Api.Playback.Hls return builder.ToString(); } + private bool EnableAdaptiveBitrateStreaming(StreamState state) + { + if (string.IsNullOrWhiteSpace(state.MediaPath)) + { + // Opening live streams is so slow it's not even worth it + return false; + } + + return state.VideoRequest.VideoBitRate.HasValue; + } + private void AppendPlaylist(StringBuilder builder, string url, int bitrate) { builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture)); @@ -368,8 +379,8 @@ namespace MediaBrowser.Api.Playback.Hls private int GetBitrateVariation(int bitrate) { - // By default, vary by just 200k - var variation = 200000; + // By default, vary by just 100k + var variation = 100000; if (bitrate >= 10000000) { @@ -391,6 +402,10 @@ namespace MediaBrowser.Api.Playback.Hls { variation = 300000; } + else if (bitrate >= 600000) + { + variation = 200000; + } return variation; } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index dd8960649..80428269b 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -149,7 +149,7 @@ namespace MediaBrowser.Api.Playback.Progressive using (state) { - return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest); + return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, null, FileShare.Read, responseHeaders, isHeadRequest); } } @@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Progressive try { - return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest); + return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, null, FileShare.Read, responseHeaders, isHeadRequest); } finally { @@ -285,7 +285,8 @@ namespace MediaBrowser.Api.Playback.Progressive state.Dispose(); } - var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem); + var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive); + var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job); result.Options["Content-Type"] = contentType; diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 36ae7f100..a4c55443c 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Api.Playback.Progressive private string Path { get; set; } private ILogger Logger { get; set; } private readonly IFileSystem _fileSystem; + private readonly TranscodingJob _job; /// <summary> /// The _options @@ -32,11 +33,12 @@ namespace MediaBrowser.Api.Playback.Progressive /// <param name="path">The path.</param> /// <param name="logger">The logger.</param> /// <param name="fileSystem">The file system.</param> - public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem) + public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem, TranscodingJob job) { Path = path; Logger = logger; _fileSystem = fileSystem; + _job = job; } /// <summary> @@ -59,7 +61,8 @@ namespace MediaBrowser.Api.Playback.Progressive { try { - await new ProgressiveFileCopier(_fileSystem).StreamFile(Path, responseStream).ConfigureAwait(false); + await new ProgressiveFileCopier(_fileSystem, _job) + .StreamFile(Path, responseStream).ConfigureAwait(false); } catch { @@ -77,10 +80,12 @@ namespace MediaBrowser.Api.Playback.Progressive public class ProgressiveFileCopier { private readonly IFileSystem _fileSystem; + private readonly TranscodingJob _job; - public ProgressiveFileCopier(IFileSystem fileSystem) + public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job) { _fileSystem = fileSystem; + _job = job; } public async Task StreamFile(string path, Stream outputStream) @@ -102,7 +107,10 @@ namespace MediaBrowser.Api.Playback.Progressive if (bytesRead == 0) { - eofCount++; + if (_job == null || _job.HasExited) + { + eofCount++; + } await Task.Delay(100).ConfigureAwait(false); } else diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs new file mode 100644 index 000000000..57b5fa3f9 --- /dev/null +++ b/MediaBrowser.Api/Sync/SyncService.cs @@ -0,0 +1,130 @@ +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Sync; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Sync; +using ServiceStack; +using System.Threading.Tasks; + +namespace MediaBrowser.Api.Sync +{ + [Route("/Sync/Jobs/{Id}", "DELETE", Summary = "Cancels a sync job.")] + public class CancelSyncJob : IReturnVoid + { + [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Sync/Schedules/{Id}", "DELETE", Summary = "Cancels a sync job.")] + public class CancelSyncSchedule : IReturnVoid + { + [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Sync/Jobs/{Id}", "GET", Summary = "Gets a sync job.")] + public class GetSyncJob : IReturn<SyncJob> + { + [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Sync/Schedules/{Id}", "GET", Summary = "Gets a sync job.")] + public class GetSyncSchedule : IReturn<SyncSchedule> + { + [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Sync/Jobs", "GET", Summary = "Gets sync jobs.")] + public class GetSyncJobs : IReturn<QueryResult<SyncJob>> + { + } + + [Route("/Sync/Schedules", "GET", Summary = "Gets sync schedules.")] + public class GetSyncSchedules : IReturn<QueryResult<SyncSchedule>> + { + } + + [Route("/Sync/Jobs", "POST", Summary = "Gets sync jobs.")] + public class CreateSyncJob : SyncJobRequest + { + } + + [Route("/Sync/Schedules", "POST", Summary = "Gets sync schedules.")] + public class CreateSyncSchedule : SyncScheduleRequest + { + } + + [Authenticated] + public class SyncService : BaseApiService + { + private readonly ISyncManager _syncManager; + + public SyncService(ISyncManager syncManager) + { + _syncManager = syncManager; + } + + public object Get(GetSyncJobs request) + { + var result = _syncManager.GetJobs(new SyncJobQuery + { + + }); + + return ToOptimizedResult(result); + } + + public object Get(GetSyncSchedules request) + { + var result = _syncManager.GetSchedules(new SyncScheduleQuery + { + + }); + + return ToOptimizedResult(result); + } + + public object Get(GetSyncJob request) + { + var result = _syncManager.GetJob(request.Id); + + return ToOptimizedResult(result); + } + + public object Get(GetSyncSchedule request) + { + var result = _syncManager.GetSchedule(request.Id); + + return ToOptimizedResult(result); + } + + public void Delete(CancelSyncJob request) + { + var task = _syncManager.CancelJob(request.Id); + + Task.WaitAll(task); + } + + public void Delete(CancelSyncSchedule request) + { + var task = _syncManager.CancelSchedule(request.Id); + + Task.WaitAll(task); + } + + public void Post(CreateSyncJob request) + { + var task = _syncManager.CreateJob(request); + + Task.WaitAll(task); + } + + public void Post(CreateSyncSchedule request) + { + var task = _syncManager.CreateSchedule(request); + + Task.WaitAll(task); + } + } +} diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 0edd013df..b7a43c237 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -11,6 +12,7 @@ using ServiceStack.Text.Controller; using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading.Tasks; namespace MediaBrowser.Api @@ -168,6 +170,7 @@ namespace MediaBrowser.Api private readonly IDtoService _dtoService; private readonly ISessionManager _sessionMananger; private readonly IServerConfigurationManager _config; + private readonly INetworkManager _networkManager; public IAuthorizationContext AuthorizationContext { get; set; } @@ -178,12 +181,13 @@ namespace MediaBrowser.Api /// <param name="dtoService">The dto service.</param> /// <param name="sessionMananger">The session mananger.</param> /// <exception cref="System.ArgumentNullException">xmlSerializer</exception> - public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config) + public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager) { _userManager = userManager; _dtoService = dtoService; _sessionMananger = sessionMananger; _config = config; + _networkManager = networkManager; } public object Get(GetPublicUsers request) @@ -200,18 +204,65 @@ namespace MediaBrowser.Api }); } - // TODO: Add or is authenticated - if (_sessionMananger.IsInLocalNetwork(Request.RemoteIp)) + // TODO: Uncomment this once all clients can handle an empty user list. + return Get(new GetUsers { - return Get(new GetUsers + IsHidden = false, + IsDisabled = false + }); + + //// TODO: Add or is authenticated + //if (Request.IsLocal || IsInLocalNetwork(Request.RemoteIp)) + //{ + // return Get(new GetUsers + // { + // IsHidden = false, + // IsDisabled = false + // }); + //} + + //// Return empty when external + //return ToOptimizedResult(new List<UserDto>()); + } + + private bool IsInLocalNetwork(string remoteEndpoint) + { + if (string.IsNullOrWhiteSpace(remoteEndpoint)) + { + throw new ArgumentNullException("remoteEndpoint"); + } + + IPAddress address; + if (!IPAddress.TryParse(remoteEndpoint, out address)) + { + return true; + } + + const int lengthMatch = 4; + + if (remoteEndpoint.Length >= lengthMatch) + { + var prefix = remoteEndpoint.Substring(0, lengthMatch); + + if (_networkManager.GetLocalIpAddresses() + .Any(i => i.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) { - IsHidden = false, - IsDisabled = false - }); + return true; + } } - // Return empty when external - return ToOptimizedResult(new List<UserDto>()); + // Private address space: + // http://en.wikipedia.org/wiki/Private_network + + return + + // If url was requested with computer name, we may see this + remoteEndpoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 || + + remoteEndpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || + remoteEndpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) || + remoteEndpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) || + remoteEndpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase); } /// <summary> @@ -273,6 +324,10 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException("User not found"); } + var revokeTask = _sessionMananger.RevokeUserTokens(user.Id.ToString("N")); + + Task.WaitAll(revokeTask); + var task = _userManager.DeleteUser(user); Task.WaitAll(task); @@ -302,8 +357,25 @@ namespace MediaBrowser.Api { var auth = AuthorizationContext.GetAuthorizationInfo(Request); + if (string.IsNullOrWhiteSpace(auth.Client)) + { + auth.Client = "Unknown app"; + } + if (string.IsNullOrWhiteSpace(auth.Device)) + { + auth.Device = "Unknown device"; + } + if (string.IsNullOrWhiteSpace(auth.Version)) + { + auth.Version = "Unknown version"; + } + if (string.IsNullOrWhiteSpace(auth.DeviceId)) + { + auth.DeviceId = "Unknown device id"; + } + var result = _sessionMananger.AuthenticateNewSession(request.Username, request.Password, auth.Client, auth.Version, - auth.DeviceId, auth.Device, Request.RemoteIp).Result; + auth.DeviceId, auth.Device, Request.RemoteIp, Request.IsLocal).Result; return ToOptimizedResult(result); } |
