diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 06e042d74..4ebbbfd0a 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -33,7 +33,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll False diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index b8300ac97..cb60d1123 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs @@ -155,6 +155,7 @@ namespace Emby.Drawing.ImageMagick AutoOrientImage(originalImage); } + AddForegroundLayer(originalImage, options); DrawIndicator(originalImage, width, height, options); originalImage.CurrentImage.CompressionQuality = quality; @@ -177,6 +178,8 @@ namespace Emby.Drawing.ImageMagick } wand.CurrentImage.CompositeImage(originalImage, CompositeOperator.OverCompositeOp, 0, 0); + + AddForegroundLayer(wand, options); DrawIndicator(wand, width, height, options); wand.CurrentImage.CompressionQuality = quality; @@ -189,6 +192,23 @@ namespace Emby.Drawing.ImageMagick SaveDelay(); } + private void AddForegroundLayer(MagickWand wand, ImageProcessingOptions options) + { + if (string.IsNullOrWhiteSpace(options.ForegroundLayer)) + { + return; + } + + Double opacity; + if (!Double.TryParse(options.ForegroundLayer, out opacity)) opacity = .4; + + using (var pixel = new PixelWand("#000", opacity)) + using (var overlay = new MagickWand(wand.CurrentImage.Width, wand.CurrentImage.Height, pixel)) + { + wand.CurrentImage.CompositeImage(overlay, CompositeOperator.OverCompositeOp, 0, 0); + } + } + private void AutoOrientImage(MagickWand wand) { wand.CurrentImage.AutoOrientImage(); diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index e01612700..89e2649b5 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -234,7 +234,7 @@ namespace Emby.Drawing var quality = options.Quality; var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]); - var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor); + var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor, options.ForegroundLayer); var semaphore = GetLock(cacheFilePath); @@ -473,7 +473,7 @@ namespace Emby.Drawing /// /// Gets the cache file path based on a set of parameters /// - private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) + private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor, string foregroundLayer) { var filename = originalPath; @@ -507,6 +507,11 @@ namespace Emby.Drawing filename += "b=" + backgroundColor; } + if (!string.IsNullOrEmpty(foregroundLayer)) + { + filename += "fl=" + foregroundLayer; + } + filename += "v=" + Version; return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower()); diff --git a/Emby.Drawing/packages.config b/Emby.Drawing/packages.config index 62a241f1f..fab573efc 100644 --- a/Emby.Drawing/packages.config +++ b/Emby.Drawing/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs index bdd2eeaad..4bcd33d9e 100644 --- a/MediaBrowser.Api/ConnectService.cs +++ b/MediaBrowser.Api/ConnectService.cs @@ -1,10 +1,8 @@ -using System; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Connect; -using MediaBrowser.Model.Dto; using ServiceStack; using System.Collections.Generic; using System.Linq; @@ -75,28 +73,6 @@ namespace MediaBrowser.Api public string ConnectUserId { get; set; } } - [Route("/Connect/Supporters", "GET")] - [Authenticated(Roles = "Admin")] - public class GetConnectSupporterSummary : IReturn - { - } - - [Route("/Connect/Supporters", "DELETE")] - [Authenticated(Roles = "Admin")] - public class RemoveConnectSupporter : IReturnVoid - { - [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/Connect/Supporters", "POST")] - [Authenticated(Roles = "Admin")] - public class AddConnectSupporter : IReturnVoid - { - [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Id { get; set; } - } - public class ConnectService : BaseApiService { private readonly IConnectManager _connectManager; @@ -108,35 +84,6 @@ namespace MediaBrowser.Api _userManager = userManager; } - public async Task Get(GetConnectSupporterSummary request) - { - var result = await _connectManager.GetConnectSupporterSummary().ConfigureAwait(false); - var existingConnectUserIds = result.Users.Select(i => i.Id).ToList(); - - result.EligibleUsers = _userManager.Users - .Where(i => !string.IsNullOrWhiteSpace(i.ConnectUserId)) - .Where(i => !existingConnectUserIds.Contains(i.ConnectUserId, StringComparer.OrdinalIgnoreCase)) - .OrderBy(i => i.Name) - .Select(i => _userManager.GetUserDto(i)) - .ToList(); - - return ToOptimizedResult(result); - } - - public void Delete(RemoveConnectSupporter request) - { - var task = _connectManager.RemoveConnectSupporter(request.Id); - - Task.WaitAll(task); - } - - public void Post(AddConnectSupporter request) - { - var task = _connectManager.AddConnectSupporter(request.Id); - - Task.WaitAll(task); - } - public object Post(CreateConnectLink request) { return _connectManager.LinkUser(request.Id, request.ConnectUsername); diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs index cdd348bb5..8b86ee7e0 100644 --- a/MediaBrowser.Api/Images/ImageRequest.cs +++ b/MediaBrowser.Api/Images/ImageRequest.cs @@ -66,7 +66,10 @@ namespace MediaBrowser.Api.Images [ApiMember(Name = "BackgroundColor", Description = "Optional. Apply a background color for transparent images.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string BackgroundColor { get; set; } - + + [ApiMember(Name = "ForegroundLayer", Description = "Optional. Apply a foreground layer on top of the image.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ForegroundLayer { get; set; } + public ImageRequest() { EnableImageEnhancers = true; diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 7122c8fc1..8d58070fd 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -624,6 +624,7 @@ namespace MediaBrowser.Api.Images PercentPlayed = request.PercentPlayed ?? 0, UnplayedCount = request.UnplayedCount, BackgroundColor = request.BackgroundColor, + ForegroundLayer = request.ForegroundLayer, SupportedOutputFormats = supportedFormats }; diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs index 1224fa957..849e9cf0d 100644 --- a/MediaBrowser.Api/Library/FileOrganizationService.cs +++ b/MediaBrowser.Api/Library/FileOrganizationService.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Querying; using ServiceStack; using System.Threading.Tasks; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Api.Library { @@ -54,7 +55,7 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } - [Route("/Library/FileOrganizations/{Id}/Episode/Organize", "POST", Summary = "Performs an organization")] + [Route("/Library/FileOrganizations/{Id}/Episode/Organize", "POST", Summary = "Performs organization of a tv episode")] public class OrganizeEpisode { [ApiMember(Name = "Id", Description = "Result Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] @@ -74,6 +75,18 @@ namespace MediaBrowser.Api.Library [ApiMember(Name = "RememberCorrection", Description = "Whether or not to apply the same correction to future episodes of the same series.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool RememberCorrection { get; set; } + + [ApiMember(Name = "NewSeriesProviderIds", Description = "A list of provider IDs identifying a new series.", IsRequired = false, DataType = "Dictionary", ParameterType = "query", Verb = "POST")] + public Dictionary NewSeriesProviderIds { get; set; } + + [ApiMember(Name = "NewSeriesName", Description = "Name of a series to add.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string NewSeriesName { get; set; } + + [ApiMember(Name = "NewSeriesYear", Description = "Year of a series to add.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string NewSeriesYear { get; set; } + + [ApiMember(Name = "TargetFolder", Description = "Target Folder", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string TargetFolder { get; set; } } [Route("/Library/FileOrganizations/SmartMatches", "GET", Summary = "Gets smart match entries")] @@ -106,9 +119,14 @@ namespace MediaBrowser.Api.Library { private readonly IFileOrganizationService _iFileOrganizationService; - public FileOrganizationService(IFileOrganizationService iFileOrganizationService) + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + public FileOrganizationService(IFileOrganizationService iFileOrganizationService, IJsonSerializer jsonSerializer) { _iFileOrganizationService = iFileOrganizationService; + _jsonSerializer = jsonSerializer; } public object Get(GetFileOrganizationActivity request) @@ -145,6 +163,13 @@ namespace MediaBrowser.Api.Library public void Post(OrganizeEpisode request) { + var dicNewProviderIds = new Dictionary(); + + if (request.NewSeriesProviderIds != null) + { + dicNewProviderIds = request.NewSeriesProviderIds; + } + var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest { EndingEpisodeNumber = request.EndingEpisodeNumber, @@ -152,9 +177,17 @@ namespace MediaBrowser.Api.Library RememberCorrection = request.RememberCorrection, ResultId = request.Id, SeasonNumber = request.SeasonNumber, - SeriesId = request.SeriesId + SeriesId = request.SeriesId, + NewSeriesName = request.NewSeriesName, + NewSeriesYear = request.NewSeriesYear, + NewSeriesProviderIds = dicNewProviderIds, + TargetFolder = request.TargetFolder }); + // For async processing (close dialog early instead of waiting until the file has been copied) + //var tasks = new Task[] { task }; + //Task.WaitAll(tasks, 8000); + Task.WaitAll(task); } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index f711c69e6..be79f4d74 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -47,7 +47,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll ..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll @@ -80,7 +80,6 @@ - diff --git a/MediaBrowser.Api/PinLoginService.cs b/MediaBrowser.Api/PinLoginService.cs deleted file mode 100644 index 8b63de10a..000000000 --- a/MediaBrowser.Api/PinLoginService.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Globalization; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Connect; -using ServiceStack; - -namespace MediaBrowser.Api -{ - [Route("/Auth/Pin", "POST", Summary = "Creates a pin request")] - public class CreatePinRequest : IReturn - { - [ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string DeviceId { get; set; } - } - - [Route("/Auth/Pin", "GET", Summary = "Gets pin status")] - public class GetPinStatusRequest : IReturn - { - [ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string DeviceId { get; set; } - [ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Pin { get; set; } - } - - [Route("/Auth/Pin/Exchange", "POST", Summary = "Exchanges a pin")] - public class ExchangePinRequest : IReturn - { - [ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string DeviceId { get; set; } - [ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Pin { get; set; } - } - - [Route("/Auth/Pin/Validate", "POST", Summary = "Validates a pin")] - [Authenticated] - public class ValidatePinRequest : IReturnVoid - { - [ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Pin { get; set; } - } - - public class PinLoginService : BaseApiService - { - private readonly ConcurrentDictionary _activeRequests = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - public object Post(CreatePinRequest request) - { - var pin = GetNewPin(); - - var value = new MyPinStatus - { - CreationTimeUtc = DateTime.UtcNow, - IsConfirmed = false, - IsExpired = false, - Pin = pin, - DeviceId = request.DeviceId - }; - - _activeRequests.AddOrUpdate(pin, value, (k, v) => value); - - return ToOptimizedResult(new PinCreationResult - { - DeviceId = request.DeviceId, - IsConfirmed = false, - IsExpired = false, - Pin = pin - }); - } - - public object Get(GetPinStatusRequest request) - { - MyPinStatus status; - - if (!_activeRequests.TryGetValue(request.Pin, out status)) - { - throw new ResourceNotFoundException(); - } - - EnsureValid(request.DeviceId, status); - - return ToOptimizedResult(new PinStatusResult - { - Pin = status.Pin, - IsConfirmed = status.IsConfirmed, - IsExpired = status.IsExpired - }); - } - - public object Post(ExchangePinRequest request) - { - MyPinStatus status; - - if (!_activeRequests.TryGetValue(request.Pin, out status)) - { - throw new ResourceNotFoundException(); - } - - EnsureValid(request.DeviceId, status); - - if (!status.IsConfirmed) - { - throw new ResourceNotFoundException(); - } - - return ToOptimizedResult(new PinExchangeResult - { - // TODO: Add access token - UserId = status.UserId - }); - } - - public void Post(ValidatePinRequest request) - { - MyPinStatus status; - - if (!_activeRequests.TryGetValue(request.Pin, out status)) - { - throw new ResourceNotFoundException(); - } - - EnsureValid(status); - - status.IsConfirmed = true; - status.UserId = AuthorizationContext.GetAuthorizationInfo(Request).UserId; - } - - private void EnsureValid(string requestedDeviceId, MyPinStatus status) - { - if (!string.Equals(requestedDeviceId, status.DeviceId, StringComparison.OrdinalIgnoreCase)) - { - throw new ResourceNotFoundException(); - } - - EnsureValid(status); - } - - private void EnsureValid(MyPinStatus status) - { - if ((DateTime.UtcNow - status.CreationTimeUtc).TotalMinutes > 10) - { - status.IsExpired = true; - } - - if (status.IsExpired) - { - throw new ResourceNotFoundException(); - } - } - - private string GetNewPin() - { - var pin = GetNewPinInternal(); - - while (IsPinActive(pin)) - { - pin = GetNewPinInternal(); - } - - return pin; - } - - private string GetNewPinInternal() - { - var length = 5; - var pin = string.Empty; - - while (pin.Length < length) - { - var digit = new Random().Next(0, 9); - pin += digit.ToString(CultureInfo.InvariantCulture); - } - - return pin; - } - - private bool IsPinActive(string pin) - { - MyPinStatus status; - - if (!_activeRequests.TryGetValue(pin, out status)) - { - return true; - } - - if (status.IsExpired) - { - return true; - } - - return false; - } - - public class MyPinStatus : PinStatusResult - { - public DateTime CreationTimeUtc { get; set; } - public string DeviceId { get; set; } - public string UserId { get; set; } - } - } -} diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index bae8074fd..f66363da6 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -477,7 +477,7 @@ namespace MediaBrowser.Api.Playback var pts = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream) + if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && !state.VideoRequest.CopyTimestamps) { var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds; @@ -604,6 +604,10 @@ namespace MediaBrowser.Api.Playback { var seconds = Math.Round(TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds); + var setPtsParam = state.VideoRequest.CopyTimestamps + ? string.Empty + : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(UsCulture)); + if (state.SubtitleStream.IsExternal) { var subtitlePath = state.SubtitleStream.Path; @@ -621,18 +625,18 @@ namespace MediaBrowser.Api.Playback } // TODO: Perhaps also use original_size=1920x800 ?? - return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB", + return string.Format("subtitles=filename='{0}'{1}{2}", MediaEncoder.EscapeSubtitleFilterPath(subtitlePath), charsetParam, - seconds.ToString(UsCulture)); + setPtsParam); } var mediaPath = state.MediaPath ?? string.Empty; - return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB", + return string.Format("subtitles='{0}:si={1}'{2}", MediaEncoder.EscapeSubtitleFilterPath(mediaPath), state.InternalSubtitleStreamOffset.ToString(UsCulture), - seconds.ToString(UsCulture)); + setPtsParam); } /// @@ -865,6 +869,15 @@ namespace MediaBrowser.Api.Playback { if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { + if (state.VideoStream != null && state.VideoStream.Width.HasValue) + { + // This is hacky but not sure how to get the exact subtitle resolution + double height = state.VideoStream.Width.Value; + height /= 16; + height *= 9; + + arg += string.Format(" -canvas_size {0}:{1}", state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture), Convert.ToInt32(height).ToString(CultureInfo.InvariantCulture)); + } arg += " -i \"" + state.SubtitleStream.Path + "\""; } } @@ -1462,6 +1475,13 @@ namespace MediaBrowser.Api.Playback { // Duplicating ItemId because of MediaMonkey } + else if (i == 24) + { + if (videoRequest != null) + { + videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); + } + } } } @@ -2021,6 +2041,11 @@ namespace MediaBrowser.Api.Playback state.EstimateContentLength = transcodingProfile.EstimateContentLength; state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; + + if (state.VideoRequest != null) + { + state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps; + } } } } @@ -2184,9 +2209,9 @@ namespace MediaBrowser.Api.Playback if (state.VideoRequest != null) { - if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase) && state.VideoRequest.CopyTimestamps) { - //inputModifier += " -noaccurate_seek"; + inputModifier += " -noaccurate_seek"; } } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index eaf65bd6b..b7e180eca 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -36,6 +36,7 @@ namespace MediaBrowser.Api.Playback.Progressive [Route("/Videos/{Id}/stream.wmv", "GET")] [Route("/Videos/{Id}/stream.wtv", "GET")] [Route("/Videos/{Id}/stream.mov", "GET")] + [Route("/Videos/{Id}/stream.iso", "GET")] [Route("/Videos/{Id}/stream", "GET")] [Route("/Videos/{Id}/stream.ts", "HEAD")] [Route("/Videos/{Id}/stream.webm", "HEAD")] @@ -53,6 +54,7 @@ namespace MediaBrowser.Api.Playback.Progressive [Route("/Videos/{Id}/stream.wtv", "HEAD")] [Route("/Videos/{Id}/stream.m2ts", "HEAD")] [Route("/Videos/{Id}/stream.mov", "HEAD")] + [Route("/Videos/{Id}/stream.iso", "HEAD")] [Route("/Videos/{Id}/stream", "HEAD")] [Api(Description = "Gets a video stream")] public class GetVideoStream : VideoStreamRequest @@ -65,7 +67,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) + : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient) { } @@ -137,11 +140,6 @@ namespace MediaBrowser.Api.Playback.Progressive var isOutputMkv = string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase); - if (state.RunTimeTicks.HasValue) - { - //args += " -copyts -avoid_negative_ts disabled -start_at_zero"; - } - if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (state.VideoStream != null && IsH264(state.VideoStream) && @@ -150,6 +148,11 @@ namespace MediaBrowser.Api.Playback.Progressive args += " -bsf:v h264_mp4toannexb"; } + if (state.RunTimeTicks.HasValue && state.VideoRequest.CopyTimestamps) + { + args += " -copyts -avoid_negative_ts disabled -start_at_zero"; + } + return args; } @@ -160,10 +163,22 @@ namespace MediaBrowser.Api.Playback.Progressive var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + var hasCopyTs = false; // Add resolution params, if specified if (!hasGraphicalSubs) { - args += GetOutputSizeParam(state, videoCodec); + var outputSizeParam = GetOutputSizeParam(state, videoCodec); + args += outputSizeParam; + hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1; + } + + if (state.RunTimeTicks.HasValue && state.VideoRequest.CopyTimestamps) + { + if (!hasCopyTs) + { + args += " -copyts"; + } + args += " -avoid_negative_ts disabled -start_at_zero"; } var qualityParam = GetVideoQualityParam(state, videoCodec); diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 69f8e6e04..1135a3a54 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -187,6 +187,9 @@ namespace MediaBrowser.Api.Playback [ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool EnableAutoStreamCopy { get; set; } + [ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool CopyTimestamps { get; set; } + [ApiMember(Name = "Cabac", Description = "Enable if cabac encoding is required", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? Cabac { get; set; } diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index 949dac926..d2da2ee84 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -6,6 +6,7 @@ using ServiceStack; using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Controller.Configuration; namespace MediaBrowser.Api.ScheduledTasks { @@ -90,12 +91,14 @@ namespace MediaBrowser.Api.ScheduledTasks /// The task manager. private ITaskManager TaskManager { get; set; } + private readonly IServerConfigurationManager _config; + /// /// Initializes a new instance of the class. /// /// The task manager. - /// taskManager - public ScheduledTaskService(ITaskManager taskManager) + /// taskManager + public ScheduledTaskService(ITaskManager taskManager, IServerConfigurationManager config) { if (taskManager == null) { @@ -103,6 +106,7 @@ namespace MediaBrowser.Api.ScheduledTasks } TaskManager = taskManager; + _config = config; } /// @@ -194,6 +198,20 @@ namespace MediaBrowser.Api.ScheduledTasks throw new ResourceNotFoundException("Task not found"); } + var hasKey = task.ScheduledTask as IHasKey; + if (hasKey != null) + { + if (string.Equals(hasKey.Key, "SystemUpdateTask", StringComparison.OrdinalIgnoreCase)) + { + // This is a hack for now just to get the update application function to work when auto-update is disabled + if (!_config.Configuration.EnableAutoUpdate) + { + _config.Configuration.EnableAutoUpdate = true; + _config.SaveConfiguration(); + } + } + } + TaskManager.Execute(task); } diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 3996a0311..a35a1c3a2 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -415,23 +415,6 @@ 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 = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest { App = auth.Client, diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config index 83890e697..ecb1109ca 100644 --- a/MediaBrowser.Api/packages.config +++ b/MediaBrowser.Api/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index cb3a284c1..4bee8c042 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -49,7 +49,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll ..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index ff11c889a..1b5e260d7 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Implementations.Networking Logger = logger; } - private volatile List _localIpAddresses; + private List _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); /// @@ -29,24 +29,20 @@ namespace MediaBrowser.Common.Implementations.Networking /// IPAddress. public IEnumerable GetLocalIpAddresses() { - const int cacheMinutes = 3; - var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + const int cacheMinutes = 5; - if (_localIpAddresses == null || forceRefresh) + lock (_localIpAddressSyncLock) { - lock (_localIpAddressSyncLock) + var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + + if (_localIpAddresses == null || forceRefresh) { - forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + var addresses = GetLocalIpAddressesInternal().ToList(); - if (_localIpAddresses == null || forceRefresh) - { - var addresses = GetLocalIpAddressesInternal().ToList(); + _localIpAddresses = addresses; + _lastRefresh = DateTime.UtcNow; - _localIpAddresses = addresses; - _lastRefresh = DateTime.UtcNow; - - return addresses; - } + return addresses; } } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index a4ccbb6f8..8d727a112 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks Logger = logger; _fileSystem = fileSystem; - ReloadTriggerEvents(true); + InitTriggerEvents(); } /// @@ -233,11 +233,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// /// The _triggers /// - private volatile List _triggers; - /// - /// The _triggers sync lock - /// - private readonly object _triggersSyncLock = new object(); + private List _triggers; /// /// Gets the triggers that define when the task will run /// @@ -247,17 +243,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks { get { - if (_triggers == null) - { - lock (_triggersSyncLock) - { - if (_triggers == null) - { - _triggers = LoadTriggers(); - } - } - } - return _triggers; } set @@ -303,6 +288,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks } } + private void InitTriggerEvents() + { + _triggers = LoadTriggers(); + ReloadTriggerEvents(true); + } + public void ReloadTriggerEvents() { ReloadTriggerEvents(false); diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index 14f0f4719..814927228 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -1,6 +1,6 @@  - + diff --git a/MediaBrowser.Controller/Connect/IConnectManager.cs b/MediaBrowser.Controller/Connect/IConnectManager.cs index 1f7652221..e004eaccf 100644 --- a/MediaBrowser.Controller/Connect/IConnectManager.cs +++ b/MediaBrowser.Controller/Connect/IConnectManager.cs @@ -76,25 +76,5 @@ namespace MediaBrowser.Controller.Connect /// The token. /// true if [is authorization token valid] [the specified token]; otherwise, false. bool IsAuthorizationTokenValid(string token); - - /// - /// Gets the connect supporter summary. - /// - /// Task<ConnectSupporterSummary>. - Task GetConnectSupporterSummary(); - - /// - /// Removes the connect supporter. - /// - /// The identifier. - /// Task. - Task RemoveConnectSupporter(string id); - - /// - /// Adds the connect supporter. - /// - /// The identifier. - /// Task. - Task AddConnectSupporter(string id); } } diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 2b80b701e..3fd8d84dd 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -39,6 +39,7 @@ namespace MediaBrowser.Controller.Drawing public double PercentPlayed { get; set; } public string BackgroundColor { get; set; } + public string ForegroundLayer { get; set; } public bool HasDefaultOptions(string originalImagePath) { @@ -83,7 +84,8 @@ namespace MediaBrowser.Controller.Drawing !AddPlayedIndicator && PercentPlayed.Equals(0) && !UnplayedCount.HasValue && - string.IsNullOrEmpty(BackgroundColor); + string.IsNullOrEmpty(BackgroundColor) && + string.IsNullOrEmpty(ForegroundLayer); } private bool IsFormatSupported(string originalImagePath) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index d52e2b37f..3dfbdec56 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1359,7 +1359,7 @@ namespace MediaBrowser.Controller.Entities { if (!string.IsNullOrEmpty(info.Path)) { - var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path); + var itemByPath = LibraryManager.FindByPath(info.Path); if (itemByPath == null) { diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index 85ce3c781..832b9f6c1 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace MediaBrowser.Controller.Entities { - public interface IHasMediaSources : IHasId + public interface IHasMediaSources : IHasUserData { /// /// Gets the media sources. diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 0595d0569..8b623d64e 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -45,6 +45,8 @@ namespace MediaBrowser.Controller.Entities public string NameLessThan { get; set; } public string NameContains { get; set; } + public string Path { get; set; } + public string Person { get; set; } public string[] PersonIds { get; set; } public string[] ItemIds { get; set; } @@ -96,7 +98,7 @@ namespace MediaBrowser.Controller.Entities public int? MinIndexNumber { get; set; } public double? MinCriticRating { get; set; } public double? MinCommunityRating { get; set; } - + public string[] ChannelIds { get; set; } internal List ItemIdsFromPersonFilters { get; set; } @@ -112,7 +114,8 @@ namespace MediaBrowser.Controller.Entities public string[] TopParentIds { get; set; } public LocationType[] ExcludeLocationTypes { get; set; } - + public string[] PresetViews { get; set; } + public InternalItemsQuery() { BlockUnratedItems = new UnratedItem[] { }; @@ -137,6 +140,7 @@ namespace MediaBrowser.Controller.Entities TopParentIds = new string[] { }; ExcludeTags = new string[] { }; ExcludeLocationTypes = new LocationType[] { }; + PresetViews = new string[] { }; } public InternalItemsQuery(User user) @@ -153,7 +157,7 @@ namespace MediaBrowser.Controller.Entities } ExcludeTags = policy.BlockedTags; - + User = user; } } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 93eac058d..9efa609ef 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Providers; +using System; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Users; @@ -6,6 +7,7 @@ using MoreLinq; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using System.Threading.Tasks; using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Entities.TV @@ -127,6 +129,30 @@ namespace MediaBrowser.Controller.Entities.TV get { return (IndexNumber ?? -1) == 0; } } + public override Task> GetItems(InternalItemsQuery query) + { + var user = query.User; + + Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + IEnumerable items; + + if (query.User == null) + { + items = query.Recursive + ? GetRecursiveChildren(filter) + : Children.Where(filter); + } + else + { + items = GetEpisodes(query.User).Where(filter); + } + + var result = PostFilterAndSort(items, query); + + return Task.FromResult(result); + } + /// /// Gets the episodes. /// diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 420b3c313..aa07ab378 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -157,6 +157,32 @@ namespace MediaBrowser.Controller.Entities.TV return GetSeasons(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } + public override Task> GetItems(InternalItemsQuery query) + { + var user = query.User; + + Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + IEnumerable items; + + if (query.User == null) + { + items = query.Recursive + ? GetRecursiveChildren(filter) + : Children.Where(filter); + } + else + { + items = query.Recursive + ? GetRecursiveChildren(user, filter) + : GetSeasons(user).Where(filter); + } + + var result = PostFilterAndSort(items, query); + + return Task.FromResult(result); + } + public IEnumerable GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired) { var seasons = base.GetChildren(user, true) diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs index 5f0e62537..16c37e7d3 100644 --- a/MediaBrowser.Controller/Entities/UserItemData.cs +++ b/MediaBrowser.Controller/Entities/UserItemData.cs @@ -78,7 +78,17 @@ namespace MediaBrowser.Controller.Entities /// /// true if played; otherwise, false. public bool Played { get; set; } - + /// + /// Gets or sets the index of the audio stream. + /// + /// The index of the audio stream. + public int? AudioStreamIndex { get; set; } + /// + /// Gets or sets the index of the subtitle stream. + /// + /// The index of the subtitle stream. + public int? SubtitleStreamIndex { get; set; } + /// /// This is an interpreted property to indicate likes or dislikes /// This should never be serialized. diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index b7946cb92..daf590871 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -30,7 +30,8 @@ namespace MediaBrowser.Controller.Entities var result = await UserViewManager.GetUserViews(new UserViewQuery { - UserId = query.User.Id.ToString("N") + UserId = query.User.Id.ToString("N"), + PresetViews = query.PresetViews }, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 1c515edd5..ff44953ef 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -56,6 +56,13 @@ namespace MediaBrowser.Controller.Library /// Task{Person}. Person GetPerson(string name); + /// + /// Finds the by path. + /// + /// The path. + /// BaseItem. + BaseItem FindByPath(string path); + /// /// Gets the artist. /// diff --git a/MediaBrowser.Controller/Library/ILibraryMonitor.cs b/MediaBrowser.Controller/Library/ILibraryMonitor.cs index 918382f04..e965e47d6 100644 --- a/MediaBrowser.Controller/Library/ILibraryMonitor.cs +++ b/MediaBrowser.Controller/Library/ILibraryMonitor.cs @@ -32,5 +32,12 @@ namespace MediaBrowser.Controller.Library /// /// The path. void ReportFileSystemChanged(string path); + + /// + /// Determines whether [is path locked] [the specified path]. + /// + /// The path. + /// true if [is path locked] [the specified path]; otherwise, false. + bool IsPathLocked(string path); } } \ No newline at end of file diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 32b8abdc5..7d8df96ed 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -25,6 +25,12 @@ namespace MediaBrowser.Controller.LiveTv /// The id of the channel. public string Id { get; set; } + /// + /// Gets or sets the tuner host identifier. + /// + /// The tuner host identifier. + public string TunerHostId { get; set; } + /// /// Gets or sets the type of the channel. /// diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 05c7448c3..501e48a74 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Channels; +using System; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; @@ -343,11 +344,11 @@ namespace MediaBrowser.Controller.LiveTv /// /// Adds the information to program dto. /// - /// The item. - /// The dto. + /// The programs. /// The fields. /// The user. - void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, List fields, User user = null); + /// Task. + Task AddInfoToProgramDto(List> programs, List fields, User user = null); /// /// Saves the tuner host. /// diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 2e3a71f70..498602ddf 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -46,6 +46,9 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + } + public interface IConfigurableTunerHost + { /// /// Validates the specified information. /// diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs index 46cf4dd98..5c001f288 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs @@ -59,6 +59,12 @@ namespace MediaBrowser.Controller.LiveTv /// The clients. public List Clients { get; set; } + /// + /// Gets or sets a value indicating whether this instance can reset. + /// + /// true if this instance can reset; otherwise, false. + public bool CanReset { get; set; } + public LiveTvTunerInfo() { Clients = new List(); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 9c17c8d9b..f74d82caa 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -46,7 +46,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll ..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index bb8841222..f8f4e9ec9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -44,6 +44,7 @@ namespace MediaBrowser.Controller.MediaEncoding public int? CpuCoreLimit { get; set; } public bool ReadInputAtNativeFramerate { get; set; } public SubtitleDeliveryMethod SubtitleMethod { get; set; } + public bool CopyTimestamps { get; set; } /// /// Gets a value indicating whether this instance has fixed resolution. diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index 36ef6ca1f..2192ebcac 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -63,7 +63,8 @@ namespace MediaBrowser.Controller.Providers try { // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks) - var list = _fileSystem.GetFileSystemEntries(path); + var list = _fileSystem.GetFileSystemEntries(path) + .ToList(); // Seeing dupes on some users file system for some reason foreach (var item in list) diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index dc9612c84..fa74c5749 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -250,6 +250,13 @@ namespace MediaBrowser.Controller.Session /// Task{SessionInfo}. Task AuthenticateNewSession(AuthenticationRequest request); + /// + /// Creates the new session. + /// + /// The request. + /// Task<AuthenticationResult>. + Task CreateNewSession(AuthenticationRequest request); + /// /// Reports the capabilities. /// diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config index 8439bee10..34e16d2ad 100644 --- a/MediaBrowser.Controller/packages.config +++ b/MediaBrowser.Controller/packages.config @@ -1,6 +1,6 @@  - + diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 9071eecb2..d1a415f73 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -453,32 +453,17 @@ namespace MediaBrowser.Dlna.ContentDirectory sortOrders.Add(ItemSortBy.SortName); } - QueryResult queryResult; - - if (folder is UserRootFolder) + var queryResult = await folder.GetItems(new InternalItemsQuery { - var views = await _userViewManager.GetUserViews(new UserViewQuery { UserId = user.Id.ToString("N"), PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } }, CancellationToken.None) - .ConfigureAwait(false); + Limit = limit, + StartIndex = startIndex, + SortBy = sortOrders.ToArray(), + SortOrder = sort.SortOrder, + User = user, + Filter = FilterUnsupportedContent, + PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } - queryResult = new QueryResult - { - Items = views.Cast().ToArray() - }; - queryResult.TotalRecordCount = queryResult.Items.Length; - } - else - { - queryResult = await folder.GetItems(new InternalItemsQuery - { - Limit = limit, - StartIndex = startIndex, - SortBy = sortOrders.ToArray(), - SortOrder = sort.SortOrder, - User = user, - Filter = FilterUnsupportedContent - - }).ConfigureAwait(false); - } + }).ConfigureAwait(false); var options = _config.GetDlnaConfiguration(); diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index 2d672ee87..78dde72c5 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -42,7 +42,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll ..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index 06697dee6..0328111c5 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -85,8 +85,6 @@ namespace MediaBrowser.Dlna.PlayTo try { - var uri = new Uri(location); - lock (_nonRendererUrls) { if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10) @@ -101,6 +99,7 @@ namespace MediaBrowser.Dlna.PlayTo } } + var uri = new Uri(location); var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false); if (device.RendererCommands == null) diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index 37ce0ce67..09e10cecf 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -31,8 +31,8 @@ namespace MediaBrowser.Dlna.Profiles MaxIconWidth = 48; MaxIconHeight = 48; - MaxStreamingBitrate = 10000000; - MaxStaticBitrate = 10000000; + MaxStreamingBitrate = 12000000; + MaxStaticBitrate = 12000000; MusicStreamingTranscodingBitrate = 128000; MusicSyncBitrate = 128000; diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs index e26c3c443..1b06e1d6a 100644 --- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -119,8 +119,7 @@ namespace MediaBrowser.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp3", - AudioCodec = "mp3", + Container = "mp3,flac", Type = DlnaProfileType.Audio }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs b/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs index e82f9b58c..367aa744b 100644 --- a/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs +++ b/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs @@ -60,8 +60,8 @@ namespace MediaBrowser.Dlna.Profiles new DirectPlayProfile { Container = "ts", - VideoCodec = "h264", - AudioCodec = "ac3", + VideoCodec = "h264,mpeg2video", + AudioCodec = "ac3,aac,mp3", Type = DlnaProfileType.Video }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml b/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml index 29f7f7c75..409fe95e8 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -41,9 +41,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index 6559e8971..ad378b01c 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -17,8 +17,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -34,9 +34,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index da033f86e..ea0ea8143 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -22,8 +22,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -38,9 +38,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml index d916f9984..7ce7668a1 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -40,8 +40,8 @@ - - + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml index 702f4ee6c..49f0dde8c 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -44,9 +44,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml b/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml index 4a06c3122..d3bc20545 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml @@ -41,9 +41,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml index e2b42a77e..95aed7c1d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -43,9 +43,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index 3d37a9c9e..c7dbc62f9 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -21,8 +21,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -38,9 +38,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml index 4249fd6ca..2f28f67a1 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -44,9 +44,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml index b468391ef..044e32eb4 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -51,9 +51,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml b/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml index ae9fc4de4..10944c3ac 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml @@ -17,8 +17,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -39,9 +39,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml index 2355fe7a4..4dd7db809 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -47,13 +47,13 @@ - + - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml index f273fcae0..5dd426a51 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -49,9 +49,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index 6132e19dc..ac4ce57a0 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml @@ -25,8 +25,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -48,9 +48,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml index fbd67262a..1f2ebc86d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -46,9 +46,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml index 3f61b930b..5dc86c32e 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -49,9 +49,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml index 0774d53f9..b34828366 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -51,9 +51,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml index c052cf85e..e94b0058b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -56,9 +56,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml index 91cfc0db2..653702b4a 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -56,9 +56,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml index b0825c8f6..8f78626b2 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -46,9 +46,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml index 670c8a6df..d4277d155 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -46,9 +46,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml b/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml index 03d39350f..433db1958 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -41,9 +41,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index e17fc087b..0bbb3f2f1 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -52,9 +52,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index 0724c2a83..969bcb563 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -46,9 +46,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index e4344f6ab..8dbbf19e9 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -24,8 +24,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -37,7 +37,7 @@ false - + @@ -47,9 +47,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index 8812118ea..b59ce5f2a 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -23,8 +23,8 @@ 480 48 48 - 10000000 - 10000000 + 12000000 + 12000000 128000 128000 DMS-1.50 @@ -44,9 +44,9 @@ - - - + + + diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs index 4f7dffdd9..70f6d0e53 100644 --- a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs +++ b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs @@ -127,10 +127,14 @@ namespace MediaBrowser.Dlna.Ssdp args.EndPoint = endPoint; args.LocalEndPoint = new IPEndPoint(localIp, 0); - if (!_ssdpHandler.IsSelfNotification(args)) + if (_ssdpHandler.IgnoreMessage(args, true)) { - TryCreateDevice(args); + return; } + + _ssdpHandler.LogMessageReceived(args, true); + + TryCreateDevice(args); } } @@ -217,14 +221,6 @@ namespace MediaBrowser.Dlna.Ssdp return; } - if (_config.GetDlnaConfiguration().EnableDebugLog) - { - var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); - var headerText = string.Join(",", headerTexts.ToArray()); - - _logger.Debug("{0} Device message received from {1}. Headers: {2}", args.Method, args.EndPoint, headerText); - } - EventHelper.FireEventIfNotNull(DeviceDiscovered, this, args, _logger); } diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 881f70165..e48471e34 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -86,8 +86,15 @@ namespace MediaBrowser.Dlna.Ssdp public event EventHandler MessageReceived; - private async void OnMessageReceived(SsdpMessageEventArgs args) + private async void OnMessageReceived(SsdpMessageEventArgs args, bool isMulticast) { + if (IgnoreMessage(args, isMulticast)) + { + return; + } + + LogMessageReceived(args, isMulticast); + var headers = args.Headers; string st; @@ -108,6 +115,59 @@ namespace MediaBrowser.Dlna.Ssdp EventHelper.FireEventIfNotNull(MessageReceived, this, args, _logger); } + internal void LogMessageReceived(SsdpMessageEventArgs args, bool isMulticast) + { + var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog; + + if (enableDebugLogging) + { + var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); + var headerText = string.Join(",", headerTexts.ToArray()); + + var protocol = isMulticast ? "Multicast" : "Unicast"; + var localEndPointString = args.LocalEndPoint == null ? "null" : args.LocalEndPoint.ToString(); + _logger.Debug("{0} message received from {1} on {3}. Protocol: {4} Headers: {2}", args.Method, args.EndPoint, headerText, localEndPointString, protocol); + } + } + + internal bool IgnoreMessage(SsdpMessageEventArgs args, bool isMulticast) + { + string usn; + if (args.Headers.TryGetValue("USN", out usn)) + { + // USN=uuid:b67df29b5c379445fde78c3774ab518d::urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1 + if (RegisteredDevices.Select(i => i.USN).Contains(usn, StringComparer.OrdinalIgnoreCase)) + { + //var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); + //var headerText = string.Join(",", headerTexts.ToArray()); + + //var protocol = isMulticast ? "Multicast" : "Unicast"; + //var localEndPointString = args.LocalEndPoint == null ? "null" : args.LocalEndPoint.ToString(); + //_logger.Debug("IGNORING {0} message received from {1} on {3}. Protocol: {4} Headers: {2}", args.Method, args.EndPoint, headerText, localEndPointString, protocol); + + return true; + } + } + + string serverId; + if (args.Headers.TryGetValue("X-EMBY-SERVERID", out serverId)) + { + if (string.Equals(serverId, _appHost.SystemId, StringComparison.OrdinalIgnoreCase)) + { + //var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); + //var headerText = string.Join(",", headerTexts.ToArray()); + + //var protocol = isMulticast ? "Multicast" : "Unicast"; + //var localEndPointString = args.LocalEndPoint == null ? "null" : args.LocalEndPoint.ToString(); + //_logger.Debug("IGNORING {0} message received from {1} on {3}. Protocol: {4} Headers: {2}", args.Method, args.EndPoint, headerText, localEndPointString, protocol); + + return true; + } + } + + return false; + } + public IEnumerable RegisteredDevices { get @@ -126,7 +186,7 @@ namespace MediaBrowser.Dlna.Ssdp RestartSocketListener(); ReloadAliveNotifier(); - //CreateUnicastClient(); + CreateUnicastClient(); SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; @@ -146,6 +206,7 @@ namespace MediaBrowser.Dlna.Ssdp values["HOST"] = "239.255.255.250:1900"; values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2"; + values["X-EMBY-SERVERID"] = _appHost.SystemId; values["MAN"] = "\"ssdp:discover\""; @@ -162,7 +223,7 @@ namespace MediaBrowser.Dlna.Ssdp // UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2) SendDatagram(msg, _ssdpEndp, localIp, true); - //SendUnicastRequest(msg); + SendUnicastRequest(msg); } public async void SendDatagram(string msg, @@ -177,7 +238,7 @@ namespace MediaBrowser.Dlna.Ssdp { if (i > 0) { - await Task.Delay(200).ConfigureAwait(false); + await Task.Delay(500).ConfigureAwait(false); } var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging); @@ -242,8 +303,8 @@ namespace MediaBrowser.Dlna.Ssdp var msg = new SsdpMessageBuilder().BuildMessage(header, values); - SendDatagram(msg, endpoint, null, false, 1); - SendDatagram(msg, endpoint, new IPEndPoint(d.Address, 0), false, 1); + SendDatagram(msg, endpoint, null, false, 2); + SendDatagram(msg, endpoint, new IPEndPoint(d.Address, 0), false, 2); //SendDatagram(header, values, endpoint, null, true); if (enableDebugLogging) @@ -324,20 +385,7 @@ namespace MediaBrowser.Dlna.Ssdp var args = SsdpHelper.ParseSsdpResponse(received); args.EndPoint = endpoint; - if (IsSelfNotification(args)) - { - return; - } - - if (enableDebugLogging) - { - var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)); - var headerText = string.Join(",", headerTexts.ToArray()); - - _logger.Debug("{0} message received from {1} on {3}. Headers: {2}", args.Method, args.EndPoint, headerText, _multicastSocket.LocalEndPoint); - } - - OnMessageReceived(args); + OnMessageReceived(args, true); } catch (ObjectDisposedException) { @@ -357,26 +405,6 @@ namespace MediaBrowser.Dlna.Ssdp } } - internal bool IsSelfNotification(SsdpMessageEventArgs args) - { - // Avoid responding to self search messages - //string serverId; - //if (args.Headers.TryGetValue("X-EMBYSERVERID", out serverId) && - // string.Equals(serverId, _appHost.SystemId, StringComparison.OrdinalIgnoreCase)) - //{ - // return true; - //} - - string server; - args.Headers.TryGetValue("SERVER", out server); - - if (string.Equals(server, _serverSignature, StringComparison.OrdinalIgnoreCase)) - { - //return true; - } - return false; - } - public void Dispose() { _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated; @@ -448,7 +476,8 @@ namespace MediaBrowser.Dlna.Ssdp var msg = new SsdpMessageBuilder().BuildMessage(header, values); - SendDatagram(msg, _ssdpEndp, new IPEndPoint(dev.Address, 0), true); + SendDatagram(msg, _ssdpEndp, new IPEndPoint(dev.Address, 0), true, 1); + //SendUnicastRequest(msg, 1); } public void RegisterNotification(string uuid, Uri descriptionUri, IPAddress address, IEnumerable services) @@ -489,14 +518,7 @@ namespace MediaBrowser.Dlna.Ssdp _logger.ErrorException("Error creating unicast client", ex); } - try - { - UnicastSetBeginReceive(); - } - catch (Exception ex) - { - _logger.ErrorException("Error in UnicastSetBeginReceive", ex); - } + UnicastSetBeginReceive(); } } @@ -522,11 +544,18 @@ namespace MediaBrowser.Dlna.Ssdp /// private void UnicastSetBeginReceive() { - var ipRxEnd = new IPEndPoint(IPAddress.Any, _unicastPort); - var udpListener = new UdpState { EndPoint = ipRxEnd }; + try + { + var ipRxEnd = new IPEndPoint(IPAddress.Any, _unicastPort); + var udpListener = new UdpState { EndPoint = ipRxEnd }; - udpListener.UdpClient = _unicastClient; - _unicastClient.BeginReceive(UnicastReceiveCallback, udpListener); + udpListener.UdpClient = _unicastClient; + _unicastClient.BeginReceive(UnicastReceiveCallback, udpListener); + } + catch (Exception ex) + { + _logger.ErrorException("Error in UnicastSetBeginReceive", ex); + } } /// @@ -540,37 +569,66 @@ namespace MediaBrowser.Dlna.Ssdp var endpoint = ((UdpState)(ar.AsyncState)).EndPoint; if (udpClient.Client != null) { - var responseBytes = udpClient.EndReceive(ar, ref endpoint); - var args = SsdpHelper.ParseSsdpResponse(responseBytes); + try + { + var responseBytes = udpClient.EndReceive(ar, ref endpoint); + var args = SsdpHelper.ParseSsdpResponse(responseBytes); - args.EndPoint = endpoint; + args.EndPoint = endpoint; - OnMessageReceived(args); + OnMessageReceived(args, false); - UnicastSetBeginReceive(); + UnicastSetBeginReceive(); + } + catch (ObjectDisposedException) + { + + } + catch (SocketException) + { + + } } } - private async void SendUnicastRequest(string request) + private void SendUnicastRequest(string request, int sendCount = 3) { if (_unicastClient == null) { return; } - _logger.Debug("Sending unicast search request"); - - byte[] req = Encoding.ASCII.GetBytes(request); var ipSsdp = IPAddress.Parse(SSDPAddr); var ipTxEnd = new IPEndPoint(ipSsdp, SSDPPort); - for (var i = 0; i < 3; i++) + SendUnicastRequest(request, ipTxEnd, sendCount); + } + + private async void SendUnicastRequest(string request, IPEndPoint toEndPoint, int sendCount = 3) + { + if (_unicastClient == null) { - if (i > 0) + return; + } + + //_logger.Debug("Sending unicast request"); + + byte[] req = Encoding.ASCII.GetBytes(request); + + try + { + for (var i = 0; i < sendCount; i++) { - await Task.Delay(50).ConfigureAwait(false); + if (i > 0) + { + await Task.Delay(50).ConfigureAwait(false); + } + _unicastClient.Send(req, req.Length, toEndPoint); } - _unicastClient.Send(req, req.Length, ipTxEnd); + } + catch (Exception ex) + { + _logger.ErrorException("Error in SendUnicastRequest", ex); } } diff --git a/MediaBrowser.Dlna/packages.config b/MediaBrowser.Dlna/packages.config index 83890e697..ecb1109ca 100644 --- a/MediaBrowser.Dlna/packages.config +++ b/MediaBrowser.Dlna/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index c673c01df..0ecb1e007 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -33,7 +33,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll ..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll diff --git a/MediaBrowser.LocalMetadata/packages.config b/MediaBrowser.LocalMetadata/packages.config index 28556744d..0639208dd 100644 --- a/MediaBrowser.LocalMetadata/packages.config +++ b/MediaBrowser.LocalMetadata/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 4fabed850..aef206f13 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -460,6 +460,15 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { + if (state.VideoStream != null && state.VideoStream.Width.HasValue) + { + // This is hacky but not sure how to get the exact subtitle resolution + double height = state.VideoStream.Width.Value; + height /= 16; + height *= 9; + + arg += string.Format(" -canvas_size {0}:{1}", state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture), Convert.ToInt32(height).ToString(CultureInfo.InvariantCulture)); + } arg += " -i \"" + state.SubtitleStream.Path + "\""; } } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 252386af0..c64b574a9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -794,6 +794,8 @@ namespace MediaBrowser.MediaEncoding.Encoder state.EstimateContentLength = transcodingProfile.EstimateContentLength; state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; + + state.Options.CopyTimestamps = transcodingProfile.CopyTimestamps; } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 30e50fecd..8d1b4057b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -129,7 +129,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The request. /// The cancellation token. /// Task. - public Task GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) + public Task GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) { var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; @@ -175,7 +175,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The cancellation token. /// Task{MediaInfoResult}. /// ffprobe failed - streams and format are both null. - private async Task GetMediaInfoInternal(string inputPath, + private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, @@ -934,7 +934,13 @@ namespace MediaBrowser.MediaEncoding.Encoder _mediaEncoder._runningProcesses.Remove(this); } - process.Dispose(); + try + { + process.Dispose(); + } + catch (Exception ex) + { + } } private bool _disposed; diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 69f6186c7..89138592a 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -41,7 +41,7 @@ False - ..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll + ..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 31f6af181..57c2f75cc 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -7,7 +7,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; +using System.Xml; using CommonIO; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; @@ -27,7 +30,7 @@ namespace MediaBrowser.MediaEncoding.Probing public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol) { - var info = new Model.MediaInfo.MediaInfo + var info = new MediaInfo { Path = path, Protocol = protocol @@ -56,40 +59,100 @@ namespace MediaBrowser.MediaEncoding.Probing } } - if (isAudio) + var tags = new Dictionary(StringComparer.OrdinalIgnoreCase); + var tagStreamType = isAudio ? "audio" : "video"; + + if (data.streams != null) { - SetAudioRuntimeTicks(data, info); + var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase)); - var tags = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream - // so let's create a combined list of both - - if (data.streams != null) + if (tagStream != null && tagStream.tags != null) { - var audioStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, "audio", StringComparison.OrdinalIgnoreCase)); - - if (audioStream != null && audioStream.tags != null) - { - foreach (var pair in audioStream.tags) - { - tags[pair.Key] = pair.Value; - } - } - } - - if (data.format != null && data.format.tags != null) - { - foreach (var pair in data.format.tags) + foreach (var pair in tagStream.tags) { tags[pair.Key] = pair.Value; } } + } + + if (data.format != null && data.format.tags != null) + { + foreach (var pair in data.format.tags) + { + tags[pair.Key] = pair.Value; + } + } + + FetchGenres(info, tags); + var shortOverview = FFProbeHelpers.GetDictionaryValue(tags, "description"); + var overview = FFProbeHelpers.GetDictionaryValue(tags, "synopsis"); + + if (string.IsNullOrWhiteSpace(overview)) + { + overview = shortOverview; + shortOverview = null; + } + if (string.IsNullOrWhiteSpace(overview)) + { + overview = FFProbeHelpers.GetDictionaryValue(tags, "desc"); + } + + if (!string.IsNullOrWhiteSpace(overview)) + { + info.Overview = overview; + } + + if (!string.IsNullOrWhiteSpace(shortOverview)) + { + info.ShortOverview = shortOverview; + } + + var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); + if (!string.IsNullOrWhiteSpace(title)) + { + info.Name = title; + } + + info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); + + // Several different forms of retaildate + info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "date"); + + if (isAudio) + { + SetAudioRuntimeTicks(data, info); + + // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the info stream + // so let's create a combined list of both SetAudioInfoFromTags(info, tags); } else { + FetchStudios(info, tags, "copyright"); + + var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC"); + if (!string.IsNullOrWhiteSpace(iTunEXTC)) + { + var parts = iTunEXTC.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + // Example + // mpaa|G|100|For crude humor + if (parts.Length == 4) + { + info.OfficialRating = parts[1]; + info.OfficialRatingDescription = parts[3]; + } + } + + var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI"); + if (!string.IsNullOrWhiteSpace(itunesXml)) + { + FetchFromItunesInfo(itunesXml, info); + } + if (data.format != null && !string.IsNullOrEmpty(data.format.duration)) { info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks; @@ -108,10 +171,223 @@ namespace MediaBrowser.MediaEncoding.Probing return info; } + private void FetchFromItunesInfo(string xml, MediaInfo info) + { + // Make things simpler and strip out the dtd + xml = xml.Substring(xml.IndexOf("" + xml; + + // \n\n\n\n\tcast\n\t\n\t\t\n\t\t\tname\n\t\t\tBlender Foundation\n\t\t\n\t\t\n\t\t\tname\n\t\t\tJanus Bager Kristensen\n\t\t\n\t\n\tdirectors\n\t\n\t\t\n\t\t\tname\n\t\t\tSacha Goedegebure\n\t\t\n\t\n\tstudio\n\tBlender Foundation\n\n\n + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml))) + { + using (var streamReader = new StreamReader(stream)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader)) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "dict": + using (var subtree = reader.ReadSubtree()) + { + ReadFromDictNode(subtree, info); + } + break; + default: + reader.Skip(); + break; + } + } + } + } + } + } + } + + private void ReadFromDictNode(XmlReader reader, MediaInfo info) + { + reader.MoveToContent(); + + string currentKey = null; + List pairs = new List(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "key": + if (!string.IsNullOrWhiteSpace(currentKey)) + { + ProcessPairs(currentKey, pairs, info); + } + currentKey = reader.ReadElementContentAsString(); + pairs = new List(); + break; + case "string": + var value = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(value)) + { + pairs.Add(new NameValuePair + { + Name = value, + Value = value + }); + } + break; + case "array": + if (!string.IsNullOrWhiteSpace(currentKey)) + { + using (var subtree = reader.ReadSubtree()) + { + pairs.AddRange(ReadValueArray(subtree)); + } + } + break; + default: + reader.Skip(); + break; + } + } + } + } + + private List ReadValueArray(XmlReader reader) + { + reader.MoveToContent(); + + List pairs = new List(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "dict": + using (var subtree = reader.ReadSubtree()) + { + var dict = GetNameValuePair(subtree); + if (dict != null) + { + pairs.Add(dict); + } + } + break; + default: + reader.Skip(); + break; + } + } + } + + return pairs; + } + + private void ProcessPairs(string key, List pairs, MediaInfo info) + { + if (string.Equals(key, "studio", StringComparison.OrdinalIgnoreCase)) + { + foreach (var pair in pairs) + { + info.Studios.Add(pair.Value); + } + + info.Studios = info.Studios + .Where(i => !string.IsNullOrWhiteSpace(i)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + } + else if (string.Equals(key, "screenwriters", StringComparison.OrdinalIgnoreCase)) + { + foreach (var pair in pairs) + { + info.People.Add(new BaseItemPerson + { + Name = pair.Value, + Type = PersonType.Writer + }); + } + } + else if (string.Equals(key, "producers", StringComparison.OrdinalIgnoreCase)) + { + foreach (var pair in pairs) + { + info.People.Add(new BaseItemPerson + { + Name = pair.Value, + Type = PersonType.Producer + }); + } + } + else if (string.Equals(key, "directors", StringComparison.OrdinalIgnoreCase)) + { + foreach (var pair in pairs) + { + info.People.Add(new BaseItemPerson + { + Name = pair.Value, + Type = PersonType.Director + }); + } + } + } + + private NameValuePair GetNameValuePair(XmlReader reader) + { + reader.MoveToContent(); + + string name = null; + string value = null; + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "key": + name = reader.ReadElementContentAsString(); + break; + case "string": + value = reader.ReadElementContentAsString(); + break; + default: + reader.Skip(); + break; + } + } + } + + if (string.IsNullOrWhiteSpace(name) || + string.IsNullOrWhiteSpace(value)) + { + return null; + } + + return new NameValuePair + { + Name = name, + Value = value + }; + } + /// /// Converts ffprobe stream info to our MediaStream class /// - /// if set to true [is audio]. + /// if set to true [is info]. /// The stream info. /// The format info. /// MediaStream. @@ -176,7 +452,7 @@ namespace MediaBrowser.MediaEncoding.Probing } else if (string.Equals(streamInfo.codec_type, "video", StringComparison.OrdinalIgnoreCase)) { - stream.Type = isAudio || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) + stream.Type = isAudio || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) ? MediaStreamType.EmbeddedImage : MediaStreamType.Video; @@ -388,11 +664,11 @@ namespace MediaBrowser.MediaEncoding.Probing return null; } - private void SetAudioRuntimeTicks(InternalMediaInfoResult result, Model.MediaInfo.MediaInfo data) + private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data) { if (result.streams != null) { - // Get the first audio stream + // Get the first info stream var stream = result.streams.FirstOrDefault(s => string.Equals(s.codec_type, "audio", StringComparison.OrdinalIgnoreCase)); if (stream != null) @@ -430,16 +706,8 @@ namespace MediaBrowser.MediaEncoding.Probing } } - private void SetAudioInfoFromTags(Model.MediaInfo.MediaInfo audio, Dictionary tags) + private void SetAudioInfoFromTags(MediaInfo audio, Dictionary tags) { - var title = FFProbeHelpers.GetDictionaryValue(tags, "title"); - - // Only set Name if title was found in the dictionary - if (!string.IsNullOrEmpty(title)) - { - audio.Title = title; - } - var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); if (!string.IsNullOrWhiteSpace(composer)) { @@ -458,6 +726,26 @@ namespace MediaBrowser.MediaEncoding.Probing } } + var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist"); + + if (!string.IsNullOrWhiteSpace(lyricist)) + { + foreach (var person in Split(lyricist, false)) + { + audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); + } + } + // Check for writer some music is tagged that way as alternative to composer/lyricist + var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer"); + + if (!string.IsNullOrWhiteSpace(writer)) + { + foreach (var person in Split(writer, false)) + { + audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); + } + } + audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); @@ -511,22 +799,12 @@ namespace MediaBrowser.MediaEncoding.Probing // Disc number audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc"); - audio.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); - - // Several different forms of retaildate - audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "date"); - // If we don't have a ProductionYear try and get it from PremiereDate if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) { audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; } - FetchGenres(audio, tags); - // There's several values in tags may or may not be present FetchStudios(audio, tags, "organization"); FetchStudios(audio, tags, "ensemble"); @@ -655,10 +933,10 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// Gets the studios from the tags collection /// - /// The audio. + /// The info. /// The tags. /// Name of the tag. - private void FetchStudios(Model.MediaInfo.MediaInfo audio, Dictionary tags, string tagName) + private void FetchStudios(MediaInfo info, Dictionary tags, string tagName) { var val = FFProbeHelpers.GetDictionaryValue(tags, tagName); @@ -669,19 +947,19 @@ namespace MediaBrowser.MediaEncoding.Probing foreach (var studio in studios) { // Sometimes the artist name is listed here, account for that - if (audio.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase)) + if (info.Artists.Contains(studio, StringComparer.OrdinalIgnoreCase)) { continue; } - if (audio.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase)) + if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase)) { continue; } - audio.Studios.Add(studio); + info.Studios.Add(studio); } - audio.Studios = audio.Studios + info.Studios = info.Studios .Where(i => !string.IsNullOrWhiteSpace(i)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); @@ -693,7 +971,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The information. /// The tags. - private void FetchGenres(Model.MediaInfo.MediaInfo info, Dictionary tags) + private void FetchGenres(MediaInfo info, Dictionary tags) { var val = FFProbeHelpers.GetDictionaryValue(tags, "genre"); @@ -764,7 +1042,7 @@ namespace MediaBrowser.MediaEncoding.Probing private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames) - private void FetchWtvInfo(Model.MediaInfo.MediaInfo video, InternalMediaInfoResult data) + private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) { if (data.format == null || data.format.tags == null) { @@ -775,15 +1053,16 @@ namespace MediaBrowser.MediaEncoding.Probing if (!string.IsNullOrWhiteSpace(genres)) { - //genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre"); - } - - if (!string.IsNullOrWhiteSpace(genres)) - { - video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries) + var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()) .ToList(); + + // If this is empty then don't overwrite genres that might have been fetched earlier + if (genreList.Count > 0) + { + video.Genres = genreList; + } } var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating"); diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config index d6a4fc90f..0a8dd0adc 100644 --- a/MediaBrowser.MediaEncoding/packages.config +++ b/MediaBrowser.MediaEncoding/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MediaBrowser.Model.Portable/Fody.targets b/MediaBrowser.Model.Portable/Fody.targets deleted file mode 100644 index a668a51fc..000000000 --- a/MediaBrowser.Model.Portable/Fody.targets +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - $(NCrunchOriginalSolutionDir) - - - - - $(SolutionDir) - - - - - $(MSBuildProjectDirectory)\..\ - - - - - - - $(KeyOriginatorFile) - - - - - $(AssemblyOriginatorKeyFile) - - - - - - - - - - $(ProjectDir)$(IntermediateOutputPath) - Low - $(SignAssembly) - $(MSBuildThisFileDirectory) - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index cea13f86e..5dcfded6f 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -15,7 +15,6 @@ 512 {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} ..\ - ..\packages\Fody.1.19.1.0 @@ -58,14 +57,6 @@ - - - - - - ..\packages\PropertyChanged.Fody.1.41.0.0\Lib\portable-net4+sl4+wp7+win8+MonoAndroid16+MonoTouch40\PropertyChanged.dll - False - @@ -1254,7 +1245,13 @@ - + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + - - - - - \ No newline at end of file diff --git a/MediaBrowser.Model/FodyWeavers.xml b/MediaBrowser.Model/FodyWeavers.xml index bb0f322ee..736992810 100644 --- a/MediaBrowser.Model/FodyWeavers.xml +++ b/MediaBrowser.Model/FodyWeavers.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 838325d68..e705102db 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Model.LiveTv public int PrePaddingSeconds { get; set; } public int PostPaddingSeconds { get; set; } - + public LiveTvOptions() { EnableMovieProviders = true; @@ -32,6 +32,8 @@ namespace MediaBrowser.Model.LiveTv public bool ImportFavoritesOnly { get; set; } public bool IsEnabled { get; set; } + public int DataVersion { get; set; } + public TunerHostInfo() { IsEnabled = true; @@ -47,5 +49,15 @@ namespace MediaBrowser.Model.LiveTv public string ListingsId { get; set; } public string ZipCode { get; set; } public string Country { get; set; } + public string Path { get; set; } + + public string[] EnabledTuners { get; set; } + public bool EnableAllTuners { get; set; } + + public ListingsProviderInfo() + { + EnabledTuners = new string[] { }; + EnableAllTuners = true; + } } } \ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs index fcb19427b..9af96df43 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvTunerInfoDto.cs @@ -64,6 +64,12 @@ namespace MediaBrowser.Model.LiveTv /// The clients. public List Clients { get; set; } + /// + /// Gets or sets a value indicating whether this instance can reset. + /// + /// true if this instance can reset; otherwise, false. + public bool CanReset { get; set; } + public LiveTvTunerInfoDto() { Clients = new List(); diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index a5191192c..fda240249 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -11,7 +11,6 @@ MediaBrowser.Model 512 ..\ - ..\packages\Fody.1.19.1.0 v4.5 @@ -442,30 +441,30 @@ - - - - ..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll - False - - + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + +